Add Prompt caching and reasoning support for MiniMax, GLM, Xiaomi

This commit is contained in:
Sameer Kankute 2026-01-28 17:25:26 +05:30
parent ab655ef296
commit f6ead49afe
5 changed files with 337 additions and 5 deletions

View file

@ -1,11 +1,12 @@
"""
MiniMax OpenAI transformation config - extends OpenAI chat config for MiniMax's OpenAI-compatible API
"""
from typing import Optional
from typing import List, Optional, Tuple
import litellm
from litellm.llms.openai.chat.gpt_transformation import OpenAIGPTConfig
from litellm.secret_managers.main import get_secret_str
from litellm.types.llms.openai import AllMessageValues, ChatCompletionToolParam
class MinimaxChatConfig(OpenAIGPTConfig):
@ -73,11 +74,33 @@ class MinimaxChatConfig(OpenAIGPTConfig):
else:
return f"{base_url}/v1/chat/completions"
def remove_cache_control_flag_from_messages_and_tools(
self,
model: str,
messages: List[AllMessageValues],
tools: Optional[List[ChatCompletionToolParam]] = None,
) -> Tuple[List[AllMessageValues], Optional[List[ChatCompletionToolParam]]]:
"""
Override to preserve cache_control for MiniMax.
MiniMax supports cache_control - don't strip it.
"""
# MiniMax supports cache_control, so return messages and tools unchanged
return messages, tools
def get_supported_openai_params(self, model: str) -> list:
"""
Get supported OpenAI parameters for MiniMax.
Adds reasoning_split to the list of supported params.
Adds reasoning_split and thinking to the list of supported params.
"""
base_params = super().get_supported_openai_params(model=model)
return base_params + ["reasoning_split"]
additional_params = ["reasoning_split"]
# Add thinking parameter if model supports reasoning
try:
if litellm.supports_reasoning(model=model, custom_llm_provider="minimax"):
additional_params.append("thinking")
except Exception:
pass
return base_params + additional_params

View file

@ -26,6 +26,9 @@ class CacheControlSupportedModels(str, Enum):
"""Models that support cache_control in content blocks."""
CLAUDE = "claude"
GEMINI = "gemini"
MINIMAX = "minimax"
GLM = "glm"
ZAI = "z-ai"
class OpenrouterConfig(OpenAIGPTConfig):
@ -39,6 +42,7 @@ class OpenrouterConfig(OpenAIGPTConfig):
model=model, custom_llm_provider="openrouter"
) or litellm.supports_reasoning(model=model):
supported_params.append("reasoning_effort")
supported_params.append("thinking")
except Exception:
pass
return list(dict.fromkeys(supported_params))

View file

@ -1,6 +1,7 @@
from typing import Optional, Tuple
from typing import List, Optional, Tuple
from litellm.secret_managers.main import get_secret_str
from litellm.types.llms.openai import AllMessageValues, ChatCompletionToolParam
from ...openai.chat.gpt_transformation import OpenAIGPTConfig
@ -19,6 +20,19 @@ class ZAIChatConfig(OpenAIGPTConfig):
dynamic_api_key = api_key or get_secret_str("ZAI_API_KEY")
return api_base, dynamic_api_key
def remove_cache_control_flag_from_messages_and_tools(
self,
model: str,
messages: List[AllMessageValues],
tools: Optional[List[ChatCompletionToolParam]] = None,
) -> Tuple[List[AllMessageValues], Optional[List[ChatCompletionToolParam]]]:
"""
Override to preserve cache_control for GLM/ZAI.
GLM supports cache_control - don't strip it.
"""
# GLM/ZAI supports cache_control, so return messages and tools unchanged
return messages, tools
def get_supported_openai_params(self, model: str) -> list:
base_params = [
"max_tokens",

View file

@ -20628,6 +20628,7 @@
"supports_function_calling": true,
"supports_tool_choice": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_system_messages": true,
"max_input_tokens": 1000000,
"max_output_tokens": 8192
@ -20642,6 +20643,7 @@
"supports_function_calling": true,
"supports_tool_choice": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_system_messages": true,
"max_input_tokens": 1000000,
"max_output_tokens": 8192
@ -20656,6 +20658,7 @@
"supports_function_calling": true,
"supports_tool_choice": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_system_messages": true,
"max_input_tokens": 200000,
"max_output_tokens": 8192
@ -23540,7 +23543,7 @@
"mode": "chat",
"output_cost_per_token": 1.02e-06,
"supports_function_calling": true,
"supports_prompt_caching": false,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_tool_choice": true
},
@ -24232,6 +24235,7 @@
"output_cost_per_token": 1.75e-06,
"source": "https://openrouter.ai/z-ai/glm-4.6",
"supports_function_calling": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_tool_choice": true
},
@ -24245,6 +24249,7 @@
"output_cost_per_token": 1.9e-06,
"source": "https://openrouter.ai/z-ai/glm-4.6:exacto",
"supports_function_calling": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_tool_choice": true
},
@ -30872,11 +30877,14 @@
"max_output_tokens": 128000,
"mode": "chat",
"supports_function_calling": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_tool_choice": true,
"source": "https://docs.z.ai/guides/overview/pricing"
},
"zai/glm-4.6": {
"cache_creation_input_token_cost": 0,
"cache_read_input_token_cost": 1.1e-07,
"input_cost_per_token": 6e-07,
"output_cost_per_token": 2.2e-06,
"litellm_provider": "zai",
@ -30884,6 +30892,8 @@
"max_output_tokens": 128000,
"mode": "chat",
"supports_function_calling": true,
"supports_prompt_caching": true,
"supports_reasoning": true,
"supports_tool_choice": true,
"source": "https://docs.z.ai/guides/overview/pricing"
},

View file

@ -0,0 +1,281 @@
"""
Test cache_control and reasoning parameter support for MiniMax, GLM/ZAI, and OpenRouter.
This test file verifies the fixes for Issue #19923:
- cache_control is preserved (not stripped) for MiniMax, GLM, and OpenRouter variants
- thinking parameter is supported for reasoning-capable models
- Model metadata correctly reflects capabilities
"""
import os
import sys
import pytest
sys.path.insert(
0, os.path.abspath("../../..")
) # Adds the parent directory to the system path
from litellm.llms.minimax.chat.transformation import MinimaxChatConfig
from litellm.llms.openrouter.chat.transformation import OpenrouterConfig
from litellm.llms.zai.chat.transformation import ZAIChatConfig
def test_minimax_preserves_cache_control_in_messages():
"""MiniMax should NOT strip cache_control from messages."""
config = MinimaxChatConfig()
messages = [
{
"role": "system",
"content": "You are a helpful assistant.",
"cache_control": {"type": "ephemeral"},
},
{
"role": "user",
"content": "Hello, world!",
},
]
transformed_messages, _ = config.remove_cache_control_flag_from_messages_and_tools(
model="minimax/MiniMax-M2.1", messages=messages
)
# cache_control should be preserved
assert transformed_messages[0].get("cache_control") == {"type": "ephemeral"}
def test_minimax_preserves_cache_control_in_tools():
"""MiniMax should NOT strip cache_control from tools."""
config = MinimaxChatConfig()
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get weather information",
"parameters": {"type": "object", "properties": {}},
},
"cache_control": {"type": "ephemeral"},
}
]
_, transformed_tools = config.remove_cache_control_flag_from_messages_and_tools(
model="minimax/MiniMax-M2.1", messages=[], tools=tools
)
# cache_control should be preserved
assert transformed_tools[0].get("cache_control") == {"type": "ephemeral"}
def test_minimax_supports_thinking_param():
"""MiniMax reasoning models should support thinking parameter."""
config = MinimaxChatConfig()
supported_params = config.get_supported_openai_params(
model="minimax/MiniMax-M2.1"
)
# thinking should be in supported params for reasoning models
assert "thinking" in supported_params
# reasoning_split should also be supported
assert "reasoning_split" in supported_params
def test_zai_preserves_cache_control_in_messages():
"""ZAI should NOT strip cache_control from messages."""
config = ZAIChatConfig()
messages = [
{
"role": "system",
"content": "You are a helpful assistant.",
"cache_control": {"type": "ephemeral"},
},
{
"role": "user",
"content": "Hello, world!",
},
]
transformed_messages, _ = config.remove_cache_control_flag_from_messages_and_tools(
model="zai/glm-4.7", messages=messages
)
# cache_control should be preserved
assert transformed_messages[0].get("cache_control") == {"type": "ephemeral"}
def test_zai_preserves_cache_control_in_tools():
"""ZAI should NOT strip cache_control from tools."""
config = ZAIChatConfig()
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get weather information",
"parameters": {"type": "object", "properties": {}},
},
"cache_control": {"type": "ephemeral"},
}
]
_, transformed_tools = config.remove_cache_control_flag_from_messages_and_tools(
model="zai/glm-4.7", messages=[], tools=tools
)
# cache_control should be preserved
assert transformed_tools[0].get("cache_control") == {"type": "ephemeral"}
def test_zai_supports_thinking_param_for_reasoning_models():
"""ZAI reasoning models (glm-4.7, glm-4.6) should support thinking parameter."""
config = ZAIChatConfig()
# glm-4.7 supports reasoning
supported_params_47 = config.get_supported_openai_params(model="zai/glm-4.7")
assert "thinking" in supported_params_47
# glm-4.6 supports reasoning
supported_params_46 = config.get_supported_openai_params(model="zai/glm-4.6")
assert "thinking" in supported_params_46
def test_openrouter_minimax_supports_cache_control():
"""OpenRouter should preserve cache_control for MiniMax models."""
config = OpenrouterConfig()
messages = [
{
"role": "user",
"content": "Hello, world!",
"cache_control": {"type": "ephemeral"},
}
]
# Test that cache_control is not removed
transformed_messages, _ = config.remove_cache_control_flag_from_messages_and_tools(
model="openrouter/minimax/minimax-m2", messages=messages
)
# The method should preserve cache_control for minimax models
assert transformed_messages[0].get("cache_control") == {"type": "ephemeral"}
def test_openrouter_glm_supports_cache_control():
"""OpenRouter should preserve cache_control for GLM models."""
config = OpenrouterConfig()
messages = [
{
"role": "user",
"content": "Hello, world!",
"cache_control": {"type": "ephemeral"},
}
]
# Test that cache_control is not removed for GLM models
transformed_messages, _ = config.remove_cache_control_flag_from_messages_and_tools(
model="openrouter/z-ai/glm-4.6", messages=messages
)
# The method should preserve cache_control for GLM models
assert transformed_messages[0].get("cache_control") == {"type": "ephemeral"}
def test_openrouter_deepseek_strips_cache_control():
"""OpenRouter should still strip cache_control for non-supported models."""
config = OpenrouterConfig()
messages = [
{
"role": "user",
"content": "Hello, world!",
"cache_control": {"type": "ephemeral"},
}
]
# DeepSeek doesn't support cache_control, so it should be stripped
transformed_messages, _ = config.remove_cache_control_flag_from_messages_and_tools(
model="openrouter/deepseek/deepseek-chat", messages=messages
)
# cache_control should be removed for non-supported models
assert transformed_messages[0].get("cache_control") is None
def test_openrouter_minimax_transform_moves_cache_control_to_content():
"""OpenRouter should move cache_control to content blocks for MiniMax."""
config = OpenrouterConfig()
messages = [
{
"role": "user",
"content": "Analyze this data",
"cache_control": {"type": "ephemeral"},
}
]
transformed_request = config.transform_request(
model="openrouter/minimax/minimax-m2",
messages=messages,
optional_params={},
litellm_params={},
headers={},
)
# cache_control should be moved to content blocks
assert "messages" in transformed_request
user_message = transformed_request["messages"][0]
assert isinstance(user_message["content"], list)
assert user_message["content"][0]["cache_control"] == {"type": "ephemeral"}
# Message-level cache_control should be removed
assert "cache_control" not in user_message
def test_openrouter_glm_transform_moves_cache_control_to_content():
"""OpenRouter should move cache_control to content blocks for GLM."""
config = OpenrouterConfig()
messages = [
{
"role": "user",
"content": "Analyze this data",
"cache_control": {"type": "ephemeral"},
}
]
transformed_request = config.transform_request(
model="openrouter/z-ai/glm-4.6",
messages=messages,
optional_params={},
litellm_params={},
headers={},
)
# cache_control should be moved to content blocks
assert "messages" in transformed_request
user_message = transformed_request["messages"][0]
assert isinstance(user_message["content"], list)
assert user_message["content"][0]["cache_control"] == {"type": "ephemeral"}
def test_openrouter_supports_thinking_param_for_reasoning_models():
"""OpenRouter should support thinking parameter for reasoning-capable models."""
config = OpenrouterConfig()
# Test MiniMax (supports reasoning)
supported_params_minimax = config.get_supported_openai_params(
model="openrouter/minimax/minimax-m2"
)
assert "thinking" in supported_params_minimax
assert "reasoning_effort" in supported_params_minimax
# Test GLM (supports reasoning)
supported_params_glm = config.get_supported_openai_params(
model="openrouter/z-ai/glm-4.6"
)
assert "thinking" in supported_params_glm
assert "reasoning_effort" in supported_params_glm