feat(moonshot): advertise json_schema response support on live models (#29683)

litellm.responses() already routes Moonshot through the responses->chat-completions
bridge, and Moonshot honors response_format json_schema on chat completions. The
cost-map entries left supports_response_schema unset, so discovery layers that gate
on that flag dropped Moonshot from structured-output / responses listings even though
the capability works end to end.

Set supports_response_schema on the nine models currently live on api.moonshot.ai:
kimi-k2.5, kimi-k2.6, the moonshot-v1 8k/32k/128k text and vision-preview variants,
and moonshot-v1-auto. Verified against the live API that each honors json_schema and
that litellm.responses() returns schema-valid structured output through the bridge.
This commit is contained in:
Daniel Yudelevich 2026-06-05 02:08:36 -07:00 • committed by GitHub
parent 13dd954593
commit 21596c82be
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 54 additions and 23 deletions

View file

@ -25024,6 +25024,7 @@
"source": "https://platform.moonshot.ai/docs/guide/kimi-k2-5-quickstart",
"supports_function_calling": true,
"supports_reasoning": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_video_input": true,
"supports_vision": true
@ -25040,6 +25041,7 @@
"source": "https://platform.kimi.ai/docs/pricing/chat-k26",
"supports_function_calling": true,
"supports_reasoning": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_video_input": true,
"supports_vision": true
@ -25152,6 +25154,7 @@
"output_cost_per_token": 5e-06,
"source": "https://platform.moonshot.ai/docs/pricing",
"supports_function_calling": true,
"supports_response_schema": true,
"supports_tool_choice": true
},
"moonshot/moonshot-v1-128k-0430": {
@ -25176,6 +25179,7 @@
"output_cost_per_token": 5e-06,
"source": "https://platform.moonshot.ai/docs/pricing",
"supports_function_calling": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true
},
@ -25189,6 +25193,7 @@
"output_cost_per_token": 3e-06,
"source": "https://platform.moonshot.ai/docs/pricing",
"supports_function_calling": true,
"supports_response_schema": true,
"supports_tool_choice": true
},
"moonshot/moonshot-v1-32k-0430": {
@ -25213,6 +25218,7 @@
"output_cost_per_token": 3e-06,
"source": "https://platform.moonshot.ai/docs/pricing",
"supports_function_calling": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true
},
@ -25226,6 +25232,7 @@
"output_cost_per_token": 2e-06,
"source": "https://platform.moonshot.ai/docs/pricing",
"supports_function_calling": true,
"supports_response_schema": true,
"supports_tool_choice": true
},
"moonshot/moonshot-v1-8k-0430": {
@ -25250,6 +25257,7 @@
"output_cost_per_token": 2e-06,
"source": "https://platform.moonshot.ai/docs/pricing",
"supports_function_calling": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true
},
@ -25263,6 +25271,7 @@
"output_cost_per_token": 5e-06,
"source": "https://platform.moonshot.ai/docs/pricing",
"supports_function_calling": true,
"supports_response_schema": true,
"supports_tool_choice": true
},
"morph/morph-v3-fast": {

View file

@ -25024,6 +25024,7 @@
"source": "https://platform.moonshot.ai/docs/guide/kimi-k2-5-quickstart",
"supports_function_calling": true,
"supports_reasoning": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_video_input": true,
"supports_vision": true
@ -25040,6 +25041,7 @@
"source": "https://platform.kimi.ai/docs/pricing/chat-k26",
"supports_function_calling": true,
"supports_reasoning": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_video_input": true,
"supports_vision": true
@ -25152,6 +25154,7 @@
"output_cost_per_token": 5e-06,
"source": "https://platform.moonshot.ai/docs/pricing",
"supports_function_calling": true,
"supports_response_schema": true,
"supports_tool_choice": true
},
"moonshot/moonshot-v1-128k-0430": {
@ -25176,6 +25179,7 @@
"output_cost_per_token": 5e-06,
"source": "https://platform.moonshot.ai/docs/pricing",
"supports_function_calling": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true
},
@ -25189,6 +25193,7 @@
"output_cost_per_token": 3e-06,
"source": "https://platform.moonshot.ai/docs/pricing",
"supports_function_calling": true,
"supports_response_schema": true,
"supports_tool_choice": true
},
"moonshot/moonshot-v1-32k-0430": {
@ -25213,6 +25218,7 @@
"output_cost_per_token": 3e-06,
"source": "https://platform.moonshot.ai/docs/pricing",
"supports_function_calling": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true
},
@ -25226,6 +25232,7 @@
"output_cost_per_token": 2e-06,
"source": "https://platform.moonshot.ai/docs/pricing",
"supports_function_calling": true,
"supports_response_schema": true,
"supports_tool_choice": true
},
"moonshot/moonshot-v1-8k-0430": {
@ -25250,6 +25257,7 @@
"output_cost_per_token": 2e-06,
"source": "https://platform.moonshot.ai/docs/pricing",
"supports_function_calling": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true
},
@ -25263,6 +25271,7 @@
"output_cost_per_token": 5e-06,
"source": "https://platform.moonshot.ai/docs/pricing",
"supports_function_calling": true,
"supports_response_schema": true,
"supports_tool_choice": true
},
"morph/morph-v3-fast": {

View file

@ -9,15 +9,12 @@ import os
import sys
from unittest.mock import patch
sys.path.insert(
0, os.path.abspath("../../../../..")
) # Adds the parent directory to the system path
sys.path.insert(0, os.path.abspath("../../../../..")) # Adds the parent directory to the system path
import pytest
import litellm
import litellm.utils
from litellm import completion
from litellm.litellm_core_utils.get_model_cost_map import GetModelCostMap
from litellm.llms.moonshot.chat.transformation import MoonshotChatConfig
@ -232,10 +229,7 @@ class TestMoonshotConfig:
assert result["messages"][0]["role"] == "user"
assert result["messages"][0]["content"] == "What's the weather like?"
assert result["messages"][1]["role"] == "user"
assert (
result["messages"][1]["content"]
== "Please select a tool to handle the current issue."
)
assert result["messages"][1]["content"] == "Please select a tool to handle the current issue."
# Check that tool_choice was removed but tools are preserved
assert "tool_choice" not in result
@ -273,10 +267,7 @@ class TestMoonshotConfig:
# Check that the message was added
assert len(result["messages"]) == 2
assert (
result["messages"][1]["content"]
== "Please select a tool to handle the current issue."
)
assert result["messages"][1]["content"] == "Please select a tool to handle the current issue."
def test_tool_choice_non_required_preserved(self):
"""Test that non-'required' tool_choice values are preserved"""
@ -501,9 +492,7 @@ class TestMoonshotConfig:
assert result[0].get("reasoning_content") == "stored thinking"
# The promoted key must be removed from provider_specific_fields to
# avoid sending the value twice in the serialised request body
assert "reasoning_content" not in (
result[0].get("provider_specific_fields") or {}
)
assert "reasoning_content" not in (result[0].get("provider_specific_fields") or {})
def test_reasoning_model_fill_called_from_transform_request(self):
"""transform_request injects reasoning_content end-to-end for reasoning models."""
@ -603,10 +592,7 @@ class TestMoonshotConfig:
result = config.fill_reasoning_content(messages)
# reasoning_content should be preserved, not replaced with placeholder
assert (
result[0].get("reasoning_content")
== "<thinking>User wants weather</thinking>"
)
assert result[0].get("reasoning_content") == "<thinking>User wants weather</thinking>"
def test_reasoning_content_preserved_in_multi_turn_flow(self):
"""reasoning_content is preserved through multi-turn conversation flow.
@ -650,10 +636,7 @@ class TestMoonshotConfig:
result = config.fill_reasoning_content(messages)
# reasoning_content should be preserved in the assistant message
assert (
result[1].get("reasoning_content")
== "<thinking>Planning to call weather tool</thinking>"
)
assert result[1].get("reasoning_content") == "<thinking>Planning to call weather tool</thinking>"
class TestKimiK26ModelRegistry:
@ -695,3 +678,33 @@ class TestKimiK26ModelRegistry:
"""kimi-k2.6 should be assigned to the moonshot provider."""
model_info = model_cost_map["moonshot/kimi-k2.6"]
assert model_info["litellm_provider"] == "moonshot"
class TestMoonshotResponseSchemaSupport:
"""Every model currently live on api.moonshot.ai supports json_schema
response_format, which gates discovery via litellm.responses(). The flag
must be true so the capability is advertised honestly."""
LIVE_MODELS = [
"moonshot/kimi-k2.5",
"moonshot/kimi-k2.6",
"moonshot/moonshot-v1-8k",
"moonshot/moonshot-v1-32k",
"moonshot/moonshot-v1-128k",
"moonshot/moonshot-v1-8k-vision-preview",
"moonshot/moonshot-v1-32k-vision-preview",
"moonshot/moonshot-v1-128k-vision-preview",
"moonshot/moonshot-v1-auto",
]
@pytest.fixture(autouse=True)
def model_cost_map(self):
return GetModelCostMap.load_local_model_cost_map()
@pytest.mark.parametrize("model", LIVE_MODELS)
def test_live_model_supports_response_schema(self, model, model_cost_map):
assert model_cost_map[model].get("supports_response_schema") is True
def test_supports_response_schema_utility_reports_true(self, model_cost_map, monkeypatch):
monkeypatch.setattr(litellm, "model_cost", model_cost_map)
assert litellm.utils.supports_response_schema(model="moonshot/kimi-k2.5") is True