Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 28 additions & 12 deletions plugins/model-providers/minimax/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,21 @@ def _is_minimax_global_openai_base_url(base_url: str | None) -> bool:
return path == "/v1"


def _is_minimax_global_anthropic_base_url(base_url: str | None) -> bool:
parsed = urlparse(str(base_url or "").strip())
if (parsed.hostname or "").lower() != "api.minimax.io":
return False
path = parsed.path.rstrip("/").lower()
return path == "/anthropic"


def _is_minimax_m3(model: str | None) -> bool:
normalized = str(model or "").strip().lower()
return normalized in {"minimax-m3", "minimax/minimax-m3"}


class MiniMaxProfile(ProviderProfile):
"""MiniMax — M3 OpenAI-compatible reasoning controls."""
"""MiniMax — M3 reasoning controls (OpenAI-compatible + Anthropic-compatible routes)."""

def build_api_kwargs_extras(
self,
Expand All @@ -37,23 +45,31 @@ def build_api_kwargs_extras(
base_url: str | None = None,
**context: Any,
) -> tuple[dict[str, Any], dict[str, Any]]:
"""Emit M3 reasoning controls for api.minimax.io/v1.

MiniMax-M3's OpenAI-compatible endpoint keeps thinking inline unless
``reasoning_split`` is sent, so always request the split format on that
route. ``thinking`` controls the M3 mode; Hermes' effort levels are not
a MiniMax depth knob here, so they only select adaptive vs disabled.
"""Emit M3 reasoning controls for api.minimax.io (both /v1 and /anthropic).

MiniMax-M3's /v1 endpoint keeps thinking inline unless ``reasoning_split``
is sent, so always request the split format on that route. The /anthropic
endpoint returns thinking as native ``thinking`` content blocks already, so
no split flag is needed there. ``thinking`` controls the M3 mode; Hermes'
effort levels are not a MiniMax depth knob here — they only select
adaptive vs disabled. On /anthropic, omitting ``thinking`` causes M3 to
default to OFF (per MiniMax docs), which is the bug this branch fixes.
"""
if not _is_minimax_global_openai_base_url(base_url) or not _is_minimax_m3(model):
is_m3 = _is_minimax_m3(model)
is_oai = _is_minimax_global_openai_base_url(base_url)
is_ant = _is_minimax_global_anthropic_base_url(base_url)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/anthropic resolves to the anthropic_messages transport (hermes_cli/runtime_provider.py:135), which calls build_anthropic_kwargs() directly (agent/transports/anthropic.py:63-78) and does not consume this profile hook. Please place this endpoint/model-specific behavior in the Anthropic adapter (or consolidate it with #66694), where it changes the actual wire request.


if not is_m3 or (not is_oai and not is_ant):
Comment on lines +58 to +62
return {}, {}

extra_body: dict[str, Any] = {"reasoning_split": True}
extra_body: dict[str, Any] = {}

if is_oai:
extra_body["reasoning_split"] = True

if isinstance(reasoning_config, dict) and reasoning_config.get("enabled") is False:
extra_body["thinking"] = {"type": "disabled"}
return extra_body, {}

if reasoning_config is not None:
elif reasoning_config is not None:
extra_body["thinking"] = {"type": "adaptive"}

return extra_body, {}
Expand Down