Skip to navigation

Reasoning Parsing (Dynamo)

Configure Dynamo’s built-in reasoning parsers for models that emit thinking content

View as Markdown

Some models emit reasoning separately from their final response. Dynamo can split that output into reasoning_content and normal assistant content by configuring --dyn-reasoning-parser on the backend worker. This page covers Dynamo-native parsing; if your model is not listed, use engine fallback, which also explains valid combinations of --dyn-reasoning-parser, --dyn-chat-processor, and --dyn-tool-call-parser.

See Parser Generation Routing for version selection and compatible names.

Configure Reasoning Parsing

Set DYN_PARSER_VERSION in the frontend and backend worker environments before startup: 1 selects legacy V1 parsing, 2 requires a compatible unified V2 parser, and unset or auto prefers compatible V2 with V1 fallback. See Parser Generation Routing for supported configurations and family exceptions.

1

Launch Dynamo backend

Select --dyn-reasoning-parser from the supported list below. The backend can be SGLang, TensorRT-LLM, vLLM, or another installed backend.

For vLLM structured output or SGLang required/named tool choice, also configure the engine’s native --reasoning-parser. The native parser controls when the grammar starts; Dynamo’s parser populates reasoning_content. Parser names can differ between registries.

To inspect the available worker flags, run:

python -m dynamo.<backend> --help

The following example starts an SGLang worker for Qwen3.5:

python -m dynamo.sglang \
--model Qwen/Qwen3.5-4B \
--dyn-tool-call-parser qwen3_coder \
--reasoning-parser qwen3 \
--dyn-reasoning-parser qwen3
Some models need both parsers configured together. Common pairings include:
  • openai/gpt-oss-*: --dyn-tool-call-parser harmony --dyn-reasoning-parser gpt_oss
  • deepseek-ai/DeepSeek-V4-*: --dyn-tool-call-parser deepseek_v4 --dyn-reasoning-parser deepseek_v4
  • zai-org/GLM-4.7: --dyn-tool-call-parser glm47 --dyn-reasoning-parser glm45
  • moonshotai/Kimi-K2.5* / Kimi K2.6 format-compatible outputs: --dyn-tool-call-parser kimi_k2 --dyn-reasoning-parser kimi_k25
  • google/gemma-4-* thinking models: --dyn-tool-call-parser gemma4 --dyn-reasoning-parser gemma4 --custom-jinja-template examples/chat_templates/gemma4_tool.jinja
  • Qwen/Qwen3.5*: --dyn-tool-call-parser qwen3_coder --dyn-reasoning-parser qwen3
  • Beam channel-grammar models: --dyn-tool-call-parser beam --dyn-reasoning-parser beam
  • MiniMax M2 style outputs: --dyn-tool-call-parser minimax_m2 --dyn-reasoning-parser minimax_m2
  • MiniMax M3 style outputs: --dyn-tool-call-parser minimax_m3 --dyn-reasoning-parser minimax_m3

minimax_append_think is deprecated for MiniMax M2 tool-calling deployments. Use --dyn-reasoning-parser minimax_m2 with --dyn-tool-call-parser minimax_m2 so Dynamo can separate reasoning and pass MiniMax XML tool calls to the tool parser.

On Unified routes one parser preserves reasoning, text, and tool order. The legacy split route extracts reasoning before tool parsing. See Tool Call Parsing (Dynamo) for supported --dyn-tool-call-parser values.

2

Start the Frontend

Start the Dynamo frontend in another terminal:

python -m dynamo.frontend
3

Wait for Readiness

Wait for the frontend health endpoint to return a successful response:

curl --fail http://localhost:8000/health
4

Send a Reasoning Request

Send an OpenAI-compatible chat completion request:

curl -s http://localhost:8000/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{
"model": "Qwen/Qwen3.5-4B",
"messages": [
{
"role": "user",
"content": "If a train leaves at 3pm going 60 mph and another leaves at 4pm going 80 mph, when does the second catch up?"
}
]
}'

Dynamo places the chain of thought in reasoning_content and the user-facing answer in content.

{
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"reasoning_content": "The first train has a 1-hour head start at 60 mph, so it is 60 miles ahead at 4pm. The second train closes the gap at 80 - 60 = 20 mph. 60 / 20 = 3 hours after 4pm.",
"content": "The second train catches up at 7pm."
},
"finish_reason": "stop"
}
]
}

Deployment-Level Thinking Default

Set --dyn-default-thinking-mode on a backend worker to choose the thinking mode used when a request does not provide one. The worker publishes the value through model runtime metadata, and the frontend applies it while rendering the chat template. The option works with vLLM, SGLang, and TensorRT-LLM workers.

CLI argumentEnvironment variableValuesDefault
--dyn-default-thinking-modeDYN_DEFAULT_THINKING_MODEenabled, disabledUnset

For example, make thinking disabled by default for a Qwen3 deployment:

python -m dynamo.vllm \
--model Qwen/Qwen3-0.6B \
--dyn-default-thinking-mode disabled

You can configure the same value through the environment:

DYN_DEFAULT_THINKING_MODE=disabled \
python -m dynamo.vllm --model Qwen/Qwen3-0.6B

Use the same setting on every worker that serves the model. When the option is unset, Dynamo preserves the model or chat template’s native default.

The frontend’s --default-chat-template-kwargs can also set a thinking default, for example '{"enable_thinking": false}'. It applies to every model that the frontend serves, including models behind a sidecar. On /v1/messages with a reasoning parser, the Anthropic thinking parameter decides instead, because that endpoint turns thinking on unless the request disables it.

Thinking controls use this precedence order:

  1. An explicit request control
  2. Thinking keys in the frontend’s --default-chat-template-kwargs
  3. --dyn-default-thinking-mode or DYN_DEFAULT_THINKING_MODE
  4. The model or chat template’s native default

Kimi K3 overrides this precedence for named calls: --dyn-tool-call-parser kimi_k3 (or kimi-k3) forces thinking off, even if requested. The reasoning selector alone does not apply this override.

Explicit request controls include root-level thinking, top-level reasoning_effort, and the thinking, enable_thinking, thinking_mode, or reasoning_effort fields in chat_template_args or chat_template_kwargs. When the frontend sets --default-chat-template-kwargs, a request value of null or "auto" counts as unset.

The deployment option intentionally accepts only enabled and disabled. Models that support adaptive thinking can still select it per request:

{
"model": "MiniMaxAI/MiniMax-M3",
"messages": [{"role": "user", "content": "Explain this result."}],
"thinking": {"type": "adaptive"}
}

Dynamo normalizes this request to thinking_mode=adaptive and removes any thinking or enable_thinking key, so the decision stays with the model. A reasoning_effort value still reaches models that grade on it, but under adaptive it does not turn reasoning on or off. Because adaptive is an explicit request control, it takes precedence over an enabled or disabled deployment default.

This option controls chat-template rendering. It does not force the inference engine or model to follow the requested mode. Models that ignore their template’s thinking control may still emit reasoning.

Supported Reasoning Parsers

Choose a model family, then expand the matching model option to see its parser name and configuration details.

The Upstream name column lists engine names that differ from Dynamo’s, for engine fallback and native reasoning gates. Names vary by engine version; Dynamo-only describes this catalog, not every engine release.

Parsers marked Force-reasoning: Yes start reasoning without an opening tag. Other parsers can start from a generated opener or supported prompt-prefilled state. Prompt prefill and force-reasoning are separate mechanisms.

Parser: kimi_k3 (Unified alias kimi-k3)

Notes: XTML thinking and response channels, including prompt-prefilled reasoning. See Tool Call Parsing for the K3 named-tool thinking override.

Parser: kimi_k25

Upstream name: Dynamo-only

Force-reasoning: Yes (V1)

Notes: V1 forces reasoning from the start. The default Unified Kimi K2 route begins as visible text unless an opening marker or supported prompt-prefilled reasoning state selects reasoning.

Parser: kimi

Upstream name: Dynamo-only

Force-reasoning: No

Notes: ◁think▷...◁/think▷

Kimi K2.7 may ignore chat_template_kwargs.thinking=false and continue to generate reasoning. Dynamo can separate emitted reasoning when a compatible parser is configured, but it cannot force the model to disable reasoning. Treat the request flag as best-effort for Kimi K2.7.

Parser: minimax_m2

Upstream name: vLLM: minimax_m2

Force-reasoning: Yes

Notes: <think>...</think> with force-reasoning

Parser: minimax_m3

Upstream name: vLLM: minimax_m3

Force-reasoning: No

Notes: <mm:think>...</mm:think>. Auto uses Unified for compatible M3 selectors; 1 selects legacy parsing. Both routes support prompt-prefilled reasoning. See guided-decoding configuration for the required Dynamo and engine reasoning parsers. Alias: minimax-m3

Parser: minimax_append_think Deprecated

Upstream name: Dynamo-only

Force-reasoning: No

Notes: Legacy pass-through with an implicit <think> opener. Use minimax_m2 for MiniMax M2 tool-calling deployments.

Parser: deepseek_v41

Notes: Unified V4.1 reasoning and tool grammar; use V2/auto with matching selectors. Not available in V1.

Parser: deepseek_v4

Upstream name: vLLM: deepseek_v4; SGLang: deepseek-v4

Force-reasoning: No

Notes: <think>...</think>. Aliases: deepseek-v4, deepseekv4

Parser: deepseek_r1

Upstream name: Same name across Dynamo, vLLM, and SGLang

Force-reasoning: Yes

Notes: V1 also accepts deepseek_v3, deepseek_v3_1, and deepseek_v3_2 as reasoning aliases. These names do not identify a Unified family.

Parser: qwen3

Upstream name: Same name across Dynamo, vLLM, and SGLang

Force-reasoning: No

Notes: <think>...</think>

Parser: glm45

Upstream name: vLLM: glm45; SGLang: glm45

Force-reasoning: No

Notes: <think>...</think>. With the default V2 parser, glm45 selects the glm47 unified family; glm47 is also accepted as a Dynamo selector. With DYN_PARSER_VERSION=1, glm45 uses the legacy nemotron_deci implementation.

GLM does not assume reasoning by default. If the chat template already ends the generation prompt with <think>, Dynamo starts parsing inside reasoning without requiring the model to repeat the opening tag.

Thinking controls such as chat_template_kwargs: {"enable_thinking": false} or chat_template_kwargs: {"thinking": false} affect the model’s chat template, not response visibility. If a GLM model still generates reasoning, Dynamo keeps it in reasoning_content and returns the answer in content.

For the default V2 parser on SGLang or vLLM, configure Dynamo’s --dyn-reasoning-parser glm45 (or glm47) and the engine’s native --reasoning-parser glm45. Dynamo reconciles its reasoning state with the rendered generation prompt, even if the request supplied a false thinking flag:

  • An open <think> defers JSON answer constraints and required/named-tool constraints until </think>.
  • A closed reasoning block starts in the answer phase, so those constraints apply from the first generated token.
  • With no generation prompt or no recognized open/closed reasoning suffix, Dynamo retains the request-based decision.

A max_thinking_tokens budget also enables the reasoning gate when the rendered prompt leaves reasoning open, even without JSON or required/named-tool constraints. The budget caps reasoning tokens; JSON and tool constraints still apply in the answer phase. Plain chat without constraints or a thinking budget does not request this gate.

TensorRT-LLM also supports Dynamo’s reasoning/content separation. The native grammar-gate behavior described here is specific to SGLang and vLLM; it does not configure TensorRT-LLM guided decoding.

Parser: nemotron3

Upstream name: vLLM: nemotron_v3

Force-reasoning: Yes

Notes: Auto pairs qwen3_coder tools with nemotron_v3 / nemotron3 reasoning on Unified; reasoning-only auto stays on V1. Explicit V2 selects Unified. V1 forces think-tag reasoning with tool boundaries.

Parser: nemotron_deci

Upstream name: Dynamo-only

Force-reasoning: No

Notes: <think>...</think>

Parser: nemotron_nano

Upstream name: Dynamo-only

Force-reasoning: Yes

Notes: Alias for deepseek_r1

Parser: gemma4

Upstream name: vLLM: gemma4

Force-reasoning: No

Notes: <\|channel>thought\n...<channel\|> with thought\n role label stripped. Aliases: gemma-4

Parser: beam

Upstream name: none (Dynamo-native unified family)

Force-reasoning: No

Notes: <\|channel\|>analysis<\|message\|>...<\|end\|> is reasoning; the final channel is the answer. Pair with --dyn-tool-call-parser beam (both flags required). Supplied prior reasoning is always rendered back into the history (preserve_thinking: true and omitted are the same; false is accepted and has no effect); history turns without reasoning render an empty analysis block (allow_missing_thinking defaults to true on this route).

Parser: gpt_oss

Upstream name: Dynamo-only

Force-reasoning: No

Notes: Harmony channel reasoning format

Parser: mistral

Upstream name: Same name across Dynamo, vLLM, and SGLang

Force-reasoning: Yes

Notes: [THINK]...[/THINK]

Parser: granite

Upstream name: Same name across Dynamo, vLLM, and SGLang

Force-reasoning: No

Notes: Here's my thought process: / Here's my response:

Parser: step3

Upstream name: Dynamo-only

Force-reasoning: Yes

Notes: <think>...</think>

Parser: muse_glimmer (Dynamo alias muse)

Notes: Unified recipient-routed channels; to=self carries reasoning. No V1 reasoning parser exists.

Parser: muse_spark (alias muse_spark_open)

Notes: Unified recipient-routed channels: to=self carries reasoning and commentary remains visible text. No V1 reasoning parser exists.

Parser: inkling

Notes: V1 block/channel reasoning parser. This name does not select a Unified family; auto uses the legacy route and explicit V2 rejects it.

Parser: basic

Upstream name: Dynamo-only

Force-reasoning: No

Notes: Plain <think>...</think>