modelscope / modelscope/ms-swift

swift deploy cannot fully disable Qwen3 “thinking”; no way to pass vLLM --chat-template. enable_thinking=False ignored on vLLM 0.8.5.post1

Open
#5,836 4 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
15.7k
Forks
1.7k
Avg merge
1d 16h
Merged PRs (30d)
136

Description

Describe the bug

Summary

When serving Qwen3 via swift deploy (vLLM backend pinned to vLLM 0.8.5.post1), I cannot reliably disable “thinking”:
• enable_thinking=False is ignored when the reasoning parser is enabled (known limitation in vLLM ≤0.8.5).
• swift deploy doesn’t expose a way to forward vLLM’s --chat-template flag, so I can’t use a non-thinking chat template (qwen3_nonthinking.jinja) as recommended by Qwen’s deployment docs.
• Depending on the route, the server either always separates thinking vs. answer, or never exposes reasoning_content, and the per-request toggle doesn’t work.

Per Qwen docs: in vLLM 0.9.0 with the qwen3 reasoning parser, enable_thinking=False works. Also, you can force non-thinking with a custom chat template. With ms-swift today, neither option is achievable out of the box.

Environment
• ms-swift: following docs at https://swift.readthedocs.io/en/latest/Instruction/Inference-and-deployment.html
• vLLM: 0.8.5.post1 (as in the swift docs)
• Model: Qwen/Qwen3-8B (also reproduced with a local Qwen3 checkpoint)
• Server: swift deploy (OpenAI-compatible /v1 API), vLLM backend
• Client(s):
• OpenAI Python SDK (chat.completions)
• swift.llm client (InferClient, InferRequest, RequestConfig)

What I run

Server (Swift)

swift deploy
--model Qwen/Qwen3-8B
--stream true
--infer_backend vllm
--use_hf true
--vllm_tensor_parallel_size 4
--vllm_gpu_memory_utilization 0.9
--vllm_max_model_len 32768
--vllm_reasoning_parser qwen3

I would like to add a non-thinking template, but swift deploy does not accept a --vllm_chat_template (or similar) flag. The vLLM CLI equivalent would be:
vllm serve Qwen/Qwen3-8B --chat-template ./qwen3_nonthinking.jinja

Client (Swift client)

from swift.llm import InferRequest, InferClient, RequestConfig
engine = InferClient(host='127.0.0.1', port=8000)

Attempt 1: per-request toggle via RequestConfig

request_config = RequestConfig(
max_tokens=512, temperature=0,
# hoping ms-swift forwards this to OpenAI body
extra_body={"chat_template_kwargs": {"enable_thinking": False}},
)

resp = engine.infer([InferRequest(messages=[{"role":"user","content":"Explain what is opacity in chest xray?"}])],
request_config)
print(resp[0].choices[0].message.dict)

Also tried attaching via InferRequest(objects={"chat_template_kwargs": {"enable_thinking": False}}).

Expected behavior
• On vLLM ≥ 0.9.0 with --reasoning-parser qwen3:
enable_thinking=False should suppress thinking (no …), and the API should either omit reasoning_content or return it empty.
• On vLLM 0.8.5.post1 (if staying pinned):
Allow me to force non-thinking at serve time by passing a custom chat template, e.g. forward vLLM’s --chat-template ./qwen3_nonthinking.jinja via a swift deploy flag.

Actual behavior
• With --vllm_reasoning_parser qwen3 on vLLM 0.8.5.post1:
• The server continues to produce “thinking” traces.
• enable_thinking=False sent from the client is ignored (consistent with vLLM ≤ 0.8.5 limitation).
• Depending on the route:
• OpenAI /v1/chat/completions: sometimes splits reasoning_content vs content, but the toggle still doesn’t disable thinking.
• Swift /infer: returns a single response string and does not surface reasoning_content at all, so I can’t inspect or hide it cleanly.
• I cannot pass a non-thinking chat template because swift deploy has no flag to forward vLLM’s --chat-template.

Relevant docs
• Qwen vLLM docs (Thinking / Non-thinking modes):
https://qwen.readthedocs.io/en/latest/deployment/vllm.html#thinking-non-thinking-modes
“As of vLLM 0.8.5, enable_thinking=False is not compatible with this feature… This is resolved in vLLM 0.9.0 with the qwen3 reasoning parser.”
“To completely disable thinking, use a custom chat template… --chat-template ./qwen3_nonthinking.jinja”
• ms-swift deployment docs (currently indicating vLLM 0.8.5.post1).

Steps to reproduce
1. Start swift deploy as shown above with --vllm_reasoning_parser qwen3.
2. From either the OpenAI SDK or swift.llm client, send:

extra_body={"chat_template_kwargs": {"enable_thinking": False}}

3.	Observe that the model still thinks (and/or the response schema doesn’t let you suppress/exclude reasoning cleanly).

Attempt to pass a non-thinking chat template from swift deploy — there is no supported flag, so this path is blocked.

Workarounds (not ideal)
• Bypass Swift and run vLLM directly:

vllm serve Qwen/Qwen3-8B --reasoning-parser qwen3 --chat-template ./qwen3_nonthinking.jinja

This immediately solves the problem, but loses Swift’s deployment ergonomics.

•	Strip <think>…</think> client-side with a regex — brittle and wastes tokens.

Requests / Proposed fixes
1. Expose vLLM --chat-template via swift deploy (e.g., --vllm_chat_template ), so we can enforce a non-thinking template when needed.
2. Upgrade/allow vLLM ≥ 0.9.0 in ms-swift deployments, and document that enable_thinking=False works with --vllm_reasoning_parser qwen3.
3. Ensure the Swift client (InferClient) has a clear, documented way to forward chat_template_kwargs (e.g., RequestConfig.extra_body and/or InferRequest.objects) to the OpenAI body.
4. Clarify the difference between the /infer route and the OpenAI /v1 route, and how/when reasoning_content is surfaced or suppressed.

Happy to provide more logs or test a PR if helpful.

Your hardware and system info
=== System Info ===
Linux qrnd22.l40.sr.internal.qure.ai 5.15.0-1083-nvidia #84-Ubuntu SMP Mon Jul 14 22:35:46 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux

=== OS Release ===
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=22.04
DISTRIB_CODENAME=jammy
DISTRIB_DESCRIPTION="Ubuntu 22.04.5 LTS"
PRETTY_NAME="Ubuntu 22.04.5 LTS"

=== Python & Torch ===
Python 3.12.11
Torch version: 2.8.0+cu128
CUDA (built): 12.8
CUDA available: True
GPU 0: NVIDIA L40S
GPU 1: NVIDIA L40S
GPU 2: NVIDIA L40S
GPU 3: NVIDIA L40S

=== GCC / GLIBC ===
gcc (Ubuntu 11.4.0-1ubuntu1~22.04.2) 11.4.0
ldd (Ubuntu GLIBC 2.35-0ubuntu3.10) 2.35

=== Installed Packages (vllm, swift, torch) ===
Name: vllm
Version: 0.10.2rc2.dev89+gf94619747.cu129
Name: torch
Version: 2.8.0+cu128

ADDITION CONTEXT

the code i use to deploy

swift deploy \
    --model Qwen/Qwen3-8B \
    --stream true \
    --infer_backend vllm \
    --use_hf true \
    --vllm_tensor_parallel_size 4 \
    --vllm_gpu_memory_utilization 0.9 \
    --vllm_max_model_len 32768 \
    --vllm_reasoning_parser qwen3 \
    #--vllm_chat_template qwen3_nonthinking.jinja

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at the swift deploy entry point and its vLLM argument forwarding, then trace InferClient and RequestConfig handling of extra_body across the /infer and OpenAI /v1 routes. Reproduce with Qwen/Qwen3-8B, qwen3 reasoning-parser settings, and the proposed qwen3_nonthinking.jinja template. Done means the template can be passed at deployment, request options are forwarded consistently, and the route behavior is documented or tested.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
ai, backend-api-design, cli
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.