mirror of
https://github.com/TauricResearch/TradingAgents.git
synced 2026-06-16 21:06:15 +03:00
fix(llm): gate MiniMax reasoning_split by model capability (#826)
MinimaxChatOpenAI unconditionally set reasoning_split=True, but the kwarg is only valid on M2.x reasoning models. The openai SDK's strict kwarg validation raised TypeError for Coding Plan and any other non- reasoning MiniMax model. Adds requires_reasoning_split to ModelCapabilities, gates the payload injection on it, and only sets True for _MINIMAX_THINKING (M2.x exact IDs and the ^MiniMax-M\d forward-compat pattern). Same shape as the existing supports_tool_choice gate. Regression tests cover both halves: M2.x models still receive the flag, non-reasoning MiniMax models do not.
This commit is contained in:
@@ -75,6 +75,22 @@ class TestMinimaxExactMatches:
|
|||||||
def test_m2_base_rejects_tool_choice(self):
|
def test_m2_base_rejects_tool_choice(self):
|
||||||
assert get_capabilities("MiniMax-M2").supports_tool_choice is False
|
assert get_capabilities("MiniMax-M2").supports_tool_choice is False
|
||||||
|
|
||||||
|
def test_m2_x_requires_reasoning_split(self):
|
||||||
|
# M2.x reasoning models need reasoning_split=True so <think> blocks
|
||||||
|
# land in reasoning_details instead of content (#826).
|
||||||
|
for model in ("MiniMax-M2.7", "MiniMax-M2.5-highspeed", "MiniMax-M2"):
|
||||||
|
assert get_capabilities(model).requires_reasoning_split is True
|
||||||
|
|
||||||
|
def test_future_m3_inherits_reasoning_split(self):
|
||||||
|
assert get_capabilities("MiniMax-M3-highspeed").requires_reasoning_split is True
|
||||||
|
|
||||||
|
def test_non_reasoning_minimax_does_not_get_reasoning_split(self):
|
||||||
|
# Coding Plan, MiniMax-Text-01, and any non-M2-prefixed MiniMax model
|
||||||
|
# reject the reasoning_split kwarg via the openai SDK's strict
|
||||||
|
# validation (#826). Default capability has it disabled.
|
||||||
|
for model in ("minimax-text-01", "MiniMax-Coding-Plan", "abab6.5-chat"):
|
||||||
|
assert get_capabilities(model).requires_reasoning_split is False
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
class TestDefault:
|
class TestDefault:
|
||||||
|
|||||||
@@ -42,6 +42,18 @@ class TestMinimaxReasoningSplit:
|
|||||||
# the caller passed. setdefault leaves an existing value alone.
|
# the caller passed. setdefault leaves an existing value alone.
|
||||||
assert payload.get("reasoning_split") in (False, True)
|
assert payload.get("reasoning_split") in (False, True)
|
||||||
|
|
||||||
|
def test_non_reasoning_minimax_does_not_inject_reasoning_split(self):
|
||||||
|
"""Coding Plan / MiniMax-Text-01 / any non-M2-prefixed model must NOT
|
||||||
|
receive reasoning_split — the openai SDK rejects unknown kwargs with
|
||||||
|
TypeError (#826)."""
|
||||||
|
for model in ("minimax-text-01", "MiniMax-Coding-Plan"):
|
||||||
|
payload = _client(model)._get_request_payload(
|
||||||
|
[HumanMessage(content="hi")]
|
||||||
|
)
|
||||||
|
assert "reasoning_split" not in payload, (
|
||||||
|
f"{model!r} payload unexpectedly contains reasoning_split"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
class TestMinimaxStructuredOutputDispatch:
|
class TestMinimaxStructuredOutputDispatch:
|
||||||
|
|||||||
@@ -38,6 +38,12 @@ class ModelCapabilities:
|
|||||||
# DeepSeek thinking-mode models 400 if reasoning_content from prior
|
# DeepSeek thinking-mode models 400 if reasoning_content from prior
|
||||||
# assistant turns is not echoed back on the next request.
|
# assistant turns is not echoed back on the next request.
|
||||||
requires_reasoning_content_roundtrip: bool = False
|
requires_reasoning_content_roundtrip: bool = False
|
||||||
|
# MiniMax M2.x reasoning models need ``reasoning_split=True`` so the
|
||||||
|
# <think> block lands in ``reasoning_details`` instead of polluting
|
||||||
|
# ``content``. The flag is rejected by non-reasoning MiniMax models
|
||||||
|
# (Coding Plan, MiniMax-Text-01, etc.), so we only set it where the
|
||||||
|
# model actually consumes it. (#826)
|
||||||
|
requires_reasoning_split: bool = False
|
||||||
|
|
||||||
|
|
||||||
# DeepSeek's thinking models accept the ``tools`` array but reject the
|
# DeepSeek's thinking models accept the ``tools`` array but reject the
|
||||||
@@ -74,6 +80,7 @@ _MINIMAX_THINKING = ModelCapabilities(
|
|||||||
supports_json_mode=False,
|
supports_json_mode=False,
|
||||||
supports_json_schema=False,
|
supports_json_schema=False,
|
||||||
preferred_structured_method="function_calling",
|
preferred_structured_method="function_calling",
|
||||||
|
requires_reasoning_split=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
_DEFAULT = ModelCapabilities(
|
_DEFAULT = ModelCapabilities(
|
||||||
|
|||||||
@@ -118,6 +118,11 @@ class MinimaxChatOpenAI(NormalizedChatOpenAI):
|
|||||||
``reasoning_split=True`` in the request body redirects the thinking
|
``reasoning_split=True`` in the request body redirects the thinking
|
||||||
block into ``reasoning_details`` so ``content`` stays clean.
|
block into ``reasoning_details`` so ``content`` stays clean.
|
||||||
|
|
||||||
|
The flag is gated by ``ModelCapabilities.requires_reasoning_split``
|
||||||
|
because non-reasoning MiniMax endpoints (Coding Plan, MiniMax-Text-01)
|
||||||
|
reject the parameter via the openai SDK's strict kwarg validation
|
||||||
|
(#826).
|
||||||
|
|
||||||
Tool-choice handling for M2.x — those models accept only the string
|
Tool-choice handling for M2.x — those models accept only the string
|
||||||
enum ``{"none", "auto"}`` and reject langchain's function-spec dict —
|
enum ``{"none", "auto"}`` and reject langchain's function-spec dict —
|
||||||
is handled by the capability dispatch in
|
is handled by the capability dispatch in
|
||||||
@@ -126,7 +131,8 @@ class MinimaxChatOpenAI(NormalizedChatOpenAI):
|
|||||||
|
|
||||||
def _get_request_payload(self, input_, *, stop=None, **kwargs):
|
def _get_request_payload(self, input_, *, stop=None, **kwargs):
|
||||||
payload = super()._get_request_payload(input_, stop=stop, **kwargs)
|
payload = super()._get_request_payload(input_, stop=stop, **kwargs)
|
||||||
payload.setdefault("reasoning_split", True)
|
if get_capabilities(self.model_name).requires_reasoning_split:
|
||||||
|
payload.setdefault("reasoning_split", True)
|
||||||
return payload
|
return payload
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user