diff --git a/litellm/llms/moonshot/chat/transformation.py b/litellm/llms/moonshot/chat/transformation.py index 07a963e95fd..9961cfbb238 100644 --- a/litellm/llms/moonshot/chat/transformation.py +++ b/litellm/llms/moonshot/chat/transformation.py @@ -92,6 +92,10 @@ class MoonshotChatConfig(OpenAIGPTConfig): - functions parameter is not supported (use tools instead) - tool_choice doesn't support "required" value - kimi-thinking-preview doesn't support tool calls at all + + Reasoning models (kimi-k3, ...) expose a top-level `reasoning_effort` + field to control the thinking budget, so advertise it for them. + https://platform.kimi.ai/docs/pricing/chat-k3 """ excluded_params: List[str] = ["functions"] @@ -100,10 +104,10 @@ class MoonshotChatConfig(OpenAIGPTConfig): excluded_params.extend(["tools", "tool_choice"]) base_openai_params = super().get_supported_openai_params(model=model) - final_params: List[str] = [] - for param in base_openai_params: - if param not in excluded_params: - final_params.append(param) + final_params = [p for p in base_openai_params if p not in excluded_params] + + if supports_reasoning(model=model, custom_llm_provider="moonshot"): + final_params = [*final_params, "reasoning_effort"] return final_params diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index e5afc81b641..5e30112b702 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -27604,6 +27604,22 @@ "supports_video_input": true, "supports_vision": true }, + "moonshot/kimi-k3": { + "cache_read_input_token_cost": 3e-07, + "input_cost_per_token": 3e-06, + "litellm_provider": "moonshot", + "max_input_tokens": 1048576, + "max_output_tokens": 1048576, + "max_tokens": 1048576, + "mode": "chat", + "output_cost_per_token": 1.5e-05, + "source": "https://platform.kimi.ai/docs/pricing/chat-k3", + "supports_function_calling": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true + }, "moonshot/kimi-latest": { "cache_read_input_token_cost": 1.5e-07, "deprecation_date": "2026-01-28", @@ -34015,6 +34031,38 @@ "supports_response_schema": true, "supports_tool_choice": true }, + "together_ai/thinkingmachines/Inkling": { + "cache_read_input_token_cost": 1.7e-07, + "input_cost_per_token": 1e-06, + "litellm_provider": "together_ai", + "max_input_tokens": 524288, + "max_tokens": 524288, + "mode": "chat", + "output_cost_per_token": 4.05e-06, + "source": "https://www.together.ai/models/inkling", + "supports_audio_input": true, + "supports_function_calling": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true + }, + "together_ai/thinkingmachines/inkling": { + "cache_read_input_token_cost": 1.7e-07, + "input_cost_per_token": 1e-06, + "litellm_provider": "together_ai", + "max_input_tokens": 524288, + "max_tokens": 524288, + "mode": "chat", + "output_cost_per_token": 4.05e-06, + "source": "https://www.together.ai/models/inkling", + "supports_audio_input": true, + "supports_function_calling": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true + }, "together_ai/togethercomputer/CodeLlama-34b-Instruct": { "litellm_provider": "together_ai", "mode": "chat", diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 5cf99ba8bac..b0e670482b3 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -27679,6 +27679,22 @@ "supports_video_input": true, "supports_vision": true }, + "moonshot/kimi-k3": { + "cache_read_input_token_cost": 3e-07, + "input_cost_per_token": 3e-06, + "litellm_provider": "moonshot", + "max_input_tokens": 1048576, + "max_output_tokens": 1048576, + "max_tokens": 1048576, + "mode": "chat", + "output_cost_per_token": 1.5e-05, + "source": "https://platform.kimi.ai/docs/pricing/chat-k3", + "supports_function_calling": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true + }, "moonshot/kimi-latest": { "cache_read_input_token_cost": 1.5e-07, "deprecation_date": "2026-01-28", @@ -34106,6 +34122,38 @@ "supports_response_schema": true, "supports_tool_choice": true }, + "together_ai/thinkingmachines/Inkling": { + "cache_read_input_token_cost": 1.7e-07, + "input_cost_per_token": 1e-06, + "litellm_provider": "together_ai", + "max_input_tokens": 524288, + "max_tokens": 524288, + "mode": "chat", + "output_cost_per_token": 4.05e-06, + "source": "https://www.together.ai/models/inkling", + "supports_audio_input": true, + "supports_function_calling": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true + }, + "together_ai/thinkingmachines/inkling": { + "cache_read_input_token_cost": 1.7e-07, + "input_cost_per_token": 1e-06, + "litellm_provider": "together_ai", + "max_input_tokens": 524288, + "max_tokens": 524288, + "mode": "chat", + "output_cost_per_token": 4.05e-06, + "source": "https://www.together.ai/models/inkling", + "supports_audio_input": true, + "supports_function_calling": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true + }, "together_ai/togethercomputer/CodeLlama-34b-Instruct": { "litellm_provider": "together_ai", "mode": "chat", 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 417dd4a767c..ae94870ca6a 100644 --- a/tests/test_litellm/llms/moonshot/test_moonshot_chat_transformation.py +++ b/tests/test_litellm/llms/moonshot/test_moonshot_chat_transformation.py @@ -63,6 +63,61 @@ class TestMoonshotConfig: # Should NOT include functions (not supported by Moonshot AI) assert "functions" not in supported_params + def test_reasoning_effort_supported_for_reasoning_models(self): + """Reasoning models (kimi-k3) expose a top-level reasoning_effort control, + so it must be advertised as a supported OpenAI param for them.""" + config = MoonshotChatConfig() + + with patch( + "litellm.llms.moonshot.chat.transformation.supports_reasoning", + return_value=True, + ): + assert "reasoning_effort" in config.get_supported_openai_params("kimi-k3") + + def test_reasoning_effort_not_supported_for_non_reasoning_models(self): + """Non-reasoning Moonshot models must not advertise reasoning_effort.""" + config = MoonshotChatConfig() + + with patch( + "litellm.llms.moonshot.chat.transformation.supports_reasoning", + return_value=False, + ): + assert "reasoning_effort" not in config.get_supported_openai_params("moonshot-v1-8k") + + def test_reasoning_effort_passed_through_for_reasoning_models(self): + """reasoning_effort should be forwarded to the request body for reasoning models.""" + config = MoonshotChatConfig() + + with patch( + "litellm.llms.moonshot.chat.transformation.supports_reasoning", + return_value=True, + ): + result = config.map_openai_params( + non_default_params={"reasoning_effort": "max"}, + optional_params={}, + model="kimi-k3", + drop_params=False, + ) + + assert result.get("reasoning_effort") == "max" + + def test_reasoning_effort_dropped_for_non_reasoning_models(self): + """reasoning_effort must not leak into requests for non-reasoning models.""" + config = MoonshotChatConfig() + + with patch( + "litellm.llms.moonshot.chat.transformation.supports_reasoning", + return_value=False, + ): + result = config.map_openai_params( + non_default_params={"reasoning_effort": "max"}, + optional_params={}, + model="moonshot-v1-8k", + drop_params=True, + ) + + assert "reasoning_effort" not in result + def test_map_openai_params_excludes_functions(self): """Test that functions parameter is not mapped""" config = MoonshotChatConfig() @@ -744,6 +799,46 @@ class TestKimiK26ModelRegistry: assert model_info["litellm_provider"] == "moonshot" +class TestKimiK3ModelRegistry: + """Tests that kimi-k3 is correctly registered in the model registry.""" + + @pytest.fixture(autouse=True) + def model_cost_map(self): + """Load directly from the bundled backup so tests don't depend on remote fetch.""" + return GetModelCostMap.load_local_model_cost_map() + + def test_kimi_k3_in_model_cost_map(self, model_cost_map): + """kimi-k3 should be present in the model cost map.""" + assert "moonshot/kimi-k3" in model_cost_map, "moonshot/kimi-k3 not found in model_cost" + + def test_kimi_k3_pricing(self, model_cost_map): + """kimi-k3 pricing should match official Kimi API rates.""" + model_info = model_cost_map["moonshot/kimi-k3"] + assert model_info["input_cost_per_token"] == pytest.approx(3e-06) + assert model_info["output_cost_per_token"] == pytest.approx(1.5e-05) + assert model_info["cache_read_input_token_cost"] == pytest.approx(3e-07) + + def test_kimi_k3_context_window(self, model_cost_map): + """kimi-k3 should have a 1M (1048576 token) context window.""" + model_info = model_cost_map["moonshot/kimi-k3"] + assert model_info["max_input_tokens"] == 1048576 + assert model_info["max_tokens"] == 1048576 + + def test_kimi_k3_capabilities(self, model_cost_map): + """kimi-k3 should support function calling, vision, tool choice, reasoning, and json schema.""" + model_info = model_cost_map["moonshot/kimi-k3"] + assert model_info.get("supports_function_calling") is True + assert model_info.get("supports_tool_choice") is True + assert model_info.get("supports_vision") is True + assert model_info.get("supports_reasoning") is True + assert model_info.get("supports_response_schema") is True + + def test_kimi_k3_provider(self, model_cost_map): + """kimi-k3 should be assigned to the moonshot provider.""" + model_info = model_cost_map["moonshot/kimi-k3"] + 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 @@ -752,6 +847,7 @@ class TestMoonshotResponseSchemaSupport: LIVE_MODELS = [ "moonshot/kimi-k2.5", "moonshot/kimi-k2.6", + "moonshot/kimi-k3", "moonshot/moonshot-v1-8k", "moonshot/moonshot-v1-32k", "moonshot/moonshot-v1-128k", diff --git a/tests/test_litellm/llms/together_ai/test_together_ai_cost_map.py b/tests/test_litellm/llms/together_ai/test_together_ai_cost_map.py new file mode 100644 index 00000000000..42ef8b4757a --- /dev/null +++ b/tests/test_litellm/llms/together_ai/test_together_ai_cost_map.py @@ -0,0 +1,58 @@ +""" +Registry tests for Together AI models in the bundled model cost map. +""" + +import os +import sys + +sys.path.insert(0, os.path.abspath("../../../../..")) + +import pytest + +from litellm.litellm_core_utils.get_model_cost_map import GetModelCostMap + +INKLING_KEYS = [ + "together_ai/thinkingmachines/Inkling", + "together_ai/thinkingmachines/inkling", +] + + +class TestInklingModelRegistry: + """Tests that Thinking Machines Lab's Inkling is registered under together_ai.""" + + @pytest.fixture(autouse=True) + def model_cost_map(self): + """Load directly from the bundled backup so tests don't depend on remote fetch.""" + return GetModelCostMap.load_local_model_cost_map() + + @pytest.mark.parametrize("model", INKLING_KEYS) + def test_inkling_in_model_cost_map(self, model, model_cost_map): + assert model in model_cost_map, f"{model} not found in model_cost" + + @pytest.mark.parametrize("model", INKLING_KEYS) + def test_inkling_pricing(self, model, model_cost_map): + """Inkling pricing should match Together AI's published serverless rates.""" + model_info = model_cost_map[model] + assert model_info["input_cost_per_token"] == pytest.approx(1e-06) + assert model_info["output_cost_per_token"] == pytest.approx(4.05e-06) + assert model_info["cache_read_input_token_cost"] == pytest.approx(1.7e-07) + + @pytest.mark.parametrize("model", INKLING_KEYS) + def test_inkling_context_window(self, model, model_cost_map): + """Inkling should expose a 524K token context window.""" + assert model_cost_map[model]["max_input_tokens"] == 524288 + + @pytest.mark.parametrize("model", INKLING_KEYS) + def test_inkling_capabilities(self, model, model_cost_map): + """Inkling is a multimodal reasoning model with tools and json schema support.""" + model_info = model_cost_map[model] + assert model_info.get("supports_function_calling") is True + assert model_info.get("supports_tool_choice") is True + assert model_info.get("supports_reasoning") is True + assert model_info.get("supports_vision") is True + assert model_info.get("supports_audio_input") is True + assert model_info.get("supports_response_schema") is True + + @pytest.mark.parametrize("model", INKLING_KEYS) + def test_inkling_provider(self, model, model_cost_map): + assert model_cost_map[model]["litellm_provider"] == "together_ai"