From 21596c82bea800e8e9c6ab4568672ead8453c135 Mon Sep 17 00:00:00 2001 From: Daniel Yudelevich <4537920+yudelevi@users.noreply.github.com> Date: Fri, 5 Jun 2026 02:08:36 -0700 Subject: [PATCH] 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. --- ...odel_prices_and_context_window_backup.json | 9 +++ model_prices_and_context_window.json | 9 +++ .../test_moonshot_chat_transformation.py | 59 +++++++++++-------- 3 files changed, 54 insertions(+), 23 deletions(-) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 75547fe42d4..25e4c388552 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -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": { diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 1ebc816b261..88627ada1bb 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -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": { diff --git a/tests/test_litellm/llms/moonshot/test_moonshot_chat_transformation.py b/tests/test_litellm/llms/moonshot/test_moonshot_chat_transformation.py index b4744a7ed18..1e4d79963c1 100644 --- a/tests/test_litellm/llms/moonshot/test_moonshot_chat_transformation.py +++ b/tests/test_litellm/llms/moonshot/test_moonshot_chat_transformation.py @@ -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") - == "User wants weather" - ) + assert result[0].get("reasoning_content") == "User wants weather" 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") - == "Planning to call weather tool" - ) + assert result[1].get("reasoning_content") == "Planning to call weather tool" 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