fix: treat reasoning_effort="none" as thinking disabled and route gemma to Gemini provider

- Do not send reasoning_effort="none" to APIs (prevents 400 on gemma/Gemini)
- Treat "none" as thinking disabled in thinking_style, Kimi, and reasoning_content backfill paths
- Fix Anthropic extended thinking not respecting "none"
- Fix Azure OpenAI temperature suppression and reasoning body for "none"
- Fix Codex reasoning body for "none"
- Add "gemma" keyword to Gemini ProviderSpec for correct auto routing
This commit is contained in:
masterlyj
2026-04-29 15:41:11 +08:00
committed by Xubin Ren
parent 28f9bbff31
commit b94bc18e59
5 changed files with 10 additions and 10 deletions
+1 -1
View File
@@ -434,7 +434,7 @@ class AnthropicProvider(LLMProvider):
) )
max_tokens = max(1, max_tokens) max_tokens = max(1, max_tokens)
thinking_enabled = bool(reasoning_effort) thinking_enabled = bool(reasoning_effort) and reasoning_effort.lower() != "none"
# claude-opus-4-7 deprecated the `temperature` parameter entirely — the # claude-opus-4-7 deprecated the `temperature` parameter entirely — the
# API returns 400 if it is present, on any code path. # API returns 400 if it is present, on any code path.
+2 -2
View File
@@ -71,7 +71,7 @@ class AzureOpenAIProvider(LLMProvider):
reasoning_effort: str | None = None, reasoning_effort: str | None = None,
) -> bool: ) -> bool:
"""Return True when temperature is likely supported for this deployment.""" """Return True when temperature is likely supported for this deployment."""
if reasoning_effort: if reasoning_effort and reasoning_effort.lower() != "none":
return False return False
name = deployment_name.lower() name = deployment_name.lower()
return not any(token in name for token in ("gpt-5", "o1", "o3", "o4")) return not any(token in name for token in ("gpt-5", "o1", "o3", "o4"))
@@ -102,7 +102,7 @@ class AzureOpenAIProvider(LLMProvider):
if self._supports_temperature(deployment, reasoning_effort): if self._supports_temperature(deployment, reasoning_effort):
body["temperature"] = temperature body["temperature"] = temperature
if reasoning_effort: if reasoning_effort and reasoning_effort.lower() != "none":
body["reasoning"] = {"effort": reasoning_effort} body["reasoning"] = {"effort": reasoning_effort}
body["include"] = ["reasoning.encrypted_content"] body["include"] = ["reasoning.encrypted_content"]
+1 -1
View File
@@ -60,7 +60,7 @@ class OpenAICodexProvider(LLMProvider):
"tool_choice": tool_choice or "auto", "tool_choice": tool_choice or "auto",
"parallel_tool_calls": True, "parallel_tool_calls": True,
} }
if reasoning_effort: if reasoning_effort and reasoning_effort.lower() != "none":
body["reasoning"] = {"effort": reasoning_effort} body["reasoning"] = {"effort": reasoning_effort}
if tools: if tools:
body["tools"] = convert_tools(tools) body["tools"] = convert_tools(tools)
+5 -5
View File
@@ -570,7 +570,7 @@ class OpenAICompatProvider(LLMProvider):
# DashScope accepts none/minimum/low/medium/high/xhigh; "minimal" 400s. # DashScope accepts none/minimum/low/medium/high/xhigh; "minimal" 400s.
wire_effort = "minimum" wire_effort = "minimum"
if wire_effort: if wire_effort and semantic_effort != "none":
kwargs["reasoning_effort"] = wire_effort kwargs["reasoning_effort"] = wire_effort
# Provider-specific thinking parameters. # Provider-specific thinking parameters.
@@ -579,7 +579,7 @@ class OpenAICompatProvider(LLMProvider):
# The mapping is driven by ProviderSpec.thinking_style so that adding # The mapping is driven by ProviderSpec.thinking_style so that adding
# a new provider never requires touching this function. # a new provider never requires touching this function.
if spec and spec.thinking_style and reasoning_effort is not None: if spec and spec.thinking_style and reasoning_effort is not None:
thinking_enabled = semantic_effort != "minimal" thinking_enabled = semantic_effort not in ("none", "minimal")
extra = _THINKING_STYLE_MAP.get(spec.thinking_style, lambda _: None)(thinking_enabled) extra = _THINKING_STYLE_MAP.get(spec.thinking_style, lambda _: None)(thinking_enabled)
if extra: if extra:
kwargs.setdefault("extra_body", {}).update(extra) kwargs.setdefault("extra_body", {}).update(extra)
@@ -589,7 +589,7 @@ class OpenAICompatProvider(LLMProvider):
# so that OpenRouter-style names like "moonshotai/kimi-k2.5" are handled # so that OpenRouter-style names like "moonshotai/kimi-k2.5" are handled
# identically to bare names like "kimi-k2.5". # identically to bare names like "kimi-k2.5".
if reasoning_effort is not None and _is_kimi_thinking_model(model_name): if reasoning_effort is not None and _is_kimi_thinking_model(model_name):
thinking_enabled = semantic_effort != "minimal" thinking_enabled = semantic_effort not in ("none", "minimal")
kwargs.setdefault("extra_body", {}).update( kwargs.setdefault("extra_body", {}).update(
{"thinking": {"type": "enabled" if thinking_enabled else "disabled"}} {"thinking": {"type": "enabled" if thinking_enabled else "disabled"}}
) )
@@ -609,9 +609,9 @@ class OpenAICompatProvider(LLMProvider):
# thinking happened on that turn"). # thinking happened on that turn").
thinking_active = ( thinking_active = (
(spec and spec.thinking_style and reasoning_effort is not None (spec and spec.thinking_style and reasoning_effort is not None
and semantic_effort != "minimal") and semantic_effort not in ("none", "minimal"))
or (reasoning_effort is not None and _is_kimi_thinking_model(model_name) or (reasoning_effort is not None and _is_kimi_thinking_model(model_name)
and semantic_effort != "minimal") and semantic_effort not in ("none", "minimal"))
) )
if thinking_active: if thinking_active:
for msg in kwargs["messages"]: for msg in kwargs["messages"]:
+1 -1
View File
@@ -267,7 +267,7 @@ PROVIDERS: tuple[ProviderSpec, ...] = (
# Gemini: Google's OpenAI-compatible endpoint # Gemini: Google's OpenAI-compatible endpoint
ProviderSpec( ProviderSpec(
name="gemini", name="gemini",
keywords=("gemini",), keywords=("gemini", "gemma"),
env_key="GEMINI_API_KEY", env_key="GEMINI_API_KEY",
display_name="Gemini", display_name="Gemini",
backend="openai_compat", backend="openai_compat",