joerowell commited on
Commit
e73ae89
·
verified ·
1 Parent(s): a7228e8

Add files using upload-large-folder tool

Browse files
README.md ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ library_name: vllm
3
+ pipeline_tag: text-generation
4
+ tags:
5
+ - laguna
6
+ - vllm
7
+ - fp8
8
+ - moe
9
+ ---
10
+
11
+ # Laguna-M.1 (FP8 + FP8 KV-cache)
12
+
13
+ Poolside Laguna-M, FP8-quantized weights (block scheme, 128×128) with FP8 KV-cache scales.
14
+ Packaged as a self-contained HuggingFace repo so partners can `hf download` once and
15
+ serve via the bundled vLLM overlay image without additional build steps.
16
+
17
+ ## Contents
18
+
19
+ | Path | Size | What it is |
20
+ |-----------------------------------------------|--------|------------------------------------------------------------|
21
+ | `config.json`, `*.safetensors`, `tokenizer.*` | 214 GB | Laguna-M FP8 weights + FP8 KV-cache scales + tokenizer |
22
+ | `image/laguna-vllm-v0.19.0-overlay.tar.gz` | 8.9 GB | vLLM v0.19.0 Docker image with Laguna support overlaid |
23
+
24
+ ## Hardware
25
+
26
+ Built and tested on **NVIDIA H200 (141 GB HBM3e)**. Recommended serving config: **4× H200, TP=4**.
27
+ Other GPU generations (H100, B200) may work but are untested.
28
+
29
+ ## Quickstart
30
+
31
+ ```bash
32
+ # 1. Download.
33
+ hf download poolside/Laguna-M.1 --local-dir laguna-m
34
+
35
+ # 2. Load the vLLM image.
36
+ gunzip -c laguna-m/image/laguna-vllm-v0.19.0-overlay.tar.gz | docker load
37
+ # -> laguna-vllm:v0.19.0-overlay
38
+
39
+ # 3. Serve on 4× H200.
40
+ docker run --gpus '"device=0,1,2,3"' --network host \
41
+ -v "$PWD/laguna-m":/model \
42
+ laguna-vllm:v0.19.0-overlay \
43
+ /model \
44
+ --tensor-parallel-size 4 \
45
+ --trust-remote-code \
46
+ --dtype bfloat16 \
47
+ --kv-cache-dtype fp8 \
48
+ --max-model-len 4096 \
49
+ --gpu-memory-utilization 0.85 \
50
+ --served-model-name laguna \
51
+ --reasoning-parser poolside_v1 \
52
+ --tool-call-parser poolside_v1 \
53
+ --enable-auto-tool-choice \
54
+ --port 8000
55
+ ```
56
+
57
+ Smoke test once the server logs `Application startup complete`:
58
+
59
+ ```bash
60
+ curl -s http://127.0.0.1:8000/v1/completions \
61
+ -H 'Content-Type: application/json' \
62
+ -d '{"model":"laguna","prompt":"The capital of France is","max_tokens":8,"temperature":0}'
63
+ ```
64
+
65
+ ## Eval (5-shot)
66
+
67
+ - MMLU: 79.06% ± 0.33% *(all 14k questions, TP=4 H200)*
68
+ - GSM8K: 92% strict-match *(n=50 smoke, TP=4 H200; full run to come)*
69
+
70
+ ## Architecture
71
+
72
+ Laguna-M is a 70-layer MoE transformer:
73
+
74
+ - hidden=4096, Q-heads=64, KV-heads=8, head_dim=128 (Q-projection is 2× hidden → 8192)
75
+ - First 3 layers dense SwiGLU; remaining 67 are sparse MoE (256 experts, top-k=16, sigmoid router, shared expert)
76
+ - Per-element attention output gating (softplus)
77
+ - RoPE θ=500000, YaRN factor=32, original ctx 4096
78
+ - Auxiliary-loss-free load balancing via `e_score_correction_bias`
79
+
80
+ Weights use compressed-tensors FP8 block format (128×128 blocks, dynamic activation quant).
81
+ FP8 KV-cache scales are included under `model.layers.*.self_attn.{k,v}_scale`.
image/laguna-vllm-v0.19.0-overlay.tar.gz ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3a3dd628330235925fa2d9bb07c7438c41236ffc6f18e6e079f1bd0a4e019814
3
+ size 9548969617
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,671 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ Laguna model implementation for transformers 4.56-4.x (used by vLLM).
17
+
18
+ This avoids v5-only APIs (use_kernel_forward_from_hub, create_causal_mask,
19
+ dynamic_rope_update, auto_docstring, can_return_tuple, etc.) while keeping
20
+ the architecture identical to the v5 version.
21
+ """
22
+
23
+ from typing import Optional
24
+
25
+ import torch
26
+ import torch.nn.functional as F
27
+ from torch import nn
28
+ from transformers.generation import GenerationMixin
29
+ from transformers.activations import ACT2FN
30
+ from transformers.cache_utils import Cache, DynamicCache
31
+ from transformers.utils.generic import OutputRecorder, check_model_inputs
32
+ from transformers.modeling_utils import ALL_ATTENTION_FUNCTIONS, PreTrainedModel
33
+ from transformers.modeling_outputs import MoeModelOutputWithPast, MoeCausalLMOutputWithPast
34
+ from transformers.modeling_rope_utils import ROPE_INIT_FUNCTIONS
35
+
36
+ from .configuration_laguna import LagunaConfig
37
+
38
+
39
+ class LagunaRMSNorm(nn.Module):
40
+ def __init__(self, hidden_size, eps=1e-6):
41
+ """
42
+ LagunaRMSNorm is equivalent to T5LayerNorm
43
+ """
44
+ super().__init__()
45
+ self.weight = nn.Parameter(torch.ones(hidden_size))
46
+ self.variance_epsilon = eps
47
+
48
+ def forward(self, hidden_states):
49
+ input_dtype = hidden_states.dtype
50
+ hidden_states = hidden_states.to(torch.float32)
51
+ variance = hidden_states.pow(2).mean(-1, keepdim=True)
52
+ hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon)
53
+ return self.weight * hidden_states.to(input_dtype)
54
+
55
+ def extra_repr(self):
56
+ return f"{tuple(self.weight.shape)}, eps={self.variance_epsilon}"
57
+
58
+
59
+ class LagunaRotaryEmbedding(nn.Module):
60
+ inv_freq: torch.Tensor # fix linting for `register_buffer`
61
+
62
+ def __init__(self, config: LagunaConfig, device=None):
63
+ super().__init__()
64
+ self.max_seq_len_cached = config.max_position_embeddings
65
+ self.original_max_seq_len = config.max_position_embeddings
66
+
67
+ self.config = config
68
+
69
+ # v4 uses rope_theta + rope_scaling (top-level config fields)
70
+ rope_type = "default"
71
+ if config.rope_scaling is not None:
72
+ rope_type = config.rope_scaling.get("rope_type", config.rope_scaling.get("type", "default"))
73
+
74
+ self.rope_type = rope_type
75
+ if self.rope_type == "default":
76
+ inv_freq, self.attention_scaling = self._compute_default_rope_parameters(config, device)
77
+ else:
78
+ rope_init_fn = ROPE_INIT_FUNCTIONS[self.rope_type]
79
+ inv_freq, self.attention_scaling = rope_init_fn(config, device)
80
+
81
+ self.register_buffer("inv_freq", inv_freq, persistent=False)
82
+ self.register_buffer("original_inv_freq", inv_freq.clone(), persistent=False)
83
+
84
+ @staticmethod
85
+ def _compute_default_rope_parameters(
86
+ config: LagunaConfig,
87
+ device: Optional["torch.device"] = None,
88
+ ) -> tuple["torch.Tensor", float]:
89
+ base = config.rope_theta
90
+ dim = getattr(config, "head_dim", None) or config.hidden_size // config.num_attention_heads
91
+ attention_factor = 1.0
92
+ inv_freq = 1.0 / (
93
+ base ** (torch.arange(0, dim, 2, dtype=torch.int64).to(device=device, dtype=torch.float) / dim)
94
+ )
95
+ return inv_freq, attention_factor
96
+
97
+ @torch.no_grad()
98
+ def forward(self, x, position_ids):
99
+ inv_freq_expanded = self.inv_freq[None, :, None].float().expand(position_ids.shape[0], -1, 1).to(x.device)
100
+ position_ids_expanded = position_ids[:, None, :].float()
101
+
102
+ device_type = x.device.type if isinstance(x.device.type, str) and x.device.type != "mps" else "cpu"
103
+ with torch.autocast(device_type=device_type, enabled=False): # Force float32
104
+ freqs = (inv_freq_expanded.float() @ position_ids_expanded.float()).transpose(1, 2)
105
+ emb = torch.cat((freqs, freqs), dim=-1)
106
+ cos = emb.cos() * self.attention_scaling
107
+ sin = emb.sin() * self.attention_scaling
108
+
109
+ return cos.to(dtype=x.dtype), sin.to(dtype=x.dtype)
110
+
111
+
112
+ class LagunaMLP(nn.Module):
113
+ def __init__(self, config, intermediate_size=None):
114
+ super().__init__()
115
+ self.config = config
116
+ self.hidden_size = config.hidden_size
117
+ self.intermediate_size = config.intermediate_size if intermediate_size is None else intermediate_size
118
+ self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
119
+ self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
120
+ self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=False)
121
+ self.act_fn = ACT2FN[config.hidden_act]
122
+
123
+ def forward(self, x):
124
+ down_proj = self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x))
125
+ return down_proj
126
+
127
+
128
+ class LagunaTopKRouter(nn.Module):
129
+ """Laguna MoE router using sigmoid scoring (not softmax)."""
130
+
131
+ def __init__(self, config):
132
+ super().__init__()
133
+ self.top_k = config.num_experts_per_tok
134
+ self.num_experts = config.num_experts
135
+ self.norm_topk_prob = config.norm_topk_prob
136
+ self.hidden_dim = config.hidden_size
137
+ self.weight = nn.Parameter(torch.zeros(self.num_experts, self.hidden_dim))
138
+
139
+ def forward(self, hidden_states: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
140
+ hidden_states = hidden_states.reshape(-1, self.hidden_dim)
141
+ router_logits = F.linear(hidden_states, self.weight)
142
+ # Laguna-specific: sigmoid routing in float32 for precision
143
+ routing_weights = torch.sigmoid(router_logits.float())
144
+ routing_weights, selected_experts = torch.topk(routing_weights, self.top_k, dim=-1)
145
+ if self.norm_topk_prob:
146
+ routing_weights = routing_weights / routing_weights.sum(dim=-1, keepdim=True)
147
+ routing_weights = routing_weights.to(hidden_states.dtype)
148
+ return router_logits, routing_weights, selected_experts
149
+
150
+
151
+ class LagunaSparseMoeBlock(nn.Module):
152
+ """Laguna MoE block using sigmoid router, per-expert MLPs, and a shared expert."""
153
+
154
+ def __init__(self, config):
155
+ super().__init__()
156
+ self.num_experts = config.num_experts
157
+ self.top_k = config.num_experts_per_tok
158
+ self.gate = LagunaTopKRouter(config)
159
+ self.experts = nn.ModuleList(
160
+ [LagunaMLP(config, intermediate_size=config.moe_intermediate_size) for _ in range(self.num_experts)]
161
+ )
162
+ self.shared_expert = LagunaMLP(config, intermediate_size=config.shared_expert_intermediate_size)
163
+ self.shared_expert_gate = (
164
+ nn.Linear(config.hidden_size, 1, bias=False) if getattr(config, "moe_shared_gate", False) else None
165
+ )
166
+
167
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
168
+ batch_size, sequence_length, hidden_dim = hidden_states.shape
169
+ hidden_states = hidden_states.view(-1, hidden_dim)
170
+
171
+ shared_expert_output = self.shared_expert(hidden_states)
172
+ if self.shared_expert_gate is not None:
173
+ shared_expert_output = shared_expert_output * torch.sigmoid(self.shared_expert_gate(hidden_states))
174
+
175
+ # Routed experts
176
+ _, routing_weights, selected_experts = self.gate(hidden_states)
177
+ final_hidden_states = torch.zeros_like(hidden_states)
178
+
179
+ expert_mask = F.one_hot(selected_experts, num_classes=self.num_experts)
180
+ expert_mask = expert_mask.permute(2, 1, 0)
181
+
182
+ for expert_idx in range(self.num_experts):
183
+ top_k_pos, token_idx = torch.where(expert_mask[expert_idx])
184
+ if token_idx.shape[0] == 0:
185
+ continue
186
+ current_state = hidden_states[token_idx]
187
+ current_hidden_states = self.experts[expert_idx](current_state)
188
+ current_hidden_states = current_hidden_states * routing_weights[token_idx, top_k_pos, None]
189
+ final_hidden_states.index_add_(0, token_idx, current_hidden_states.to(final_hidden_states.dtype))
190
+
191
+ final_hidden_states = final_hidden_states + shared_expert_output
192
+ final_hidden_states = final_hidden_states.reshape(batch_size, sequence_length, hidden_dim)
193
+ return final_hidden_states
194
+
195
+
196
+ def rotate_half(x):
197
+ """Rotates half the hidden dims of the input."""
198
+ x1 = x[..., : x.shape[-1] // 2]
199
+ x2 = x[..., x.shape[-1] // 2 :]
200
+ return torch.cat((-x2, x1), dim=-1)
201
+
202
+
203
+ def apply_rotary_pos_emb(q, k, cos, sin, unsqueeze_dim=1):
204
+ """Applies Rotary Position Embedding to the query and key tensors."""
205
+ cos = cos.unsqueeze(unsqueeze_dim)
206
+ sin = sin.unsqueeze(unsqueeze_dim)
207
+ q_embed = (q * cos) + (rotate_half(q) * sin)
208
+ k_embed = (k * cos) + (rotate_half(k) * sin)
209
+ return q_embed, k_embed
210
+
211
+
212
+ def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor:
213
+ """
214
+ This is the equivalent of torch.repeat_interleave(x, dim=1, repeats=n_rep). The hidden states go from (batch,
215
+ num_key_value_heads, seqlen, head_dim) to (batch, num_attention_heads, seqlen, head_dim)
216
+ """
217
+ batch, num_key_value_heads, slen, head_dim = hidden_states.shape
218
+ if n_rep == 1:
219
+ return hidden_states
220
+ hidden_states = hidden_states[:, :, None, :, :].expand(batch, num_key_value_heads, n_rep, slen, head_dim)
221
+ return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)
222
+
223
+
224
+ def eager_attention_forward(
225
+ module: nn.Module,
226
+ query: torch.Tensor,
227
+ key: torch.Tensor,
228
+ value: torch.Tensor,
229
+ attention_mask: torch.Tensor | None,
230
+ scaling: float,
231
+ dropout: float = 0.0,
232
+ **kwargs,
233
+ ):
234
+ key_states = repeat_kv(key, module.num_key_value_groups)
235
+ value_states = repeat_kv(value, module.num_key_value_groups)
236
+
237
+ attn_weights = torch.matmul(query, key_states.transpose(2, 3)) * scaling
238
+ if attention_mask is not None:
239
+ causal_mask = attention_mask[:, :, :, : key_states.shape[-2]]
240
+ attn_weights = attn_weights + causal_mask
241
+
242
+ attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(query.dtype)
243
+ attn_weights = nn.functional.dropout(attn_weights, p=dropout, training=module.training)
244
+ attn_output = torch.matmul(attn_weights, value_states)
245
+ attn_output = attn_output.transpose(1, 2).contiguous()
246
+
247
+ return attn_output, attn_weights
248
+
249
+
250
+ class LagunaAttention(nn.Module):
251
+ """Laguna attention with QK normalization and output gating."""
252
+
253
+ def __init__(self, config: LagunaConfig, layer_idx: int):
254
+ super().__init__()
255
+ self.config = config
256
+ self.layer_idx = layer_idx
257
+ self.head_dim = config.head_dim
258
+ self.num_key_value_groups = config.num_attention_heads // config.num_key_value_heads
259
+ self.scaling = self.head_dim**-0.5
260
+ self.attention_dropout = config.attention_dropout
261
+ self.is_causal = True
262
+
263
+ # Laguna: no QKV bias, explicit head_dim
264
+ self.q_proj = nn.Linear(config.hidden_size, config.num_attention_heads * config.head_dim, bias=False)
265
+ self.k_proj = nn.Linear(config.hidden_size, config.num_key_value_heads * config.head_dim, bias=False)
266
+ self.v_proj = nn.Linear(config.hidden_size, config.num_key_value_heads * config.head_dim, bias=False)
267
+ self.o_proj = nn.Linear(config.num_attention_heads * config.head_dim, config.hidden_size, bias=False)
268
+ # Laguna-specific: gating projection
269
+ self.g_proj = nn.Linear(config.hidden_size, config.num_attention_heads * config.head_dim, bias=False)
270
+ # QK normalization (RMSNorm applied per-head after reshape, before RoPE)
271
+ self.q_norm = LagunaRMSNorm(config.head_dim, eps=config.rms_norm_eps)
272
+ self.k_norm = LagunaRMSNorm(config.head_dim, eps=config.rms_norm_eps)
273
+
274
+ def forward(
275
+ self,
276
+ hidden_states: torch.Tensor,
277
+ position_embeddings: tuple[torch.Tensor, torch.Tensor],
278
+ attention_mask: torch.Tensor | None,
279
+ past_key_values: Cache | None = None,
280
+ cache_position: torch.LongTensor | None = None,
281
+ **kwargs,
282
+ ) -> tuple[torch.Tensor, torch.Tensor | None]:
283
+ input_shape = hidden_states.shape[:-1]
284
+ hidden_shape = (*input_shape, -1, self.head_dim)
285
+
286
+ query_states = self.q_proj(hidden_states)
287
+ key_states = self.k_proj(hidden_states)
288
+ value_states = self.v_proj(hidden_states)
289
+
290
+ query_states = query_states.view(hidden_shape).transpose(1, 2)
291
+ key_states = key_states.view(hidden_shape).transpose(1, 2)
292
+ value_states = value_states.view(hidden_shape).transpose(1, 2)
293
+
294
+ # QK normalization (applied per-head before RoPE)
295
+ query_states = self.q_norm(query_states)
296
+ key_states = self.k_norm(key_states)
297
+
298
+ cos, sin = position_embeddings
299
+ query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin)
300
+
301
+ if past_key_values is not None:
302
+ cache_kwargs = {"sin": sin, "cos": cos, "cache_position": cache_position}
303
+ key_states, value_states = past_key_values.update(key_states, value_states, self.layer_idx, cache_kwargs)
304
+
305
+ attention_interface = eager_attention_forward
306
+ if self.config._attn_implementation != "eager":
307
+ attention_interface = ALL_ATTENTION_FUNCTIONS[self.config._attn_implementation]
308
+
309
+ attn_output, attn_weights = attention_interface(
310
+ self,
311
+ query_states,
312
+ key_states,
313
+ value_states,
314
+ attention_mask,
315
+ dropout=0.0 if not self.training else self.attention_dropout,
316
+ scaling=self.scaling,
317
+ **kwargs,
318
+ )
319
+
320
+ attn_output = attn_output.reshape(*input_shape, -1).contiguous()
321
+
322
+ # Laguna-specific: apply gating BEFORE o_proj
323
+ gate = F.softplus(self.g_proj(hidden_states).float()).to(attn_output.dtype)
324
+ attn_output = attn_output * gate
325
+
326
+ attn_output = self.o_proj(attn_output)
327
+
328
+ return attn_output, attn_weights
329
+
330
+
331
+ class LagunaDecoderLayer(nn.Module):
332
+ """Laguna decoder layer with gated attention and sigmoid-routed MoE."""
333
+
334
+ def __init__(self, config: LagunaConfig, layer_idx: int):
335
+ super().__init__()
336
+ self.self_attn = LagunaAttention(config, layer_idx)
337
+ # Use MoE or dense MLP based on layer configuration
338
+ if (layer_idx not in config.mlp_only_layers) and (
339
+ config.num_experts > 0 and (layer_idx + 1) % config.decoder_sparse_step == 0
340
+ ):
341
+ self.mlp = LagunaSparseMoeBlock(config)
342
+ else:
343
+ self.mlp = LagunaMLP(config, intermediate_size=config.intermediate_size)
344
+ self.input_layernorm = LagunaRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
345
+ self.post_attention_layernorm = LagunaRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
346
+ self.hidden_size = config.hidden_size
347
+
348
+ def forward(
349
+ self,
350
+ hidden_states: torch.Tensor,
351
+ attention_mask: torch.Tensor | None = None,
352
+ position_ids: torch.LongTensor | None = None,
353
+ past_key_values: Cache | None = None,
354
+ use_cache: bool | None = False,
355
+ cache_position: torch.LongTensor | None = None,
356
+ position_embeddings: tuple[torch.Tensor, torch.Tensor] | None = None,
357
+ **kwargs,
358
+ ) -> torch.Tensor:
359
+ residual = hidden_states
360
+ hidden_states = self.input_layernorm(hidden_states)
361
+ # Self Attention
362
+ hidden_states, _ = self.self_attn(
363
+ hidden_states=hidden_states,
364
+ attention_mask=attention_mask,
365
+ position_ids=position_ids,
366
+ past_key_values=past_key_values,
367
+ use_cache=use_cache,
368
+ cache_position=cache_position,
369
+ position_embeddings=position_embeddings,
370
+ **kwargs,
371
+ )
372
+ hidden_states = residual + hidden_states
373
+
374
+ # Fully Connected
375
+ residual = hidden_states
376
+ hidden_states = self.post_attention_layernorm(hidden_states)
377
+ hidden_states = self.mlp(hidden_states)
378
+ hidden_states = residual + hidden_states
379
+ return hidden_states
380
+
381
+
382
+ class LagunaPreTrainedModel(PreTrainedModel):
383
+ config_class = LagunaConfig
384
+ base_model_prefix = "model"
385
+ supports_gradient_checkpointing = True
386
+ _no_split_modules = ["LagunaDecoderLayer"]
387
+ _skip_keys_device_placement = ["past_key_values"]
388
+ _supports_flash_attn_2 = True
389
+ _supports_sdpa = True
390
+ _supports_cache_class = True
391
+ _can_record_outputs = {
392
+ "router_logits": OutputRecorder(LagunaTopKRouter, index=0),
393
+ "hidden_states": LagunaDecoderLayer,
394
+ "attentions": LagunaAttention,
395
+ }
396
+
397
+ def _init_weights(self, module):
398
+ std = self.config.initializer_range
399
+ if isinstance(module, nn.Linear):
400
+ module.weight.data.normal_(mean=0.0, std=std)
401
+ if module.bias is not None:
402
+ module.bias.data.zero_()
403
+ elif isinstance(module, nn.Embedding):
404
+ module.weight.data.normal_(mean=0.0, std=std)
405
+ if module.padding_idx is not None:
406
+ module.weight.data[module.padding_idx].zero_()
407
+ elif isinstance(module, LagunaTopKRouter):
408
+ module.weight.data.normal_(mean=0.0, std=std)
409
+
410
+
411
+ def _prepare_4d_causal_attention_mask_with_cache_position(
412
+ attention_mask: torch.Tensor,
413
+ sequence_length: int,
414
+ target_length: int,
415
+ dtype: torch.dtype,
416
+ device: torch.device,
417
+ cache_position: torch.Tensor,
418
+ batch_size: int,
419
+ ):
420
+ """Create 4D causal mask from 2D attention mask, compatible with transformers 4.x."""
421
+ if attention_mask is not None and attention_mask.dim() == 4:
422
+ # Already a 4D mask
423
+ causal_mask = attention_mask
424
+ else:
425
+ min_dtype = torch.finfo(dtype).min
426
+ causal_mask = torch.full((sequence_length, target_length), fill_value=min_dtype, dtype=dtype, device=device)
427
+ if sequence_length != 1:
428
+ causal_mask = torch.triu(causal_mask, diagonal=1)
429
+ causal_mask *= torch.arange(target_length, device=device) > cache_position.reshape(-1, 1)
430
+ causal_mask = causal_mask[None, None, :, :].expand(batch_size, 1, -1, -1)
431
+ if attention_mask is not None:
432
+ causal_mask = causal_mask.clone()
433
+ mask_length = attention_mask.shape[-1]
434
+ padding_mask = causal_mask[:, :, :, :mask_length] + attention_mask[:, None, None, :]
435
+ padding_mask = padding_mask == 0
436
+ causal_mask[:, :, :, :mask_length] = causal_mask[:, :, :, :mask_length].masked_fill(padding_mask, min_dtype)
437
+
438
+ return causal_mask
439
+
440
+
441
+ class LagunaModel(LagunaPreTrainedModel):
442
+ def __init__(self, config: LagunaConfig):
443
+ super().__init__(config)
444
+ self.padding_idx = config.pad_token_id
445
+ self.vocab_size = config.vocab_size
446
+
447
+ self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size, self.padding_idx)
448
+ self.layers = nn.ModuleList(
449
+ [LagunaDecoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]
450
+ )
451
+ self.norm = LagunaRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
452
+ self.rotary_emb = LagunaRotaryEmbedding(config=config)
453
+ self.gradient_checkpointing = False
454
+
455
+ # Initialize weights and apply final processing
456
+ self.post_init()
457
+
458
+ @check_model_inputs
459
+ def forward(
460
+ self,
461
+ input_ids: torch.LongTensor | None = None,
462
+ attention_mask: torch.Tensor | None = None,
463
+ position_ids: torch.LongTensor | None = None,
464
+ past_key_values: Cache | None = None,
465
+ inputs_embeds: torch.FloatTensor | None = None,
466
+ use_cache: bool | None = None,
467
+ cache_position: torch.LongTensor | None = None,
468
+ **kwargs,
469
+ ):
470
+ if (input_ids is None) ^ (inputs_embeds is not None):
471
+ raise ValueError("You must specify exactly one of input_ids or inputs_embeds")
472
+
473
+ if use_cache and past_key_values is None:
474
+ past_key_values = DynamicCache()
475
+
476
+ if inputs_embeds is None:
477
+ inputs_embeds = self.embed_tokens(input_ids)
478
+
479
+ if cache_position is None:
480
+ past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0
481
+ cache_position = torch.arange(
482
+ past_seen_tokens, past_seen_tokens + inputs_embeds.shape[1], device=inputs_embeds.device
483
+ )
484
+
485
+ if position_ids is None:
486
+ position_ids = cache_position.unsqueeze(0)
487
+
488
+ causal_mask = _prepare_4d_causal_attention_mask_with_cache_position(
489
+ attention_mask,
490
+ sequence_length=inputs_embeds.shape[1],
491
+ target_length=cache_position[-1].item() + 1 if cache_position is not None else inputs_embeds.shape[1],
492
+ dtype=inputs_embeds.dtype,
493
+ device=inputs_embeds.device,
494
+ cache_position=cache_position,
495
+ batch_size=inputs_embeds.shape[0],
496
+ )
497
+
498
+ hidden_states = inputs_embeds
499
+ position_embeddings = self.rotary_emb(hidden_states, position_ids)
500
+
501
+ for decoder_layer in self.layers[: self.config.num_hidden_layers]:
502
+ if self.gradient_checkpointing and self.training:
503
+ hidden_states = self._gradient_checkpointing_func(
504
+ decoder_layer.__call__,
505
+ hidden_states,
506
+ causal_mask,
507
+ position_ids,
508
+ past_key_values,
509
+ use_cache,
510
+ cache_position,
511
+ position_embeddings,
512
+ )
513
+ else:
514
+ hidden_states = decoder_layer(
515
+ hidden_states,
516
+ attention_mask=causal_mask,
517
+ position_ids=position_ids,
518
+ past_key_values=past_key_values,
519
+ use_cache=use_cache,
520
+ cache_position=cache_position,
521
+ position_embeddings=position_embeddings,
522
+ **kwargs,
523
+ )
524
+
525
+ hidden_states = self.norm(hidden_states)
526
+
527
+ return MoeModelOutputWithPast(
528
+ last_hidden_state=hidden_states,
529
+ past_key_values=past_key_values,
530
+ )
531
+
532
+
533
+ def load_balancing_loss_func(
534
+ gate_logits: torch.Tensor | tuple[torch.Tensor] | None,
535
+ num_experts: int | None = None,
536
+ top_k=2,
537
+ attention_mask: torch.Tensor | None = None,
538
+ ) -> torch.Tensor | int:
539
+ r"""
540
+ Computes auxiliary load balancing loss as in Switch Transformer.
541
+
542
+ See Switch Transformer (https://huggingface.co/papers/2101.03961) for more details.
543
+ """
544
+ if gate_logits is None or not isinstance(gate_logits, tuple):
545
+ return 0
546
+
547
+ if isinstance(gate_logits, tuple):
548
+ compute_device = gate_logits[0].device
549
+ concatenated_gate_logits = torch.cat([layer_gate.to(compute_device) for layer_gate in gate_logits], dim=0)
550
+
551
+ routing_weights = torch.nn.functional.softmax(concatenated_gate_logits, dim=-1)
552
+
553
+ _, selected_experts = torch.topk(routing_weights, top_k, dim=-1)
554
+
555
+ expert_mask = torch.nn.functional.one_hot(selected_experts, num_experts)
556
+
557
+ if attention_mask is None:
558
+ tokens_per_expert = torch.mean(expert_mask.float(), dim=0)
559
+ router_prob_per_expert = torch.mean(routing_weights, dim=0)
560
+ else:
561
+ batch_size, sequence_length = attention_mask.shape
562
+ num_hidden_layers = concatenated_gate_logits.shape[0] // (batch_size * sequence_length)
563
+
564
+ expert_attention_mask = (
565
+ attention_mask[None, :, :, None, None]
566
+ .expand((num_hidden_layers, batch_size, sequence_length, top_k, num_experts))
567
+ .reshape(-1, top_k, num_experts)
568
+ .to(compute_device)
569
+ )
570
+
571
+ tokens_per_expert = torch.sum(expert_mask.float() * expert_attention_mask, dim=0) / torch.sum(
572
+ expert_attention_mask, dim=0
573
+ )
574
+
575
+ router_per_expert_attention_mask = (
576
+ attention_mask[None, :, :, None]
577
+ .expand((num_hidden_layers, batch_size, sequence_length, num_experts))
578
+ .reshape(-1, num_experts)
579
+ .to(compute_device)
580
+ )
581
+
582
+ router_prob_per_expert = torch.sum(routing_weights * router_per_expert_attention_mask, dim=0) / torch.sum(
583
+ router_per_expert_attention_mask, dim=0
584
+ )
585
+
586
+ overall_loss = torch.sum(tokens_per_expert * router_prob_per_expert.unsqueeze(0))
587
+ return overall_loss * num_experts
588
+
589
+
590
+ class LagunaForCausalLM(LagunaPreTrainedModel, GenerationMixin):
591
+ _tied_weights_keys = {"lm_head.weight": "model.embed_tokens.weight"}
592
+ _tp_plan = {"lm_head": "colwise_rep"}
593
+
594
+ def __init__(self, config):
595
+ super().__init__(config)
596
+ self.model = LagunaModel(config)
597
+ self.vocab_size = config.vocab_size
598
+ self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
599
+ self.router_aux_loss_coef = config.router_aux_loss_coef
600
+ self.num_experts = config.num_experts
601
+ self.num_experts_per_tok = config.num_experts_per_tok
602
+
603
+ # Initialize weights and apply final processing
604
+ self.post_init()
605
+
606
+ def forward(
607
+ self,
608
+ input_ids: torch.LongTensor | None = None,
609
+ attention_mask: torch.Tensor | None = None,
610
+ position_ids: torch.LongTensor | None = None,
611
+ past_key_values: Cache | None = None,
612
+ inputs_embeds: torch.FloatTensor | None = None,
613
+ labels: torch.LongTensor | None = None,
614
+ use_cache: bool | None = None,
615
+ output_router_logits: bool | None = None,
616
+ cache_position: torch.LongTensor | None = None,
617
+ logits_to_keep: int | torch.Tensor = 0,
618
+ **kwargs,
619
+ ) -> MoeCausalLMOutputWithPast:
620
+ r"""
621
+ Labels for computing the masked language modeling loss. Indices should either be in `[0, ...,
622
+ config.vocab_size]` or -100. Tokens with indices set to `-100` are ignored (masked), the loss is
623
+ only computed for the tokens with labels in `[0, ..., config.vocab_size]`.
624
+ """
625
+ output_router_logits = (
626
+ output_router_logits if output_router_logits is not None else self.config.output_router_logits
627
+ )
628
+
629
+ outputs: MoeModelOutputWithPast = self.model(
630
+ input_ids=input_ids,
631
+ attention_mask=attention_mask,
632
+ position_ids=position_ids,
633
+ past_key_values=past_key_values,
634
+ inputs_embeds=inputs_embeds,
635
+ use_cache=use_cache,
636
+ output_router_logits=output_router_logits,
637
+ cache_position=cache_position,
638
+ **kwargs,
639
+ )
640
+
641
+ hidden_states = outputs.last_hidden_state
642
+ slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
643
+ logits = self.lm_head(hidden_states[:, slice_indices, :])
644
+
645
+ loss = None
646
+ if labels is not None:
647
+ loss = self.loss_function(logits, labels, self.vocab_size, **kwargs)
648
+
649
+ aux_loss = None
650
+ if output_router_logits:
651
+ aux_loss = load_balancing_loss_func(
652
+ outputs.router_logits,
653
+ self.num_experts,
654
+ self.num_experts_per_tok,
655
+ attention_mask,
656
+ )
657
+ if labels is not None and isinstance(aux_loss, torch.Tensor):
658
+ loss += self.router_aux_loss_coef * aux_loss.to(loss.device)
659
+
660
+ return MoeCausalLMOutputWithPast(
661
+ loss=loss,
662
+ aux_loss=aux_loss,
663
+ logits=logits,
664
+ past_key_values=outputs.past_key_values,
665
+ hidden_states=outputs.hidden_states,
666
+ attentions=outputs.attentions,
667
+ router_logits=outputs.router_logits,
668
+ )
669
+
670
+
671
+ __all__ = ["LagunaForCausalLM", "LagunaModel", "LagunaPreTrainedModel"]
serve.sh ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ # Minimal serve command for Laguna-M.1 on 4× H200.
3
+ # Expects the bundled image to be loaded (`docker load < image/...tar.gz`)
4
+ # and this script run from the downloaded repo root.
5
+ set -euo pipefail
6
+
7
+ MODEL_DIR="${MODEL_DIR:-$(cd "$(dirname "$0")" && pwd)}"
8
+ PORT="${PORT:-8000}"
9
+ GPUS="${GPUS:-0,1,2,3}"
10
+
11
+ docker run --rm --gpus "\"device=${GPUS}\"" --network host \
12
+ -v "${MODEL_DIR}":/model \
13
+ laguna-vllm:v0.19.0-overlay \
14
+ /model \
15
+ --tensor-parallel-size 4 \
16
+ --trust-remote-code \
17
+ --dtype bfloat16 \
18
+ --kv-cache-dtype fp8 \
19
+ --max-model-len 4096 \
20
+ --gpu-memory-utilization 0.85 \
21
+ --served-model-name laguna \
22
+ --reasoning-parser poolside_v1 \
23
+ --tool-call-parser poolside_v1 \
24
+ --enable-auto-tool-choice \
25
+ --default-chat-template-kwargs '{"enable_thinking": true}' \
26
+ --port "${PORT}"
special_tokens_map.json ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "bos_token": "〈|EOS|〉",
3
+ "cls_token": "〈|CLS|〉",
4
+ "eos_token": "〈|EOS|〉",
5
+ "mask_token": "〈|MASK|〉",
6
+ "pad_token": "〈|PAD|〉",
7
+ "sep_token": "〈|SEP|〉",
8
+ "unk_token": "〈|UNK|〉"
9
+ }
tokenizer.json ADDED
The diff for this file is too large to render. See raw diff
 
tokenizer_config.json ADDED
@@ -0,0 +1,576 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "added_tokens_decoder": {
3
+ "0": {
4
+ "content": "〈|UNK|〉",
5
+ "lstrip": false,
6
+ "normalized": false,
7
+ "rstrip": false,
8
+ "single_word": false,
9
+ "special": true
10
+ },
11
+ "1": {
12
+ "content": "〈|CODE_START|〉",
13
+ "lstrip": false,
14
+ "normalized": false,
15
+ "rstrip": false,
16
+ "single_word": false,
17
+ "special": true
18
+ },
19
+ "2": {
20
+ "content": "〈|EOS|〉",
21
+ "lstrip": false,
22
+ "normalized": false,
23
+ "rstrip": false,
24
+ "single_word": false,
25
+ "special": true
26
+ },
27
+ "3": {
28
+ "content": "〈|CODE_END|〉",
29
+ "lstrip": false,
30
+ "normalized": false,
31
+ "rstrip": false,
32
+ "single_word": false,
33
+ "special": true
34
+ },
35
+ "4": {
36
+ "content": "〈|META_START|〉",
37
+ "lstrip": false,
38
+ "normalized": false,
39
+ "rstrip": false,
40
+ "single_word": false,
41
+ "special": true
42
+ },
43
+ "5": {
44
+ "content": "〈|META_END|〉",
45
+ "lstrip": false,
46
+ "normalized": false,
47
+ "rstrip": false,
48
+ "single_word": false,
49
+ "special": true
50
+ },
51
+ "6": {
52
+ "content": "〈|FIM_MIDDLE|〉",
53
+ "lstrip": false,
54
+ "normalized": false,
55
+ "rstrip": false,
56
+ "single_word": false,
57
+ "special": true
58
+ },
59
+ "7": {
60
+ "content": "〈|FIM_SUFFIX|〉",
61
+ "lstrip": false,
62
+ "normalized": false,
63
+ "rstrip": false,
64
+ "single_word": false,
65
+ "special": true
66
+ },
67
+ "8": {
68
+ "content": "〈|SEP|〉",
69
+ "lstrip": false,
70
+ "normalized": false,
71
+ "rstrip": false,
72
+ "single_word": false,
73
+ "special": true
74
+ },
75
+ "9": {
76
+ "content": "〈|PAD|〉",
77
+ "lstrip": false,
78
+ "normalized": false,
79
+ "rstrip": false,
80
+ "single_word": false,
81
+ "special": true
82
+ },
83
+ "10": {
84
+ "content": "〈|CLS|〉",
85
+ "lstrip": false,
86
+ "normalized": false,
87
+ "rstrip": false,
88
+ "single_word": false,
89
+ "special": true
90
+ },
91
+ "11": {
92
+ "content": "〈|FIM_START|〉",
93
+ "lstrip": false,
94
+ "normalized": false,
95
+ "rstrip": false,
96
+ "single_word": false,
97
+ "special": true
98
+ },
99
+ "12": {
100
+ "content": "〈|MASK|〉",
101
+ "lstrip": false,
102
+ "normalized": false,
103
+ "rstrip": false,
104
+ "single_word": false,
105
+ "special": true
106
+ },
107
+ "13": {
108
+ "content": "|◊|",
109
+ "lstrip": false,
110
+ "normalized": false,
111
+ "rstrip": false,
112
+ "single_word": false,
113
+ "special": true
114
+ },
115
+ "14": {
116
+ "content": "〈|",
117
+ "lstrip": false,
118
+ "normalized": false,
119
+ "rstrip": false,
120
+ "single_word": false,
121
+ "special": true
122
+ },
123
+ "15": {
124
+ "content": "|〉",
125
+ "lstrip": false,
126
+ "normalized": false,
127
+ "rstrip": false,
128
+ "single_word": false,
129
+ "special": true
130
+ },
131
+ "16": {
132
+ "content": "〈|/",
133
+ "lstrip": false,
134
+ "normalized": false,
135
+ "rstrip": false,
136
+ "single_word": false,
137
+ "special": true
138
+ },
139
+ "17": {
140
+ "content": "/|〉",
141
+ "lstrip": false,
142
+ "normalized": false,
143
+ "rstrip": false,
144
+ "single_word": false,
145
+ "special": true
146
+ },
147
+ "20": {
148
+ "content": "〈|SPECIAL_1|〉",
149
+ "lstrip": false,
150
+ "normalized": false,
151
+ "rstrip": false,
152
+ "single_word": false,
153
+ "special": true
154
+ },
155
+ "21": {
156
+ "content": "〈|SPECIAL_2|〉",
157
+ "lstrip": false,
158
+ "normalized": false,
159
+ "rstrip": false,
160
+ "single_word": false,
161
+ "special": true
162
+ },
163
+ "22": {
164
+ "content": "〈|SPECIAL_3|〉",
165
+ "lstrip": false,
166
+ "normalized": false,
167
+ "rstrip": false,
168
+ "single_word": false,
169
+ "special": true
170
+ },
171
+ "27": {
172
+ "content": "〈|SPECIAL_8|〉",
173
+ "lstrip": false,
174
+ "normalized": false,
175
+ "rstrip": false,
176
+ "single_word": false,
177
+ "special": true
178
+ },
179
+ "28": {
180
+ "content": "〈|SPECIAL_9|〉",
181
+ "lstrip": false,
182
+ "normalized": false,
183
+ "rstrip": false,
184
+ "single_word": false,
185
+ "special": true
186
+ },
187
+ "29": {
188
+ "content": "〈|SPECIAL_10|〉",
189
+ "lstrip": false,
190
+ "normalized": false,
191
+ "rstrip": false,
192
+ "single_word": false,
193
+ "special": true
194
+ },
195
+ "30": {
196
+ "content": "〈|SPECIAL_11|〉",
197
+ "lstrip": false,
198
+ "normalized": false,
199
+ "rstrip": false,
200
+ "single_word": false,
201
+ "special": true
202
+ },
203
+ "31": {
204
+ "content": "〈|SPECIAL_12|〉",
205
+ "lstrip": false,
206
+ "normalized": false,
207
+ "rstrip": false,
208
+ "single_word": false,
209
+ "special": true
210
+ },
211
+ "32": {
212
+ "content": "〈|SPECIAL_13|〉",
213
+ "lstrip": false,
214
+ "normalized": false,
215
+ "rstrip": false,
216
+ "single_word": false,
217
+ "special": true
218
+ },
219
+ "33": {
220
+ "content": "〈|SPECIAL_14|〉",
221
+ "lstrip": false,
222
+ "normalized": false,
223
+ "rstrip": false,
224
+ "single_word": false,
225
+ "special": true
226
+ },
227
+ "34": {
228
+ "content": "〈|SPECIAL_15|〉",
229
+ "lstrip": false,
230
+ "normalized": false,
231
+ "rstrip": false,
232
+ "single_word": false,
233
+ "special": true
234
+ },
235
+ "35": {
236
+ "content": "〈|SPECIAL_16|〉",
237
+ "lstrip": false,
238
+ "normalized": false,
239
+ "rstrip": false,
240
+ "single_word": false,
241
+ "special": true
242
+ },
243
+ "36": {
244
+ "content": "〈|SPECIAL_17|〉",
245
+ "lstrip": false,
246
+ "normalized": false,
247
+ "rstrip": false,
248
+ "single_word": false,
249
+ "special": true
250
+ },
251
+ "37": {
252
+ "content": "〈|SPECIAL_18|〉",
253
+ "lstrip": false,
254
+ "normalized": false,
255
+ "rstrip": false,
256
+ "single_word": false,
257
+ "special": true
258
+ },
259
+ "38": {
260
+ "content": "〈|SPECIAL_19|〉",
261
+ "lstrip": false,
262
+ "normalized": false,
263
+ "rstrip": false,
264
+ "single_word": false,
265
+ "special": true
266
+ },
267
+ "39": {
268
+ "content": "〈|SPECIAL_20|〉",
269
+ "lstrip": false,
270
+ "normalized": false,
271
+ "rstrip": false,
272
+ "single_word": false,
273
+ "special": true
274
+ },
275
+ "40": {
276
+ "content": "〈|SPECIAL_21|〉",
277
+ "lstrip": false,
278
+ "normalized": false,
279
+ "rstrip": false,
280
+ "single_word": false,
281
+ "special": true
282
+ },
283
+ "41": {
284
+ "content": "〈|SPECIAL_22|〉",
285
+ "lstrip": false,
286
+ "normalized": false,
287
+ "rstrip": false,
288
+ "single_word": false,
289
+ "special": true
290
+ },
291
+ "42": {
292
+ "content": "〈|SPECIAL_23|〉",
293
+ "lstrip": false,
294
+ "normalized": false,
295
+ "rstrip": false,
296
+ "single_word": false,
297
+ "special": true
298
+ },
299
+ "43": {
300
+ "content": "〈|SPECIAL_24|〉",
301
+ "lstrip": false,
302
+ "normalized": false,
303
+ "rstrip": false,
304
+ "single_word": false,
305
+ "special": true
306
+ },
307
+ "44": {
308
+ "content": "〈|SPECIAL_25|〉",
309
+ "lstrip": false,
310
+ "normalized": false,
311
+ "rstrip": false,
312
+ "single_word": false,
313
+ "special": true
314
+ },
315
+ "45": {
316
+ "content": "〈|SPECIAL_26|〉",
317
+ "lstrip": false,
318
+ "normalized": false,
319
+ "rstrip": false,
320
+ "single_word": false,
321
+ "special": true
322
+ },
323
+ "46": {
324
+ "content": "〈|SPECIAL_27|〉",
325
+ "lstrip": false,
326
+ "normalized": false,
327
+ "rstrip": false,
328
+ "single_word": false,
329
+ "special": true
330
+ },
331
+ "47": {
332
+ "content": "〈|SPECIAL_28|〉",
333
+ "lstrip": false,
334
+ "normalized": false,
335
+ "rstrip": false,
336
+ "single_word": false,
337
+ "special": true
338
+ },
339
+ "48": {
340
+ "content": "〈|SPECIAL_29|〉",
341
+ "lstrip": false,
342
+ "normalized": false,
343
+ "rstrip": false,
344
+ "single_word": false,
345
+ "special": true
346
+ },
347
+ "49": {
348
+ "content": "〈|SPECIAL_30|〉",
349
+ "lstrip": false,
350
+ "normalized": false,
351
+ "rstrip": false,
352
+ "single_word": false,
353
+ "special": true
354
+ },
355
+ "50": {
356
+ "content": "〈|SPECIAL_31|〉",
357
+ "lstrip": false,
358
+ "normalized": false,
359
+ "rstrip": false,
360
+ "single_word": false,
361
+ "special": true
362
+ },
363
+ "51": {
364
+ "content": "〈|SPECIAL_32|〉",
365
+ "lstrip": false,
366
+ "normalized": false,
367
+ "rstrip": false,
368
+ "single_word": false,
369
+ "special": true
370
+ },
371
+ "52": {
372
+ "content": "〈|SPECIAL_33|〉",
373
+ "lstrip": false,
374
+ "normalized": false,
375
+ "rstrip": false,
376
+ "single_word": false,
377
+ "special": true
378
+ },
379
+ "53": {
380
+ "content": "〈|SPECIAL_34|〉",
381
+ "lstrip": false,
382
+ "normalized": false,
383
+ "rstrip": false,
384
+ "single_word": false,
385
+ "special": true
386
+ },
387
+ "54": {
388
+ "content": "〈|SPECIAL_35|〉",
389
+ "lstrip": false,
390
+ "normalized": false,
391
+ "rstrip": false,
392
+ "single_word": false,
393
+ "special": true
394
+ },
395
+ "55": {
396
+ "content": "〈|SPECIAL_36|〉",
397
+ "lstrip": false,
398
+ "normalized": false,
399
+ "rstrip": false,
400
+ "single_word": false,
401
+ "special": true
402
+ },
403
+ "56": {
404
+ "content": "〈|SPECIAL_37|〉",
405
+ "lstrip": false,
406
+ "normalized": false,
407
+ "rstrip": false,
408
+ "single_word": false,
409
+ "special": true
410
+ },
411
+ "57": {
412
+ "content": "〈|SPECIAL_38|〉",
413
+ "lstrip": false,
414
+ "normalized": false,
415
+ "rstrip": false,
416
+ "single_word": false,
417
+ "special": true
418
+ },
419
+ "58": {
420
+ "content": "〈|SPECIAL_39|〉",
421
+ "lstrip": false,
422
+ "normalized": false,
423
+ "rstrip": false,
424
+ "single_word": false,
425
+ "special": true
426
+ },
427
+ "59": {
428
+ "content": "〈|SPECIAL_40|〉",
429
+ "lstrip": false,
430
+ "normalized": false,
431
+ "rstrip": false,
432
+ "single_word": false,
433
+ "special": true
434
+ },
435
+ "60": {
436
+ "content": "〈|SPECIAL_41|〉",
437
+ "lstrip": false,
438
+ "normalized": false,
439
+ "rstrip": false,
440
+ "single_word": false,
441
+ "special": true
442
+ },
443
+ "61": {
444
+ "content": "〈|SPECIAL_42|〉",
445
+ "lstrip": false,
446
+ "normalized": false,
447
+ "rstrip": false,
448
+ "single_word": false,
449
+ "special": true
450
+ },
451
+ "62": {
452
+ "content": "〈|SPECIAL_43|〉",
453
+ "lstrip": false,
454
+ "normalized": false,
455
+ "rstrip": false,
456
+ "single_word": false,
457
+ "special": true
458
+ },
459
+ "63": {
460
+ "content": "〈|SPECIAL_44|〉",
461
+ "lstrip": false,
462
+ "normalized": false,
463
+ "rstrip": false,
464
+ "single_word": false,
465
+ "special": true
466
+ },
467
+ "64": {
468
+ "content": "〈|SPECIAL_45|〉",
469
+ "lstrip": false,
470
+ "normalized": false,
471
+ "rstrip": false,
472
+ "single_word": false,
473
+ "special": true
474
+ },
475
+ "65": {
476
+ "content": "〈|SPECIAL_46|〉",
477
+ "lstrip": false,
478
+ "normalized": false,
479
+ "rstrip": false,
480
+ "single_word": false,
481
+ "special": true
482
+ },
483
+ "66": {
484
+ "content": "〈|SPECIAL_47|〉",
485
+ "lstrip": false,
486
+ "normalized": false,
487
+ "rstrip": false,
488
+ "single_word": false,
489
+ "special": true
490
+ },
491
+ "67": {
492
+ "content": "〈|SPECIAL_48|〉",
493
+ "lstrip": false,
494
+ "normalized": false,
495
+ "rstrip": false,
496
+ "single_word": false,
497
+ "special": true
498
+ },
499
+ "68": {
500
+ "content": "〈|SPECIAL_49|〉",
501
+ "lstrip": false,
502
+ "normalized": false,
503
+ "rstrip": false,
504
+ "single_word": false,
505
+ "special": true
506
+ },
507
+ "69": {
508
+ "content": "〈|SPECIAL_50|〉",
509
+ "lstrip": false,
510
+ "normalized": false,
511
+ "rstrip": false,
512
+ "single_word": false,
513
+ "special": true
514
+ },
515
+ "18": {
516
+ "content": "<think>",
517
+ "single_word": false,
518
+ "lstrip": false,
519
+ "rstrip": false,
520
+ "normalized": false,
521
+ "special": false
522
+ },
523
+ "19": {
524
+ "content": "</think>",
525
+ "single_word": false,
526
+ "lstrip": false,
527
+ "rstrip": false,
528
+ "normalized": false,
529
+ "special": false
530
+ },
531
+ "23": {
532
+ "content": "<assistant>",
533
+ "single_word": false,
534
+ "lstrip": false,
535
+ "rstrip": false,
536
+ "normalized": false,
537
+ "special": false
538
+ },
539
+ "24": {
540
+ "content": "</assistant>",
541
+ "single_word": false,
542
+ "lstrip": false,
543
+ "rstrip": false,
544
+ "normalized": false,
545
+ "special": false
546
+ },
547
+ "25": {
548
+ "content": "<tool_call>",
549
+ "single_word": false,
550
+ "lstrip": false,
551
+ "rstrip": false,
552
+ "normalized": false,
553
+ "special": false
554
+ },
555
+ "26": {
556
+ "content": "</tool_call>",
557
+ "single_word": false,
558
+ "lstrip": false,
559
+ "rstrip": false,
560
+ "normalized": false,
561
+ "special": false
562
+ }
563
+ },
564
+ "bos_token": "〈|EOS|〉",
565
+ "clean_up_tokenization_spaces": false,
566
+ "cls_token": "〈|CLS|〉",
567
+ "eos_token": "〈|EOS|〉",
568
+ "extra_special_tokens": {},
569
+ "mask_token": "〈|MASK|〉",
570
+ "model_max_length": 1000000000000000019884624838656,
571
+ "pad_token": "〈|PAD|〉",
572
+ "sep_token": "〈|SEP|〉",
573
+ "tokenizer_class": "PreTrainedTokenizerFast",
574
+ "unk_token": "〈|UNK|〉",
575
+ "chat_template": "{% include 'chat_template.jinja' %}"
576
+ }