fix(providers): enable thinking for Kimi K2.7 models
This commit is contained in:
@@ -60,8 +60,15 @@ _DEFAULT_OPENROUTER_HEADERS = {
|
|||||||
_KIMI_THINKING_MODELS: frozenset[str] = frozenset({
|
_KIMI_THINKING_MODELS: frozenset[str] = frozenset({
|
||||||
"kimi-k2.5",
|
"kimi-k2.5",
|
||||||
"kimi-k2.6",
|
"kimi-k2.6",
|
||||||
|
"kimi-k2.7",
|
||||||
|
"kimi-k2.7-code",
|
||||||
|
"kimi-k2.7-code-highspeed",
|
||||||
"k2.6-code-preview",
|
"k2.6-code-preview",
|
||||||
})
|
})
|
||||||
|
_KIMI_ALWAYS_THINKING_MODELS: frozenset[str] = frozenset({
|
||||||
|
"kimi-k2.7-code",
|
||||||
|
"kimi-k2.7-code-highspeed",
|
||||||
|
})
|
||||||
# Thinking-capable MiMo models per Xiaomi docs (see
|
# Thinking-capable MiMo models per Xiaomi docs (see
|
||||||
# tests/providers/test_xiaomi_mimo_thinking.py). mimo-v2-flash is omitted
|
# tests/providers/test_xiaomi_mimo_thinking.py). mimo-v2-flash is omitted
|
||||||
# because it does not support thinking.
|
# because it does not support thinking.
|
||||||
@@ -692,13 +699,20 @@ class OpenAICompatProvider(LLMProvider):
|
|||||||
# Only send thinking controls when reasoning_effort is explicit so
|
# Only send thinking controls when reasoning_effort is explicit so
|
||||||
# omitting the config preserves each provider's default.
|
# omitting the config preserves each provider's default.
|
||||||
if reasoning_effort is not None:
|
if reasoning_effort is not None:
|
||||||
|
slug = _model_slug(model_name)
|
||||||
thinking_enabled = semantic_effort not in ("none", "minimal")
|
thinking_enabled = semantic_effort not in ("none", "minimal")
|
||||||
for thinking_style in _thinking_styles_for(spec, model_name):
|
for thinking_style in _thinking_styles_for(spec, model_name):
|
||||||
|
if not thinking_enabled and slug in _KIMI_ALWAYS_THINKING_MODELS:
|
||||||
|
continue
|
||||||
extra = _thinking_extra_body(thinking_style, thinking_enabled)
|
extra = _thinking_extra_body(thinking_style, thinking_enabled)
|
||||||
if extra:
|
if extra:
|
||||||
kwargs.setdefault("extra_body", {}).update(extra)
|
kwargs.setdefault("extra_body", {}).update(extra)
|
||||||
gateway_style = getattr(spec, "gateway_reasoning_style", "") if spec else ""
|
gateway_style = getattr(spec, "gateway_reasoning_style", "") if spec else ""
|
||||||
if gateway_style and _model_thinking_style(model_name):
|
if (
|
||||||
|
gateway_style
|
||||||
|
and _model_thinking_style(model_name)
|
||||||
|
and (thinking_enabled or slug not in _KIMI_ALWAYS_THINKING_MODELS)
|
||||||
|
):
|
||||||
extra = _gateway_reasoning_extra_body(gateway_style, semantic_effort)
|
extra = _gateway_reasoning_extra_body(gateway_style, semantic_effort)
|
||||||
if extra:
|
if extra:
|
||||||
kwargs.setdefault("extra_body", {}).update(extra)
|
kwargs.setdefault("extra_body", {}).update(extra)
|
||||||
@@ -708,7 +722,7 @@ class OpenAICompatProvider(LLMProvider):
|
|||||||
# user's intent via the provider-native shape, so drop the
|
# user's intent via the provider-native shape, so drop the
|
||||||
# redundant wire-level kwarg. Only kimi models need this —
|
# redundant wire-level kwarg. Only kimi models need this —
|
||||||
# Xiaomi's API accepts both params.
|
# Xiaomi's API accepts both params.
|
||||||
if _model_slug(model_name) in _KIMI_THINKING_MODELS:
|
if slug in _KIMI_THINKING_MODELS:
|
||||||
kwargs.pop("reasoning_effort", None)
|
kwargs.pop("reasoning_effort", None)
|
||||||
|
|
||||||
if tools:
|
if tools:
|
||||||
|
|||||||
@@ -352,7 +352,7 @@ PROVIDERS: tuple[ProviderSpec, ...] = (
|
|||||||
default_api_base="https://dashscope.aliyuncs.com/compatible-mode/v1",
|
default_api_base="https://dashscope.aliyuncs.com/compatible-mode/v1",
|
||||||
thinking_style="enable_thinking",
|
thinking_style="enable_thinking",
|
||||||
),
|
),
|
||||||
# Moonshot (月之暗面): Kimi K2.5 / K2.6 enforce temperature >= 1.0.
|
# Moonshot (月之暗面): Kimi K2.5+ enforce temperature >= 1.0.
|
||||||
ProviderSpec(
|
ProviderSpec(
|
||||||
name="moonshot",
|
name="moonshot",
|
||||||
keywords=("moonshot", "kimi"),
|
keywords=("moonshot", "kimi"),
|
||||||
@@ -363,6 +363,9 @@ PROVIDERS: tuple[ProviderSpec, ...] = (
|
|||||||
model_overrides=(
|
model_overrides=(
|
||||||
("kimi-k2.5", {"temperature": 1.0}),
|
("kimi-k2.5", {"temperature": 1.0}),
|
||||||
("kimi-k2.6", {"temperature": 1.0}),
|
("kimi-k2.6", {"temperature": 1.0}),
|
||||||
|
("kimi-k2.7", {"temperature": 1.0}),
|
||||||
|
("kimi-k2.7-code", {"temperature": 1.0}),
|
||||||
|
("kimi-k2.7-code-highspeed", {"temperature": 1.0}),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
# MiniMax: OpenAI-compatible API
|
# MiniMax: OpenAI-compatible API
|
||||||
|
|||||||
@@ -1573,12 +1573,49 @@ def test_kimi_k26_thinking_enabled_with_openrouter_prefix() -> None:
|
|||||||
assert "reasoning_effort" not in kw
|
assert "reasoning_effort" not in kw
|
||||||
|
|
||||||
|
|
||||||
|
def test_kimi_k27_code_thinking_enabled() -> None:
|
||||||
|
"""Kimi K2.7 Code supports native thinking controls."""
|
||||||
|
kw = _build_kwargs_for("moonshot", "kimi-k2.7-code", reasoning_effort="medium")
|
||||||
|
assert kw.get("extra_body") == {"thinking": {"type": "enabled"}}
|
||||||
|
assert "reasoning_effort" not in kw
|
||||||
|
|
||||||
|
|
||||||
|
def test_kimi_k27_code_thinking_enabled_with_openrouter_prefix() -> None:
|
||||||
|
"""OpenRouter-routed Kimi K2.7 Code should carry both thinking shapes."""
|
||||||
|
kw = _build_kwargs_for("openrouter", "moonshotai/kimi-k2.7-code", reasoning_effort="high")
|
||||||
|
assert kw.get("extra_body") == {
|
||||||
|
"thinking": {"type": "enabled"},
|
||||||
|
"reasoning": {"effort": "high"},
|
||||||
|
}
|
||||||
|
assert "reasoning_effort" not in kw
|
||||||
|
|
||||||
|
|
||||||
|
def test_kimi_k27_code_thinking_none_omits_disabled() -> None:
|
||||||
|
"""Kimi K2.7 Code is always-thinking; disabled thinking is invalid upstream."""
|
||||||
|
kw = _build_kwargs_for("moonshot", "kimi-k2.7-code", reasoning_effort="none")
|
||||||
|
assert "extra_body" not in kw
|
||||||
|
assert "reasoning_effort" not in kw
|
||||||
|
|
||||||
|
|
||||||
|
def test_kimi_k27_code_thinking_none_with_openrouter_prefix_omits_disabled() -> None:
|
||||||
|
"""OpenRouter-routed Kimi K2.7 Code should not request disabled thinking."""
|
||||||
|
kw = _build_kwargs_for("openrouter", "moonshotai/kimi-k2.7-code", reasoning_effort="none")
|
||||||
|
assert "extra_body" not in kw
|
||||||
|
assert "reasoning_effort" not in kw
|
||||||
|
|
||||||
|
|
||||||
def test_moonshot_kimi_k26_temperature_override() -> None:
|
def test_moonshot_kimi_k26_temperature_override() -> None:
|
||||||
"""Moonshot registry forces temperature 1.0 for kimi-k2.6 (API requirement)."""
|
"""Moonshot registry forces temperature 1.0 for kimi-k2.6 (API requirement)."""
|
||||||
kw = _build_kwargs_for("moonshot", "kimi-k2.6", reasoning_effort=None)
|
kw = _build_kwargs_for("moonshot", "kimi-k2.6", reasoning_effort=None)
|
||||||
assert kw["temperature"] == 1.0
|
assert kw["temperature"] == 1.0
|
||||||
|
|
||||||
|
|
||||||
|
def test_moonshot_kimi_k27_code_temperature_override() -> None:
|
||||||
|
"""Moonshot registry should force temperature 1.0 for Kimi K2.7 Code."""
|
||||||
|
kw = _build_kwargs_for("moonshot", "kimi-k2.7-code", reasoning_effort=None)
|
||||||
|
assert kw["temperature"] == 1.0
|
||||||
|
|
||||||
|
|
||||||
def test_kimi_k25_thinking_disabled_with_openrouter_prefix() -> None:
|
def test_kimi_k25_thinking_disabled_with_openrouter_prefix() -> None:
|
||||||
"""OpenRouter names must NOT trigger thinking without reasoning_effort."""
|
"""OpenRouter names must NOT trigger thinking without reasoning_effort."""
|
||||||
kw = _build_kwargs_for("openrouter", "moonshotai/kimi-k2.5", reasoning_effort=None)
|
kw = _build_kwargs_for("openrouter", "moonshotai/kimi-k2.5", reasoning_effort=None)
|
||||||
|
|||||||
Reference in New Issue
Block a user