fix(kimi): send thinking xor reasoning_effort, never both
The standalone Kimi/Moonshot profile (api.moonshot.ai/v1) sent both extra_body.thinking AND a top-level reasoning_effort. With no reasoning config it even defaulted to thinking:enabled + reasoning_effort:medium, pairing them on every default call. Moonshot treats these as mutually exclusive (cannot specify both 'thinking' and 'reasoning_effort'). Align with the kimi-k2 handling already shipped for the opencode-go relay: send effort when a recognized low|medium|high is requested, otherwise fall back to the extra_body.thinking toggle. Disabled sends thinking:disabled only. Never both. Reported by Cars29 (NOUS Discord). DeepSeek was deliberately left untouched: its native endpoint accepts both (verified by the live guardrail in test_deepseek_v4_thinking_live.py), so the report's DeepSeek claim does not hold there. Tests: tests/plugins/model_providers/test_kimi_profile.py pins the xor contract across all config shapes.
This commit is contained in:
@@ -14,19 +14,28 @@ from providers.base import OMIT_TEMPERATURE, ProviderProfile
|
||||
|
||||
|
||||
class KimiProfile(ProviderProfile):
|
||||
"""Kimi/Moonshot — temperature omitted, thinking + reasoning_effort."""
|
||||
"""Kimi/Moonshot — temperature omitted, thinking xor reasoning_effort."""
|
||||
|
||||
def build_api_kwargs_extras(
|
||||
self, *, reasoning_config: dict | None = None, **context
|
||||
) -> tuple[dict[str, Any], dict[str, Any]]:
|
||||
"""Kimi uses extra_body.thinking + top-level reasoning_effort."""
|
||||
"""Kimi reasoning controls.
|
||||
|
||||
Moonshot's wire shape treats ``extra_body.thinking`` (a binary toggle)
|
||||
and a top-level ``reasoning_effort`` as mutually exclusive — sending
|
||||
both is at best redundant and risks "cannot specify both 'thinking' and
|
||||
'reasoning_effort'" (HTTP 400). This mirrors the kimi-k2 handling on the
|
||||
opencode-go relay: send effort when one is requested, otherwise fall
|
||||
back to ``extra_body.thinking`` — never both.
|
||||
"""
|
||||
extra_body = {}
|
||||
top_level = {}
|
||||
|
||||
if not reasoning_config or not isinstance(reasoning_config, dict):
|
||||
# No config → thinking enabled, default effort
|
||||
# No config → thinking enabled, let the server pick the depth.
|
||||
# (Previously also sent reasoning_effort="medium", which paired
|
||||
# thinking + effort on every default call.)
|
||||
extra_body["thinking"] = {"type": "enabled"}
|
||||
top_level["reasoning_effort"] = "medium"
|
||||
return extra_body, top_level
|
||||
|
||||
enabled = reasoning_config.get("enabled", True)
|
||||
@@ -34,13 +43,13 @@ class KimiProfile(ProviderProfile):
|
||||
extra_body["thinking"] = {"type": "disabled"}
|
||||
return extra_body, top_level
|
||||
|
||||
# Enabled
|
||||
extra_body["thinking"] = {"type": "enabled"}
|
||||
# Enabled: prefer an explicit effort; only fall back to extra_body
|
||||
# thinking when no recognized effort is requested.
|
||||
effort = (reasoning_config.get("effort") or "").strip().lower()
|
||||
if effort in {"low", "medium", "high"}:
|
||||
top_level["reasoning_effort"] = effort
|
||||
else:
|
||||
top_level["reasoning_effort"] = "medium"
|
||||
extra_body["thinking"] = {"type": "enabled"}
|
||||
|
||||
return extra_body, top_level
|
||||
|
||||
|
||||
Reference in New Issue
Block a user