[Bugfix] Map reasoning_effort=low to Nemotron-3 Super low_effort + warn on unsupported levels (#30463)

Co-authored-by: EazyReal <8047065+EazyReal@users.noreply.github.com>
This commit is contained in:
Xinyuan Tong
2026-07-08 12:19:44 -07:00
committed by GitHub
co-authored by EazyReal
parent 8d2b66fd90
commit 45019b56ce
6 changed files with 140 additions and 14 deletions
@@ -800,6 +800,7 @@ class ChatCompletionRequest(BaseModel):
@classmethod
def normalize_reasoning_inputs(cls, values: Dict):
r = values.get("reasoning")
thinking = None
if r is not None and isinstance(r, dict):
effort = r.get("effort") or r.get("reasoning_effort")
@@ -814,25 +815,21 @@ class ChatCompletionRequest(BaseModel):
if isinstance(enabled, str):
enabled = enabled.strip().lower() in {"1", "true", "yes", "y", "on"}
if enabled:
ctk = values.get("chat_template_kwargs")
if not isinstance(ctk, dict):
ctk = {}
# different models check different keys:
# - "thinking" for deepseek-v3, kimi_k2
# - "enable_thinking" for qwen3, glm45, nemotron_3, interns1, mimo
ctk.setdefault("thinking", True)
ctk.setdefault("enable_thinking", True)
values["chat_template_kwargs"] = ctk
thinking = True
if values.get("reasoning_effort") == "none":
effort = values.get("reasoning_effort")
if effort is not None:
thinking = effort != "none"
if thinking is not None:
ctk = values.get("chat_template_kwargs")
if not isinstance(ctk, dict):
ctk = {}
# different models check different keys:
# - "thinking" for deepseek-v3, kimi_k2
# - "enable_thinking" for qwen3, glm45, nemotron_3, interns1
ctk.setdefault("thinking", False)
ctk.setdefault("enable_thinking", False)
ctk.setdefault("thinking", thinking)
ctk.setdefault("enable_thinking", thinking)
values["chat_template_kwargs"] = ctk
return values
@@ -851,6 +851,18 @@ class OpenAIServingChat(OpenAIServingBase):
if request.chat_template_kwargs:
extra_template_kwargs.update(request.chat_template_kwargs)
rc = self.template_manager.reasoning_config
if rc is not None and rc.effort_kwarg is not None:
if request.reasoning_effort == "low":
extra_template_kwargs.setdefault(rc.effort_kwarg, True)
elif request.reasoning_effort in ("medium", "high", "max"):
logger.warning(
"Model '%s' supports only 'low' reasoning effort; "
"requested '%s' treated as default thinking",
self.tokenizer_manager.server_args.served_model_name,
request.reasoning_effort,
)
# Split apply_chat_template(tokenize=True) into render + encode so we
# can skip add_special_tokens=False on tokenizers that don't auto-add
# specials (Kimi-like, OpenAI-chat analogue of #25265). Chat
@@ -64,6 +64,7 @@ class ReasoningToggleConfig:
toggle_param: Optional[str] = None
default_enabled: Optional[bool] = None
special_case: Optional[str] = None
effort_kwarg: Optional[str] = None
@property
def always_on(self) -> bool:
@@ -104,6 +105,16 @@ REASONING_MODE_RULES = (
re.DOTALL,
),
),
DetectionRule(
name="nemotron_3_super_low_effort",
value=ReasoningToggleConfig(
toggle_param="enable_thinking",
default_enabled=True,
effort_kwarg="low_effort",
),
predicate=lambda ctx: ctx.has_text("low_effort")
and ctx.has_text("truncate_history_thinking"),
),
DetectionRule(
name="enable_thinking_default_true",
value=ReasoningToggleConfig(
@@ -193,8 +204,10 @@ def _is_kimi_k2(ctx):
def _is_nemotron_3(ctx):
return ctx.has_text("truncate_history_thinking") and ctx.reasoning_config == (
ReasoningToggleConfig(toggle_param="enable_thinking", default_enabled=True)
return ctx.has_text("truncate_history_thinking") and (
ctx.reasoning_config is not None
and ctx.reasoning_config.toggle_param == "enable_thinking"
and ctx.reasoning_config.default_enabled is True
)