mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-09 03:18:44 +00:00
Merge pull request #18380 from BerriAI/litellm_minmax_chat_completion_support
(Feat) Minimax chat completion support
This commit is contained in:
commit
3c25320cb1
11 changed files with 751 additions and 183 deletions
385
docs/my-website/docs/providers/minimax.md
Normal file
385
docs/my-website/docs/providers/minimax.md
Normal file
|
|
@ -0,0 +1,385 @@
|
|||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
# MiniMax
|
||||
|
||||
# MiniMax - v1/messages
|
||||
|
||||
## Overview
|
||||
|
||||
Litellm provides anthropic specs compatible support for minmax
|
||||
|
||||
## Supported Models
|
||||
|
||||
MiniMax offers three models through their Anthropic-compatible API:
|
||||
|
||||
| Model | Description | Input Cost | Output Cost | Prompt Caching Read | Prompt Caching Write |
|
||||
|-------|-------------|------------|-------------|---------------------|----------------------|
|
||||
| **MiniMax-M2.1** | Powerful Multi-Language Programming with Enhanced Programming Experience (~60 tps) | $0.3/M tokens | $1.2/M tokens | $0.03/M tokens | $0.375/M tokens |
|
||||
| **MiniMax-M2.1-lightning** | Faster and More Agile (~100 tps) | $0.3/M tokens | $2.4/M tokens | $0.03/M tokens | $0.375/M tokens |
|
||||
| **MiniMax-M2** | Agentic capabilities, Advanced reasoning | $0.3/M tokens | $1.2/M tokens | $0.03/M tokens | $0.375/M tokens |
|
||||
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Basic Chat Completion
|
||||
|
||||
```python
|
||||
import litellm
|
||||
|
||||
response = litellm.anthropic.messages.acreate(
|
||||
model="minimax/MiniMax-M2.1",
|
||||
messages=[{"role": "user", "content": "Hello, how are you?"}],
|
||||
api_key="your-minimax-api-key",
|
||||
api_base="https://api.minimax.io/anthropic/v1/messages",
|
||||
max_tokens=1000
|
||||
)
|
||||
|
||||
print(response.choices[0].message.content)
|
||||
```
|
||||
|
||||
### Using Environment Variables
|
||||
|
||||
```bash
|
||||
export MINIMAX_API_KEY="your-minimax-api-key"
|
||||
export MINIMAX_API_BASE="https://api.minimax.io/anthropic/v1/messages"
|
||||
```
|
||||
|
||||
```python
|
||||
import litellm
|
||||
|
||||
response = litellm.anthropic.messages.acreate(
|
||||
model="minimax/MiniMax-M2.1",
|
||||
messages=[{"role": "user", "content": "Hello!"}],
|
||||
max_tokens=1000
|
||||
)
|
||||
```
|
||||
|
||||
### With Thinking (M2.1 Feature)
|
||||
|
||||
```python
|
||||
response = litellm.anthropic.messages.acreate(
|
||||
model="minimax/MiniMax-M2.1",
|
||||
messages=[{"role": "user", "content": "Solve: 2+2=?"}],
|
||||
thinking={"type": "enabled", "budget_tokens": 1000},
|
||||
api_key="your-minimax-api-key"
|
||||
)
|
||||
|
||||
# Access thinking content
|
||||
for block in response.choices[0].message.content:
|
||||
if hasattr(block, 'type') and block.type == 'thinking':
|
||||
print(f"Thinking: {block.thinking}")
|
||||
```
|
||||
|
||||
### With Tool Calling
|
||||
|
||||
```python
|
||||
tools = [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_weather",
|
||||
"description": "Get current weather",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"location": {"type": "string"}
|
||||
},
|
||||
"required": ["location"]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
response = litellm.anthropic.messages.acreate(
|
||||
model="minimax/MiniMax-M2.1",
|
||||
messages=[{"role": "user", "content": "What's the weather in SF?"}],
|
||||
tools=tools,
|
||||
api_key="your-minimax-api-key",
|
||||
max_tokens=1000
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Usage with LiteLLM Proxy
|
||||
|
||||
You can use MiniMax models with the Anthropic SDK by routing through LiteLLM Proxy:
|
||||
|
||||
| Step | Description |
|
||||
|------|-------------|
|
||||
| **1. Start LiteLLM Proxy** | Configure proxy with MiniMax models in `config.yaml` |
|
||||
| **2. Set Environment Variables** | Point Anthropic SDK to proxy endpoint |
|
||||
| **3. Use Anthropic SDK** | Call MiniMax models using native Anthropic SDK |
|
||||
|
||||
### Step 1: Configure LiteLLM Proxy
|
||||
|
||||
Create a `config.yaml`:
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: minimax/MiniMax-M2.1
|
||||
litellm_params:
|
||||
model: minimax/MiniMax-M2.1
|
||||
api_key: os.environ/MINIMAX_API_KEY
|
||||
api_base: https://api.minimax.io/anthropic/v1/messages
|
||||
```
|
||||
|
||||
Start the proxy:
|
||||
|
||||
```bash
|
||||
litellm --config config.yaml
|
||||
```
|
||||
|
||||
### Step 2: Use with Anthropic SDK
|
||||
|
||||
```python
|
||||
import os
|
||||
os.environ["ANTHROPIC_BASE_URL"] = "http://localhost:4000"
|
||||
os.environ["ANTHROPIC_API_KEY"] = "sk-1234" # Your LiteLLM proxy key
|
||||
|
||||
import anthropic
|
||||
|
||||
client = anthropic.Anthropic()
|
||||
|
||||
message = client.messages.create(
|
||||
model="minimax/MiniMax-M2.1",
|
||||
max_tokens=1000,
|
||||
system="You are a helpful assistant.",
|
||||
messages=[
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Hi, how are you?"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
for block in message.content:
|
||||
if block.type == "thinking":
|
||||
print(f"Thinking:\n{block.thinking}\n")
|
||||
elif block.type == "text":
|
||||
print(f"Text:\n{block.text}\n")
|
||||
```
|
||||
|
||||
# MiniMax - v1/chat/completions
|
||||
|
||||
## Usage with LiteLLM SDK
|
||||
|
||||
You can use MiniMax's OpenAI-compatible API directly with LiteLLM:
|
||||
|
||||
### Basic Chat Completion
|
||||
|
||||
```python
|
||||
import litellm
|
||||
|
||||
response = litellm.completion(
|
||||
model="minimax/MiniMax-M2.1",
|
||||
messages=[
|
||||
{"role": "system", "content": "You are a helpful assistant."},
|
||||
{"role": "user", "content": "Hello, how are you?"}
|
||||
],
|
||||
api_key="your-minimax-api-key",
|
||||
api_base="https://api.minimax.io/v1"
|
||||
)
|
||||
|
||||
print(response.choices[0].message.content)
|
||||
```
|
||||
|
||||
### Using Environment Variables
|
||||
|
||||
```bash
|
||||
export MINIMAX_API_KEY="your-minimax-api-key"
|
||||
export MINIMAX_API_BASE="https://api.minimax.io/v1"
|
||||
```
|
||||
|
||||
```python
|
||||
import litellm
|
||||
|
||||
response = litellm.completion(
|
||||
model="minimax/MiniMax-M2.1",
|
||||
messages=[{"role": "user", "content": "Hello!"}]
|
||||
)
|
||||
```
|
||||
|
||||
### With Reasoning Split
|
||||
|
||||
```python
|
||||
response = litellm.completion(
|
||||
model="minimax/MiniMax-M2.1",
|
||||
messages=[
|
||||
{"role": "system", "content": "You are a helpful assistant."},
|
||||
{"role": "user", "content": "Solve: 2+2=?"}
|
||||
],
|
||||
extra_body={"reasoning_split": True},
|
||||
api_key="your-minimax-api-key",
|
||||
api_base="https://api.minimax.io/v1"
|
||||
)
|
||||
|
||||
# Access reasoning details if available
|
||||
if hasattr(response.choices[0].message, 'reasoning_details'):
|
||||
print(f"Thinking: {response.choices[0].message.reasoning_details}")
|
||||
print(f"Response: {response.choices[0].message.content}")
|
||||
```
|
||||
|
||||
### With Tool Calling
|
||||
|
||||
```python
|
||||
tools = [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_weather",
|
||||
"description": "Get current weather",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"location": {"type": "string"}
|
||||
},
|
||||
"required": ["location"]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
response = litellm.completion(
|
||||
model="minimax/MiniMax-M2.1",
|
||||
messages=[{"role": "user", "content": "What's the weather in SF?"}],
|
||||
tools=tools,
|
||||
api_key="your-minimax-api-key",
|
||||
api_base="https://api.minimax.io/v1"
|
||||
)
|
||||
```
|
||||
|
||||
### Streaming
|
||||
|
||||
```python
|
||||
response = litellm.completion(
|
||||
model="minimax/MiniMax-M2.1",
|
||||
messages=[{"role": "user", "content": "Tell me a story"}],
|
||||
stream=True,
|
||||
api_key="your-minimax-api-key",
|
||||
api_base="https://api.minimax.io/v1"
|
||||
)
|
||||
|
||||
for chunk in response:
|
||||
if chunk.choices[0].delta.content:
|
||||
print(chunk.choices[0].delta.content, end="")
|
||||
```
|
||||
|
||||
|
||||
## Usage with OpenAI SDK via LiteLLM Proxy
|
||||
|
||||
You can also use MiniMax models with the OpenAI SDK by routing through LiteLLM Proxy:
|
||||
|
||||
| Step | Description |
|
||||
|------|-------------|
|
||||
| **1. Start LiteLLM Proxy** | Configure proxy with MiniMax models in `config.yaml` |
|
||||
| **2. Set Environment Variables** | Point OpenAI SDK to proxy endpoint |
|
||||
| **3. Use OpenAI SDK** | Call MiniMax models using native OpenAI SDK |
|
||||
|
||||
### Step 1: Configure LiteLLM Proxy
|
||||
|
||||
Create a `config.yaml`:
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: minimax/MiniMax-M2.1
|
||||
litellm_params:
|
||||
model: minimax/MiniMax-M2.1
|
||||
api_key: os.environ/MINIMAX_API_KEY
|
||||
api_base: https://api.minimax.io/v1
|
||||
```
|
||||
|
||||
Start the proxy:
|
||||
|
||||
```bash
|
||||
litellm --config config.yaml
|
||||
```
|
||||
|
||||
### Step 2: Use with OpenAI SDK
|
||||
|
||||
```python
|
||||
import os
|
||||
os.environ["OPENAI_BASE_URL"] = "http://localhost:4000"
|
||||
os.environ["OPENAI_API_KEY"] = "sk-1234" # Your LiteLLM proxy key
|
||||
|
||||
from openai import OpenAI
|
||||
|
||||
client = OpenAI()
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model="minimax/MiniMax-M2.1",
|
||||
messages=[
|
||||
{"role": "system", "content": "You are a helpful assistant."},
|
||||
{"role": "user", "content": "Hi, how are you?"},
|
||||
],
|
||||
# Set reasoning_split=True to separate thinking content
|
||||
extra_body={"reasoning_split": True},
|
||||
)
|
||||
|
||||
# Access thinking and response
|
||||
if hasattr(response.choices[0].message, 'reasoning_details'):
|
||||
print(f"Thinking:\n{response.choices[0].message.reasoning_details[0]['text']}\n")
|
||||
print(f"Text:\n{response.choices[0].message.content}\n")
|
||||
```
|
||||
|
||||
### Streaming with OpenAI SDK
|
||||
|
||||
```python
|
||||
from openai import OpenAI
|
||||
|
||||
client = OpenAI()
|
||||
|
||||
stream = client.chat.completions.create(
|
||||
model="minimax/MiniMax-M2.1",
|
||||
messages=[
|
||||
{"role": "system", "content": "You are a helpful assistant."},
|
||||
{"role": "user", "content": "Tell me a story"},
|
||||
],
|
||||
extra_body={"reasoning_split": True},
|
||||
stream=True,
|
||||
)
|
||||
|
||||
reasoning_buffer = ""
|
||||
text_buffer = ""
|
||||
|
||||
for chunk in stream:
|
||||
if hasattr(chunk.choices[0].delta, "reasoning_details") and chunk.choices[0].delta.reasoning_details:
|
||||
for detail in chunk.choices[0].delta.reasoning_details:
|
||||
if "text" in detail:
|
||||
reasoning_text = detail["text"]
|
||||
new_reasoning = reasoning_text[len(reasoning_buffer):]
|
||||
if new_reasoning:
|
||||
print(new_reasoning, end="", flush=True)
|
||||
reasoning_buffer = reasoning_text
|
||||
|
||||
if chunk.choices[0].delta.content:
|
||||
content_text = chunk.choices[0].delta.content
|
||||
new_text = content_text[len(text_buffer):] if text_buffer else content_text
|
||||
if new_text:
|
||||
print(new_text, end="", flush=True)
|
||||
text_buffer = content_text
|
||||
```
|
||||
|
||||
## Cost Calculation
|
||||
|
||||
Cost calculation works automatically using the pricing information in `model_prices_and_context_window.json`.
|
||||
|
||||
Example:
|
||||
```python
|
||||
response = litellm.completion(
|
||||
model="minimax/MiniMax-M2.1",
|
||||
messages=[{"role": "user", "content": "Hello!"}],
|
||||
api_key="your-minimax-api-key"
|
||||
)
|
||||
|
||||
# Access cost information
|
||||
print(f"Cost: ${response._hidden_params.get('response_cost', 0)}")
|
||||
```
|
||||
|
||||
|
||||
|
|
@ -1,182 +0,0 @@
|
|||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
# MiniMax - v1/messages
|
||||
|
||||
## Overview
|
||||
|
||||
Litellm provides anthropic specs compatible support for minmax
|
||||
|
||||
## Supported Models
|
||||
|
||||
MiniMax offers three models through their Anthropic-compatible API:
|
||||
|
||||
| Model | Description | Input Cost | Output Cost | Prompt Caching Read | Prompt Caching Write |
|
||||
|-------|-------------|------------|-------------|---------------------|----------------------|
|
||||
| **MiniMax-M2.1** | Powerful Multi-Language Programming with Enhanced Programming Experience (~60 tps) | $0.3/M tokens | $1.2/M tokens | $0.03/M tokens | $0.375/M tokens |
|
||||
| **MiniMax-M2.1-lightning** | Faster and More Agile (~100 tps) | $0.3/M tokens | $2.4/M tokens | $0.03/M tokens | $0.375/M tokens |
|
||||
| **MiniMax-M2** | Agentic capabilities, Advanced reasoning | $0.3/M tokens | $1.2/M tokens | $0.03/M tokens | $0.375/M tokens |
|
||||
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Basic Chat Completion
|
||||
|
||||
```python
|
||||
import litellm
|
||||
|
||||
response = litellm.anthropic.messages.acreate(
|
||||
model="minimax/MiniMax-M2.1",
|
||||
messages=[{"role": "user", "content": "Hello, how are you?"}],
|
||||
api_key="your-minimax-api-key",
|
||||
api_base="https://api.minimax.io/anthropic/v1/messages",
|
||||
max_tokens=1000
|
||||
)
|
||||
|
||||
print(response.choices[0].message.content)
|
||||
```
|
||||
|
||||
### Using Environment Variables
|
||||
|
||||
```bash
|
||||
export MINIMAX_API_KEY="your-minimax-api-key"
|
||||
export MINIMAX_API_BASE="https://api.minimax.io/anthropic/v1/messages"
|
||||
```
|
||||
|
||||
```python
|
||||
import litellm
|
||||
|
||||
response = litellm.anthropic.messages.acreate(
|
||||
model="minimax/MiniMax-M2.1",
|
||||
messages=[{"role": "user", "content": "Hello!"}],
|
||||
max_tokens=1000
|
||||
)
|
||||
```
|
||||
|
||||
### With Thinking (M2.1 Feature)
|
||||
|
||||
```python
|
||||
response = litellm.anthropic.messages.acreate(
|
||||
model="minimax/MiniMax-M2.1",
|
||||
messages=[{"role": "user", "content": "Solve: 2+2=?"}],
|
||||
thinking={"type": "enabled", "budget_tokens": 1000},
|
||||
api_key="your-minimax-api-key"
|
||||
)
|
||||
|
||||
# Access thinking content
|
||||
for block in response.choices[0].message.content:
|
||||
if hasattr(block, 'type') and block.type == 'thinking':
|
||||
print(f"Thinking: {block.thinking}")
|
||||
```
|
||||
|
||||
### With Tool Calling
|
||||
|
||||
```python
|
||||
tools = [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_weather",
|
||||
"description": "Get current weather",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"location": {"type": "string"}
|
||||
},
|
||||
"required": ["location"]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
response = litellm.anthropic.messages.acreate(
|
||||
model="minimax/MiniMax-M2.1",
|
||||
messages=[{"role": "user", "content": "What's the weather in SF?"}],
|
||||
tools=tools,
|
||||
api_key="your-minimax-api-key",
|
||||
max_tokens=1000
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Usage with LiteLLM Proxy
|
||||
|
||||
You can use MiniMax models with the Anthropic SDK by routing through LiteLLM Proxy:
|
||||
|
||||
| Step | Description |
|
||||
|------|-------------|
|
||||
| **1. Start LiteLLM Proxy** | Configure proxy with MiniMax models in `config.yaml` |
|
||||
| **2. Set Environment Variables** | Point Anthropic SDK to proxy endpoint |
|
||||
| **3. Use Anthropic SDK** | Call MiniMax models using native Anthropic SDK |
|
||||
|
||||
### Step 1: Configure LiteLLM Proxy
|
||||
|
||||
Create a `config.yaml`:
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: minimax/MiniMax-M2.1
|
||||
litellm_params:
|
||||
model: minimax/MiniMax-M2.1
|
||||
api_key: os.environ/MINIMAX_API_KEY
|
||||
api_base: https://api.minimax.io/anthropic/v1/messages
|
||||
```
|
||||
|
||||
Start the proxy:
|
||||
|
||||
```bash
|
||||
litellm --config config.yaml
|
||||
```
|
||||
|
||||
### Step 2: Use with Anthropic SDK
|
||||
|
||||
```python
|
||||
import os
|
||||
os.environ["ANTHROPIC_BASE_URL"] = "http://localhost:4000"
|
||||
os.environ["ANTHROPIC_API_KEY"] = "sk-1234" # Your LiteLLM proxy key
|
||||
|
||||
import anthropic
|
||||
|
||||
client = anthropic.Anthropic()
|
||||
|
||||
message = client.messages.create(
|
||||
model="minimax/MiniMax-M2.1",
|
||||
max_tokens=1000,
|
||||
system="You are a helpful assistant.",
|
||||
messages=[
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Hi, how are you?"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
for block in message.content:
|
||||
if block.type == "thinking":
|
||||
print(f"Thinking:\n{block.thinking}\n")
|
||||
elif block.type == "text":
|
||||
print(f"Text:\n{block.text}\n")
|
||||
```
|
||||
## Cost Calculation
|
||||
|
||||
Cost calculation works automatically using the pricing information in `model_prices_and_context_window.json`.
|
||||
|
||||
Example:
|
||||
```python
|
||||
response = litellm.completion(
|
||||
model="minimax/MiniMax-M2.1",
|
||||
messages=[{"role": "user", "content": "Hello!"}],
|
||||
api_key="your-minimax-api-key"
|
||||
)
|
||||
|
||||
# Access cost information
|
||||
print(f"Cost: ${response._hidden_params.get('response_cost', 0)}")
|
||||
```
|
||||
|
||||
|
||||
|
|
@ -1482,6 +1482,7 @@ if TYPE_CHECKING:
|
|||
from .llms.bytez.chat.transformation import BytezChatConfig as BytezChatConfig
|
||||
from .llms.compactifai.chat.transformation import CompactifAIChatConfig as CompactifAIChatConfig
|
||||
from .llms.empower.chat.transformation import EmpowerChatConfig as EmpowerChatConfig
|
||||
from .llms.minimax.chat.transformation import MinimaxChatConfig as MinimaxChatConfig
|
||||
from .llms.aiohttp_openai.chat.transformation import AiohttpOpenAIChatConfig as AiohttpOpenAIChatConfig
|
||||
from .llms.huggingface.chat.transformation import HuggingFaceChatConfig as HuggingFaceChatConfig
|
||||
from .llms.huggingface.embedding.transformation import HuggingFaceEmbeddingConfig as HuggingFaceEmbeddingConfig
|
||||
|
|
|
|||
|
|
@ -165,6 +165,7 @@ LLM_CONFIG_NAMES = (
|
|||
"BytezChatConfig",
|
||||
"CompactifAIChatConfig",
|
||||
"EmpowerChatConfig",
|
||||
"MinimaxChatConfig",
|
||||
"AiohttpOpenAIChatConfig",
|
||||
"HuggingFaceChatConfig",
|
||||
"HuggingFaceEmbeddingConfig",
|
||||
|
|
@ -750,6 +751,14 @@ def _lazy_import_llm_configs(name: str) -> Any: # noqa: PLR0915
|
|||
_globals["EmpowerChatConfig"] = _EmpowerChatConfig
|
||||
return _EmpowerChatConfig
|
||||
|
||||
if name == "MinimaxChatConfig":
|
||||
from .llms.minimax.chat.transformation import (
|
||||
MinimaxChatConfig as _MinimaxChatConfig,
|
||||
)
|
||||
|
||||
_globals["MinimaxChatConfig"] = _MinimaxChatConfig
|
||||
return _MinimaxChatConfig
|
||||
|
||||
if name == "AiohttpOpenAIChatConfig":
|
||||
from .llms.aiohttp_openai.chat.transformation import (
|
||||
AiohttpOpenAIChatConfig as _AiohttpOpenAIChatConfig,
|
||||
|
|
|
|||
|
|
@ -270,6 +270,9 @@ def get_llm_provider( # noqa: PLR0915
|
|||
elif endpoint == "api.minimax.io/anthropic" or endpoint == "api.minimaxi.com/anthropic":
|
||||
custom_llm_provider = "minimax"
|
||||
dynamic_api_key = get_secret_str("MINIMAX_API_KEY")
|
||||
elif endpoint == "api.minimax.io/v1" or endpoint == "api.minimaxi.com/v1":
|
||||
custom_llm_provider = "minimax"
|
||||
dynamic_api_key = get_secret_str("MINIMAX_API_KEY")
|
||||
elif endpoint == "platform.publicai.co/v1":
|
||||
custom_llm_provider = "publicai"
|
||||
dynamic_api_key = get_secret_str("PUBLICAI_API_KEY")
|
||||
|
|
|
|||
4
litellm/llms/minimax/chat/__init__.py
Normal file
4
litellm/llms/minimax/chat/__init__.py
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
"""
|
||||
MiniMax OpenAI-compatible chat API
|
||||
"""
|
||||
|
||||
83
litellm/llms/minimax/chat/transformation.py
Normal file
83
litellm/llms/minimax/chat/transformation.py
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
"""
|
||||
MiniMax OpenAI transformation config - extends OpenAI chat config for MiniMax's OpenAI-compatible API
|
||||
"""
|
||||
from typing import Optional
|
||||
|
||||
import litellm
|
||||
from litellm.llms.openai.chat.gpt_transformation import OpenAIGPTConfig
|
||||
from litellm.secret_managers.main import get_secret_str
|
||||
|
||||
|
||||
class MinimaxChatConfig(OpenAIGPTConfig):
|
||||
"""
|
||||
MiniMax OpenAI configuration that extends OpenAIGPTConfig.
|
||||
MiniMax provides an OpenAI-compatible API at:
|
||||
- International: https://api.minimax.io/v1
|
||||
- China: https://api.minimaxi.com/v1
|
||||
|
||||
Supported models:
|
||||
- MiniMax-M2.1
|
||||
- MiniMax-M2.1-lightning
|
||||
- MiniMax-M2
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def get_api_key(api_key: Optional[str] = None) -> Optional[str]:
|
||||
"""
|
||||
Get MiniMax API key from environment or parameters.
|
||||
"""
|
||||
return (
|
||||
api_key
|
||||
or get_secret_str("MINIMAX_API_KEY")
|
||||
or litellm.api_key
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def get_api_base(
|
||||
api_base: Optional[str] = None,
|
||||
) -> str:
|
||||
"""
|
||||
Get MiniMax API base URL.
|
||||
Defaults to international endpoint: https://api.minimax.io/v1
|
||||
For China, set to: https://api.minimaxi.com/v1
|
||||
"""
|
||||
return (
|
||||
api_base
|
||||
or get_secret_str("MINIMAX_API_BASE")
|
||||
or "https://api.minimax.io/v1"
|
||||
)
|
||||
|
||||
def get_complete_url(
|
||||
self,
|
||||
api_base: Optional[str],
|
||||
api_key: Optional[str],
|
||||
model: str,
|
||||
optional_params: dict,
|
||||
litellm_params: dict,
|
||||
stream: Optional[bool] = None,
|
||||
) -> str:
|
||||
"""
|
||||
Get the complete URL for MiniMax OpenAI API.
|
||||
Override to ensure we use MiniMax's endpoint.
|
||||
"""
|
||||
# Get the base URL (either provided or default MiniMax endpoint)
|
||||
base_url = self.get_api_base(api_base=api_base)
|
||||
|
||||
# Ensure it ends with /chat/completions
|
||||
if base_url.endswith("/chat/completions"):
|
||||
return base_url
|
||||
elif base_url.endswith("/v1"):
|
||||
return f"{base_url}/chat/completions"
|
||||
elif base_url.endswith("/"):
|
||||
return f"{base_url}v1/chat/completions"
|
||||
else:
|
||||
return f"{base_url}/v1/chat/completions"
|
||||
|
||||
def get_supported_openai_params(self, model: str) -> list:
|
||||
"""
|
||||
Get supported OpenAI parameters for MiniMax.
|
||||
Adds reasoning_split to the list of supported params.
|
||||
"""
|
||||
base_params = super().get_supported_openai_params(model=model)
|
||||
return base_params + ["reasoning_split"]
|
||||
|
||||
|
|
@ -68,7 +68,6 @@ from litellm.constants import (
|
|||
DEFAULT_MOCK_RESPONSE_PROMPT_TOKEN_COUNT,
|
||||
)
|
||||
from litellm.exceptions import LiteLLMUnknownProvider
|
||||
from litellm.llms.openai_like.json_loader import JSONProviderRegistry
|
||||
from litellm.integrations.custom_logger import CustomLogger
|
||||
from litellm.litellm_core_utils.asyncify import run_async_function
|
||||
from litellm.litellm_core_utils.audio_utils.utils import (
|
||||
|
|
@ -98,6 +97,7 @@ from litellm.llms.base_llm.base_model_iterator import (
|
|||
from litellm.llms.bedrock.common_utils import BedrockModelInfo
|
||||
from litellm.llms.cohere.common_utils import CohereModelInfo
|
||||
from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler, HTTPHandler
|
||||
from litellm.llms.openai_like.json_loader import JSONProviderRegistry
|
||||
from litellm.llms.vertex_ai.common_utils import (
|
||||
VertexAIModelRoute,
|
||||
get_vertex_ai_model_route,
|
||||
|
|
@ -2247,6 +2247,42 @@ def completion( # type: ignore # noqa: PLR0915
|
|||
logging.post_call(
|
||||
input=messages, api_key=api_key, original_response=response
|
||||
)
|
||||
elif custom_llm_provider == "minimax":
|
||||
api_key = (
|
||||
api_key
|
||||
or get_secret_str("MINIMAX_API_KEY")
|
||||
or litellm.api_key
|
||||
)
|
||||
|
||||
api_base = (
|
||||
api_base
|
||||
or litellm.api_base
|
||||
or get_secret_str("MINIMAX_API_BASE")
|
||||
or "https://api.minimax.io/v1"
|
||||
)
|
||||
|
||||
response = base_llm_http_handler.completion(
|
||||
model=model,
|
||||
messages=messages,
|
||||
api_base=api_base,
|
||||
custom_llm_provider=custom_llm_provider,
|
||||
model_response=model_response,
|
||||
encoding=_get_encoding(),
|
||||
logging_obj=logging,
|
||||
optional_params=optional_params,
|
||||
timeout=timeout,
|
||||
litellm_params=litellm_params,
|
||||
shared_session=shared_session,
|
||||
acompletion=acompletion,
|
||||
stream=stream,
|
||||
api_key=api_key,
|
||||
headers=headers,
|
||||
client=client,
|
||||
provider_config=provider_config,
|
||||
)
|
||||
logging.post_call(
|
||||
input=messages, api_key=api_key, original_response=response
|
||||
)
|
||||
elif (
|
||||
model in litellm.open_ai_chat_completion_models
|
||||
or custom_llm_provider == "custom_openai"
|
||||
|
|
|
|||
|
|
@ -7224,6 +7224,8 @@ class ProviderConfigManager:
|
|||
return litellm.IBMWatsonXAIConfig()
|
||||
elif litellm.LlmProviders.EMPOWER == provider:
|
||||
return litellm.EmpowerChatConfig()
|
||||
elif litellm.LlmProviders.MINIMAX == provider:
|
||||
return litellm.MinimaxChatConfig()
|
||||
elif litellm.LlmProviders.GITHUB == provider:
|
||||
return litellm.GithubChatConfig()
|
||||
elif litellm.LlmProviders.COMPACTIFAI == provider:
|
||||
|
|
|
|||
2
tests/test_litellm/llms/minimax/chat/__init__.py
Normal file
2
tests/test_litellm/llms/minimax/chat/__init__.py
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
# MiniMax chat tests
|
||||
|
||||
225
tests/test_litellm/llms/minimax/chat/test_transformation.py
Normal file
225
tests/test_litellm/llms/minimax/chat/test_transformation.py
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
"""
|
||||
Test MiniMax OpenAI-compatible API support
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
sys.path.insert(
|
||||
0, os.path.abspath("../")
|
||||
) # Adds the parent directory to the system path
|
||||
|
||||
import litellm
|
||||
from litellm import completion
|
||||
from litellm.llms.minimax.chat.transformation import MinimaxChatConfig
|
||||
|
||||
|
||||
def test_minimax_chat_config():
|
||||
"""Test that MinimaxChatConfig is properly configured"""
|
||||
config = MinimaxChatConfig()
|
||||
|
||||
# Test get_api_base default
|
||||
api_base = config.get_api_base()
|
||||
assert api_base == "https://api.minimax.io/v1"
|
||||
|
||||
# Test get_api_base with custom value
|
||||
custom_base = config.get_api_base(api_base="https://api.minimaxi.com/v1")
|
||||
assert custom_base == "https://api.minimaxi.com/v1"
|
||||
|
||||
# Test get_complete_url
|
||||
complete_url = config.get_complete_url(
|
||||
api_base="https://api.minimax.io/v1",
|
||||
api_key=None,
|
||||
model="MiniMax-M2.1",
|
||||
optional_params={},
|
||||
litellm_params={},
|
||||
stream=False
|
||||
)
|
||||
assert complete_url == "https://api.minimax.io/v1/chat/completions"
|
||||
|
||||
|
||||
def test_minimax_chat_config_url_variations():
|
||||
"""Test URL handling with different base URL formats"""
|
||||
config = MinimaxChatConfig()
|
||||
|
||||
# Test with /v1 ending
|
||||
url1 = config.get_complete_url(
|
||||
api_base="https://api.minimax.io/v1",
|
||||
api_key=None,
|
||||
model="MiniMax-M2.1",
|
||||
optional_params={},
|
||||
litellm_params={},
|
||||
)
|
||||
assert url1 == "https://api.minimax.io/v1/chat/completions"
|
||||
|
||||
# Test with trailing slash
|
||||
url2 = config.get_complete_url(
|
||||
api_base="https://api.minimax.io/",
|
||||
api_key=None,
|
||||
model="MiniMax-M2.1",
|
||||
optional_params={},
|
||||
litellm_params={},
|
||||
)
|
||||
assert url2 == "https://api.minimax.io/v1/chat/completions"
|
||||
|
||||
# Test without trailing slash
|
||||
url3 = config.get_complete_url(
|
||||
api_base="https://api.minimax.io",
|
||||
api_key=None,
|
||||
model="MiniMax-M2.1",
|
||||
optional_params={},
|
||||
litellm_params={},
|
||||
)
|
||||
assert url3 == "https://api.minimax.io/v1/chat/completions"
|
||||
|
||||
# Test with full path already
|
||||
url4 = config.get_complete_url(
|
||||
api_base="https://api.minimax.io/v1/chat/completions",
|
||||
api_key=None,
|
||||
model="MiniMax-M2.1",
|
||||
optional_params={},
|
||||
litellm_params={},
|
||||
)
|
||||
assert url4 == "https://api.minimax.io/v1/chat/completions"
|
||||
|
||||
|
||||
def test_minimax_provider_routing():
|
||||
"""Test that minimax provider is properly routed"""
|
||||
from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider
|
||||
|
||||
# Test with minimax/ prefix
|
||||
model, provider, api_key, api_base = get_llm_provider(
|
||||
model="minimax/MiniMax-M2.1",
|
||||
api_base="https://api.minimax.io/v1"
|
||||
)
|
||||
assert provider == "minimax"
|
||||
assert model == "MiniMax-M2.1"
|
||||
|
||||
|
||||
def test_minimax_provider_config_manager():
|
||||
"""Test that ProviderConfigManager returns MinimaxChatConfig"""
|
||||
from litellm.types.utils import LlmProviders
|
||||
from litellm.utils import ProviderConfigManager
|
||||
|
||||
config = ProviderConfigManager.get_provider_chat_config(
|
||||
model="MiniMax-M2.1",
|
||||
provider=LlmProviders.MINIMAX
|
||||
)
|
||||
|
||||
assert config is not None
|
||||
assert isinstance(config, MinimaxChatConfig)
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="Requires actual MiniMax API key")
|
||||
def test_minimax_chat_completion_basic():
|
||||
"""Test basic chat completion with MiniMax OpenAI-compatible API"""
|
||||
response = completion(
|
||||
model="minimax/MiniMax-M2.1",
|
||||
messages=[
|
||||
{"role": "system", "content": "You are a helpful assistant."},
|
||||
{"role": "user", "content": "Hello, how are you?"}
|
||||
],
|
||||
api_key=os.getenv("MINIMAX_API_KEY"),
|
||||
api_base="https://api.minimax.io/v1"
|
||||
)
|
||||
|
||||
assert response is not None
|
||||
assert hasattr(response, "choices")
|
||||
assert len(response.choices) > 0
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="Requires actual MiniMax API key")
|
||||
def test_minimax_chat_completion_with_reasoning_split():
|
||||
"""Test completion with reasoning_split parameter (MiniMax M2.1 feature)"""
|
||||
response = completion(
|
||||
model="minimax/MiniMax-M2.1",
|
||||
messages=[
|
||||
{"role": "system", "content": "You are a helpful assistant."},
|
||||
{"role": "user", "content": "Solve this problem: 2+2=?"}
|
||||
],
|
||||
api_key=os.getenv("MINIMAX_API_KEY"),
|
||||
api_base="https://api.minimax.io/v1",
|
||||
extra_body={"reasoning_split": True}
|
||||
)
|
||||
|
||||
assert response is not None
|
||||
# Check if reasoning_details is present in response
|
||||
if hasattr(response.choices[0].message, "reasoning_details"):
|
||||
assert response.choices[0].message.reasoning_details is not None
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="Requires actual MiniMax API key")
|
||||
def test_minimax_chat_completion_with_tools():
|
||||
"""Test completion with tool calling (function calling)"""
|
||||
tools = [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_weather",
|
||||
"description": "Get the current weather in a location",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"location": {
|
||||
"type": "string",
|
||||
"description": "The city and state, e.g. San Francisco, CA",
|
||||
}
|
||||
},
|
||||
"required": ["location"],
|
||||
},
|
||||
},
|
||||
}
|
||||
]
|
||||
|
||||
response = completion(
|
||||
model="minimax/MiniMax-M2.1",
|
||||
messages=[{"role": "user", "content": "What's the weather in San Francisco?"}],
|
||||
tools=tools,
|
||||
api_key=os.getenv("MINIMAX_API_KEY"),
|
||||
api_base="https://api.minimax.io/v1"
|
||||
)
|
||||
|
||||
assert response is not None
|
||||
assert hasattr(response, "choices")
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="Requires actual MiniMax API key")
|
||||
def test_minimax_chat_completion_streaming():
|
||||
"""Test streaming completion"""
|
||||
response = completion(
|
||||
model="minimax/MiniMax-M2.1",
|
||||
messages=[{"role": "user", "content": "Count to 5"}],
|
||||
stream=True,
|
||||
api_key=os.getenv("MINIMAX_API_KEY"),
|
||||
api_base="https://api.minimax.io/v1"
|
||||
)
|
||||
|
||||
chunks = []
|
||||
for chunk in response:
|
||||
chunks.append(chunk)
|
||||
|
||||
assert len(chunks) > 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Run basic tests that don't require API key
|
||||
print("Testing MiniMax Chat Config...")
|
||||
test_minimax_chat_config()
|
||||
print("✓ Config test passed")
|
||||
|
||||
print("\nTesting MiniMax Chat Config URL Variations...")
|
||||
test_minimax_chat_config_url_variations()
|
||||
print("✓ URL variations test passed")
|
||||
|
||||
print("\nTesting MiniMax Provider Routing...")
|
||||
test_minimax_provider_routing()
|
||||
print("✓ Routing test passed")
|
||||
|
||||
print("\nTesting MiniMax Provider Config Manager...")
|
||||
test_minimax_provider_config_manager()
|
||||
print("✓ Provider config manager test passed")
|
||||
|
||||
print("\n✅ All basic tests passed!")
|
||||
|
||||
Loading…
Add table
Reference in a new issue