diff --git a/unstract/sdk1/src/unstract/sdk1/adapters/base1.py b/unstract/sdk1/src/unstract/sdk1/adapters/base1.py index 366c404f3a..504cce454e 100644 --- a/unstract/sdk1/src/unstract/sdk1/adapters/base1.py +++ b/unstract/sdk1/src/unstract/sdk1/adapters/base1.py @@ -565,6 +565,35 @@ def _minimax_context_window(model_id: str) -> int | None: return None +def _normalize_minimax_thinking( + adapter_metadata: dict[str, "Any"], model_id: str +) -> None: + is_m2_model = _is_minimax_m2_model(model_id) + if "enable_thinking" in adapter_metadata: + enable_thinking = adapter_metadata.pop("enable_thinking") + if not isinstance(enable_thinking, bool): + raise ValueError("enable_thinking must be a boolean.") + if is_m2_model and not enable_thinking: + raise ValueError(f"{model_id} does not support disabling thinking.") + if not is_m2_model: + adapter_metadata["thinking"] = { + "type": "adaptive" if enable_thinking else "disabled" + } + + thinking = adapter_metadata.get("thinking") + if thinking is None: + return + if is_m2_model: + # M2.x always thinks; the provider accepts this parameter but ignores it. + adapter_metadata.pop("thinking") + return + if not isinstance(thinking, dict) or thinking.get("type") not in { + "adaptive", + "disabled", + }: + raise ValueError("thinking.type must be adaptive or disabled.") + + class NvidiaBuildLLMParameters(OpenAICompatibleLLMParameters): """OpenAI-compatible adapter for NVIDIA's hosted models (build.nvidia.com).""" @@ -601,26 +630,7 @@ def validate(adapter_metadata: dict[str, "Any"]) -> dict[str, "Any"]: if service_tier not in {None, "standard", "priority"}: raise ValueError("service_tier must be standard or priority.") - if "enable_thinking" in adapter_metadata: - enable_thinking = adapter_metadata.pop("enable_thinking") - if not isinstance(enable_thinking, bool): - raise ValueError("enable_thinking must be a boolean.") - adapter_metadata["thinking"] = { - "type": "adaptive" if enable_thinking else "disabled" - } - - thinking = adapter_metadata.get("thinking") - if thinking is None and _is_minimax_m2_model(model_id): - thinking = {"type": "adaptive"} - adapter_metadata["thinking"] = thinking - if thinking is not None: - if not isinstance(thinking, dict) or thinking.get("type") not in { - "adaptive", - "disabled", - }: - raise ValueError("thinking.type must be adaptive or disabled.") - if _is_minimax_m2_model(model_id) and thinking["type"] == "disabled": - raise ValueError(f"{model_id} does not support disabling thinking.") + _normalize_minimax_thinking(adapter_metadata, model_id) validated = MiniMaxLLMParameters(**adapter_metadata).model_dump() validated["cost_model"] = f"{_MINIMAX_PROVIDER_PREFIX}{model_id}" diff --git a/unstract/sdk1/src/unstract/sdk1/adapters/llm1/static/minimax.json b/unstract/sdk1/src/unstract/sdk1/adapters/llm1/static/minimax.json index 3075271e97..e473cdb622 100644 --- a/unstract/sdk1/src/unstract/sdk1/adapters/llm1/static/minimax.json +++ b/unstract/sdk1/src/unstract/sdk1/adapters/llm1/static/minimax.json @@ -73,7 +73,7 @@ "enable_thinking": { "type": "boolean", "title": "Enable Thinking", - "description": "Override the protocol default for MiniMax-M3: OpenAI-compatible requests default to adaptive thinking, while Anthropic-compatible requests default to disabled thinking. MiniMax-M2.x models always keep thinking enabled. See [MiniMax API docs](https://platform.minimax.io/docs/api-reference/text-openai-api)." + "description": "Override the protocol default for MiniMax-M3: OpenAI-compatible requests default to adaptive thinking, while Anthropic-compatible requests default to disabled thinking. MiniMax-M2.x models use always-on thinking and only accept this setting as true. See [MiniMax API docs](https://platform.minimax.io/docs/api-reference/text-openai-api)." } } } diff --git a/unstract/sdk1/tests/test_branded_openai_adapters.py b/unstract/sdk1/tests/test_branded_openai_adapters.py index c1556a3dd7..b0d38e585a 100644 --- a/unstract/sdk1/tests/test_branded_openai_adapters.py +++ b/unstract/sdk1/tests/test_branded_openai_adapters.py @@ -186,10 +186,30 @@ def test_minimax_m2_rejects_disabling_thinking() -> None: ) -def test_minimax_m2_defaults_to_adaptive_thinking() -> None: +def test_minimax_m2_uses_always_on_thinking_without_request_parameter() -> None: validated = MiniMaxLLMParameters.validate({"model": "MiniMax-M2.7", "api_key": "k"}) - assert validated["thinking"] == {"type": "adaptive"} + assert validated["thinking"] is None + assert MiniMaxLLMParameters.validate(dict(validated))["thinking"] is None + + explicitly_enabled = MiniMaxLLMParameters.validate( + {"model": "MiniMax-M2.7", "api_key": "k", "enable_thinking": True} + ) + assert explicitly_enabled["thinking"] is None + + +def test_minimax_m2_strips_explicit_thinking_payload() -> None: + # The provider accepts a thinking payload for M2.x but ignores it, so drop it + # instead of failing a call that works today. + validated = MiniMaxLLMParameters.validate( + { + "model": "MiniMax-M2.7", + "api_key": "k", + "thinking": {"type": "disabled"}, + } + ) + + assert validated["thinking"] is None def test_minimax_m2_thinking_rules_require_model_family_boundary() -> None: