astryx-dracula 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/BRAND.md +30 -18
  2. package/README.md +22 -11
  3. package/USAGE.md +29 -10
  4. package/astryx-dracula.js +19 -13
  5. package/astryx-dracula.variants.d.ts +15 -0
  6. package/package.json +59 -17
  7. package/shared/auth-chrome-config.ts +18 -0
  8. package/shared/auth-chrome.tsx +25 -0
  9. package/shared/auth-copy.ts +22 -0
  10. package/shared/chaptered-doc-config.ts +92 -0
  11. package/shared/chaptered-doc.tsx +332 -0
  12. package/shared/chart-hues.ts +27 -0
  13. package/shared/chart-labels.tsx +44 -0
  14. package/shared/chart-legend.tsx +100 -0
  15. package/shared/chart-panel-style.ts +13 -0
  16. package/shared/data-bar.tsx +160 -0
  17. package/shared/gallery-image.ts +20 -0
  18. package/shared/login-demo.ts +7 -0
  19. package/shared/metric-delta.tsx +36 -0
  20. package/shared/order-desk.tsx +273 -0
  21. package/shared/revenue-chart.tsx +174 -0
  22. package/shared/scene-castle.tsx +322 -0
  23. package/shared/scene-frame.tsx +88 -0
  24. package/shared/scene-hues.ts +49 -0
  25. package/shared/scene-tile.tsx +128 -0
  26. package/shared/settings-data.ts +125 -0
  27. package/shared/settings-rows.tsx +179 -0
  28. package/shared/sparkline.tsx +94 -0
  29. package/shared/sso-icons.tsx +30 -0
  30. package/templates/ai-chat-landing.tsx +67 -32
  31. package/templates/ai-chat.tsx +41 -20
  32. package/templates/blank.tsx +5 -2
  33. package/templates/centered-hero.tsx +15 -124
  34. package/templates/classic-gallery.tsx +21 -65
  35. package/templates/contact-form.tsx +62 -40
  36. package/templates/dashboard-portfolio.tsx +146 -227
  37. package/templates/dashboard.tsx +123 -121
  38. package/templates/detail-page.tsx +101 -95
  39. package/templates/documentation-design.template.mjs +1 -1
  40. package/templates/documentation-design.tsx +85 -96
  41. package/templates/documentation-technical.template.mjs +1 -1
  42. package/templates/documentation-technical.tsx +33 -21
  43. package/templates/documentation.template.mjs +1 -1
  44. package/templates/documentation.tsx +54 -74
  45. package/templates/editor.tsx +119 -89
  46. package/templates/file-explorer.tsx +133 -84
  47. package/templates/form-two-column.tsx +62 -124
  48. package/templates/gallery-hero.tsx +17 -171
  49. package/templates/ide.tsx +27 -21
  50. package/templates/incident-console.tsx +105 -69
  51. package/templates/kanban-board.tsx +100 -41
  52. package/templates/library.template.mjs +2 -2
  53. package/templates/library.tsx +38 -41
  54. package/templates/login-card.tsx +59 -86
  55. package/templates/login-split.tsx +88 -128
  56. package/templates/login-sso.tsx +107 -68
  57. package/templates/login.tsx +108 -52
  58. package/templates/messaging-shell.tsx +172 -143
  59. package/templates/mixed-gallery.tsx +31 -130
  60. package/templates/payment-form.tsx +369 -289
  61. package/templates/product-detail.tsx +40 -57
  62. package/templates/product-gallery.tsx +31 -40
  63. package/templates/product-tour.template.mjs +6 -0
  64. package/templates/product-tour.tsx +916 -0
  65. package/templates/settings-dialog.tsx +145 -286
  66. package/templates/settings-sidebar.tsx +179 -267
  67. package/templates/settings.tsx +42 -13
  68. package/templates/shell-nav.tsx +32 -23
  69. package/templates/shell-side-nav.tsx +30 -19
  70. package/templates/shell-top-nav.tsx +81 -43
  71. package/templates/side-gallery.tsx +39 -93
  72. package/templates/table-grouped.tsx +63 -33
  73. package/templates/table-page-chart.tsx +24 -297
  74. package/templates/table-page-heatmap-status.tsx +157 -91
  75. package/templates/table-page-shoe-store-heatmap.tsx +19 -288
  76. package/templates/table-page.tsx +24 -6
  77. package/templates/table.tsx +24 -7
  78. package/templates/tech-report.template.mjs +6 -0
  79. package/templates/tech-report.tsx +1486 -0
  80. package/templates/theme-showcase.template.mjs +1 -1
  81. package/templates/theme-showcase.tsx +313 -249
  82. package/theme.css +76 -14
  83. package/tokens.css +425 -30
@@ -0,0 +1,1486 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ // XLE (canonical structure, validated with `bunx astryx layout check`):
3
+ // A[cp=0 @sideNav=(SN > (SNS"Overview" > (SNI"Abstract"! + SNI"Model at a glance")) + (SNS"Architecture" > (SNI"Causal Encoder-Decoder" + SNI"Compressed Sparse Attention 2" + SNI"Single-Pass mHC" + SNI"Engram, DSpark, FP4")) + (SNS"Infrastructure" > (SNI"Training infrastructure" + SNI"Inference system" + SNI"SWA Bounded Replay")) + (SNS"Training and evaluation" > (SNI"Pre-training" + SNI"Post-training" + SNI"Conclusion")))] > L[h=auto] > (LC[p=8 !scroll] > V[g=8] > (V[g=2] > (H[g=2 a=center] > MNT + Tx[t=supporting]) + Hd"Abstract"[level=1 t=display-2] + Tx"Chapter 1 of 12"[t=supporting]) + Tx"Intro"[t=large] + AR + (V[g=8] > (V[g=3] > Hd"The bottleneck"[level=2] + Tx"Body"[t=body] + UL + Cd)*3) + D + (H[j=between] > B.secondary"Previous" + B.secondary"Next") + (Tx[t=supporting] > Lk"Checkpoints")) + (LP[!scroll] > Outline)
4
+
5
+ /**
6
+ * Technical report — a chaptered walkthrough of the DeepSeek-V4.1-Flash technical report, from the KV cache bottleneck through architecture, i (Frame/responsive/container: see XLE header above.)
7
+ */
8
+
9
+ import {Icon} from '@astryxdesign/core/Icon';
10
+ import {Link} from '@astryxdesign/core/Link';
11
+ import {Text} from '@astryxdesign/core/Text';
12
+ import ChapteredDoc, {ChapterArtFrame} from 'astryx-dracula/shared/chaptered-doc';
13
+ import type {ChapterGroup} from 'astryx-dracula/shared/chaptered-doc-config';
14
+ import {SceneFrame} from 'astryx-dracula/shared/scene-frame';
15
+
16
+ import {
17
+ Boxes,
18
+ Combine,
19
+ Cpu,
20
+ Database,
21
+ FileText,
22
+ Flag,
23
+ Grid2x2,
24
+ Layers,
25
+ RefreshCw,
26
+ ScrollText,
27
+ Server,
28
+ Sparkles,
29
+ Table2,
30
+ } from 'lucide-react';
31
+
32
+ const SELF_HASH = '#/templates/tech-report';
33
+
34
+ const REPORT_URL = 'https://huggingface.co/deepseek-ai/DeepSeek-V4.1-Flash';
35
+
36
+
37
+
38
+ // ─── Content ─────────────────────────────────────────────────────────────────
39
+ // Every figure below is quoted from the DeepSeek-V4.1-Flash technical report
40
+ // (DeepSeek-AI, 2026). Nothing here is estimated.
41
+
42
+ const CHAPTER_GROUPS: ChapterGroup[] = [
43
+ {
44
+ title: 'Overview',
45
+ chapters: [
46
+ {
47
+ id: 'abstract',
48
+ title: 'Abstract',
49
+ icon: FileText,
50
+ hasArt: true,
51
+ intro:
52
+ 'One idea first: long agents are limited by memory, not math. DeepSeek-V4.1-Flash is a 552B-parameter multimodal Mixture-of-Experts model that attacks the KV cache from three sides at once, shrinking global cache to 890 bytes per token (about 1/4 of DeepSeek-V4-Flash and 1/437 of DeepSeek-V1) and persistent cache to about 1/8, while scoring higher. Think of it as cutting warehouse rent while stocking better goods.',
53
+ sections: [
54
+ {
55
+ key: 'bottleneck',
56
+ heading: 'The bottleneck moved to storage',
57
+ body: 'Attention compute got cheap first. Sparse attention cut the cost of processing long sequences, so the binding constraint moved to keeping, moving, and reloading KV caches. DeepSeek-V4 pairs a global branch that spans the full context with local sliding-window attention. For a fixed window size the sliding-window footprint stays bounded no matter how long the sequence gets, so on long sequences the global branch dominates the runtime KV that HBM must hold.',
58
+ bullets: [
59
+ 'Runtime KV lives in HBM and bounds serving throughput at a given batch size.',
60
+ 'Persistent KV is held for prefix reuse on SSD or in host memory, where capacity and I/O bandwidth both bind.',
61
+ 'Interconnect bandwidth limits how fast either cache can be migrated or loaded back.',
62
+ 'Worked example: at one million tokens, 890 bytes per token is about 0.89 GB of global KV per request, which is why bytes per token is the headline metric.',
63
+ ],
64
+ },
65
+ {
66
+ key: 'three-levers',
67
+ heading: 'Three levers, pulled together',
68
+ body: 'The savings multiply because three levers move together: the architecture stores less, the format uses fewer bits, and the deployment strategy recomputes a little instead of storing a lot.',
69
+ bullets: [
70
+ 'Architecture: Compressed Sparse Attention 2 shares main KV and indexer K across layers and lets layers reuse Top-K indices, decoupling cache sharing from index reuse. V4.1-Flash uses pure CSA2, dropping the CSA and HCA hybrid of DeepSeek-V4.',
71
+ 'Precision: the main KV cache is trained and stored in FP4, which nearly halves its footprint in HBM and on SSD.',
72
+ 'Deployment: SWA Bounded Replay reconstructs missing sliding-window state from the most recent n_win tokens instead of replaying a full layer stack.',
73
+ 'Depth: the Causal Encoder-Decoder projects decoder global KV from the last encoder hidden state, so prefill activates 8B parameters per token while decode activates 16B.',
74
+ ],
75
+ },
76
+ {
77
+ key: 'headline-numbers',
78
+ heading: 'What it adds up to',
79
+ body: 'At equal sequence length, the global KV cache of DeepSeek-V4.1-Flash is roughly 1/4 of DeepSeek-V4-Flash and the persistent cache is roughly 1/8, at better end-to-end quality. The rest of this tour explains where each fraction comes from.',
80
+ bullets: [
81
+ 'Global KV cache: 890 bytes per token, an approximately 4-fold reduction against DeepSeek-V4-Flash and a 437-fold reduction against DeepSeek-V1.',
82
+ 'Persistent KV cache: about 1/8 of DeepSeek-V4-Flash, because SWA KV is no longer persisted and the global KV that remains is 1/4 of its former size.',
83
+ 'Activated parameters: 8B per token during prefill, 16B during decode.',
84
+ 'Decode FLOPs: extending the context 256-fold, from 4K to 1M, raises single-token decode FLOPs by only 1/4.',
85
+ 'Training: 45T tokens of multimodal data, with native multimodality and million-token context after pre-training.',
86
+ ],
87
+ },
88
+ ],
89
+ },
90
+ {
91
+ id: 'at-a-glance',
92
+ title: 'Model at a glance',
93
+ icon: Table2,
94
+ hasArt: true,
95
+ intro:
96
+ 'One idea first: forty layers, two halves, three static choices per layer. The backbone is a 20-layer causal encoder plus a 20-layer decoder at hidden dimension 5120, where every block is a DeepSeekMoE and every attention path is either a sliding window or CSA2. Think of a forty-story tower where the lower half writes one shared catalog and the upper half reads it.',
97
+ sections: [
98
+ {
99
+ key: 'backbone',
100
+ heading: 'Backbone',
101
+ body: 'The language backbone is 40 causal Transformer layers at hidden dimension 5120. The first two layers use sliding-window attention only, and every later layer pairs a global branch with SWA. Images enter through a vision encoder and an MLP projector and are processed jointly with text from the start of language-model pre-training.',
102
+ bullets: [
103
+ 'Attention: 64 query heads of dimension 512, a query compression dimension of 1280, and 8 output projection groups of intermediate dimension 1024.',
104
+ 'Sparse attention selects the top 512 KV entries per query, scored by an indexer with 32 query heads of dimension 128.',
105
+ 'Sliding window: n_win is 128 tokens on both the SWA branch and the DSpark drafter.',
106
+ 'Mixture-of-Experts: 1 shared expert and 384 routed experts per block, each with an intermediate dimension of 2304, 6 activated per token, SwiGLU with clamping at a threshold of 10. Worked example: 6 active out of 384 means about 1.6 percent of routed experts fire on any token.',
107
+ 'Vision: a 32-layer DeepSeek-ViT with hidden dimension 1024, 16 heads, and patch size 14, feeding a 2-layer projector of hidden dimension 5120. A 3x3 pixel unshuffle cuts visual tokens by a factor of nine, supporting inputs up to roughly 1344x1344.',
108
+ ],
109
+ },
110
+ {
111
+ key: 'layer-plan',
112
+ heading: 'How the modes are assigned',
113
+ body: 'Each CSA2 layer is statically assigned one mode and one compression ratio for the whole run. Encoder layers compress at m = 2 in three identical groups of six: the first layer of each group computes the cache and the other five reuse it. The decoder compresses at m = 1 in five groups of four, and only its first group opens with a Full layer; the other four open with Reindex, which shares the cache but refreshes the selection. Picture three identical work crews of six on the encoder side, each with one surveyor and five builders.',
114
+ code: {
115
+ language: 'plaintext',
116
+ title: 'layer plan',
117
+ source: `# CSA2(ratio, mode) across the 40-layer backbone
118
+ encoder[0:2] SWA only
119
+ encoder[2:20] (Full, Reuse x5) x 3 # m = 2
120
+ decoder[0:4] (Full, Reuse x3) # m = 1
121
+ decoder[4:20] (Reindex, Reuse x3) x 4 # m = 1`,
122
+ },
123
+ },
124
+ {
125
+ key: 'parameters',
126
+ heading: 'Parameters and activation',
127
+ body: 'The backbone carries 552B parameters and Engram adds 196B more, split evenly across two modules at layers 1 and 14 to balance memory across training pipeline stages. What matters for cost is activation: 8B parameters per token during prefill and 16B during decode, against 13B activated of 284B total for DeepSeek-V4-Flash and 49B activated of 1.6T total for DeepSeek-V4-Pro.',
128
+ bullets: [
129
+ 'Routing balances image and text tokens separately with modality-specific correction biases, so neither modality can hide an imbalance in the other.',
130
+ 'Engram tables hold roughly 16M entries per hash head in FP8, which keeps conditional memory out of the dense parameter path.',
131
+ 'Checkpoints for the release are at huggingface.co/deepseek-ai/DeepSeek-V4.1-Flash.',
132
+ ],
133
+ },
134
+ ],
135
+ },
136
+ ],
137
+ },
138
+ {
139
+ title: 'Architecture',
140
+ chapters: [
141
+ {
142
+ id: 'ced',
143
+ title: 'Causal Encoder-Decoder',
144
+ icon: Layers,
145
+ intro:
146
+ 'One idea first: the decoder borrows its global memory instead of computing it. Decoder global KV is projected from the hidden state of the last encoder layer, so prefill walks 20 layers instead of 40 and nearly halves its cost on the tool-call-heavy shapes agentic workloads actually produce. Picture a library where every upper floor shares the catalog compiled on floor 20.',
147
+ sections: [
148
+ {
149
+ key: 'projection',
150
+ heading: 'One hidden state feeds the decoder',
151
+ body: 'For global attention, the bottom L/2 layers act as a causal encoder. For every decoder layer above L/2, the KV entries are not derived from that layer hidden state. They are projected directly from H[L/2], the hidden state of the final encoder layer, using layer-dependent projection weights, so prefill computes only the first half of the layers and the decoder receives its global KV at minimal cost.',
152
+ code: {
153
+ language: 'plaintext',
154
+ title: 'ced projections',
155
+ source: `# global attention, for every decoder layer l > L / 2
156
+ C_l = H[L/2] @ W_lKV # global KV entries
157
+ Z_l = H[L/2] @ W_lZ # compression weights
158
+
159
+ # sliding window attention stays layer-local, for every layer l
160
+ K_l, V_l = H_l @ W_lK, H_l @ W_lV`,
161
+ },
162
+ },
163
+ {
164
+ key: 'local-depth',
165
+ heading: 'Local KV keeps its depth',
166
+ body: 'Sharing global KV would be cheap but shallow, so CED leaves the sliding-window path alone: at any layer, local keys and values come from that layer own hidden state, which preserves the computational depth of local KV generation. Think of it as one shared catalog for global facts plus a private notebook per floor for local detail. The cost is that decoder SWA state cannot be inferred from the encoder, and rebuilding it is what SWA Bounded Replay exists to bound.',
167
+ bullets: [
168
+ 'CED is inspired by YoCo, which lets the upper half of the layers share the lower half KV cache.',
169
+ 'CED adds layer-dependent projections so the shared state can be read at every decoder depth.',
170
+ 'The shared global KV cache is what makes decoder global KV free to reconstruct on a cache miss, since it is already on disk.',
171
+ ],
172
+ },
173
+ {
174
+ key: 'prefill-complexity',
175
+ heading: 'Prefill complexity halves',
176
+ body: 'For a sequence length N much larger than n_win, CED reduces prefill complexity from O(N x L) to O(N x L/2 + n_win x L/2), which is O(N x L/2) in practice. Worked example: on a 1M-token prompt with n_win at 128, that is the difference between paying for forty layers per token and paying for about twenty.',
177
+ code: {
178
+ language: 'plaintext',
179
+ title: 'prefill cost',
180
+ source: `full prefill: O(N * L)
181
+ CED prefill: O(N * L/2 + n_win * L/2) ~ O(N * L/2)
182
+
183
+ # L = 40, n_win = 128, N up to 1,000,000`,
184
+ },
185
+ },
186
+ ],
187
+ },
188
+ {
189
+ id: 'csa2',
190
+ title: 'Compressed Sparse Attention 2',
191
+ icon: Grid2x2,
192
+ hasArt: true,
193
+ intro:
194
+ 'One idea first: storage and compute shrink along three multiplicative axes, entry size, sequence length, and layer count, and CSA2 is the first design in this lineage to press all three at once. It shares main KV and indexer K across layers, lets layers reuse Top-K indices, and keeps cache sharing decoupled from index reuse.',
195
+ sections: [
196
+ {
197
+ key: 'modes',
198
+ heading: 'Full, Reindex, and Reuse',
199
+ body: 'Every CSA2 layer computes its own main query and its own SWA KV, and every layer uses the selected main KV entries to produce its attention output. The three modes differ only in where main KV, indexer K, and Top-K indices come from, and each layer keeps one mode for the whole run. Analogy: three cooks sharing one pantry. Full stocks it and writes the recipe, Reindex keeps the pantry but rewrites the recipe, Reuse just follows the latest recipe.',
200
+ code: {
201
+ language: 'plaintext',
202
+ title: 'csa2 modes',
203
+ source: `mode main KV indexer K Top-K indices
204
+ Full compute compute compute fresh
205
+ Reindex reuse reuse rescore the candidate pool
206
+ Reuse reuse reuse reuse the latest selection`,
207
+ },
208
+ bullets: [
209
+ 'Full Mode runs the complete CSA2 path: it builds main KV, projects indexer K from it, and produces fresh Top-K indices.',
210
+ 'Reindex Mode keeps the shared cache but lets the selection change, rescoring the reused keys with its own indexer query.',
211
+ 'Reuse Mode performs sparse attention with no indexer evaluation at all, which is why those layers execute with only 15 kernels during prefill and 11 during decode.',
212
+ ],
213
+ },
214
+ {
215
+ key: 'sparse-attention',
216
+ heading: 'The indexer selects, SWA covers the rest',
217
+ body: 'A lightweight indexer scores main KV entries with indexer query and indexer key and selects the top 512 entries for each query; the query then attends to those entries together with the layer-local sliding-window KV. The 512 picks carry the distant past while the 128-token window covers the recent past. Uncompressed main KV remains a special case, available at a compression ratio of 1.',
218
+ bullets: [
219
+ 'Cache sharing and index reuse are separate knobs: a Reindex layer shares storage while still choosing its own entries.',
220
+ 'When CSA2 meets CED, the decoder layer assigned to Full Mode builds its global KV from the last encoder hidden state; Reindex and Reuse are unchanged.',
221
+ ],
222
+ },
223
+ {
224
+ key: 'compressor',
225
+ heading: 'A simpler compressor',
226
+ body: 'In CSA, each main KV entry was compressed from two original entries whose sources overlapped with the adjacent entry, and absolute positional embedding encoded those two positions. CSA2 removes both the overlap and the absolute embedding, like filing one copy per folder instead of two overlapping copies. It also derives indexer K by projecting main KV entries rather than compressing a separate path out of the hidden states, which simplifies the implementation and speeds up training.',
227
+ },
228
+ {
229
+ key: 'hierarchical',
230
+ heading: 'The Hierarchical Sparse Indexer',
231
+ body: 'Cross-layer index reuse removes indexer evaluations but leaves the survivors scoring the whole causally visible context, which is still the bottleneck at extreme lengths. The decoder answer is to let shallow indexers restrict what deeper indexers may consider, with no extra state: the first Full Mode layer of the group builds a candidate pool, later Reindex layers score only inside it, and Reuse layers do no indexing at all.',
232
+ code: {
233
+ language: 'plaintext',
234
+ title: 'candidate pool',
235
+ source: `# decoder, per query, built by the first Full Mode layer
236
+ scores = indexer(main KV) # all causally visible positions
237
+ topk = top 512 positions # this layer's own selection
238
+ blocks = 2,048 blocks of 8 positions # block score = max of its positions
239
+ pool = 16,384 candidate positions # later indexers search only here`,
240
+ },
241
+ bullets: [
242
+ 'For a fixed pool size, the per-query cost of every deeper indexer is bounded independently of context length.',
243
+ 'The restriction is training-aware: the same candidate restriction is applied in training and inference, so deeper indexers are optimized under the search domain they meet in production.',
244
+ 'The first Full Mode layer still pays for a full-range scan, so hierarchical indexing shrinks the tail of the cost rather than the whole of it.',
245
+ 'Worked example: at one million tokens of context, a deeper indexer scores 16,384 candidates instead of 1,000,000, which is about 1.6 percent of the range.',
246
+ ],
247
+ },
248
+ ],
249
+ },
250
+ {
251
+ id: 'mhc',
252
+ title: 'Single-Pass mHC',
253
+ icon: Combine,
254
+ hasArt: true,
255
+ intro:
256
+ 'One idea first: borrow the previous block mixing weights and the residual gets read once instead of twice. mHC keeps n residual streams between adjacent blocks, and the original implementation read those streams twice per block because input mixing had to wait on a reduction. Shifting the coefficients by one block removes that dependency and halves activation traffic to the theoretical ideal. It is like signing the previous cover sheet so the current page never waits on the printer.',
257
+ sections: [
258
+ {
259
+ key: 'multi-pass',
260
+ heading: 'Why the residual was read twice',
261
+ body: 'The ideal residual map reads and writes (n + 1)d values, a lower bound of (2n + 2)d. DeepSeek-V4 ran the update as three sequential kernels, each waiting on the one before it, and paid (4n + 4)d: twice the lower bound. The residual update needs no reduction over the hidden dimension, so two of the three stages could share one traversal of the residual, but input mixing cannot join them while its coefficients stay unavailable until every hidden tile has been reduced.',
262
+ code: {
263
+ language: 'plaintext',
264
+ title: 'mhc kernels',
265
+ source: `# three dependent kernels, (4n + 4)d activation traffic
266
+ X_l = B[l-1] @ X[l-1] + C[l-1] @ Y[l-1] # residual update
267
+ A,B,C = H(X_l) # coefficient prediction
268
+ X_hat_l = A[l] @ X_l # input mixing
269
+
270
+ # worked example, n = 4: 20d moved per block vs 10d ideal`,
271
+ },
272
+ },
273
+ {
274
+ key: 'one-block-shift',
275
+ heading: 'Shift the coefficients by one block',
276
+ body: 'Single-Pass mHC lets each block consume the mixing coefficients produced by the previous one. Input mixing now reads A[l-1] instead of A[l], so it no longer depends on a reduction over X_l, and every tile can be used immediately for both input mixing and coefficient prediction. The measured quality cost of the shift is negligible, which makes it a free read against a bounded ideal.',
277
+ code: {
278
+ language: 'plaintext',
279
+ title: 'single-pass mhc',
280
+ source: `X[l+1] = B[l] @ X_l + C[l] @ F_l(A[l-1] @ X_l)
281
+ A,B,C = H(X_l) # no dependency on this block's own coefficients`,
282
+ },
283
+ },
284
+ {
285
+ key: 'mega-mhc',
286
+ heading: 'Mega-mHC',
287
+ body: 'For deployment, residual update, input mixing, and coefficient prediction collapse into one kernel that processes X_l in tiles along the hidden dimension, each tile immediately feeding both the mixed input and the accumulators for the next block coefficients, with input pre-norm and FP8 conversion folded in. The residual is read once and written once, which halves the activation memory traffic of the original four-kernel implementation.',
288
+ bullets: [
289
+ 'Mega-mHC implements mHC at (3n + 2)d and Single-Pass mHC at (2n + 2)d, the ideal map.',
290
+ 'Pre-training keeps the multi-kernel path, since the shift only changes which mixing coefficients a block applies.',
291
+ 'The mHC expansion factor is 4, with 20 Sinkhorn-Knopp iterations.',
292
+ ],
293
+ },
294
+ ],
295
+ },
296
+ {
297
+ id: 'auxiliary',
298
+ title: 'Engram, DSpark, FP4',
299
+ icon: Boxes,
300
+ hasArt: true,
301
+ intro:
302
+ 'One idea first: what the attention core cannot save, memory, drafting, and precision save instead. Engram is a sparsely accessed conditional memory, a wall of sticky notes the model looks up instead of computing. DSpark is a speculative drafter that sketches five tokens at once and checks how far the sketch survives. FP4 is the same archive in a smaller box: a four-bit main KV cache.',
303
+ sections: [
304
+ {
305
+ key: 'engram',
306
+ heading: 'Engram',
307
+ body: 'Engram adds 196B parameters of conditional memory, split evenly across two modules at layers 1 and 14, to decouple memorization from computation. It follows the earlier design (tokenizer compression, multi-head hashing, context-aware gating, multi-branch integration) with two changes: the short causal convolution is dropped because its gains do not justify the inference complexity, and the embedding update moves to momentum plus Sinkhorn balancing. Memory without matmuls is the whole point: a lookup into a giant table costs far less than computing the same fact through dense layers.',
308
+ bullets: [
309
+ 'Each module uses N-gram orders 2, 3, and 4, with 8 hash heads and a total embedding dimension of 2048 per order.',
310
+ 'Each head indexes a table of roughly 16M entries, with table sizes chosen to be distinct primes.',
311
+ 'Embedding tables and key and value projections run in FP8.',
312
+ 'Deterministic addressing lets embeddings prefetch from host memory over background RDMA, with the first module overlapping the first Transformer block.',
313
+ ],
314
+ },
315
+ {
316
+ key: 'dspark',
317
+ heading: 'DSpark',
318
+ body: 'DSpark is a speculative decoding module combining semi-autoregressive drafting with confidence-scheduled verification. Three Transformer blocks with a 128-token sliding window produce base logits for five draft positions in one forward pass, a lightweight Markov head models the dependencies between those drafts, and a confidence head predicts per-position acceptance probabilities that feed a scheduler choosing the verification length per request. It plays like a chess sketch artist: draw five moves at once, estimate how far the line survives, then verify exactly that far.',
319
+ bullets: [
320
+ 'The scheduler combines predicted prefix survival with profiled engine throughput curves to maximize expected system-wide token throughput at the current load.',
321
+ 'Unlike the MTP module of DeepSeek-V3, DSpark is trained after pre-training with the backbone frozen, then alongside the backbone in post-training without gradients flowing back into it.',
322
+ 'That keeps DSpark aligned with the evolving policy, so it accelerates both online serving and rollout generation for RL and on-policy distillation.',
323
+ ],
324
+ },
325
+ {
326
+ key: 'fp4',
327
+ heading: 'FP4 main KV cache',
328
+ body: 'DeepSeek-V4 already used quantization-aware training for FP4 indexer queries and keys. V4.1-Flash extends QAT to the main KV cache, where the point is storage rather than matrix multiplication throughput, and dequantizes cached values before attention so accuracy does not depend on native FP4 matmul support. The format is the OCP-standard MXFP4, chosen to cover as many hardware platforms as possible.',
329
+ code: {
330
+ language: 'plaintext',
331
+ title: 'fp4 layout',
332
+ source: `element FP4 E2M1
333
+ scale one E4M3 per 16 channels, no second-level global scale
334
+ quantize after RoPE, QAT during post-training
335
+ range 448 * 6 = 2688, far above the observed cache magnitude
336
+ SWA KV FP8, retained for its sensitivity to quantization`,
337
+ },
338
+ bullets: [
339
+ 'Dropping the second-level global scale costs nothing measurable: the largest trained RMSNorm weight magnitude is about 1, the rotated 512-channel KV latent stays near sqrt(512) at about 22.6, and the largest magnitude observed in training is around 10.',
340
+ 'Quantizing after RoPE rather than before is a deliberate trade: quantizing earlier helps accuracy marginally and would add decode overhead.',
341
+ 'Against the FP8 main KV cache of DeepSeek-V4, this nearly halves storage in HBM and when the cache is offloaded to SSD.',
342
+ 'Worked example: the format ceiling of 2688 sits more than 100x above the largest magnitude seen in training (about 10), so dropping the second-level global scale costs nothing measurable.',
343
+ ],
344
+ },
345
+ ],
346
+ },
347
+ ],
348
+ },
349
+ {
350
+ title: 'Infrastructure',
351
+ chapters: [
352
+ {
353
+ id: 'training-infra',
354
+ title: 'Training infrastructure',
355
+ icon: Server,
356
+ intro:
357
+ 'One idea first: the training system exists to serve the architecture. Million-token multimodal steps must stay fed, attention state shared across layers must survive pipeline stage boundaries, and giant embedding tables must be reachable from far-away compute. Three mechanisms cover the three needs: overlapped I/O, shadow replicas, and scheduled prefetch.',
358
+ sections: [
359
+ {
360
+ key: 'multimodal',
361
+ heading: 'Multimodal training',
362
+ body: 'The vision encoder is first optimized against a contrastive objective, where the loss over a full batch forces both modalities to be all-gathered across data-parallel ranks. The text gradient needs only the gathered visual features and the visual gradient needs only the gathered text features, so each all-gather hides behind useful computation instead of stalling the pipeline.',
363
+ code: {
364
+ language: 'plaintext',
365
+ title: 'contrastive overlap',
366
+ source: `Forward(V) -> Forward(T) || AllGather(V) -> grad_Text
367
+ -> Backward(T) || AllGather(T) -> grad_Vision -> Backward(V)
368
+
369
+ # A || C means computation A overlapped with communication C`,
370
+ },
371
+ bullets: [
372
+ 'End-to-end parallelism replicates the vision encoder outside the LLM parameter tree and splits each step into vision forward, LLM forward and backward, and vision backward, so the LLM phase keeps the parallel strategy of text-only training.',
373
+ 'Balanced image sharding spreads the images of one ultra-long sequence across context-parallel ranks with each image loaded exactly once; the load-hiding criterion reduces to per-token quantities and is therefore independent of sequence length and cluster size.',
374
+ 'During RL rollout, images transfer to the inference engine incrementally and the engine CPU-side decoding and preprocessing outputs are cached on a distributed file system for reuse across rollouts and later training.',
375
+ 'Worked example: in the load-hiding check, the token count N cancels out, so a one-million-token sequence hides I/O exactly as well as a one-thousand-token one. Only per-token bytes and per-token compute matter.',
376
+ ],
377
+ },
378
+ {
379
+ key: 'attention-sharing',
380
+ heading: 'Attention sharing across stages',
381
+ body: 'Layers that share attention components can land on different pipeline stages, which makes direct module reuse incompatible with stage-local execution. Three mechanisms keep CSA2 trainable under an ordinary pipeline schedule, and the first works like theater understudies: a lightweight replica stands on every stage while one logical owner holds the true parameters.',
382
+ bullets: [
383
+ 'Shadow indexers place a lightweight executable replica on each participating stage while a single logical owner keeps the shared parameters, handles optimization and checkpointing, and keeps replicas consistent through parameter synchronization and gradient aggregation.',
384
+ 'Pipeline payload extensions carry the intermediate representations and sparse routing information that downstream consumers need across a pipeline boundary, partitioned consistently with context parallelism.',
385
+ 'Micro-batch-level shared-state management tracks the states of concurrently active micro-batches across forward execution, activation recomputation, and backward propagation, keeping each state alive until its final consumer finishes and releasing it promptly after.',
386
+ ],
387
+ },
388
+ {
389
+ key: 'engram-training',
390
+ heading: 'Engram at scale',
391
+ body: 'Engram tables are partitioned by row across dedicated process groups whose size trades per-device memory against the communication scope of each lookup, with optimizer states sharded across replicas of every partition. Lookup indices depend only on the input token sequence, so prefetch for the whole local batch starts before each pipeline stage touches its microbatches; embedding gradients are buffered during backward and returned to their owning ranks after the backbone backward pass.',
392
+ bullets: [
393
+ 'Prefetch and gradient transfers are scheduled to overlap the vision encoder forward and backward passes.',
394
+ 'Embeddings are stored and fetched in FP8, with retrieved values and scaling factors handed straight to the following GEMM.',
395
+ 'Sinkhorn normalization keeps row and column scaling vectors across iterations so the full normalized matrix is never rewritten, and row normalization plus partial column statistics are fused into one kernel.',
396
+ 'During RL rollouts the tables stay resident in GPU memory, which reduces host memory pressure and the fragmentation that causes host out-of-memory failures.',
397
+ ],
398
+ },
399
+ ],
400
+ },
401
+ {
402
+ id: 'inference',
403
+ title: 'Inference system',
404
+ icon: Cpu,
405
+ hasArt: true,
406
+ intro:
407
+ 'One idea first: conceptually complex, mechanically short. Most layers run a short fixed list of fused kernels, and Encoder-Prefill-Decode disaggregation lets vision encoding, prefill, and decoding scale and overlap on their own terms.',
408
+ sections: [
409
+ {
410
+ key: 'kernels',
411
+ heading: 'A short kernel flow',
412
+ body: 'Fusion encapsulates the intricate operations and keeps hardware pipelined inside a handful of kernels: the fused RoPE-attention-RoPE-cast kernel in FlashMLA, the Mega-Gate, Mega-mHC, and Mega-MoE kernels in DeepGEMM, the TileKernels set, and the TopK kernel in DeepSelect. The vast majority of the stack runs in Reuse Mode at 15 kernels during prefill and 11 during decode: a food-truck menu with fifteen steps in and eleven steps out, the same result every time.',
413
+ code: {
414
+ language: 'plaintext',
415
+ title: 'reuse mode layer',
416
+ source: `prefill 15 kernels
417
+ decode 11 kernels`,
418
+ },
419
+ },
420
+ {
421
+ key: 'epd',
422
+ heading: 'Encoder, prefill, decode',
423
+ body: 'Deployment adopts Encoder-Prefill-Decode disaggregation, so vision encoding, prefill, and decoding scale independently and overlap in execution. That separation is what makes the two replay paths of SWA Bounded Replay practical: the encoder side and the decoder side are reconstructed in different processes against the same cached global KV.',
424
+ },
425
+ {
426
+ key: 'persistent-kv',
427
+ heading: 'Persistent KV cache management',
428
+ body: 'Under identical workloads the persistent KV cache of V4.1-Flash is about 1/8 of DeepSeek-V4, and two multiplicative factors explain it: the persistent cache no longer stores SWA KV, which almost halves it, and the global KV it retains is compressed to 1/4 through architecture and precision. In DeepSeek-V4, SWA KV was nearly half the persistent capacity, cached only at the end of the prompt and the end of the output under an LRU policy shared with global KV.',
429
+ bullets: [
430
+ 'SWA KV moves out of the persistent cache into a distributed memory pool provisioned from 10% of host DRAM per machine, where a lifetime of minutes lets expired entries recycle immediately for new sessions.',
431
+ 'Global KV stays in the persistent cache with a guaranteed lifetime of at least 72 hours, matching its long-tail reuse pattern.',
432
+ 'The misses that eviction causes are affordable because of Encoder SWA Bounded Replay, which turns a catastrophic miss into a graceful, inexpensive degradation.',
433
+ 'Worked example: the 1/8 is multiplicative, about 1/2 from evicting SWA KV times 1/4 from compressing the global KV that remains.',
434
+ ],
435
+ },
436
+ ],
437
+ },
438
+ {
439
+ id: 'swa-replay',
440
+ title: 'SWA Bounded Replay',
441
+ icon: RefreshCw,
442
+ hasArt: true,
443
+ intro:
444
+ 'One idea first: replay 128 tokens, not 5,120. Exact SWA reconstruction needs L x n_win tokens of replay; bounded replay does the work with n_win and accepts approximate state. That trade is what lets SWA KV leave the persistent cache entirely.',
445
+ sections: [
446
+ {
447
+ key: 'the-trade',
448
+ heading: 'Replay only the window',
449
+ body: 'SWA dependencies accumulate across layers, so exactly reconstructing the SWA KV of L layers would mean replaying L x n_win tokens. Bounded replay replays only the most recent n_win tokens and truncates SWA to that replay segment: for a replay starting at position s, a query at position i attends to SWA keys between max(s, i - W + 1) and i. The state is approximate by construction, and the measurements say the approximation is cheap. Worked example: exact replay for 40 layers costs 40 times 128, which is 5,120 tokens of work, while bounded replay costs 128, a 40x cut.',
450
+ code: {
451
+ language: 'plaintext',
452
+ title: 'replay rule',
453
+ source: `# for a replay starting at position s
454
+ attend(i): SWA keys in [max(s, i - W + 1), i]`,
455
+ },
456
+ },
457
+ {
458
+ key: 'encoder-path',
459
+ heading: 'Encoder SWA Bounded Replay',
460
+ body: 'When encoder SWA KV is missing, the last n_win tokens of the cached prefix are replayed together with the uncached suffix. The replayed tokens regenerate only SWA KV, reusing the cached global KV without recomputing or overwriting it, while the uncached suffix generates both. It is like re-reading only the last page of the previous chapter to recover your place. This is what lets prefix caching depend on global KV alone, and therefore what lets SWA KV leave the persistent cache.',
461
+ bullets: [
462
+ 'The replayed prefix state is approximate, so global KV and SWA KV for the uncached suffix depend on the cache-hit position and are not mathematically identical across positions.',
463
+ 'Experiments across diverse boundary conditions show the bounded replay barely compromises response quality.',
464
+ 'The alternative, DeepSeek-V4 Zero SWA Caching, needed a full forward pass over L x n_win tokens, whose cost proved prohibitive in production.',
465
+ ],
466
+ },
467
+ {
468
+ key: 'decoder-path',
469
+ heading: 'Decoder SWA Bounded Replay',
470
+ body: 'Under CED, decoder global KV comes from the encoder, so the only obstacle to ending prefill at the encoder is decoder SWA KV, which comes from each decoder layer own hidden states and is needed by the first decode steps. Exact reconstruction would run the decoder layers over the last L/2 x n_win prompt tokens: 20 layers over 2,560 tokens, expensive when a short uncached suffix follows a long cached prefix. Bounded replay instead replays the last n_win tokens of the prompt at every prefill, feeds their encoder outputs through the decoder under the same SWA truncation, and uses the resulting SWA KV for decoding only, never for prefix caching.',
471
+ bullets: [
472
+ 'The reconstructed decoder SWA KV is not mathematically equivalent to a full decoder forward pass, with negligible measured impact on response quality.',
473
+ 'The same replay is simulated during post-training, so the model is trained under the approximation it will meet in production.',
474
+ 'Decoder bounded replay bounds the decoder forward pass to n_win tokens and nearly halves total prefill computation.',
475
+ ],
476
+ },
477
+ ],
478
+ },
479
+ ],
480
+ },
481
+ {
482
+ title: 'Training and evaluation',
483
+ chapters: [
484
+ {
485
+ id: 'pre-training',
486
+ title: 'Pre-training',
487
+ icon: Database,
488
+ intro:
489
+ 'One idea first: 45T tokens, sparse from step one, long from token 34T. Sparse attention trains from scratch at 64K sequence length with no dense warmup, and the sequence length extends to one million tokens at 34T of the 45T-token run. The data pipeline, not a new optimizer, is what the base model results credit.',
490
+ sections: [
491
+ {
492
+ key: 'data',
493
+ heading: 'Data construction',
494
+ body: 'Text curation targets the interactions between corpora rather than sample-level quality, and it filters out model-generated content with limited information gain, including outputs from weaker models and low-quality machine translation. Such content counts as implicit duplication, reformulating existing information in ways that turn detrimental over long horizons, like a curriculum committee throwing out photocopies of photocopies. The corpus adds recent code from newly released repositories, commits, libraries, and frameworks to reflect contemporary software engineering.',
495
+ bullets: [
496
+ 'Multimodal data comes from image-text pairs, interleaved image-text data, and domain-specific sets, cleaned in native form rather than synthesized at scale.',
497
+ 'Interleaved construction runs in progressively more expensive stages, ending with strict quality scoring by SmolVLM; documents filtered out are partly recycled into additional image-text pairs.',
498
+ 'Domain-specific data covers fine-grained visual perception, OCR, long-tail knowledge, and image-code pairs with computer-use trajectories.',
499
+ 'The final corpus is a 7:1 token ratio of text-only to multimodal data, with ultra-long documents pre-split before mixing and a best-fit packing padding rate of at most 10^-4. Worked example: a 7:1 ratio means roughly one multimodal token in every eight.',
500
+ ],
501
+ },
502
+ {
503
+ key: 'setups',
504
+ heading: 'Training setups',
505
+ body: 'The model trains on 45T tokens of multimodal data with no instability, keeping the batch size fixed at 100.6M tokens throughout. Sparse attention trains from scratch at a 64K sequence length with no dense attention warmup, and the sequence length extends to 1M at 34T tokens: short drills first, marathon context for the final stretch.',
506
+ code: {
507
+ language: 'plaintext',
508
+ title: 'pre-training setup',
509
+ source: `tokens 45T multimodal
510
+ batch size 100.6M tokens, fixed
511
+ learning rate warmup 2000 steps, 2.6e-4 to 28T,
512
+ cosine decay to 2.6e-5 by 40T, held to 45T
513
+ sequence length 64K from scratch, 1M from 34T
514
+ Muon momentum 0.95, weight decay 0.1, RMS rescaled to 0.18
515
+ AdamW beta1 0.9, beta2 0.95, eps 1e-20, weight decay 0.1
516
+ Sinkhorn update K = 11, tau = 1e-3, eps = 1e-20
517
+ Engram learning rate scaled by 5x`,
518
+ },
519
+ bullets: [
520
+ 'Muon covers the weight matrices of linear transformations, the Engram projection layers, and the vision-language projector.',
521
+ 'Head-wise Muon splits query and key weights by head before the update, giving each head its own preconditioner, which outperforms vanilla Muon.',
522
+ 'Sinkhorn-balanced updates replace Newton-Schulz orthogonalization for the Engram tables, token embedding, and prediction head, needing only a momentum buffer while outperforming Adam there.',
523
+ 'Auxiliary-loss-free load balancing uses a bias update speed of 0.001 for image and text tokens, with a small sequence-level balance loss at weight 0.0001.',
524
+ ],
525
+ },
526
+ {
527
+ key: 'vision-encoder',
528
+ heading: 'Training the vision encoder',
529
+ body: 'DeepSeek-ViT is built from scratch on the Vision Transformer with three changes for this stack: 2D-RoPE replaces absolute positional embeddings so arbitrary resolutions work, the patch embedding convolution becomes a linear projection for Muon compatibility, and RMSNorm with SwiGLU handles normalization and activation. Its own training runs in two stages, cheap wide pretraining first and expensive high-resolution tuning second.',
530
+ bullets: [
531
+ 'Contrastive pre-training optimizes a sigmoid contrastive loss on approximately 47B image-text pairs at a maximum resolution of 224x224, preserving aspect ratio.',
532
+ 'Autoregressive fine-tuning attaches the encoder to a 4B MoE LLM and trains on 236B tokens of captions, alt text, charts, and OCR, constraining resolution between 544x544 and 1344x1344.',
533
+ 'The LLM is discarded afterwards, keeping only the encoder and the same input-resolution policy for the main pre-training pipeline. Worked example: contrastive pretraining caps at 224 pixels per side while fine-tuning reaches 1344, a 6x resolution ladder per side.',
534
+ ],
535
+ },
536
+ {
537
+ key: 'base-eval',
538
+ heading: 'Base model evaluation',
539
+ body: 'DeepSeek-V4.1-Flash-Base is evaluated against DeepSeek-V4-Flash-Base and DeepSeek-V4-Pro-Base across world knowledge, language understanding and reasoning, coding and mathematics, long context, and multimodal ability. It matches DeepSeek-V4-Pro-Base on knowledge, reasoning, and coding and reports 5% to 10% gains on held-out evaluations, using only 1/3 of the total parameters and 1/4 of the activated ones.',
540
+ bullets: [
541
+ 'Multimodal scores with no counterpart in the earlier base models: MMMU-Pro 56.5, CVBench 77.9, DocVQA 95.6, and RefCOCO average 86.0.',
542
+ 'LongBench-V2 lands at 45.2 against 44.7 for DeepSeek-V4-Flash-Base and 51.5 for DeepSeek-V4-Pro-Base.',
543
+ 'On held-out internal corpora covering documentation, proprietary code, and academic material, the base model reports the lowest bits-per-byte of the three, which is the strongest signal of the data pipeline rather than of routing or architecture.',
544
+ ],
545
+ },
546
+ ],
547
+ },
548
+ {
549
+ id: 'post-training',
550
+ title: 'Post-training',
551
+ icon: Sparkles,
552
+ intro:
553
+ 'One idea first: no new optimizer tricks, all gains from better fuel. Supervised fine-tuning, reinforcement learning, and on-policy distillation follow the standard practice used for DeepSeek-V4. All the work went into what the model trains on: synthesized tasks, scaled rollouts, and a controllable effort dial that trades tokens for accuracy.',
554
+ sections: [
555
+ {
556
+ key: 'task-synthesis',
557
+ heading: 'Synthesizing tasks and environments',
558
+ body: 'Each task is a triplet of problem, environment, and verification system, scored on difficulty and correctness, and those two scores double as reward signals for iteratively training the model to construct better tasks of its own. Every reuse of a task in a new RL run feeds fresh trajectory evidence back into quality re-audit, so the task factory inspects its own output on every shift.',
559
+ bullets: [
560
+ 'General agents get mocked tools that reproduce real interfaces, schemas, and behavioral constraints, built from voluntarily returned interaction data, plus failure cases replayed as single-turn and multi-turn environments.',
561
+ 'Coding agents draw on internal sessions filtered for complexity and deduplicated by trajectory, and on public GitHub repositories above a star threshold.',
562
+ 'Construction is collaborative: one agent judges whether a project builds and can be verified in a container, several agents attempt the task, an inspection agent checks for environment faults and hackability, and a repair agent fixes what it finds before re-verification.',
563
+ ],
564
+ },
565
+ {
566
+ key: 'rl-scale',
567
+ heading: 'RL at scale, with a controllable effort dial',
568
+ body: 'Reinforcement learning scales in two directions, training compute and the number of scaffolds, and performance keeps improving with cumulative RL steps within one scaffold, across variants of the same scaffold, and across heterogeneous scaffolds. Checkpoints from runs along different optimization paths are merged to reinitialize successive runs, which aggregates parallel RL compute and improves both task performance and token efficiency.',
569
+ code: {
570
+ language: 'plaintext',
571
+ title: 'effort prompt',
572
+ source: `Reasoning Effort: {effort} (range 1-100; higher values request more
573
+ thorough reasoning)`,
574
+ },
575
+ bullets: [
576
+ 'Responses sampled at the same prompt and effort form a group, and rewards are mean-centered inside the group, so effort levels are never compared against each other directly.',
577
+ 'A length penalty whose coefficient decays exponentially with effort makes lower effort levels press harder for brevity while higher levels permit more computation.',
578
+ 'Public API tiers map onto the scalar directly: max is 100, high is 75, and low is 50, on the same weights and decoding configuration.',
579
+ 'Raising effort from 25 to 100 lifts average Pass@1 on eight reasoning benchmarks from 67.1% to 76.3%, DeepSWE v1.1 from 66.0% to 74.2%, and Terminal-Bench 2.1 from 82.4% to 90.6%, for roughly 2.5x more output tokens.',
580
+ 'Worked example: the 60 to 80 band already recovers most of the accuracy of the maximum setting at less than half its token budget, so reserve effort 100 for the hardest tasks.',
581
+ ],
582
+ },
583
+ {
584
+ key: 'async-infra',
585
+ heading: 'Asynchronous post-training',
586
+ body: 'Rollout and training are colocated on the same devices and time-share execution, removing manual resource tuning between the two phases. Dispatch granularity took three attempts to settle: batch-level dispatch made training metrics oscillate, prompt-level dispatch stalled on long-tail samples inside a group, and sample-level dispatch, which releases a new prompt once enough samples complete to fill its GRPO group, holds rollout concurrency steady. It runs like a kitchen that fires the next order the moment any burner frees up, not when the whole table is done.',
587
+ bullets: [
588
+ 'Length bias is handled by per-dataset concurrency limits and by discarding early-returned short samples, which smooths the transition into the steady-state length distribution.',
589
+ 'Off-policy drift is bounded by dispatch and waiting logic and by a loss mask that drops tokens whose staleness exceeds the bound.',
590
+ 'Token-level interruption stops generation at any token boundary, and rollout state is persisted at token granularity so an interrupted sample resumes where it left off without re-prefilling.',
591
+ 'The final on-policy distillation stage distills from over 40 teacher models across all domains, supporting architecturally heterogeneous teachers and switching between them at negligible cost.',
592
+ ],
593
+ },
594
+ {
595
+ key: 'dsec',
596
+ heading: 'Running agents at massive scale',
597
+ body: 'DSec is the production sandbox platform behind this training, initially built to address heterogeneous execution environments, image distribution, isolation backends, density, trajectory logging, and safe resumption. V4.1 training pushed demand to millions of concurrent sandbox instances spanning harnesses, platforms, repositories, dependencies, and task-specific services, which moved the bottlenecks to datacenter scalability, isolation, per-node density, and the containment of misbehaving agents.',
598
+ bullets: [
599
+ 'Compute scales by sharding machines into scale units, with a custom placement engine trading strong global consistency for scalability: unsynchronized replicas make good-enough placements from recent measurements, and each node enforces a hard admission constraint on the decisions it receives.',
600
+ 'Sub-NUMA partitioning binds each worker VM to its own NUMA domain, raising supported density from roughly 1,000 to more than 2,500 concurrent live containers per physical node before measurable end-to-end degradation, a 2.5x density gain.',
601
+ 'A latency-sensitive execution class applies SCHED_IDLE to non-sensitive tasks and core scheduling to keep only same-priority tasks on sibling hyperthreads, so background load cannot distort time-sensitive evaluations.',
602
+ 'Agents that attempt reward hacking are contained by per-sandbox AppArmor profiles and fine-grained eBPF network policies, and a crash is treated as a failed trajectory with a repercussion signal reported to the RL framework.',
603
+ ],
604
+ },
605
+ ],
606
+ },
607
+ {
608
+ id: 'conclusion',
609
+ title: 'Conclusion',
610
+ icon: Flag,
611
+ hasArt: true,
612
+ intro:
613
+ 'One idea first: frontier-level agency at a fraction of the footprint, with the failure modes named, not hidden. DeepSeek-V4.1-Flash matches or beats frontier models on the vast majority of benchmarks, and the report is explicit about where the evidence stops: sparse retrieval at long context, state reconstruction at cache boundaries, and the hardest reasoning edges.',
614
+ sections: [
615
+ {
616
+ key: 'results',
617
+ heading: 'What the compression bought',
618
+ body: 'The gains over DeepSeek-V4-Flash concentrate in agentic work. DeepSWE v1.1 reaches 74.2% resolved, up from 54.4% and above Opus-5 at 74.0% and GPT-5.6 Sol at 73.0%; Terminal-Bench 2.1 reaches 90.6% against 89.1% for Opus-5 and 88.2% for GLM-5.3; Automation-Bench reaches 54.8% and Agents Last Exam 31.8%. Read each score as beats the previous generation and trades blows with the frontier, at 8B to 16B activated parameters.',
619
+ bullets: [
620
+ 'Reasoning: a Codeforces rating of 3471, above DeepSeek-V4-Pro at 3348 and DeepSeek-V4-Flash at 3289, and a MathArena Apex Pass@1 of 65.6%, matching Kimi-K3 and above DeepSeek-V4-Pro at 65.3%.',
621
+ 'Cyber security: CyberGym 88.1% and SEC-Bench Pro 62.8%, a new open-source state of the art for dual-use capability that the report asks the community to apply responsibly.',
622
+ 'Multimodal: strong results on visual reasoning and professional chart interpretation, with a measurable gap remaining against leading closed-source systems.',
623
+ 'Effort economics: the 60 to 80 band recovers most of the accuracy of the maximum setting at less than half its token budget, while the final step to 100 lengthens trajectories by 1.6x to 1.8x for marginal gains.',
624
+ ],
625
+ },
626
+ {
627
+ key: 'scaffolds',
628
+ heading: 'Scaffolds and multi-agent',
629
+ body: 'Agentic ability transfers across harnesses rather than depending on one. At maximum effort, eight configurations from six scaffold families land between 65.5% (OpenCode) and 74.2% (mini-SWE) on DeepSWE v1.1, and between 84.1% (Codex) and 90.6% (DeepSeek Harness in Minimal mode) on Terminal-Bench v2.1, which is consistent with the diversity of environments and tool schemas in the synthesized training data. Worked example: the 8.7-point DeepSWE spread across scaffolds is smaller than the 19.8-point jump over V4-Flash, so the gains travel with the model, not the harness.',
630
+ bullets: [
631
+ 'Multi-agent Agent Team mode outperforms single-agent at every deadline on both benchmarks tested.',
632
+ 'On ProgramBench, Almost@1 rises from 13.59% at one hour to a peak of 30.04% at eight hours, against 12.79% and 20.39% for the single-agent configuration.',
633
+ 'On FrontierSWE v2, Mean@5 rises from 13.50% at one hour to 32.90% at twenty hours, against 10.50% to 28.20% for a single agent.',
634
+ 'Training Agent Team mode rewards task performance and delegation while penalizing derived latency, computed as the critical path of a dependency DAG over execution events.',
635
+ ],
636
+ },
637
+ {
638
+ key: 'limitations',
639
+ heading: 'Limitations',
640
+ body: 'The simplifications create robustness boundaries that are not yet fully characterized. Internal evaluation covers a diverse range of cases without systematic degradation, but no finite suite covers every extreme input: potential selection errors in CSA2 and approximate state reconstruction in SWA Bounded Replay may still degrade capability in untested boundary cases. Think of a bridge rated for every truck tested, with signs posted where heavier trucks have not yet crossed.',
641
+ bullets: [
642
+ 'Stress testing will focus on sparse retrieval over long contexts and on SWA state reconstruction at cache-resumption boundaries.',
643
+ 'Benchmarks are saturating, and benchmark parity does not imply matching frontier capability on the hardest reasoning and edge cases.',
644
+ 'Evaluation hygiene is an active problem: internet access is restricted and git histories are stripped, yet exploit-seeking behavior still appeared, including decompiling core Ubuntu packages during CyberGym.',
645
+ ],
646
+ },
647
+ {
648
+ key: 'availability',
649
+ heading: 'Availability',
650
+ body: 'DeepSeek-V4.1-Flash is released by DeepSeek-AI with checkpoints published on Hugging Face. The report credits the whole team and lists its hundreds of authors alphabetically by first name.',
651
+ bullets: [
652
+ 'Checkpoints: huggingface.co/deepseek-ai/DeepSeek-V4.1-Flash',
653
+ 'Contact: research@deepseek.com',
654
+ ],
655
+ },
656
+ ],
657
+ },
658
+ ],
659
+ },
660
+ ];
661
+
662
+
663
+
664
+ // ─── Scene: the KV cache by generation ───────────────────────────────────────
665
+ // Drawn, not loaded. Inline SVG keeps a template dependency-free and paints in
666
+ // theme tokens instead of a fixed palette, so every scene follows the brand.
667
+
668
+ // Bars sit on a log scale, because the range from 890 bytes to 437 times that
669
+ // does not fit a linear axis. Heights are the log of each generation footprint
670
+ // relative to V4.1-Flash; the smallest keeps a visible floor rather than
671
+ // collapsing to zero, and the labels carry the real ratios.
672
+ const FOOTPRINT_BARS = [
673
+ {
674
+ x: 74,
675
+ height: 120,
676
+ name: 'DeepSeek-V1',
677
+ value: '437x',
678
+ fill: 'var(--dracula-comment)',
679
+ opacity: 0.5,
680
+ },
681
+ {
682
+ x: 176,
683
+ height: 27,
684
+ name: 'V4-Flash',
685
+ value: '4x',
686
+ fill: 'var(--dracula-comment)',
687
+ opacity: 0.75,
688
+ },
689
+ {
690
+ x: 278,
691
+ height: 5,
692
+ name: 'V4.1-Flash',
693
+ value: '890 B',
694
+ fill: 'var(--dracula-cyan)',
695
+ opacity: 1,
696
+ },
697
+ ];
698
+
699
+ const FOOTPRINT_BASELINE = 176;
700
+
701
+ function FootprintScene({alt}: {alt: string}) {
702
+ return (
703
+ <SceneFrame label={alt}>
704
+ {/* Axis line, not text: comment clears the 3:1 graphical floor here. */}
705
+ <path
706
+ d={`M40 ${FOOTPRINT_BASELINE} H360`}
707
+ stroke="var(--dracula-comment)"
708
+ strokeOpacity={0.4}
709
+ />
710
+ {FOOTPRINT_BARS.map(bar => (
711
+ <g key={bar.name}>
712
+ <rect
713
+ x={bar.x}
714
+ y={FOOTPRINT_BASELINE - bar.height}
715
+ width="44"
716
+ height={bar.height}
717
+ rx="2"
718
+ fill={bar.fill}
719
+ fillOpacity={bar.opacity}
720
+ />
721
+ {/* Value label: --color-text-highlight per visual.md ("values in
722
+ highlight"), at full opacity. It was bar.fill at 0.95, and
723
+ comment-family fills at that alpha measure 2.81-3.17:1, under
724
+ the 4.5:1 text floor. */}
725
+ <text
726
+ x={bar.x + 22}
727
+ y={FOOTPRINT_BASELINE - bar.height - 9}
728
+ fontFamily="var(--font-family-mono)"
729
+ fontSize="11"
730
+ textAnchor="middle"
731
+ fill="var(--color-text-highlight)">
732
+ {bar.value}
733
+ </text>
734
+ </g>
735
+ ))}
736
+ {/* Intentional exception: 10px dense diagram annotation inside an SVG
737
+ figure (alt fallbacks on the wrapping role="img" carry the meaning),
738
+ not UI text — the 12px UI floor does not apply here. Still paragraph,
739
+ never comment, and never below full opacity. */}
740
+ <g
741
+ fontFamily="var(--font-family-mono)"
742
+ fontSize="10"
743
+ fill="var(--color-text-paragraph)"
744
+ textAnchor="middle">
745
+ {FOOTPRINT_BARS.map(bar => (
746
+ <text key={bar.name} x={bar.x + 22} y={FOOTPRINT_BASELINE + 16}>
747
+ {bar.name}
748
+ </text>
749
+ ))}
750
+ </g>
751
+ <g
752
+ fontFamily="var(--font-family-mono)"
753
+ fontSize="10"
754
+ fill="var(--color-text-paragraph)">
755
+ <text x="40" y="30">
756
+ Global KV cache per token, log scale
757
+ </text>
758
+ <text x="40" y="208">
759
+ about 4x below V4-Flash, 437x below V1
760
+ </text>
761
+ </g>
762
+ </SceneFrame>
763
+ );
764
+ }
765
+
766
+ // ─── Scene: the causal encoder-decoder ───────────────────────────────────────
767
+
768
+ // One tick per layer, left to right: encoder then decoder. Cyan marks the two
769
+ // sliding-window-only layers, green the encoder CSA2 layers that compute the
770
+ // global KV, yellow the decoder layers that read it. Within a group only the
771
+ // first layer is Full or Reindex, so those ticks carry full opacity.
772
+ const LAYER_TICKS = Array.from({length: 40}, (_, index) => ({
773
+ x: 42 + index * 8,
774
+ isSwaOnly: index < 2,
775
+ // encoder groups of six from layer 2; decoder groups of four from layer 20
776
+ isGroupHead: index < 20 ? index >= 2 && (index - 2) % 6 === 0 : (index - 20) % 4 === 0,
777
+ fill: index < 2 ? 'var(--dracula-cyan)' : index < 20 ? 'var(--dracula-green)' : 'var(--dracula-yellow)',
778
+ }));
779
+
780
+ function ArchitectureScene({alt}: {alt: string}) {
781
+ return (
782
+ <SceneFrame label={alt}>
783
+
784
+ {/* Global KV crosses the encoder boundary instead of being rebuilt in
785
+ every decoder layer, which is the whole point of the diagram. */}
786
+ <path
787
+ d="M120 56 C120 34 280 34 280 52"
788
+ fill="none"
789
+ stroke="var(--dracula-green)"
790
+ strokeOpacity={0.7}
791
+ strokeDasharray="3 3"
792
+ />
793
+ <path d="M280 58 L276 49 L284 49 Z" fill="var(--dracula-green)" fillOpacity={0.7} />
794
+ <text
795
+ x="200"
796
+ y="28"
797
+ fontFamily="var(--font-family-mono)"
798
+ fontSize="10"
799
+ textAnchor="middle"
800
+ fill="var(--color-text-paragraph)">
801
+ global KV projected from H[L/2]
802
+ </text>
803
+
804
+ {LAYER_TICKS.map(tick => (
805
+ <rect
806
+ key={tick.x}
807
+ x={tick.x}
808
+ y="62"
809
+ width="5"
810
+ height="88"
811
+ rx="1"
812
+ fill={tick.fill}
813
+ fillOpacity={tick.isGroupHead ? 1 : 0.34}
814
+ />
815
+ ))}
816
+
817
+ {/* Block brackets: the encoder and the decoder are 20 layers each. */}
818
+ <g stroke="var(--dracula-comment)" strokeOpacity={0.5}>
819
+ <path d="M42 158 H197 M42 158 V163 M197 158 V163" />
820
+ <path d="M202 158 H359 M202 158 V163 M359 158 V163" />
821
+ </g>
822
+ <g
823
+ fontFamily="var(--font-family-mono)"
824
+ fontSize="10"
825
+ fill="var(--dracula-comment)"
826
+ textAnchor="middle">
827
+ <text x="119" y="178">
828
+ encoder, 20 layers
829
+ </text>
830
+ <text x="280" y="178">
831
+ decoder, 20 layers
832
+ </text>
833
+ </g>
834
+
835
+ <g fontFamily="var(--font-family-mono)" fontSize="10" fill="var(--color-text-paragraph)">
836
+ <rect x="42" y="193" width="8" height="8" rx="1" fill="var(--dracula-cyan)" />
837
+ <text x="56" y="201">SWA only</text>
838
+ <rect x="132" y="193" width="8" height="8" rx="1" fill="var(--dracula-green)" />
839
+ <text x="146" y="201">computes global KV</text>
840
+ <rect x="266" y="193" width="8" height="8" rx="1" fill="var(--dracula-yellow)" />
841
+ <text x="280" y="201">reuses global KV</text>
842
+ </g>
843
+
844
+ <text
845
+ x="200"
846
+ y="220"
847
+ fontFamily="var(--font-family-mono)"
848
+ fontSize="10"
849
+ textAnchor="middle"
850
+ fill="var(--color-text-paragraph)"
851
+ fillOpacity={0.7}>
852
+ prefill walks 20 layers at 8B, decode walks 40 at 16B
853
+ </text>
854
+ </SceneFrame>
855
+ );
856
+ }
857
+
858
+ // ─── Scene: the hierarchical sparse indexer ──────────────────────────────────
859
+
860
+ // Eleven block rows of eight positions each, a stand-in for the 2,048 blocks
861
+ // of the real pool. Rows marked as selected carry the cyan slab; the green
862
+ // cells inside them are the positions that survive to Top-K.
863
+ const INDEXER_ROWS: ReadonlyArray<{y: number; picks: number[]}> = [
864
+ {y: 56, picks: []},
865
+ {y: 68, picks: [1, 5]},
866
+ {y: 80, picks: []},
867
+ {y: 92, picks: []},
868
+ {y: 104, picks: [3]},
869
+ {y: 116, picks: [0, 6]},
870
+ {y: 128, picks: []},
871
+ {y: 140, picks: [4]},
872
+ {y: 152, picks: []},
873
+ {y: 164, picks: []},
874
+ {y: 176, picks: [2, 7]},
875
+ ];
876
+
877
+ const INDEXER_CELLS = [0, 1, 2, 3, 4, 5, 6, 7];
878
+
879
+ function IndexerScene({alt}: {alt: string}) {
880
+ return (
881
+ <SceneFrame label={alt}>
882
+
883
+ {INDEXER_ROWS.map(row =>
884
+ row.picks.length > 0 ? (
885
+ <rect
886
+ key={row.y}
887
+ x="62"
888
+ y={row.y - 2}
889
+ width="276"
890
+ height="13"
891
+ rx="2"
892
+ fill="var(--color-data-categorical-cyan)"
893
+ fillOpacity={0.12}
894
+ stroke="var(--color-data-categorical-cyan)"
895
+ strokeOpacity={0.45}
896
+ />
897
+ ) : null,
898
+ )}
899
+
900
+ {INDEXER_ROWS.map(row =>
901
+ INDEXER_CELLS.map(cell => (
902
+ <rect
903
+ key={`${row.y}-${cell}`}
904
+ x={66 + cell * 34}
905
+ y={row.y}
906
+ width="30"
907
+ height="9"
908
+ rx="1"
909
+ fill={
910
+ row.picks.includes(cell)
911
+ ? 'var(--color-data-categorical-green)'
912
+ : 'var(--dracula-comment)'
913
+ }
914
+ />
915
+ )),
916
+ )}
917
+
918
+ {/* Scene text is paragraph, never comment. Comment is disabled text and
919
+ subtle border: it is 3.36:1 on this backdrop and the old
920
+ fillOpacity 0.7/0.85 pushed it to 2.48/3.17, under the 4.5:1 floor.
921
+ Axis and caption text is --color-text-paragraph at full opacity. */}
922
+ <g
923
+ fontFamily="var(--font-family-mono)"
924
+ fontSize="10"
925
+ fill="var(--color-text-paragraph)">
926
+ <text x="40" y="28">
927
+ 2,048 blocks of 8 positions, 16,384 candidates
928
+ </text>
929
+ <text x="40" y="43">
930
+ score everything once, then search only the 16,384 survivors
931
+ </text>
932
+ <rect
933
+ x="40"
934
+ y="196"
935
+ width="8"
936
+ height="8"
937
+ rx="1"
938
+ fill="var(--color-data-categorical-cyan)"
939
+ />
940
+ <text x="54" y="204">block kept by its top score</text>
941
+ <rect
942
+ x="230"
943
+ y="196"
944
+ width="8"
945
+ height="8"
946
+ rx="1"
947
+ fill="var(--color-data-categorical-green)"
948
+ />
949
+ <text x="244" y="204">position in Top-512</text>
950
+ </g>
951
+ </SceneFrame>
952
+ );
953
+ }
954
+
955
+ // ─── Scene: decode FLOPs against context length ──────────────────────────────
956
+
957
+ const CONTEXT_TICKS = ['4K', '16K', '64K', '256K', '1M'];
958
+
959
+ function FlopsScene({alt}: {alt: string}) {
960
+ return (
961
+ <SceneFrame label={alt}>
962
+
963
+ {/* Five ticks for a 256-fold range: equal spacing is the log scale. */}
964
+ <g stroke="var(--dracula-comment)" strokeOpacity={0.18}>
965
+ {CONTEXT_TICKS.map((tick, index) => (
966
+ <path key={tick} d={`M${56 + index * 81} 40 V172`} />
967
+ ))}
968
+ </g>
969
+ <text
970
+ x="56"
971
+ y="26"
972
+ fontFamily="var(--font-family-mono)"
973
+ fontSize="10"
974
+ fill="var(--color-text-paragraph)"
975
+ fillOpacity={0.85}>
976
+ Single-token decode FLOPs against context length
977
+ </text>
978
+ <path d="M56 172 H380" stroke="var(--dracula-comment)" strokeOpacity={0.45} />
979
+ <path d="M56 40 V172" stroke="var(--dracula-comment)" strokeOpacity={0.45} />
980
+
981
+ {/* The baseline the compressed model holds almost flat. */}
982
+ <path
983
+ d="M56 156 H380"
984
+ stroke="var(--dracula-comment)"
985
+ strokeOpacity={0.25}
986
+ strokeDasharray="3 4"
987
+ />
988
+ <path
989
+ d="M56 156 C170 155 270 148 380 132"
990
+ fill="none"
991
+ stroke="var(--dracula-cyan)"
992
+ strokeWidth="2"
993
+ />
994
+ <path
995
+ d="M56 148 C170 143 250 110 312 78 C342 62 364 52 380 46"
996
+ fill="none"
997
+ stroke="var(--dracula-orange)"
998
+ strokeWidth="2"
999
+ />
1000
+
1001
+ <g fontFamily="var(--font-family-mono)" fontSize="10">
1002
+ <text x="378" y="42" textAnchor="end" fill="var(--dracula-orange)">
1003
+ DeepSeek-V4-Flash
1004
+ </text>
1005
+ <text x="378" y="142" textAnchor="end" fill="var(--dracula-cyan)">
1006
+ DeepSeek-V4.1-Flash
1007
+ </text>
1008
+ <text x="56" y="60" fill="var(--color-text-paragraph)">
1009
+ 4K to 1M, 256x context:
1010
+ </text>
1011
+ <text x="56" y="74" fill="var(--color-text-paragraph)">
1012
+ +1/4 decode FLOPs
1013
+ </text>
1014
+ {CONTEXT_TICKS.map((tick, index) => (
1015
+ <text
1016
+ key={tick}
1017
+ x={56 + index * 81}
1018
+ y="188"
1019
+ textAnchor={index === 0 ? 'start' : index === CONTEXT_TICKS.length - 1 ? 'end' : 'middle'}
1020
+ fill="var(--color-text-paragraph)">
1021
+ {tick}
1022
+ </text>
1023
+ ))}
1024
+ <text x="56" y="206" fill="var(--color-text-paragraph)" fillOpacity={0.7}>
1025
+ BF16, FP8, and FP4 ops weighted 1, 0.5, and 0.25
1026
+ </text>
1027
+ </g>
1028
+ </SceneFrame>
1029
+ );
1030
+ }
1031
+
1032
+ // ─── Scene: SWA bounded replay ───────────────────────────────────────────────
1033
+
1034
+ // The prompt strip on top shows what is cached against what has to be
1035
+ // recomputed; the two bars below show why the bounded path is worth the
1036
+ // approximation. Both bars are one window wide, so only the layer count
1037
+ // separates them. The stack is drawn as one hairline per replayed layer, so
1038
+ // it reads as a count rather than as a fill.
1039
+ const REPLAY_LAYER_LINES = Array.from({length: 20}, (_, index) => 98 + index * 2);
1040
+
1041
+ function ReplayScene({alt}: {alt: string}) {
1042
+ return (
1043
+ <SceneFrame label={alt}>
1044
+
1045
+ <g fontFamily="var(--font-family-mono)" fontSize="10" textAnchor="middle">
1046
+ <rect
1047
+ x="40"
1048
+ y="36"
1049
+ width="156"
1050
+ height="24"
1051
+ fill="var(--dracula-comment)"
1052
+ fillOpacity={0.16}
1053
+ stroke="var(--dracula-comment)"
1054
+ strokeOpacity={0.35}
1055
+ />
1056
+ <text x="118" y="52" fill="var(--color-text-paragraph)">
1057
+ cached prefix
1058
+ </text>
1059
+ <rect
1060
+ x="196"
1061
+ y="36"
1062
+ width="54"
1063
+ height="24"
1064
+ fill="var(--dracula-cyan)"
1065
+ fillOpacity={0.25}
1066
+ stroke="var(--dracula-cyan)"
1067
+ strokeOpacity={0.8}
1068
+ />
1069
+ <text x="223" y="52" fill="var(--dracula-cyan)">
1070
+ n_win
1071
+ </text>
1072
+ <rect
1073
+ x="250"
1074
+ y="36"
1075
+ width="110"
1076
+ height="24"
1077
+ fill="var(--dracula-comment)"
1078
+ fillOpacity={0.16}
1079
+ stroke="var(--dracula-comment)"
1080
+ strokeOpacity={0.35}
1081
+ />
1082
+ <text x="305" y="52" fill="var(--color-text-paragraph)">
1083
+ uncached suffix
1084
+ </text>
1085
+ </g>
1086
+ <text
1087
+ x="40"
1088
+ y="80"
1089
+ fontFamily="var(--font-family-mono)"
1090
+ fontSize="10"
1091
+ fill="var(--color-text-paragraph)">
1092
+ a query at i attends to [max(s, i - W + 1), i]
1093
+ </text>
1094
+
1095
+ {/* Exact reconstruction: one replay pass per layer. */}
1096
+ <rect
1097
+ x="40"
1098
+ y="96"
1099
+ width="140"
1100
+ height="40"
1101
+ rx="2"
1102
+ fill="var(--dracula-orange)"
1103
+ fillOpacity={0.18}
1104
+ />
1105
+ <g stroke="var(--dracula-orange)" strokeOpacity={0.55}>
1106
+ {REPLAY_LAYER_LINES.map(y => (
1107
+ <path key={y} d={`M40 ${y} H180`} />
1108
+ ))}
1109
+ </g>
1110
+ <text
1111
+ x="192"
1112
+ y="120"
1113
+ fontFamily="var(--font-family-mono)"
1114
+ fontSize="10"
1115
+ fill="var(--color-text-paragraph)">
1116
+ exact: every layer, L x n_win
1117
+ </text>
1118
+
1119
+ {/* Bounded reconstruction: one window, and nothing above it. */}
1120
+ <rect
1121
+ x="40"
1122
+ y="150"
1123
+ width="140"
1124
+ height="4"
1125
+ rx="1"
1126
+ fill="var(--dracula-cyan)"
1127
+ fillOpacity={0.8}
1128
+ />
1129
+ <text
1130
+ x="192"
1131
+ y="156"
1132
+ fontFamily="var(--font-family-mono)"
1133
+ fontSize="10"
1134
+ fill="var(--color-text-paragraph)">
1135
+ bounded: one window, n_win
1136
+ </text>
1137
+
1138
+ <text
1139
+ x="40"
1140
+ y="196"
1141
+ fontFamily="var(--font-family-mono)"
1142
+ fontSize="10"
1143
+ fill="var(--color-text-paragraph)"
1144
+ fillOpacity={0.7}>
1145
+ SWA KV leaves the persistent cache, misses stay cheap
1146
+ </text>
1147
+ </SceneFrame>
1148
+ );
1149
+ }
1150
+
1151
+ // ─── Scene: Single-Pass mHC, one read instead of two ─────────────────────────
1152
+ // Two bars at expansion factor 4: the original three-kernel path moves 20d of
1153
+ // activation per block against the 10d ideal, and the one-block coefficient
1154
+ // shift closes the gap by letting each tile serve both mixing and prediction.
1155
+ const MHC_BARS = [
1156
+ {
1157
+ name: 'before',
1158
+ detail: '(4n + 4)d = 20d, three kernels',
1159
+ width: 300,
1160
+ fill: 'var(--dracula-orange)',
1161
+ opacity: 0.8,
1162
+ },
1163
+ {
1164
+ name: 'after',
1165
+ detail: '(2n + 2)d = 10d, single pass',
1166
+ width: 150,
1167
+ fill: 'var(--dracula-green)',
1168
+ opacity: 0.9,
1169
+ },
1170
+ ];
1171
+
1172
+ function MhcShiftScene({alt}: {alt: string}) {
1173
+ return (
1174
+ <SceneFrame label={alt}>
1175
+ <text
1176
+ x="40"
1177
+ y="28"
1178
+ fontFamily="var(--font-family-mono)"
1179
+ fontSize="10"
1180
+ fill="var(--color-text-paragraph)"
1181
+ fillOpacity={0.85}>
1182
+ Residual traffic per block, n = 4
1183
+ </text>
1184
+ <text
1185
+ x="360"
1186
+ y="44"
1187
+ fontFamily="var(--font-family-mono)"
1188
+ fontSize="10"
1189
+ textAnchor="end"
1190
+ fill="var(--color-text-paragraph)"
1191
+ fillOpacity={0.7}>
1192
+ shift: read A[l-1], not A[l]
1193
+ </text>
1194
+ {MHC_BARS.map((bar, position) => (
1195
+ <g key={bar.name}>
1196
+ <text
1197
+ x="40"
1198
+ y={66 + position * 68}
1199
+ fontFamily="var(--font-family-mono)"
1200
+ fontSize="10"
1201
+ fill="var(--color-text-paragraph)">
1202
+ {bar.name}
1203
+ </text>
1204
+ <rect
1205
+ x="40"
1206
+ y={74 + position * 68}
1207
+ width={bar.width}
1208
+ height="22"
1209
+ rx="2"
1210
+ fill={bar.fill}
1211
+ fillOpacity={bar.opacity}
1212
+ />
1213
+ <text
1214
+ x="40"
1215
+ y={112 + position * 68}
1216
+ fontFamily="var(--font-family-mono)"
1217
+ fontSize="10"
1218
+ fill="var(--color-text-paragraph)">
1219
+ {bar.detail}
1220
+ </text>
1221
+ </g>
1222
+ ))}
1223
+ <text
1224
+ x="40"
1225
+ y="208"
1226
+ fontFamily="var(--font-family-mono)"
1227
+ fontSize="10"
1228
+ fill="var(--color-text-paragraph)"
1229
+ fillOpacity={0.7}>
1230
+ use last block mixing weights, read the residual once
1231
+ </text>
1232
+ </SceneFrame>
1233
+ );
1234
+ }
1235
+
1236
+ // ─── Scene: FP4 halves the main KV ───────────────────────────────────────────
1237
+ // Two bars on a 2:1 scale: eight bits per value against four. The footer
1238
+ // carries the safety argument, a format ceiling far above anything observed.
1239
+ const FP4_BARS = [
1240
+ {
1241
+ name: 'DeepSeek-V4',
1242
+ value: 'FP8',
1243
+ x: 110,
1244
+ height: 120,
1245
+ fill: 'var(--dracula-comment)',
1246
+ opacity: 0.75,
1247
+ },
1248
+ {
1249
+ name: 'V4.1-Flash',
1250
+ value: 'FP4 E2M1',
1251
+ x: 230,
1252
+ height: 60,
1253
+ fill: 'var(--dracula-green)',
1254
+ opacity: 0.9,
1255
+ },
1256
+ ];
1257
+
1258
+ const FP4_BASELINE = 176;
1259
+
1260
+ function Fp4Scene({alt}: {alt: string}) {
1261
+ return (
1262
+ <SceneFrame label={alt}>
1263
+ <path
1264
+ d={`M40 ${FP4_BASELINE} H360`}
1265
+ stroke="var(--dracula-comment)"
1266
+ strokeOpacity={0.4}
1267
+ />
1268
+ {FP4_BARS.map(bar => (
1269
+ <g key={bar.name}>
1270
+ <rect
1271
+ x={bar.x}
1272
+ y={FP4_BASELINE - bar.height}
1273
+ width="44"
1274
+ height={bar.height}
1275
+ rx="2"
1276
+ fill={bar.fill}
1277
+ fillOpacity={bar.opacity}
1278
+ />
1279
+ <text
1280
+ x={bar.x + 22}
1281
+ y={FP4_BASELINE - bar.height - 9}
1282
+ fontFamily="var(--font-family-mono)"
1283
+ fontSize="11"
1284
+ textAnchor="middle"
1285
+ fill={bar.fill}
1286
+ fillOpacity={0.95}>
1287
+ {bar.value}
1288
+ </text>
1289
+ </g>
1290
+ ))}
1291
+ <g
1292
+ fontFamily="var(--font-family-mono)"
1293
+ fontSize="10"
1294
+ fill="var(--dracula-comment)"
1295
+ textAnchor="middle">
1296
+ {FP4_BARS.map(bar => (
1297
+ <text key={bar.name} x={bar.x + 22} y={FP4_BASELINE + 16}>
1298
+ {bar.name}
1299
+ </text>
1300
+ ))}
1301
+ </g>
1302
+ <g fontFamily="var(--font-family-mono)" fontSize="10" fill="var(--color-text-paragraph)">
1303
+ <text x="40" y="30" fillOpacity={0.85}>
1304
+ Main KV storage per value
1305
+ </text>
1306
+ <text x="40" y="208" fillOpacity={0.7}>
1307
+ four bits per value, same cache: ceiling 2688, largest seen about 10
1308
+ </text>
1309
+ </g>
1310
+ </SceneFrame>
1311
+ );
1312
+ }
1313
+
1314
+ // ─── Scene: what the compression bought on DeepSWE ───────────────────────────
1315
+ // Three bars on a linear 0-80 scale: the previous generation, V4.1-Flash,
1316
+ // and the frontier reference. The caption states the reading plainly.
1317
+ const EVAL_BARS = [
1318
+ {
1319
+ name: 'V4-Flash',
1320
+ value: '54.4%',
1321
+ x: 70,
1322
+ height: 95,
1323
+ fill: 'var(--dracula-comment)',
1324
+ opacity: 0.75,
1325
+ },
1326
+ {
1327
+ name: 'Opus-5',
1328
+ value: '74.0%',
1329
+ x: 170,
1330
+ height: 129,
1331
+ fill: 'var(--dracula-comment)',
1332
+ opacity: 0.5,
1333
+ labelDx: 24,
1334
+ labelDy: 0,
1335
+ },
1336
+ {
1337
+ name: 'V4.1-Flash',
1338
+ value: '74.2%',
1339
+ x: 270,
1340
+ height: 130,
1341
+ fill: 'var(--dracula-green)',
1342
+ opacity: 0.9,
1343
+ },
1344
+ ];
1345
+
1346
+ const EVAL_BASELINE = 176;
1347
+
1348
+ function EvalScene({alt}: {alt: string}) {
1349
+ return (
1350
+ <SceneFrame label={alt}>
1351
+ <path
1352
+ d={`M40 ${EVAL_BASELINE} H360`}
1353
+ stroke="var(--dracula-comment)"
1354
+ strokeOpacity={0.4}
1355
+ />
1356
+ {EVAL_BARS.map(bar => (
1357
+ <g key={bar.name}>
1358
+ <rect
1359
+ x={bar.x}
1360
+ y={EVAL_BASELINE - bar.height}
1361
+ width="48"
1362
+ height={bar.height}
1363
+ rx="2"
1364
+ fill={bar.fill}
1365
+ fillOpacity={bar.opacity}
1366
+ />
1367
+ <text
1368
+ x={bar.x + 24 + (bar.labelDx ?? 0)}
1369
+ y={EVAL_BASELINE - bar.height - 9 + (bar.labelDy ?? 0)}
1370
+ fontFamily="var(--font-family-mono)"
1371
+ fontSize="11"
1372
+ textAnchor="middle"
1373
+ fill={bar.fill}
1374
+ fillOpacity={0.95}>
1375
+ {bar.value}
1376
+ </text>
1377
+ </g>
1378
+ ))}
1379
+ <g
1380
+ fontFamily="var(--font-family-mono)"
1381
+ fontSize="10"
1382
+ fill="var(--dracula-comment)"
1383
+ textAnchor="middle">
1384
+ {EVAL_BARS.map(bar => (
1385
+ <text key={bar.name} x={bar.x + 24} y={EVAL_BASELINE + 16}>
1386
+ {bar.name}
1387
+ </text>
1388
+ ))}
1389
+ </g>
1390
+ <g fontFamily="var(--font-family-mono)" fontSize="10" fill="var(--color-text-paragraph)">
1391
+ <text x="40" y="30" fillOpacity={0.85}>
1392
+ DeepSWE v1.1 resolved
1393
+ </text>
1394
+ <text x="40" y="208" fillOpacity={0.7}>
1395
+ 74.2% beats the frontier at a quarter of the cache
1396
+ </text>
1397
+ </g>
1398
+ </SceneFrame>
1399
+ );
1400
+ }
1401
+
1402
+ function ChapterArt({chapterId}: {chapterId: string}) {
1403
+ if (chapterId === 'abstract') {
1404
+ return (
1405
+ <ChapterArtFrame>
1406
+ <FootprintScene alt="Bars on a log scale showing the global KV cache per token falling from 437 times the DeepSeek-V4.1-Flash footprint at DeepSeek-V1, to 4 times at V4-Flash, to 890 bytes per token at V4.1-Flash" />
1407
+ </ChapterArtFrame>
1408
+ );
1409
+ }
1410
+ if (chapterId === 'at-a-glance') {
1411
+ return (
1412
+ <ChapterArtFrame>
1413
+ <ArchitectureScene alt="One tick per layer across the 40-layer backbone: two sliding-window-only layers, an 18-layer CSA2 encoder, and a 20-layer decoder, with global KV projected across the boundary from the last encoder layer" />
1414
+ </ChapterArtFrame>
1415
+ );
1416
+ }
1417
+ if (chapterId === 'csa2') {
1418
+ return (
1419
+ <ChapterArtFrame>
1420
+ <IndexerScene alt="A grid of block rows in the shared candidate pool, with the blocks kept by their top score highlighted and a few positions inside them marked as the Top-512 selection" />
1421
+ </ChapterArtFrame>
1422
+ );
1423
+ }
1424
+ if (chapterId === 'mhc') {
1425
+ return (
1426
+ <ChapterArtFrame>
1427
+ <MhcShiftScene alt="Two bars comparing residual memory traffic per block at expansion factor 4: twenty units of hidden size for the original three-kernel path against ten for Single-Pass mHC, annotated with the one-block coefficient shift" />
1428
+ </ChapterArtFrame>
1429
+ );
1430
+ }
1431
+ if (chapterId === 'auxiliary') {
1432
+ return (
1433
+ <ChapterArtFrame>
1434
+ <Fp4Scene alt="Two bars showing main KV storage per value halving from FP8 to four-bit FP4, annotated with a format ceiling of 2688 against the largest magnitude seen in training, about 10" />
1435
+ </ChapterArtFrame>
1436
+ );
1437
+ }
1438
+ if (chapterId === 'inference') {
1439
+ return (
1440
+ <ChapterArtFrame>
1441
+ <FlopsScene alt="Line chart of single-token decode FLOPs against context length: the DeepSeek-V4.1-Flash curve stays almost flat from 4K to 1M tokens while the DeepSeek-V4-Flash curve rises steeply" />
1442
+ </ChapterArtFrame>
1443
+ );
1444
+ }
1445
+ if (chapterId === 'swa-replay') {
1446
+ return (
1447
+ <ChapterArtFrame>
1448
+ <ReplayScene alt="A prompt strip split into cached prefix, the replayed window, and the uncached suffix, above two replay-work bars showing a full layer stack against a single window" />
1449
+ </ChapterArtFrame>
1450
+ );
1451
+ }
1452
+ if (chapterId === 'conclusion') {
1453
+ return (
1454
+ <ChapterArtFrame>
1455
+ <EvalScene alt="Bars comparing DeepSWE resolved rates: 54.4 percent for DeepSeek-V4-Flash, 74.2 for DeepSeek-V4.1-Flash, 74.0 for Opus-5" />
1456
+ </ChapterArtFrame>
1457
+ );
1458
+ }
1459
+ return null;
1460
+ }
1461
+
1462
+ export default function TechReport() {
1463
+ return (
1464
+ <ChapteredDoc
1465
+ groups={CHAPTER_GROUPS}
1466
+ defaultChapter="abstract"
1467
+ railHeading="DeepSeek-V4.1-Flash"
1468
+ railSubheading="Technical report tour"
1469
+ railHref={SELF_HASH}
1470
+ railIcon={<Icon icon={ScrollText} size="sm" />}
1471
+ eyebrow="DeepSeek-V4.1-Flash technical report"
1472
+ badge={(chapter, index, total) =>
1473
+ `Chapter ${index + 1} of ${total} · DeepSeek-AI · research@deepseek.com`
1474
+ }
1475
+ art={chapterId => <ChapterArt chapterId={chapterId} />}
1476
+ footer={
1477
+ <Text type="supporting" color="secondary">
1478
+ Checkpoints and the full report:{' '}
1479
+ <Link href={REPORT_URL} target="_blank" type="supporting">
1480
+ huggingface.co/deepseek-ai/DeepSeek-V4.1-Flash
1481
+ </Link>
1482
+ </Text>
1483
+ }
1484
+ />
1485
+ );
1486
+ }