From b94bc18e59b7af2bfcf00bf255ef726c56cf746a Mon Sep 17 00:00:00 2001 From: masterlyj <167326996+masterlyj@users.noreply.github.com> Date: Wed, 29 Apr 2026 03:59:21 +0800 Subject: [PATCH] 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 --- nanobot/providers/anthropic_provider.py | 2 +- nanobot/providers/azure_openai_provider.py | 4 ++-- nanobot/providers/openai_codex_provider.py | 2 +- nanobot/providers/openai_compat_provider.py | 10 +++++----- nanobot/providers/registry.py | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/nanobot/providers/anthropic_provider.py b/nanobot/providers/anthropic_provider.py index 5d6d36b7..a46cc32e 100644 --- a/nanobot/providers/anthropic_provider.py +++ b/nanobot/providers/anthropic_provider.py @@ -434,7 +434,7 @@ class AnthropicProvider(LLMProvider): ) 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 # API returns 400 if it is present, on any code path. diff --git a/nanobot/providers/azure_openai_provider.py b/nanobot/providers/azure_openai_provider.py index 9fd18e1f..bc2a9d04 100644 --- a/nanobot/providers/azure_openai_provider.py +++ b/nanobot/providers/azure_openai_provider.py @@ -71,7 +71,7 @@ class AzureOpenAIProvider(LLMProvider): reasoning_effort: str | None = None, ) -> bool: """Return True when temperature is likely supported for this deployment.""" - if reasoning_effort: + if reasoning_effort and reasoning_effort.lower() != "none": return False name = deployment_name.lower() 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): body["temperature"] = temperature - if reasoning_effort: + if reasoning_effort and reasoning_effort.lower() != "none": body["reasoning"] = {"effort": reasoning_effort} body["include"] = ["reasoning.encrypted_content"] diff --git a/nanobot/providers/openai_codex_provider.py b/nanobot/providers/openai_codex_provider.py index 0cc8e5ca..945cae9b 100644 --- a/nanobot/providers/openai_codex_provider.py +++ b/nanobot/providers/openai_codex_provider.py @@ -60,7 +60,7 @@ class OpenAICodexProvider(LLMProvider): "tool_choice": tool_choice or "auto", "parallel_tool_calls": True, } - if reasoning_effort: + if reasoning_effort and reasoning_effort.lower() != "none": body["reasoning"] = {"effort": reasoning_effort} if tools: body["tools"] = convert_tools(tools) diff --git a/nanobot/providers/openai_compat_provider.py b/nanobot/providers/openai_compat_provider.py index 363a2e14..6712b68b 100644 --- a/nanobot/providers/openai_compat_provider.py +++ b/nanobot/providers/openai_compat_provider.py @@ -570,7 +570,7 @@ class OpenAICompatProvider(LLMProvider): # DashScope accepts none/minimum/low/medium/high/xhigh; "minimal" 400s. wire_effort = "minimum" - if wire_effort: + if wire_effort and semantic_effort != "none": kwargs["reasoning_effort"] = wire_effort # Provider-specific thinking parameters. @@ -579,7 +579,7 @@ class OpenAICompatProvider(LLMProvider): # The mapping is driven by ProviderSpec.thinking_style so that adding # a new provider never requires touching this function. 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) if 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 # identically to bare names like "kimi-k2.5". 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( {"thinking": {"type": "enabled" if thinking_enabled else "disabled"}} ) @@ -609,9 +609,9 @@ class OpenAICompatProvider(LLMProvider): # thinking happened on that turn"). thinking_active = ( (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) - and semantic_effort != "minimal") + and semantic_effort not in ("none", "minimal")) ) if thinking_active: for msg in kwargs["messages"]: diff --git a/nanobot/providers/registry.py b/nanobot/providers/registry.py index 80774207..6f947bab 100644 --- a/nanobot/providers/registry.py +++ b/nanobot/providers/registry.py @@ -267,7 +267,7 @@ PROVIDERS: tuple[ProviderSpec, ...] = ( # Gemini: Google's OpenAI-compatible endpoint ProviderSpec( name="gemini", - keywords=("gemini",), + keywords=("gemini", "gemma"), env_key="GEMINI_API_KEY", display_name="Gemini", backend="openai_compat",