This guide is written for people serving DeepSeek-V4-Flash-0731 or DeepSeek-V4-Flash-Vision-Exp locally — vLLM forks on DGX Spark pairs, mostly — but the encoding facts apply anywhere. Receipts come from our production pair (2× GB10, Anemll runtime, DSpark speculative decoding), including a matched off/low/high/max suite run September 1 on the live Vision-Exp lane.
The reference encoder (encoding/ in the official repos) implements four real states:
| Level | What the encoder emits |
|---|---|
off | Closes the think block immediately — </think> right after the assistant tag. No reasoning tokens at all. |
low | Thinking enabled, no effort prefix. The model's natural reasoning appetite. |
high | A ~476-byte prefix: "Absolute maximum reasoning…" prepended to the prompt. |
max | A ~528-byte prefix: "Beyond maximum — exhaustive, relentless, and uncompromising…" (the em dash is part of the tokenized prompt). |
Two consequences fall straight out of this design:
It's a request, not a limit. Nothing caps how long low thinks or forces max to think longer than high. On our suite, low produced more reasoning than high and max on a counting task (861 chars vs 90/109), and high out-thought max on a coding task (5,968 chars over 34 s vs 2,571 over 16 s). Treat the four names as four different personalities, not a volume knob.
Label mapping varies by stack. Some serving stacks map minimal/low/medium → low and xhigh → max; DeepSeek's hosted API maps medium → high instead. At least one popular GGUF port shipped with max unreachable. If you care which prefix you're getting, dump the rendered prompt from your tokenizer once and check — don't trust the harness label. (Credit to Blackwellboy's Minefield testing for hammering this point independently.)
Matched suite, Vision-Exp on 2× Spark, temperature=0, stream:false, generous max_tokens so nothing fake-truncates. Reasoning arrives in the reasoning field; answer text was essentially identical across levels on three of four fixtures.
| Fixture | off | low | high | max |
|---|---|---|---|---|
| count to 80 — wall | 1.9 s | 6.9 s | 2.8 s | 3.0 s |
| code (palindrome) — wall | 0.5 s | 4.9 s | 34.5 s | 16.4 s |
| 80-word prose — wall | 3.7 s | 18.7 s | 23.3 s | 23.2 s |
| bat-and-ball — wall | 0.4 s | 4.9 s | 0.9 s | 1.1 s |
| prose — tok/s | 29.2 | 60.8 | 61.0 | 53.5 |
| count — tok/s | 83.7 | 77.3 | 64.6 | 61.6 |
Read the last two rows carefully — they're the trap.
If you serve with a drafter (DSpark, DFlash2, MTP), thinking traces are low-entropy text — the drafter predicts them well, acceptance climbs, and headline tok/s goes up. On prose we measured 29 tok/s with thinking off and 54–61 with it on. Keys (@u1tra_instinct) published the same effect: +33% C1 decode at max, driven by draft acceptance going from 34% to 52%.
None of that makes answers arrive sooner. Our prose answer took 3.7 s with thinking off and 19–23 s with it on — same essay either way. The extra tokens were thought. Under speculative decoding, tok/s measures drafter agreement; wall clock measures what your user feels. Publish both or you're benchmarking the wrong thing. And it's workload-dependent: on structured counting, thinking lowered tok/s (83.7 → 61.6) and tripled the wall.
"false"Thinking flags must be JSON booleans. "thinking": "false" is a truthy string in most template logic and can silently turn thinking on. If a "non-thinking" deployment is mysteriously slow, check this first.
"chat_template_kwargs": { "thinking": false } ✓
"chat_template_kwargs": { "thinking": "false" } ✗ (probably thinking!)
max_tokens covers thought + answer + tool markupOne budget pays for everything. DeepSeek designed high/max around 128K–384K output windows; a 4K cap at max can be consumed entirely by reasoning, and the request "finishes" mid-thought with one character of answer. We watched a 64-token cap on low return the letter "P" of "PONG" — the other 63 tokens were inside the reasoner. Keys's guidance of max_tokens ≥ 12288 for max matches our measurements. If outputs look broken at high effort, raise the cap before blaming the model.
A top-level reasoning_effort from any client overrides DEFAULT_THINKING=off on the server. Your carefully-chosen default is a suggestion the first agent framework in your stack will happily trample. Audit what your clients actually send.
With tools present, the encoder keeps all reasoning across tool calls and user turns. One high-effort side branch survives the whole session — a tangent amplifier in agent loops. This is why we run agent lanes at off and opt in per-request rather than the reverse.
Draft acceptance ranged 22%–50% across tasks in Keys's data, and our suite shows tok/s moving in opposite directions on prose vs counting. A single "thinking=X gives Y tok/s" claim without the workload named is not reproducible information.
| Situation | Setting |
|---|---|
| Agent / tool-loop serving default | off, server-side, booleans. Latency and tangent control beat trace quality in loops. |
| Hard reasoning or long coding, interactive | Per-request reasoning_effort: high with max_tokens ≥ 12288. In our data high reasons at least as hard as max. |
| Batch/offline where wall doesn't matter | max if you want it — but measure whether it beats high on your tasks before paying the extra wall. |
| Speed benchmarking | Report wall + tok/s + completion split (reasoning vs answer), per level, per fixture. Name the drafter. |
| Any stack you didn't build | Dump one rendered prompt per level and verify the prefix mapping before trusting the labels. |
deepseek-v4-flash-vision-exp (official weights @ 86f746b3), Anemll dspark-vllm-gx10:0.1.1, vLLM TP=2 across 2× DGX Spark GB10, DSpark drafter active, temperature=0, n=1 per cell — shapes are robust, exact digits are single runs. Full table and method: the companion results post. Encoder mechanics: DeepSeek reference encoding/ module. Community credits: Keys/@u1tra_instinct (thinking-vs-acceptance data), Blackwellboy (label-mapping minefield), Mia AI Lab (the serving recipe this ran on).