mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-05 08:07:05 +00:00
Add Prompt caching and reasoning support for MiniMax, GLM, Xiaomi
This commit is contained in:
parent
ab655ef296
commit
f6ead49afe
5 changed files with 337 additions and 5 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
},
|
||||
|
|
|
|||
281
tests/test_litellm/llms/test_cache_control_and_reasoning.py
Normal file
281
tests/test_litellm/llms/test_cache_control_and_reasoning.py
Normal 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
|
||||
Loading…
Add table
Reference in a new issue