fix(providers): wire MiMo to thinking_type to allow disabling reasoning (#3585)

The hosted Xiaomi MiMo API accepts {"thinking": {"type": "enabled"|"disabled"}}
to toggle reasoning, which is exactly the shape produced by the existing
thinking_type style. The xiaomi_mimo ProviderSpec just needed to opt in.

Before this fix, setting reasoning_effort="none" had no effect on MiMo
because no thinking_style was configured, so the disable signal never
reached the server. Default-on models (mimo-v2.5-pro and friends) kept
reasoning regardless of user configuration.

Source: https://platform.xiaomimimo.com/docs/en-US/api/chat/openai-api

Co-authored with Claude Opus 4.7. Strategy and review via Claude Desktop,
implementation via Claude Code.
This commit is contained in:
Alfredo Arenas 2026-05-10 22:43:54 -06:00 committed by Xubin Ren
parent 271b674bf1
commit c6b7a9524c
2 changed files with 124 additions and 0 deletions

View File

@ -368,6 +368,8 @@ PROVIDERS: tuple[ProviderSpec, ...] = (
reasoning_as_content=True,
),
# Xiaomi MIMO (小米): OpenAI-compatible API
# Hosted API (api.xiaomimimo.com) accepts {"thinking": {"type": "enabled"|"disabled"}}
# to toggle reasoning, matching the existing thinking_type style.
ProviderSpec(
name="xiaomi_mimo",
keywords=("xiaomi_mimo", "mimo"),
@ -375,6 +377,7 @@ PROVIDERS: tuple[ProviderSpec, ...] = (
display_name="Xiaomi MIMO",
backend="openai_compat",
default_api_base="https://api.xiaomimimo.com/v1",
thinking_style="thinking_type",
),
# LongCat: OpenAI-compatible API
ProviderSpec(

View File

@ -0,0 +1,121 @@
"""Tests for Xiaomi MiMo thinking-mode toggle via reasoning_effort.
The hosted Xiaomi MiMo API (api.xiaomimimo.com) accepts
``{"thinking": {"type": "enabled"|"disabled"}}`` in the request body
to toggle reasoning. Source: https://platform.xiaomimimo.com/docs/en-US/api/chat/openai-api
The thinking_type style already exists in _THINKING_STYLE_MAP and
produces exactly this shape, so MiMo just needs to opt in via its
ProviderSpec.thinking_style.
Default thinking behavior per Xiaomi docs:
- mimo-v2-flash: disabled
- mimo-v2.5-pro, mimo-v2.5, mimo-v2-pro, mimo-v2-omni: enabled
Without an explicit reasoning_effort, nanobot must not send the
thinking field so the provider default is preserved (issue #3585).
"""
from __future__ import annotations
from typing import Any
from nanobot.config.schema import ProvidersConfig
from nanobot.providers.openai_compat_provider import OpenAICompatProvider
from nanobot.providers.registry import PROVIDERS
def _mimo_spec():
"""Return the registered xiaomi_mimo ProviderSpec."""
specs = {s.name: s for s in PROVIDERS}
return specs["xiaomi_mimo"]
def _mimo_provider() -> OpenAICompatProvider:
return OpenAICompatProvider(
api_key="test-key",
default_model="mimo-v2.5-pro",
spec=_mimo_spec(),
)
def _simple_messages() -> list[dict[str, Any]]:
return [{"role": "user", "content": "hello"}]
# ---------------------------------------------------------------------------
# Registry
# ---------------------------------------------------------------------------
def test_xiaomi_mimo_config_field_exists():
"""ProvidersConfig should expose a xiaomi_mimo field."""
config = ProvidersConfig()
assert hasattr(config, "xiaomi_mimo")
def test_xiaomi_mimo_uses_thinking_type_style():
"""MiMo hosted API uses {"thinking": {"type": ...}}, the thinking_type style."""
spec = _mimo_spec()
assert spec.thinking_style == "thinking_type"
assert spec.backend == "openai_compat"
assert spec.default_api_base == "https://api.xiaomimimo.com/v1"
# ---------------------------------------------------------------------------
# _build_kwargs wire-format
# ---------------------------------------------------------------------------
def test_mimo_reasoning_effort_none_disables_thinking():
"""reasoning_effort="none" should send thinking.type="disabled"."""
provider = _mimo_provider()
kwargs = provider._build_kwargs(
messages=_simple_messages(),
tools=None, model=None, max_tokens=100,
temperature=0.7, reasoning_effort="none", tool_choice=None,
)
# reasoning_effort itself must NOT be sent when value is "none"
assert "reasoning_effort" not in kwargs
# The disable signal must be in extra_body
assert kwargs["extra_body"] == {"thinking": {"type": "disabled"}}
def test_mimo_reasoning_effort_medium_enables_thinking():
"""reasoning_effort="medium" should send thinking.type="enabled"."""
provider = _mimo_provider()
kwargs = provider._build_kwargs(
messages=_simple_messages(),
tools=None, model=None, max_tokens=100,
temperature=0.7, reasoning_effort="medium", tool_choice=None,
)
assert kwargs.get("reasoning_effort") == "medium"
assert kwargs["extra_body"] == {"thinking": {"type": "enabled"}}
def test_mimo_reasoning_effort_low_enables_thinking():
"""Any non-none/minimal effort enables thinking."""
provider = _mimo_provider()
kwargs = provider._build_kwargs(
messages=_simple_messages(),
tools=None, model=None, max_tokens=100,
temperature=0.7, reasoning_effort="low", tool_choice=None,
)
assert kwargs["extra_body"] == {"thinking": {"type": "enabled"}}
def test_mimo_reasoning_effort_unset_preserves_provider_default():
"""When reasoning_effort is None, no thinking field is sent.
This preserves the provider default (varies by model per Xiaomi docs).
Required so that omitting the config field behaves the same as before
this fix no behavior change for users who never set reasoning_effort.
"""
provider = _mimo_provider()
kwargs = provider._build_kwargs(
messages=_simple_messages(),
tools=None, model=None, max_tokens=100,
temperature=0.7, reasoning_effort=None, tool_choice=None,
)
assert "reasoning_effort" not in kwargs
assert "extra_body" not in kwargs