yujiepan commited on
Commit
e2fb17b
·
verified ·
1 Parent(s): 55b53bd

Upload folder using huggingface_hub

Browse files
Files changed (7) hide show
  1. .meta.json +11 -0
  2. README.md +595 -0
  3. chat_template.jinja +252 -0
  4. config.json +128 -0
  5. model.safetensors +3 -0
  6. tokenizer.json +0 -0
  7. tokenizer_config.json +14 -0
.meta.json ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "Pillow": "12.3.0",
3
+ "flashinfer-python": "0.6.15.post1",
4
+ "huggingface_hub": "1.27.0",
5
+ "numpy": "2.5.2",
6
+ "safetensors": "0.8.0",
7
+ "sympy": "1.14.0",
8
+ "tilelang": "0.1.12",
9
+ "torch": "2.13.0+cu126",
10
+ "transformers": "5.15.0.dev0"
11
+ }
README.md ADDED
@@ -0,0 +1,595 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ library_name: transformers
3
+ base_model:
4
+ - deepseek-ai/DeepSeek-V4.1-Flash
5
+ ---
6
+
7
+ This tiny model is intended for debugging. It is randomly initialized using the configuration adapted from [deepseek-ai/DeepSeek-V4.1-Flash](https://huggingface.co/deepseek-ai/DeepSeek-V4.1-Flash).
8
+
9
+ Note:
10
+ - Structural comparison with the original `deepseek-ai/DeepSeek-V4.1-Flash`:
11
+
12
+ | Structural feature | Original V4.1 Flash | This tiny model |
13
+ |---|---|---|
14
+ | Modalities | Vision + text | Same |
15
+ | Vocabulary / special-token IDs | 129,280; original BOS/EOS/PAD/image IDs | Same |
16
+ | Text backbone layers | 40 | 6 |
17
+ | Hidden size | 5,120 | 128 |
18
+ | Attention heads / head dim | 64 / 512 | 4 / 512 (SGLang DSV4 ABI; `head_dim >= 32`) |
19
+ | RoPE head dim | 64 | Same |
20
+ | Q/O low-rank projections | q=1,280; o=1,024; 8 output groups | q=128; o=128; 2 groups |
21
+ | Routed/shared experts | 384 / 1, top-6 | 32 / 1, top-6 |
22
+ | MoE intermediate size | 2,304 | 256 (128 per TP rank) |
23
+ | Sparse attention ratios | Backbone: 2 sliding-only, 18 ratio-2, 20 ratio-1; MTP: 3 sliding-only | Backbone: 2 sliding-only, 2 ratio-2, 2 ratio-1; MTP: 3 sliding-only |
24
+ | KV / index source layers | KV: 2,8,14,20; index: 2,8,14,20,24,28,32,36 | KV/index: 1,3; index head dim 128 |
25
+ | Candidate source layer | 20 | 3 |
26
+ | Hyper-Connections | 4 residual streams | Same |
27
+ | Engram | layers 1,14; 8 heads x 256; ~384M rows/table | layers 1,2; 4 heads x 64; 6,748 / 7,592 rows |
28
+ | DSpark/MTP | 3 stages; targets 37,38,39; Markov rank 256; 128 experts, top-3 | 3 stages; targets 3,4,5; rank 16; 8 experts, top-3 |
29
+ | Vision tower | 32 layers; width 1,024; 16 heads | 2 layers; width 64; 4 heads |
30
+ | Vision patch/downsample | patch 14; downsample 3 | Same |
31
+ | Quantization | Routed experts FP4; most other linears FP8; UE8M0 scales; 32x32 blocks | Same |
32
+ | Public checkpoint | 96,085 tensors; ~510.3 GB | 1,629 tensors; 96.7 MB |
33
+
34
+ - Preserved topology includes compressed sparse attention and its indexer, four-way
35
+ Hyper-Connections, n-gram Engram lookup, three-stage DSpark/MTP, the vision aligner,
36
+ FP4 expert packing, and FP8 block scales. Widths, layer cadence, expert counts,
37
+ Engram tables, and vision depth are reduced for a practical debugging checkpoint.
38
+
39
+ - The structure is derived by instantiating DeepSeek's pinned official V4.1 inference
40
+ implementation on the meta device, then converting its runtime state back to the
41
+ public Hugging Face checkpoint representation. The original and 1017 releases have
42
+ byte-identical source `config.json` and checkpoint index (96,085 tensor names), so
43
+ the 1017 source tree is also the authoritative executable structure for this release.
44
+
45
+ - The SGLang example is intentionally a conservative TP=2 launch. The
46
+ attention latent stays 512-wide and the indexer stays 128-wide because those are
47
+ fixed by SGLang's DSV4 cache/kernel ABI; all TP-sharded widths, head/group counts,
48
+ expert counts, vocabulary rows, and 32-wide quantization blocks divide cleanly by 2.
49
+ The online example enables the embedded three-stage DSpark/MTP draft explicitly.
50
+
51
+ - Validated on two NVIDIA H20 GPUs with Python 3.12.13, CUDA Toolkit 13.0,
52
+ torch 2.14.1, SGLang `0.5.23.dev384+g9ab74b851`, FlashInfer 0.7.2,
53
+ Transformers 5.19.0, TileLang 0.1.12, and safetensors 0.8.0. Separate offline
54
+ `sglang.Engine` runs using the same TP=2/backend settings reached engine-ready
55
+ with both base decoding and `speculative_algorithm="DSPARK"`; CUDA Graph remained
56
+ enabled, and the latter reported DSpark enabled, generated all three cases
57
+ (including `Hello` with exactly 10 new tokens), and shut down with exit code 0.
58
+ This artifact is byte-identical to the
59
+ separately generated 1017 tiny variant used for that runtime test (SHA-256
60
+ `53aae999fdcacd310c400a2fc3112bb68ce35588441784c75dca5820abf0f35b`). The README
61
+ keeps the equivalent online `sglang serve` form for normal deployment. The
62
+ official source `inference/convert.py` also produced two shards which loaded
63
+ strictly into the source runtime for both ranks.
64
+
65
+ | File path | Size |
66
+ |------|------|
67
+ | model.safetensors | 96.7MB |
68
+
69
+
70
+ ### Example usage:
71
+
72
+ - SGLang
73
+
74
+ ```bash
75
+ # Requires an SGLang preview/main build with DeepSeek-V4.1 support.
76
+ # Runs the target model and its embedded three-stage DSpark/MTP draft with TP=2.
77
+ model_id=tiny-random/deepseek-v4.1-flash
78
+ sglang serve \
79
+ --model-path $model_id \
80
+ --trust-remote-code \
81
+ --tp-size 2 \
82
+ --ep-size 1 \
83
+ --attention-backend dsv4 \
84
+ --moe-runner-backend flashinfer_mxfp4 \
85
+ --speculative-algorithm DSPARK \
86
+ --speculative-dspark-block-size 5 \
87
+ --context-length 4096 \
88
+ --max-total-tokens 4096 \
89
+ --chunked-prefill-size 512 \
90
+ --mem-fraction-static 0.5 \
91
+ --disable-custom-all-reduce
92
+ ```
93
+
94
+ ### Codes to create this repo:
95
+
96
+ <details><summary>Click to expand</summary>
97
+
98
+ ```python
99
+ import json
100
+ import hashlib
101
+ import sys
102
+ from pathlib import Path
103
+ from typing import Any, TypedDict
104
+
105
+ import torch
106
+ from huggingface_hub import hf_hub_download
107
+ from safetensors.torch import save_file
108
+ from transformers import AutoTokenizer
109
+
110
+ source_model_id = "deepseek-ai/DeepSeek-V4.1-Flash"
111
+ save_folder = "/tmp/tiny-random/deepseek-v41-flash"
112
+ source_revision = "314113f8a7dadb388215396ba5fbee2c610a04d6"
113
+ implementation_model_id = "deepseek-ai/DeepSeek-V4.1-Flash-1017"
114
+ implementation_revision = "1bae0e1088993b9b82de08f59de5be5a34e42170"
115
+ # DeepSeek-V4.1-Flash is a multimodal (vision + text) MoE model. The released checkpoint uses the
116
+ # inference-stack tensor naming (see the repo's inference/model.py), not HF-transformers naming, so
117
+ # this generator mirrors that layout. The config is nested: text_config + vision_config, with a
118
+ # shared quantization_config (fp8 weights, fp4 routed experts, ue8m0 scales, 32x32 blocks).
119
+ config = {
120
+ "architectures": [
121
+ "DeepseekV41ForCausalLM"
122
+ ],
123
+ "model_type": "deepseek_v41",
124
+ "dtype": "bfloat16",
125
+ "transformers_version": "5.6.0",
126
+ "bos_token_id": 0,
127
+ "eos_token_id": 1,
128
+ "pad_token_id": 2,
129
+ "image_token_id": 129264,
130
+ "quantization_config": {
131
+ "quant_method": "fp8",
132
+ "activation_scheme": "dynamic",
133
+ "weight_block_size": [
134
+ 32,
135
+ 32
136
+ ],
137
+ "scale_fmt": "ue8m0",
138
+ "expert_dtype": "fp4"
139
+ },
140
+ "text_config": {
141
+ "model_type": "deepseek_v41_text",
142
+ "vocab_size": 129280,
143
+ "hidden_size": 128,
144
+ # TP=2 leaves 128 columns per rank, the minimum accepted by the
145
+ # FlashInfer MXFP4 MoE kernel.
146
+ "moe_intermediate_size": 256,
147
+ "num_hidden_layers": 6,
148
+ "num_attention_heads": 4,
149
+ "num_key_value_heads": 1,
150
+ # SGLang's DSV4 cache/backend ABI fixes this at 448 non-RoPE + 64 RoPE.
151
+ "head_dim": 512,
152
+ "qk_rope_head_dim": 64,
153
+ "q_lora_rank": 128,
154
+ "o_lora_rank": 128,
155
+ "o_groups": 2,
156
+ "hidden_act": "silu",
157
+ "swiglu_limit": 10.0,
158
+ "rms_norm_eps": 1e-20,
159
+ "attention_bias": False,
160
+ "attention_dropout": 0.0,
161
+ "initializer_range": 0.02,
162
+ "use_cache": True,
163
+ "tie_word_embeddings": False,
164
+ "max_position_embeddings": 1048576,
165
+ "rope_theta": 10000,
166
+ "rope_scaling": {
167
+ "rope_type": "yarn",
168
+ "factor": 16,
169
+ "beta_fast": 32,
170
+ "beta_slow": 1,
171
+ "original_max_position_embeddings": 65536
172
+ },
173
+ "n_routed_experts": 32,
174
+ "n_shared_experts": 1,
175
+ "num_experts_per_tok": 6,
176
+ "scoring_func": "sqrtsoftplus",
177
+ "topk_method": "noaux_tc",
178
+ "norm_topk_prob": True,
179
+ "routed_scaling_factor": 1.5,
180
+ "sliding_window": 128,
181
+ # one entry per layer (backbone + MTP): 0 = sliding-window only, r = KV compressed r-to-1
182
+ "compress_ratios": [0, 2, 2, 1, 1, 0, 0, 0, 0],
183
+ "compress_rope_theta": 160000,
184
+ # layers that produce (own) the shared compressed KV / index keys
185
+ "kv_source_layer_ids": [1, 3],
186
+ "index_source_layer_ids": [1, 3],
187
+ "index_n_heads": 4,
188
+ # The SGLang FP4 indexer kernels use a fixed 128-wide key/query layout.
189
+ "index_head_dim": 128,
190
+ "index_topk": 512,
191
+ "candidate_source_layer_id": 3,
192
+ "candidate_topk_blocks": 2048,
193
+ "candidate_block_size": 8,
194
+ "hc_mult": 4,
195
+ "hc_sinkhorn_iters": 20,
196
+ "hc_eps": 1e-06,
197
+ # n-gram engram hash tables added into the residual at a few layers
198
+ # The final KV source layer must remain Engram-free in SGLang; the
199
+ # source model likewise carries Engram on an earlier KV source only.
200
+ "engram_layer_ids": [1, 2],
201
+ # row counts of the two hash tables; derived from engram_vocab_size via the prime layout
202
+ # in the repo's engram.py (find_next_prime chain), computed for the tiny vocab below
203
+ "engram_num_embeddings": [6748, 7592],
204
+ "engram_max_ngram_size": 4,
205
+ "engram_vocab_size": 512,
206
+ "engram_n_heads": 4,
207
+ "engram_head_dim": 64,
208
+ "engram_pad_token_id": 2,
209
+ "engram_compressed_vocab_size": 99092,
210
+ "num_nextn_predict_layers": 3,
211
+ "dspark_block_size": 5,
212
+ "dspark_noise_token_id": 128799,
213
+ "dspark_target_layer_ids": [3, 4, 5],
214
+ # Keep the source low-rank Markov head topology while trimming the two
215
+ # vocabulary-sized BF16 matrices. SGLang only requires a positive rank.
216
+ "dspark_markov_rank": 16,
217
+ "dspark_n_routed_experts": 8,
218
+ "dspark_num_experts_per_tok": 3
219
+ },
220
+ "vision_config": {
221
+ "model_type": "deepseek_v41_vision",
222
+ "num_hidden_layers": 2,
223
+ "hidden_size": 64,
224
+ "num_attention_heads": 4,
225
+ "intermediate_size": 128,
226
+ "patch_size": 14,
227
+ "rope_theta": 10000,
228
+ "downsample_ratio": 3,
229
+ "max_image_tokens": 1024,
230
+ "min_pixels": 295936,
231
+ "max_wh_ratio": None
232
+ }
233
+ }
234
+
235
+ def main():
236
+ torch.manual_seed(42)
237
+ Path(save_folder).mkdir(parents=True, exist_ok=True)
238
+ tokenizer = AutoTokenizer.from_pretrained(
239
+ source_model_id,
240
+ revision=source_revision,
241
+ trust_remote_code=True,
242
+ )
243
+ state_dict = generate(config, tokenizer)
244
+ save_file(state_dict, Path(save_folder) / "model.safetensors")
245
+ with open(Path(save_folder) / "model.safetensors", "rb") as f:
246
+ state_dict = f.read()
247
+ print("Hash: ", hashlib.sha256(state_dict).hexdigest())
248
+
249
+ with open(Path(save_folder) / "config.json", "w", encoding="utf-8") as f:
250
+ json.dump(config, f, indent=2, ensure_ascii=False)
251
+
252
+ tokenizer.save_pretrained(save_folder)
253
+
254
+ try:
255
+ from transformers import GenerationConfig
256
+ generation_config = GenerationConfig.from_pretrained(
257
+ source_model_id,
258
+ revision=source_revision,
259
+ trust_remote_code=True,
260
+ )
261
+ generation_config.save_pretrained(save_folder)
262
+ except Exception as exc: # noqa: BLE001
263
+ print(f"Skipping generation config: {exc}")
264
+
265
+ BF16 = "torch.bfloat16"
266
+ F32 = "torch.float32"
267
+ FP8 = "torch.float8_e4m3fn"
268
+ SCALE = "torch.float8_e8m0fnu"
269
+ I8 = "torch.int8"
270
+
271
+ class TensorSpec(TypedDict):
272
+ shape: list[int]
273
+ dtype: str
274
+
275
+ Config = dict[str, Any]
276
+ State = dict[str, TensorSpec]
277
+ TensorDict = dict[str, torch.Tensor]
278
+
279
+ def initialize(state: State, config: Config, init_bound: float) -> TensorDict:
280
+ tensors: TensorDict = {}
281
+ scale_dtype = torch.float8_e8m0fnu
282
+ fp4_boundaries = torch.tensor([0.25, 0.75, 1.25, 1.75, 2.5, 3.5, 5.0])
283
+
284
+ def scale_for(
285
+ amax: torch.Tensor, qmax: float, dtype: torch.dtype
286
+ ) -> torch.Tensor:
287
+ scale = amax.float().clamp_min(torch.finfo(torch.float32).tiny) / qmax
288
+ if dtype == scale_dtype:
289
+ scale = torch.pow(2.0, torch.round(torch.log2(scale)))
290
+ return scale.to(dtype)
291
+
292
+ def fp8_tensor(
293
+ shape: list[int], scale_shape: list[int], dtype: torch.dtype
294
+ ) -> tuple[torch.Tensor, torch.Tensor]:
295
+ # block sizes are inferred from the scale shape, so this also handles the engram embedding
296
+ # table, which is quantized per-row over 32-column blocks (block_out == 1)
297
+ assert shape[0] % scale_shape[0] == 0 and shape[1] % scale_shape[1] == 0
298
+ block_out = shape[0] // scale_shape[0]
299
+ block_in = shape[1] // scale_shape[1]
300
+ value = torch.empty(shape, dtype=torch.bfloat16).uniform_(
301
+ -init_bound, init_bound
302
+ )
303
+ blocks = value.view(
304
+ scale_shape[0], block_out, scale_shape[1], block_in
305
+ ).transpose(1, 2)
306
+ scales = scale_for(blocks.abs().amax(dim=(-1, -2)), 448, dtype)
307
+ weight = (
308
+ (blocks.float() / scales.float()[..., None, None])
309
+ .clamp(-448, 448)
310
+ .to(torch.float8_e4m3fn)
311
+ .transpose(1, 2)
312
+ .reshape(shape)
313
+ .contiguous()
314
+ )
315
+ return weight, scales
316
+
317
+ def fp4_tensors(
318
+ count: int,
319
+ shape: list[int],
320
+ scale_shape: list[int],
321
+ dtype: torch.dtype,
322
+ ) -> tuple[torch.Tensor, torch.Tensor]:
323
+ out_dim, packed_in_dim = shape
324
+ block_out, block_in = config["quantization_config"]["weight_block_size"]
325
+ assert out_dim % block_out == 0
326
+ assert packed_in_dim * 2 % block_in == 0
327
+ assert packed_in_dim == scale_shape[1] * 16
328
+ value = torch.empty(
329
+ count, out_dim, packed_in_dim * 2, dtype=torch.bfloat16
330
+ ).uniform_(-init_bound, init_bound)
331
+ blocks = value.view(count, out_dim, scale_shape[1], 32)
332
+ scales = scale_for(blocks.abs().amax(dim=-1), 6, dtype)
333
+ normalized = (blocks.float() / scales.float()[..., None]).clamp(-6, 6)
334
+ code = torch.bucketize(normalized.abs(), fp4_boundaries)
335
+ code += normalized.signbit() * 8
336
+ code = code.view(count, out_dim, packed_in_dim * 2)
337
+ packed = (code[..., ::2] | (code[..., 1::2] << 4)).to(torch.uint8)
338
+ weight = packed.view(torch.int8).contiguous()
339
+ return weight, scales
340
+
341
+ dtype_map: dict[str, torch.dtype] = {
342
+ BF16: torch.bfloat16,
343
+ F32: torch.float32,
344
+ FP8: torch.float8_e4m3fn,
345
+ SCALE: scale_dtype,
346
+ I8: torch.int8,
347
+ }
348
+ fp4_groups: dict[
349
+ tuple[tuple[int, ...], tuple[int, ...], torch.dtype], list[str]
350
+ ] = {}
351
+ for name, spec in state.items():
352
+ if spec["dtype"] != I8:
353
+ continue
354
+ scale_spec = state[name.replace(".weight", ".scale")]
355
+ key = (
356
+ tuple(spec["shape"]),
357
+ tuple(scale_spec["shape"]),
358
+ dtype_map[scale_spec["dtype"]],
359
+ )
360
+ fp4_groups.setdefault(key, []).append(name)
361
+
362
+ fp4_cache: dict[str, tuple[torch.Tensor, torch.Tensor]] = {}
363
+ max_batch_elements = 4 * 1024 * 1024
364
+ for (shape_tuple, scale_shape_tuple, dtype), names in fp4_groups.items():
365
+ shape = list(shape_tuple)
366
+ scale_shape = list(scale_shape_tuple)
367
+ logical_elements = shape[0] * shape[1] * 2
368
+ batch_size = max(1, max_batch_elements // logical_elements)
369
+ for start in range(0, len(names), batch_size):
370
+ batch_names = names[start: start + batch_size]
371
+ weights, scales = fp4_tensors(
372
+ len(batch_names), shape, scale_shape, dtype
373
+ )
374
+ for index, name in enumerate(batch_names):
375
+ fp4_cache[name] = (
376
+ weights[index].clone(),
377
+ scales[index].clone(),
378
+ )
379
+
380
+ for name, spec in state.items():
381
+ if name in tensors:
382
+ continue
383
+ shape, dtype = spec["shape"], dtype_map[spec["dtype"]]
384
+ scale_name = name.replace(".weight", ".scale")
385
+ if spec["dtype"] == FP8:
386
+ scale_spec = state[scale_name]
387
+ scale_type = dtype_map[scale_spec["dtype"]]
388
+ tensors[name], tensors[scale_name] = fp8_tensor(
389
+ shape, scale_spec["shape"], scale_type
390
+ )
391
+ elif spec["dtype"] == I8:
392
+ tensors[name], tensors[scale_name] = fp4_cache.pop(name)
393
+ elif not name.endswith(".scale"):
394
+ tensors[name] = torch.empty(shape, dtype=dtype).uniform_(
395
+ -init_bound, init_bound
396
+ )
397
+ print(f"{name}: {shape} {dtype}", flush=True)
398
+ file_size_by_name = {name: tensor.numel() * tensor.element_size() for name, tensor in tensors.items()}
399
+ total_file_size = sum(file_size_by_name.values())
400
+ k = 20
401
+ topk = sorted(file_size_by_name.items(), key=lambda x: x[1], reverse=True)[:k]
402
+ print(f"File size: {total_file_size / 1024 / 1024:.2f} MB")
403
+ print(f"Top {k} largest tensors:")
404
+ for name, size in topk:
405
+ print(f" {name}: {size / 1024 / 1024:.2f} MB")
406
+ return tensors
407
+
408
+ def generate(config: Config, tokenizer: Any, init_bound: float = 0.2) -> TensorDict:
409
+ assert init_bound > 0
410
+ text = config["text_config"]
411
+ vision = config["vision_config"]
412
+ quant = config["quantization_config"]
413
+ rope = text["rope_scaling"]
414
+
415
+ # Transformers has no deepseek_v41 registration or auto_map. The source repository instead
416
+ # ships a complete readable runtime under inference/. Instantiate that implementation on the
417
+ # meta device so the official modules, rather than a handwritten tensor list, define the tiny
418
+ # checkpoint structure. Pin the inspected source revision for reproducibility.
419
+ runtime_files = (
420
+ "model.py",
421
+ "engram.py",
422
+ "vision.py",
423
+ "kernel.py",
424
+ "image_processor.py",
425
+ )
426
+ runtime_dir = None
427
+ for filename in runtime_files:
428
+ file_path = hf_hub_download(
429
+ implementation_model_id,
430
+ filename=f"inference/{filename}",
431
+ revision=implementation_revision,
432
+ )
433
+ runtime_dir = str(Path(file_path).parent)
434
+ assert runtime_dir is not None
435
+ sys.path.insert(0, runtime_dir)
436
+ try:
437
+ from model import ModelArgs, Transformer
438
+ finally:
439
+ sys.path.remove(runtime_dir)
440
+
441
+ args = ModelArgs(
442
+ max_batch_size=1,
443
+ max_seq_len=min(text["max_position_embeddings"], 128),
444
+ dtype="fp8" if quant["quant_method"] == "fp8" else "bf16",
445
+ expert_dtype=quant.get("expert_dtype"),
446
+ vocab_size=text["vocab_size"],
447
+ dim=text["hidden_size"],
448
+ moe_inter_dim=text["moe_intermediate_size"],
449
+ n_layers=text["num_hidden_layers"],
450
+ n_mtp_layers=text["num_nextn_predict_layers"],
451
+ n_heads=text["num_attention_heads"],
452
+ n_routed_experts=text["n_routed_experts"],
453
+ n_shared_experts=text["n_shared_experts"],
454
+ n_activated_experts=text["num_experts_per_tok"],
455
+ score_func=text["scoring_func"],
456
+ norm_topk_prob=text["norm_topk_prob"],
457
+ route_scale=text["routed_scaling_factor"],
458
+ swiglu_limit=text["swiglu_limit"],
459
+ q_lora_rank=text["q_lora_rank"],
460
+ head_dim=text["head_dim"],
461
+ rope_head_dim=text["qk_rope_head_dim"],
462
+ norm_eps=text["rms_norm_eps"],
463
+ o_groups=text["o_groups"],
464
+ o_lora_rank=text["o_lora_rank"],
465
+ window_size=text["sliding_window"],
466
+ compress_ratios=tuple(text["compress_ratios"]),
467
+ kv_source_layers=tuple(text["kv_source_layer_ids"]),
468
+ index_source_layers=tuple(text["index_source_layer_ids"]),
469
+ compress_rope_theta=text["compress_rope_theta"],
470
+ original_seq_len=rope["original_max_position_embeddings"],
471
+ rope_theta=text["rope_theta"],
472
+ rope_factor=rope["factor"],
473
+ beta_fast=rope["beta_fast"],
474
+ beta_slow=rope["beta_slow"],
475
+ index_n_heads=text["index_n_heads"],
476
+ index_head_dim=text["index_head_dim"],
477
+ index_topk=text["index_topk"],
478
+ candidate_source_layer=text["candidate_source_layer_id"],
479
+ candidate_topk_blocks=text["candidate_topk_blocks"],
480
+ candidate_block_size=text["candidate_block_size"],
481
+ hc_mult=text["hc_mult"],
482
+ hc_sinkhorn_iters=text["hc_sinkhorn_iters"],
483
+ hc_eps=text["hc_eps"],
484
+ engram_layer_ids=tuple(text["engram_layer_ids"]),
485
+ engram_num_embeddings=tuple(text["engram_num_embeddings"]),
486
+ engram_max_ngram_size=text["engram_max_ngram_size"],
487
+ engram_vocab_size=text["engram_vocab_size"],
488
+ engram_n_heads=text["engram_n_heads"],
489
+ engram_head_dim=text["engram_head_dim"],
490
+ engram_pad_id=text["engram_pad_token_id"],
491
+ engram_compressed_vocab_size=text["engram_compressed_vocab_size"],
492
+ vision_n_layers=vision["num_hidden_layers"],
493
+ vision_dim=vision["hidden_size"],
494
+ vision_n_heads=vision["num_attention_heads"],
495
+ vision_inter_dim=vision["intermediate_size"],
496
+ vision_patch_size=vision["patch_size"],
497
+ vision_rope_theta=vision["rope_theta"],
498
+ vision_downsample_ratio=vision["downsample_ratio"],
499
+ vision_max_n_token=vision["max_image_tokens"],
500
+ vision_min_pixels=vision["min_pixels"],
501
+ vision_max_wh_ratio=vision["max_wh_ratio"],
502
+ image_token_id=config["image_token_id"],
503
+ dspark_block_size=text["dspark_block_size"],
504
+ dspark_noise_token_id=text["dspark_noise_token_id"],
505
+ dspark_target_layer_ids=tuple(text["dspark_target_layer_ids"]),
506
+ dspark_markov_rank=text["dspark_markov_rank"],
507
+ dspark_n_routed_experts=text["dspark_n_routed_experts"],
508
+ dspark_n_activated_experts=text["dspark_num_experts_per_tok"],
509
+ )
510
+
511
+ previous_dtype = torch.get_default_dtype()
512
+ try:
513
+ torch.set_default_dtype(torch.bfloat16)
514
+ with torch.device("meta"):
515
+ runtime_model = Transformer(args, tokenizer)
516
+ finally:
517
+ torch.set_default_dtype(previous_dtype)
518
+
519
+ dtype_map = {
520
+ torch.bfloat16: BF16,
521
+ torch.float32: F32,
522
+ torch.float8_e4m3fn: FP8,
523
+ torch.float8_e8m0fnu: SCALE,
524
+ torch.float4_e2m1fn_x2: I8,
525
+ }
526
+ state: State = {}
527
+ block_out, block_in = quant["weight_block_size"]
528
+ for name, tensor in runtime_model.state_dict().items():
529
+ # Runtime modules tie these to the backbone. The public HF checkpoint stores only the
530
+ # backbone copies; safetensors cannot duplicate shared storage either.
531
+ if name.startswith("mtp.") and name.split(".", 2)[-1] in (
532
+ "embed.weight",
533
+ "head.weight",
534
+ ):
535
+ continue
536
+
537
+ shape = list(tensor.shape)
538
+ dtype = dtype_map[tensor.dtype]
539
+
540
+ # inference/convert.py dequantizes wo_a to bf16 for its grouped einsum. Undo that runtime
541
+ # representation here: the public HF checkpoint stores wo_a as block-quantized fp8.
542
+ if name.endswith(".attn.wo_a.weight"):
543
+ assert shape[0] % block_out == 0 and shape[1] % block_in == 0
544
+ dtype = FP8
545
+ state[name] = {"shape": shape, "dtype": dtype}
546
+ state[name.replace(".weight", ".scale")] = {
547
+ "shape": [shape[0] // block_out, shape[1] // block_in],
548
+ "dtype": SCALE,
549
+ }
550
+ continue
551
+
552
+ # The readable runtime promotes these parameters for computation. The published HF
553
+ # checkpoint keeps them in bf16, as confirmed from all 48 source shard headers.
554
+ if (
555
+ name == "head.weight"
556
+ or name.endswith(".markov_head.head.weight")
557
+ or name.endswith(".confidence_head.proj.weight")
558
+ or ".attn.compressor.w" in name
559
+ or (
560
+ name.startswith("vision.")
561
+ and (
562
+ name == "vision.norm.weight"
563
+ or name.endswith(".norm1.weight")
564
+ or name.endswith(".norm2.weight")
565
+ )
566
+ )
567
+ ):
568
+ dtype = BF16
569
+
570
+ state[name] = {"shape": shape, "dtype": dtype}
571
+
572
+ del runtime_model
573
+ return initialize(state, config, init_bound)
574
+
575
+ main()
576
+ ```
577
+
578
+ </details>
579
+
580
+ ### Test environment:
581
+
582
+ - Pillow: 12.3.0
583
+ - flashinfer-python: 0.6.15.post1
584
+ - huggingface_hub: 1.27.0
585
+ - numpy: 2.5.2
586
+ - safetensors: 0.8.0
587
+ - sympy: 1.14.0
588
+ - tilelang: 0.1.12
589
+ - torch: 2.13.0+cu126
590
+ - transformers: 5.15.0.dev0
591
+
592
+ ### Change log:
593
+
594
+ - 2026-09-10: Initial version.
595
+ - 2026-10-20: Replace the handwritten tensor schema with the pinned official V4.1 inference implementation, align all TP=2/DSV4 dimensions, preserve the three-stage DSpark/MTP draft, add the SGLang TP=2 + DSpark launch profile, and document source-versus-tiny structure and validated runtime versions.
chat_template.jinja ADDED
@@ -0,0 +1,252 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {#- DeepSeek-V4.1 chat template, ported from encoding/encoding.py in the model repo.
2
+ Extra kwargs: thinking_mode ("thinking" | "chat", default "thinking"; enable_thinking=true/false is accepted
3
+ as an alias), drop_thinking (default true), reasoning_effort (int in [1, 100] or "low"/"medium"/"high"/"max",
4
+ default "high"). Assistant reasoning is read from reasoning_content, or from thinking as an alias. -#}
5
+ {%- set IMG = "<|deepseek_image|>" -%}
6
+ {%- set DSML = "|DSML|" -%}
7
+
8
+ {%- if thinking_mode is not defined -%}
9
+ {%- set thinking_mode = "chat" if (enable_thinking is defined and enable_thinking is not none and not enable_thinking) else "thinking" -%}
10
+ {%- endif -%}
11
+ {%- if thinking_mode not in ["chat", "thinking"] -%}
12
+ {{- raise_exception("Invalid thinking_mode `" ~ thinking_mode ~ "`, should be \"chat\" or \"thinking\"") -}}
13
+ {%- endif -%}
14
+ {%- if drop_thinking is not defined -%}{%- set drop_thinking = true -%}{%- endif -%}
15
+ {%- if add_generation_prompt is not defined -%}{%- set add_generation_prompt = false -%}{%- endif -%}
16
+ {%- if tools is not defined -%}{%- set tools = none -%}{%- endif -%}
17
+
18
+ {%- if reasoning_effort is not defined or reasoning_effort is none -%}{%- set reasoning_effort = "high" -%}{%- endif -%}
19
+ {%- set effort_map = {"low": 50, "medium": 62, "high": 75, "max": 100} -%}
20
+ {%- if reasoning_effort is string and reasoning_effort in effort_map -%}
21
+ {%- set budget = effort_map[reasoning_effort] -%}
22
+ {%- elif reasoning_effort is integer and 1 <= reasoning_effort <= 100 -%}
23
+ {%- set budget = reasoning_effort -%}
24
+ {%- else -%}
25
+ {{- raise_exception("Invalid reasoning effort: " ~ reasoning_effort ~ ", should be int within [1,100] or one of \"low\", \"medium\", \"high\", \"max\"") -}}
26
+ {%- endif -%}
27
+
28
+ {#- Text of text/image blocks joined by blank lines; other block types are dropped. -#}
29
+ {%- macro joined_text(blocks) -%}
30
+ {%- set jt = namespace(parts=[]) -%}
31
+ {%- for b in blocks -%}
32
+ {%- if b.type == "text" -%}
33
+ {%- if IMG in (b.text or "") -%}{{- raise_exception("Text block contains image placeholder " ~ IMG ~ ". Images should be separate content blocks.") -}}{%- endif -%}
34
+ {%- set jt.parts = jt.parts + [b.text or ""] -%}
35
+ {%- elif b.type in ["image", "image_url"] -%}
36
+ {%- set jt.parts = jt.parts + [IMG] -%}
37
+ {%- endif -%}
38
+ {%- endfor -%}
39
+ {{- jt.parts | join("\n\n") -}}
40
+ {%- endmacro -%}
41
+
42
+ {#- Splits/validates "ns::name" against an explicit namespace and returns the qualified name. -#}
43
+ {%- macro qualified_name(name, ns) -%}
44
+ {%- if ns is mapping -%}{%- set ns = ns.name -%}{%- endif -%}
45
+ {%- set parts = name.split("::", 1) -%}
46
+ {%- if parts | length == 2 -%}
47
+ {%- if ns is not none and ns != parts[0] -%}{{- raise_exception("Conflicting tool namespaces: " ~ ns ~ " != " ~ parts[0]) -}}{%- endif -%}
48
+ {%- set ns = parts[0] -%}
49
+ {%- set name = parts[1] -%}
50
+ {%- endif -%}
51
+ {%- if "::" in name -%}{{- raise_exception("Tool name must not contain '::': " ~ name) -}}{%- endif -%}
52
+ {%- if ns is not none and "::" in ns -%}{{- raise_exception("Tool namespace must not contain '::': " ~ ns) -}}{%- endif -%}
53
+ {{- name if ns is none else ns ~ "::" ~ name -}}
54
+ {%- endmacro -%}
55
+
56
+ {%- macro render_tools(tool_list) -%}
57
+ {%- set ts = namespace(schemas=[]) -%}
58
+ {%- for tool in tool_list -%}
59
+ {%- set fn = tool.function if tool.function is defined else tool -%}
60
+ {%- set ns = tool.namespace if tool.namespace is defined and tool.namespace is not none else fn.get("namespace") -%}
61
+ {%- set schema = namespace(d={}) -%}
62
+ {%- for k, v in fn.items() if k != "namespace" -%}{%- set schema.d = dict(schema.d, **{k: v}) -%}{%- endfor -%}
63
+ {%- set schema.d = dict(schema.d, name=qualified_name(fn.name, ns)) -%}
64
+ {%- if ns is mapping and ns.description -%}
65
+ {%- set schema.d = dict(schema.d, description=ns.description ~ "\n" ~ (fn.description or "")) -%}
66
+ {%- endif -%}
67
+ {%- set ts.schemas = ts.schemas + [schema.d | tojson] -%}
68
+ {%- endfor -%}
69
+ {{- "## Tools\n\nYou have access to a set of tools to help answer the user's question. You can invoke tools by writing a \"<|DSML| calls>\" block like the following:\n\n<|DSML| calls>\n<|DSML| invoke name=\"$TOOL_NAME\">\n<|DSML| parameter name=\"$PARAMETER_NAME\" string=\"true|false\">$PARAMETER_VALUE</|DSML| parameter>\n...\n</|DSML| invoke>\n<|DSML| invoke name=\"$TOOL_NAME2\">\n...\n</|DSML| invoke>\n</|DSML| calls>\n\nString parameters should be specified as is and set `string=\"true\"`. For all other types (numbers, booleans, arrays, objects), pass the value in JSON format and set `string=\"false\"`.\n\nIf thinking_mode is enabled (triggered by <think>), you MUST output your complete reasoning inside <think>...</think> BEFORE any tool calls or final response.\n\nOtherwise, output directly after </think> with tool calls or final response.\n\n### Available Tool Schemas\n\n" -}}
70
+ {{- ts.schemas | join("\n") -}}
71
+ {{- "\n\nYou MUST strictly follow the above defined tool name and parameter schemas to invoke tool calls.\n" -}}
72
+ {%- endmacro -%}
73
+
74
+ {#- 1. Merge tool results (and consecutive user turns) into user messages made of content blocks. -#}
75
+ {%- set mns = namespace(list=[]) -%}
76
+ {%- for msg in messages -%}
77
+ {%- set content = msg.get("content") -%}
78
+ {%- set is_blocks = content is iterable and content is not string and content is not mapping -%}
79
+ {%- if content is string and IMG in content -%}
80
+ {{- raise_exception("Message content contains image special token " ~ IMG ~ ". Images should be provided as image content blocks.") -}}
81
+ {%- endif -%}
82
+ {%- set rc = msg.reasoning_content if msg.get("reasoning_content") is not none else msg.get("thinking") -%}
83
+ {%- if rc is string and IMG in rc -%}
84
+ {{- raise_exception("reasoning_content contains image special token " ~ IMG) -}}
85
+ {%- endif -%}
86
+ {%- set prev = mns.list[-1] if mns.list else none -%}
87
+ {%- if msg.role == "tool" -%}
88
+ {%- set tblock = {"type": "tool_result", "tool_use_id": msg.get("tool_call_id", ""), "content": joined_text(content) if is_blocks else msg.get("content", "")} -%}
89
+ {%- if prev is not none and prev.role == "user" -%}
90
+ {%- set mns.list = mns.list[:-1] + [dict(prev, content_blocks=prev.content_blocks + [tblock])] -%}
91
+ {%- else -%}
92
+ {%- set mns.list = mns.list + [{"role": "user", "content_blocks": [tblock]}] -%}
93
+ {%- endif -%}
94
+ {%- elif msg.role == "user" -%}
95
+ {%- set blocks = content if is_blocks else [{"type": "text", "text": content or ""}] -%}
96
+ {%- if prev is not none and prev.role == "user" and prev.get("task") is none -%}
97
+ {%- set mns.list = mns.list[:-1] + [dict(prev, content_blocks=prev.content_blocks + blocks)] -%}
98
+ {%- else -%}
99
+ {%- set mns.list = mns.list + [dict(msg, content_blocks=blocks)] -%}
100
+ {%- endif -%}
101
+ {%- else -%}
102
+ {%- set mns.list = mns.list + [msg] -%}
103
+ {%- endif -%}
104
+ {%- endfor -%}
105
+
106
+ {#- Top-level tools attach to a leading system message, or to a new empty one. -#}
107
+ {%- if tools -%}
108
+ {%- if mns.list and mns.list[0].role == "system" -%}
109
+ {%- if not mns.list[0].get("tools") -%}{%- set mns.list = [dict(mns.list[0], tools=tools)] + mns.list[1:] -%}{%- endif -%}
110
+ {%- else -%}
111
+ {%- set mns.list = [{"role": "system", "content": "", "tools": tools}] + mns.list -%}
112
+ {%- endif -%}
113
+ {%- endif -%}
114
+
115
+ {#- 2. Reorder tool results to match the tool_calls order of the preceding assistant message. -#}
116
+ {%- set sns = namespace(list=[], ids=[]) -%}
117
+ {%- for m in mns.list -%}
118
+ {%- if m.role == "assistant" and m.get("tool_calls") -%}
119
+ {%- set sns.ids = [] -%}
120
+ {%- for tc in m.tool_calls -%}
121
+ {%- set sns.ids = sns.ids + [tc.get("id") or tc.get("function", {}).get("id", "")] -%}
122
+ {%- endfor -%}
123
+ {%- set sns.list = sns.list + [m] -%}
124
+ {%- elif m.role == "user" and m.content_blocks -%}
125
+ {%- set tb = namespace(blocks=[]) -%}
126
+ {%- for b in m.content_blocks if b.type == "tool_result" -%}
127
+ {%- set order = namespace(idx=0) -%}
128
+ {%- for tc_id in sns.ids -%}
129
+ {%- if tc_id and tc_id == b.get("tool_use_id", "") -%}{%- set order.idx = loop.index0 -%}{%- endif -%}
130
+ {%- endfor -%}
131
+ {%- set tb.blocks = tb.blocks + [dict(b, _order=order.idx)] -%}
132
+ {%- endfor -%}
133
+ {%- if tb.blocks | length > 1 -%}
134
+ {%- set sorted_blocks = tb.blocks | sort(attribute="_order") -%}
135
+ {%- set rb = namespace(blocks=[], i=0) -%}
136
+ {%- for b in m.content_blocks -%}
137
+ {%- if b.type == "tool_result" -%}
138
+ {%- set rb.blocks = rb.blocks + [sorted_blocks[rb.i]] -%}
139
+ {%- set rb.i = rb.i + 1 -%}
140
+ {%- else -%}
141
+ {%- set rb.blocks = rb.blocks + [b] -%}
142
+ {%- endif -%}
143
+ {%- endfor -%}
144
+ {%- set sns.list = sns.list + [dict(m, content_blocks=rb.blocks)] -%}
145
+ {%- else -%}
146
+ {%- set sns.list = sns.list + [m] -%}
147
+ {%- endif -%}
148
+ {%- else -%}
149
+ {%- set sns.list = sns.list + [m] -%}
150
+ {%- endif -%}
151
+ {%- endfor -%}
152
+ {%- set msgs = sns.list -%}
153
+
154
+ {#- Mid-conversation system messages count as user turns. Tools anywhere disable drop_thinking. -#}
155
+ {%- set state = namespace(last_user=-1, has_tools=false) -%}
156
+ {%- for m in msgs -%}
157
+ {%- if m.role == "user" or (m.role == "system" and loop.index0 > 0) -%}{%- set state.last_user = loop.index0 -%}{%- endif -%}
158
+ {%- if m.get("tools") -%}{%- set state.has_tools = true -%}{%- endif -%}
159
+ {%- endfor -%}
160
+ {%- set effective_drop = drop_thinking and not state.has_tools -%}
161
+ {%- set thinking = thinking_mode == "thinking" -%}
162
+
163
+ {{- "<|begin▁of▁sentence|>" -}}
164
+ {#- 3. Render each message. -#}
165
+ {%- for m in msgs -%}
166
+ {%- set idx = loop.index0 -%}
167
+ {%- set content = m.get("content") -%}
168
+ {%- if content is iterable and content is not string and content is not mapping -%}{%- set content = joined_text(content) -%}{%- endif -%}
169
+
170
+ {%- if idx == 0 -%}
171
+ {%- if thinking or m.role == "system" -%}{{- "<|System|>" -}}{%- endif -%}
172
+ {%- if thinking -%}{{- "Reasoning Effort: " ~ budget ~ " (range 1-100, the higher the value, the more thorough the reasoning)\n\n" -}}{%- endif -%}
173
+ {%- endif -%}
174
+
175
+ {%- if m.role == "system" -%}
176
+ {%- if idx > 0 -%}{{- "<|System|>" -}}{%- endif -%}
177
+ {{- content or "" -}}
178
+ {%- if m.get("tools") -%}{{- "\n\n" ~ render_tools(m.tools) -}}{%- endif -%}
179
+ {%- if m.get("response_format") -%}
180
+ {{- "\n\n## Response Format:\n\nYou MUST strictly adhere to the following schema to reply:\n" ~ (m.response_format | tojson) -}}
181
+ {%- endif -%}
182
+
183
+ {%- elif m.role == "user" -%}
184
+ {{- "<|User|>" -}}
185
+ {%- set up = namespace(parts=[]) -%}
186
+ {%- for b in m.content_blocks -%}
187
+ {%- if b.type == "text" -%}
188
+ {%- if IMG in (b.text or "") -%}{{- raise_exception("Text block contains image placeholder " ~ IMG ~ ". Images should be separate content blocks.") -}}{%- endif -%}
189
+ {%- set up.parts = up.parts + [b.text or ""] -%}
190
+ {%- elif b.type in ["image", "image_url"] -%}
191
+ {%- set up.parts = up.parts + [IMG] -%}
192
+ {%- elif b.type == "tool_result" -%}
193
+ {%- set tc = b.get("content", "") -%}
194
+ {%- if tc is iterable and tc is not string and tc is not mapping -%}
195
+ {%- set sub = namespace(parts=[]) -%}
196
+ {%- for s in tc -%}
197
+ {%- if s.type == "text" -%}{%- set sub.parts = sub.parts + [s.get("text", "")] -%}
198
+ {%- elif s.type in ["image", "image_url"] -%}{%- set sub.parts = sub.parts + [IMG] -%}
199
+ {%- else -%}{%- set sub.parts = sub.parts + ["[Unsupported " ~ s.type ~ "]"] -%}
200
+ {%- endif -%}
201
+ {%- endfor -%}
202
+ {%- set tc = sub.parts | join("\n\n") -%}
203
+ {%- endif -%}
204
+ {%- set up.parts = up.parts + ["<tool_result>" ~ tc ~ "</tool_result>"] -%}
205
+ {%- else -%}
206
+ {%- set up.parts = up.parts + ["[Unsupported " ~ b.type ~ "]"] -%}
207
+ {%- endif -%}
208
+ {%- endfor -%}
209
+ {{- up.parts | join("\n\n") -}}
210
+
211
+ {%- elif m.role == "latest_reminder" -%}
212
+ {{- "<|latest_reminder|>" ~ content -}}
213
+
214
+ {%- elif m.role == "assistant" -%}
215
+ {%- set prev_has_task = idx > 0 and msgs[idx - 1].get("task") is not none -%}
216
+ {%- if thinking and not prev_has_task and (not effective_drop or idx > state.last_user) -%}
217
+ {{- ((m.reasoning_content if m.get("reasoning_content") is not none else m.get("thinking")) or "") ~ "</think>" -}}
218
+ {%- endif -%}
219
+ {{- content or "" -}}
220
+ {%- if m.get("tool_calls") -%}
221
+ {%- set tcs = namespace(lines=[]) -%}
222
+ {%- for tc in m.tool_calls -%}
223
+ {%- set fn = tc.function -%}
224
+ {%- set params = namespace(lines=[]) -%}
225
+ {%- set args = fn.arguments if fn.arguments is mapping else {"arguments": fn.arguments} -%}
226
+ {%- for k, v in args.items() -%}
227
+ {%- set params.lines = params.lines + ["<" ~ DSML ~ " parameter name=\"" ~ k ~ "\" string=\"" ~ ("true" if v is string else "false") ~ "\">" ~ (v if v is string else v | tojson) ~ "</" ~ DSML ~ " parameter>"] -%}
228
+ {%- endfor -%}
229
+ {%- set name = qualified_name(fn.name, tc.get("namespace") or fn.get("namespace")) -%}
230
+ {%- set tcs.lines = tcs.lines + ["<" ~ DSML ~ " invoke name=\"" ~ name ~ "\">\n" ~ params.lines | join("\n") ~ "\n</" ~ DSML ~ " invoke>"] -%}
231
+ {%- endfor -%}
232
+ {{- "\n\n<" ~ DSML ~ " calls>\n" ~ tcs.lines | join("\n") ~ "\n</" ~ DSML ~ " calls>" -}}
233
+ {%- endif -%}
234
+ {%- if not m.get("wo_eos", false) -%}{{- "<|end▁of▁sentence|>" -}}{%- endif -%}
235
+
236
+ {%- else -%}
237
+ {{- raise_exception("Unknown role: " ~ m.role) -}}
238
+ {%- endif -%}
239
+
240
+ {#- Transition tokens, only when the next message is an assistant/latest_reminder turn or this is the last one. -#}
241
+ {%- if loop.last or msgs[idx + 1].role in ["assistant", "latest_reminder"] -%}
242
+ {%- set task = m.get("task") -%}
243
+ {%- if task is not none -%}
244
+ {%- set task_tokens = {"action": "<|action|>", "query": "<|query|>", "authority": "<|authority|>", "domain": "<|domain|>", "title": "<|title|>", "read_url": "<|read_url|>"} -%}
245
+ {%- if task not in task_tokens -%}{{- raise_exception("Invalid task: '" ~ task ~ "'") -}}{%- endif -%}
246
+ {%- if task == "action" -%}{{- "<|Assistant|>" ~ ("<think>" if thinking else "</think>") -}}{%- endif -%}
247
+ {{- task_tokens[task] -}}
248
+ {%- elif (m.role == "user" or (m.role == "system" and idx > 0)) and (add_generation_prompt or not loop.last) -%}
249
+ {{- "<|Assistant|>" ~ ("<think>" if thinking and (not effective_drop or idx >= state.last_user) else "</think>") -}}
250
+ {%- endif -%}
251
+ {%- endif -%}
252
+ {%- endfor -%}
config.json ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "DeepseekV41ForCausalLM"
4
+ ],
5
+ "model_type": "deepseek_v41",
6
+ "dtype": "bfloat16",
7
+ "transformers_version": "5.6.0",
8
+ "bos_token_id": 0,
9
+ "eos_token_id": 1,
10
+ "pad_token_id": 2,
11
+ "image_token_id": 129264,
12
+ "quantization_config": {
13
+ "quant_method": "fp8",
14
+ "activation_scheme": "dynamic",
15
+ "weight_block_size": [
16
+ 32,
17
+ 32
18
+ ],
19
+ "scale_fmt": "ue8m0",
20
+ "expert_dtype": "fp4"
21
+ },
22
+ "text_config": {
23
+ "model_type": "deepseek_v41_text",
24
+ "vocab_size": 129280,
25
+ "hidden_size": 128,
26
+ "moe_intermediate_size": 256,
27
+ "num_hidden_layers": 6,
28
+ "num_attention_heads": 4,
29
+ "num_key_value_heads": 1,
30
+ "head_dim": 512,
31
+ "qk_rope_head_dim": 64,
32
+ "q_lora_rank": 128,
33
+ "o_lora_rank": 128,
34
+ "o_groups": 2,
35
+ "hidden_act": "silu",
36
+ "swiglu_limit": 10.0,
37
+ "rms_norm_eps": 1e-20,
38
+ "attention_bias": false,
39
+ "attention_dropout": 0.0,
40
+ "initializer_range": 0.02,
41
+ "use_cache": true,
42
+ "tie_word_embeddings": false,
43
+ "max_position_embeddings": 1048576,
44
+ "rope_theta": 10000,
45
+ "rope_scaling": {
46
+ "rope_type": "yarn",
47
+ "factor": 16,
48
+ "beta_fast": 32,
49
+ "beta_slow": 1,
50
+ "original_max_position_embeddings": 65536
51
+ },
52
+ "n_routed_experts": 32,
53
+ "n_shared_experts": 1,
54
+ "num_experts_per_tok": 6,
55
+ "scoring_func": "sqrtsoftplus",
56
+ "topk_method": "noaux_tc",
57
+ "norm_topk_prob": true,
58
+ "routed_scaling_factor": 1.5,
59
+ "sliding_window": 128,
60
+ "compress_ratios": [
61
+ 0,
62
+ 2,
63
+ 2,
64
+ 1,
65
+ 1,
66
+ 0,
67
+ 0,
68
+ 0,
69
+ 0
70
+ ],
71
+ "compress_rope_theta": 160000,
72
+ "kv_source_layer_ids": [
73
+ 1,
74
+ 3
75
+ ],
76
+ "index_source_layer_ids": [
77
+ 1,
78
+ 3
79
+ ],
80
+ "index_n_heads": 4,
81
+ "index_head_dim": 128,
82
+ "index_topk": 512,
83
+ "candidate_source_layer_id": 3,
84
+ "candidate_topk_blocks": 2048,
85
+ "candidate_block_size": 8,
86
+ "hc_mult": 4,
87
+ "hc_sinkhorn_iters": 20,
88
+ "hc_eps": 1e-06,
89
+ "engram_layer_ids": [
90
+ 1,
91
+ 2
92
+ ],
93
+ "engram_num_embeddings": [
94
+ 6748,
95
+ 7592
96
+ ],
97
+ "engram_max_ngram_size": 4,
98
+ "engram_vocab_size": 512,
99
+ "engram_n_heads": 4,
100
+ "engram_head_dim": 64,
101
+ "engram_pad_token_id": 2,
102
+ "engram_compressed_vocab_size": 99092,
103
+ "num_nextn_predict_layers": 3,
104
+ "dspark_block_size": 5,
105
+ "dspark_noise_token_id": 128799,
106
+ "dspark_target_layer_ids": [
107
+ 3,
108
+ 4,
109
+ 5
110
+ ],
111
+ "dspark_markov_rank": 16,
112
+ "dspark_n_routed_experts": 8,
113
+ "dspark_num_experts_per_tok": 3
114
+ },
115
+ "vision_config": {
116
+ "model_type": "deepseek_v41_vision",
117
+ "num_hidden_layers": 2,
118
+ "hidden_size": 64,
119
+ "num_attention_heads": 4,
120
+ "intermediate_size": 128,
121
+ "patch_size": 14,
122
+ "rope_theta": 10000,
123
+ "downsample_ratio": 3,
124
+ "max_image_tokens": 1024,
125
+ "min_pixels": 295936,
126
+ "max_wh_ratio": null
127
+ }
128
+ }
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:53aae999fdcacd310c400a2fc3112bb68ce35588441784c75dca5820abf0f35b
3
+ size 96712592
tokenizer.json ADDED
The diff for this file is too large to render. See raw diff
 
tokenizer_config.json ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "backend": "tokenizers",
3
+ "bos_token": "<|begin▁of▁sentence|>",
4
+ "clean_up_tokenization_spaces": false,
5
+ "eos_token": "<|end▁of▁sentence|>",
6
+ "is_local": false,
7
+ "legacy": true,
8
+ "local_files_only": false,
9
+ "model_max_length": 1048576,
10
+ "pad_token": "<|end▁of▁sentence|>",
11
+ "sp_model_kwargs": {},
12
+ "tokenizer_class": "TokenizersBackend",
13
+ "unk_token": null
14
+ }