Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,9 @@ The Web version's API for Android also supports other devices. See [Python sampl
- [StepFun](https://platform.stepfun.com)
- [vLLM](https://github.com/vllm-project/vllm)
- [Silicon🏷️](https://cloud.siliconflow.cn/s/tpoisonooo)
- [PPIO🏷️](https://ppinfra.com/user/register?invited_by=7GF8QS)
- [PPIO🏷️](https://ppinfra.com/user/register?invited_by=7GF8QS)
- [Xi-Api](https://api.xi-ai.cn)
- [MiniMax](https://platform.minimaxi.com)

</td>
<td>
Expand Down Expand Up @@ -253,6 +254,10 @@ remote_llm_model = "auto"
# remote_type = "ppio"
# remote_api_key = "sk-xxxxxxxxxxxxx"
# remote_llm_model = "thudm/glm-4-9b-chat"

# remote_type = "minimax"
# remote_api_key = "sk-xxxxxxxxxxxxx"
# remote_llm_model = "MiniMax-M1"
```

Then run the test:
Expand Down
8 changes: 6 additions & 2 deletions README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,9 @@ Web 版给 android 的接口,也支持非 android 调用,见[python 样例
- [StepFun](https://platform.stepfun.com)
- [vLLM](https://github.com/vllm-project/vllm)
- [Silicon🏷️](https://cloud.siliconflow.cn/s/tpoisonooo)
- [PPIO🏷️](https://ppinfra.com/user/register?invited_by=7GF8QS)
- [PPIO🏷️](https://ppinfra.com/user/register?invited_by=7GF8QS)
- [Xi-Api](https://api.xi-ai.cn)

- [MiniMax](https://platform.minimaxi.com)

</td>
<td>
Expand Down Expand Up @@ -250,6 +250,10 @@ remote_llm_model = "auto"
# remote_type = "ppio"
# remote_api_key = "sk-xxxxxxxxxxxxx"
# remote_llm_model = "thudm/glm-4-9b-chat"

# remote_type = "minimax"
# remote_api_key = "sk-xxxxxxxxxxxxx"
# remote_llm_model = "MiniMax-M1"
```

然后运行测试:
Expand Down
5 changes: 3 additions & 2 deletions config.ini
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@ save_dir = "logs/web_search_result"
[llm]
[llm.server]
# remote LLM service configuration
# support "gpt", "kimi", "deepseek", "zhipuai", "step", "internlm", "xi-api", "vllm", "siliconcloud" and "ppio"
# support "gpt", "kimi", "deepseek", "zhipuai", "step", "internlm", "xi-api", "vllm", "siliconcloud", "ppio" and "minimax"
# support "siliconcloud", see https://siliconflow.cn/zh-cn/siliconcloud
# xi-api and alles-apin is chinese gpt proxy
# for internlm, see https://internlm.intern-ai.org.cn/api/document

remote_type = "kimi"
remote_api_key = "sk_XXXXXXXXXXXXXXXXXXXXXx"
# max token length for LLM input.
# use 128000 for kimi, 192000 for gpt/xi-api, 16000 for deepseek, 128000 for zhipuai, 32000 for others.
# use 128000 for kimi, 192000 for gpt/xi-api, 16000 for deepseek, 128000 for zhipuai, 1000000 for minimax, 32000 for others.
remote_llm_max_text_length = 32000
# openai API model type, support model list:
# "auto" or "kimi-k2-0711-preview" for kimi. To save money, we auto select model name by prompt length.
Expand All @@ -54,6 +54,7 @@ remote_llm_max_text_length = 32000
# for example "alibaba/Qwen1.5-110B-Chat", see https://siliconflow.readme.io/reference/chat-completions-1
# ppio
# for example "thudm/glm-4-9b-chat", see https://ppinfra.com/model-api/console
# "MiniMax-M1" or "auto" for minimax, see https://platform.minimaxi.com
remote_llm_model = "kimi-k2-0711-preview"
# request per minute
rpm = 500
Expand Down
13 changes: 11 additions & 2 deletions huixiangdou/services/llm.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
'local': 'http://localhost:8000/v1',
'vllm': 'http://localhost:8000/v1',
'ppio': 'https://api.ppinfra.com/v3/openai',
'internlm': 'https://chat.intern-ai.org.cn/api/v1'
'internlm': 'https://chat.intern-ai.org.cn/api/v1',
'minimax': 'https://api.minimax.io/v1'
}

backend2model = {
Expand All @@ -37,7 +38,8 @@
"deepseek": "deepseek-chat",
"zhipuai": "glm-4",
"siliconcloud": "Qwen/Qwen2.5-14B-Instruct",
"ppio": "thudm/glm-4-9b-chat"
"ppio": "thudm/glm-4-9b-chat",
"minimax": "MiniMax-M1"
}

def limit_async_func_call(max_size: int, waitting_time: float = 0.1):
Expand Down Expand Up @@ -125,6 +127,13 @@ def choose_model(self, backend: Backend, token_size: int) -> str:
model = 'step-1-256k'
else:
raise ValueError('Input token length exceeds 256k')
elif backend.name == 'minimax' and model == 'auto':
if token_size <= 204000 - response_reserve_length:
model = 'MiniMax-M1'
elif token_size <= 1000000 - response_reserve_length:
model = 'MiniMax-M1'
else:
raise ValueError('Input token length exceeds 1M')
elif not model and backend.name in backend2model:
model = backend2model[backend.name]
return model
Expand Down
33 changes: 33 additions & 0 deletions tests/test_minimax.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# python3
# Test MiniMax LLM API via OpenAI-compatible interface
# Usage: MINIMAX_API_KEY=your_key python3 tests/test_minimax.py
import os
from openai import OpenAI

api_key = os.environ.get('MINIMAX_API_KEY', '')
if not api_key:
print('Please set MINIMAX_API_KEY environment variable')
exit(1)

client = OpenAI(api_key=api_key, base_url='https://api.minimax.io/v1')
queries = [
'What is the full name of ncnn, given that cnn stands for Convolutional Neural Network and n is for nihui, the author of ncnn?',
'"How to install mmdeploy?"\nPlease read the above carefully and determine if it is a question with a clear topic. Rate it from 0 to 10. Provide only the score without explanation.\nCriteria: 10 points for a question with subject, verb, and object; deduct for missing components; 0 for declarative sentences or non-questions.',
]

for query in queries:
response = client.chat.completions.create(
model='MiniMax-M1',
messages=[
{
'role': 'system',
'content': 'You are a helpful assistant'
},
{
'role': 'user',
'content': query
},
],
temperature=0.7)

print(response.choices[0].message.content)
114 changes: 114 additions & 0 deletions tests/test_minimax_integration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
"""Integration tests for MiniMax LLM provider.

These tests make real API calls to the MiniMax API.
They require MINIMAX_API_KEY to be set in the environment.

Run with: MINIMAX_API_KEY=your_key pytest tests/test_minimax_integration.py -v
"""
import os
import sys
import tempfile
import pytest
import asyncio

sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))

MINIMAX_API_KEY = os.environ.get('MINIMAX_API_KEY', '')
skip_no_key = pytest.mark.skipif(
not MINIMAX_API_KEY,
reason='MINIMAX_API_KEY not set'
)


@skip_no_key
class TestMiniMaxLLMIntegration:
"""Integration tests using real MiniMax API."""

def _make_config(self, model='MiniMax-M1'):
config_content = f"""
[llm]
[llm.server]
remote_type = "minimax"
remote_api_key = "{MINIMAX_API_KEY}"
remote_llm_max_text_length = 1000000
remote_llm_model = "{model}"
rpm = 500
tpm = 200000
"""
f = tempfile.NamedTemporaryFile(mode='w', suffix='.ini', delete=False)
f.write(config_content)
f.flush()
f.close()
return f.name

def test_chat_basic(self):
"""Test basic chat completion with MiniMax."""
from huixiangdou.services.llm import LLM
config_path = self._make_config()
try:
llm = LLM(config_path=config_path)
result = asyncio.get_event_loop().run_until_complete(
llm.chat(
prompt='What is 2+2? Reply with just the number.',
system_prompt='You are a helpful assistant. Be concise.',
max_tokens=1024,
timeout=60
)
)
assert result is not None
assert len(result) > 0
finally:
os.unlink(config_path)

def test_chat_stream(self):
"""Test streaming chat completion with MiniMax."""
from huixiangdou.services.llm import LLM
config_path = self._make_config()
try:
llm = LLM(config_path=config_path)

async def collect_stream():
chunks = []
async for chunk in llm.chat_stream(
prompt='Say hello.',
system_prompt='You are a helpful assistant. Be concise.',
max_tokens=1024,
timeout=60
):
chunks.append(chunk)
return ''.join(chunks)

result = asyncio.get_event_loop().run_until_complete(collect_stream())
assert result is not None
assert len(result) > 0
finally:
os.unlink(config_path)

def test_chat_with_history(self):
"""Test chat with conversation history."""
from huixiangdou.services.llm import LLM
config_path = self._make_config()
try:
llm = LLM(config_path=config_path)
history = [
{'role': 'user', 'content': 'My name is Alice.'},
{'role': 'assistant', 'content': 'Hello Alice! Nice to meet you.'},
]
result = asyncio.get_event_loop().run_until_complete(
llm.chat(
prompt='What is my name? Reply in one word.',
system_prompt='You are a helpful assistant. Be concise.',
history=history,
max_tokens=1024,
timeout=60
)
)
assert result is not None
assert len(result) > 0
assert 'Alice' in result
finally:
os.unlink(config_path)


if __name__ == '__main__':
pytest.main([__file__, '-v'])
Loading