Merge branch 'main' into feat/sandbox-provisioner-shared-storage
This commit is contained in:
commit
4cc795da9c
@ -49,6 +49,7 @@ TAVILY_API_KEY= # 获取搜索服务的 api key 请访问 https://app.tavily.co
|
|||||||
# DASHSCOPE_API_KEY=
|
# DASHSCOPE_API_KEY=
|
||||||
# DEEPSEEK_API_KEY=
|
# DEEPSEEK_API_KEY=
|
||||||
# ARK_API_KEY=
|
# ARK_API_KEY=
|
||||||
|
# MINIMAX_API_KEY= # MiniMax 大模型 https://platform.minimaxi.com/
|
||||||
# TOGETHER_API_KEY=
|
# TOGETHER_API_KEY=
|
||||||
# # endregion model_provider
|
# # endregion model_provider
|
||||||
|
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
| [硅基流动](https://cloud.siliconflow.cn/i/Eo5yTHGJ) | `SILICONFLOW_API_KEY` | 🆓 免费额度,默认推荐 |
|
| [硅基流动](https://cloud.siliconflow.cn/i/Eo5yTHGJ) | `SILICONFLOW_API_KEY` | 🆓 免费额度,默认推荐 |
|
||||||
| OpenAI | `OPENAI_API_KEY` | GPT 系列模型 |
|
| OpenAI | `OPENAI_API_KEY` | GPT 系列模型 |
|
||||||
| DeepSeek | `DEEPSEEK_API_KEY` | 国产大模型 |
|
| DeepSeek | `DEEPSEEK_API_KEY` | 国产大模型 |
|
||||||
|
| [MiniMax](https://platform.minimaxi.com/) | `MINIMAX_API_KEY` | M2.7/M2.5 系列,百万 token 上下文 |
|
||||||
| OpenRouter | `OPENROUTER_API_KEY` | 多模型聚合平台 |
|
| OpenRouter | `OPENROUTER_API_KEY` | 多模型聚合平台 |
|
||||||
| 智谱清言 | `ZHIPUAI_API_KEY` | GLM 系列模型 |
|
| 智谱清言 | `ZHIPUAI_API_KEY` | GLM 系列模型 |
|
||||||
| 阿里云百炼 | `DASHSCOPE_API_KEY` | 通义千问系列 |
|
| 阿里云百炼 | `DASHSCOPE_API_KEY` | 通义千问系列 |
|
||||||
|
|||||||
@ -231,13 +231,14 @@ async def test_mcp_server(
|
|||||||
await get_server_or_404(db, name)
|
await get_server_or_404(db, name)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
tools = await get_all_mcp_tools(name)
|
tools = await get_all_mcp_tools(name, raise_on_error=True)
|
||||||
return {
|
return {
|
||||||
"success": True,
|
"success": True,
|
||||||
"message": f"连接成功,共发现 {len(tools)} 个工具",
|
"message": f"连接成功,共发现 {len(tools)} 个工具",
|
||||||
"tool_count": len(tools),
|
"tool_count": len(tools),
|
||||||
}
|
}
|
||||||
except Exception as test_error:
|
except Exception as test_error:
|
||||||
|
logger.warning(f"MCP server test failed for '{name}': {test_error}")
|
||||||
raise HTTPException(status_code=500, detail=f"连接失败: {str(test_error)}")
|
raise HTTPException(status_code=500, detail=f"连接失败: {str(test_error)}")
|
||||||
except HTTPException:
|
except HTTPException:
|
||||||
raise
|
raise
|
||||||
|
|||||||
@ -115,6 +115,19 @@ DEFAULT_CHAT_MODEL_PROVIDERS: dict[str, ChatModelProvider] = {
|
|||||||
"doubao-seed-2-0-mini-260215",
|
"doubao-seed-2-0-mini-260215",
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
"minimax": ChatModelProvider(
|
||||||
|
name="MiniMax",
|
||||||
|
url="https://platform.minimaxi.com/document/introduction",
|
||||||
|
base_url="https://api.minimax.io/v1",
|
||||||
|
default="MiniMax-M2.7",
|
||||||
|
env="MINIMAX_API_KEY",
|
||||||
|
models=[
|
||||||
|
"MiniMax-M2.7",
|
||||||
|
"MiniMax-M2.7-highspeed",
|
||||||
|
"MiniMax-M2.5",
|
||||||
|
"MiniMax-M2.5-highspeed",
|
||||||
|
],
|
||||||
|
),
|
||||||
"openrouter": ChatModelProvider(
|
"openrouter": ChatModelProvider(
|
||||||
name="OpenRouter",
|
name="OpenRouter",
|
||||||
url="https://openrouter.ai/models",
|
url="https://openrouter.ai/models",
|
||||||
|
|||||||
@ -210,6 +210,7 @@ async def get_mcp_tools(
|
|||||||
disabled_tools: list[str] = None,
|
disabled_tools: list[str] = None,
|
||||||
cache: bool = True,
|
cache: bool = True,
|
||||||
force_refresh: bool = False,
|
force_refresh: bool = False,
|
||||||
|
raise_on_error: bool = False,
|
||||||
) -> list[Callable[..., Any]]:
|
) -> list[Callable[..., Any]]:
|
||||||
"""Get MCP tools for a specific server.
|
"""Get MCP tools for a specific server.
|
||||||
|
|
||||||
@ -288,11 +289,15 @@ async def get_mcp_tools(
|
|||||||
|
|
||||||
except AssertionError as e:
|
except AssertionError as e:
|
||||||
logger.warning(f"[assert] Failed to load tools from MCP server '{server_name}': {e}")
|
logger.warning(f"[assert] Failed to load tools from MCP server '{server_name}': {e}")
|
||||||
|
if raise_on_error:
|
||||||
|
raise
|
||||||
return []
|
return []
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(
|
logger.error(
|
||||||
f"Failed to load tools from MCP server '{server_name}': {e}, traceback: {traceback.format_exc()}"
|
f"Failed to load tools from MCP server '{server_name}': {e}, traceback: {traceback.format_exc()}"
|
||||||
)
|
)
|
||||||
|
if raise_on_error:
|
||||||
|
raise
|
||||||
return []
|
return []
|
||||||
|
|
||||||
# 3. Filtering (Apply to Return Value Only)
|
# 3. Filtering (Apply to Return Value Only)
|
||||||
@ -602,7 +607,7 @@ async def get_servers_config(names: list[str]) -> dict[str, dict[str, Any]]:
|
|||||||
return {name: MCP_SERVERS[name] for name in names if name in MCP_SERVERS}
|
return {name: MCP_SERVERS[name] for name in names if name in MCP_SERVERS}
|
||||||
|
|
||||||
|
|
||||||
async def get_all_mcp_tools(server_name: str) -> list:
|
async def get_all_mcp_tools(server_name: str, raise_on_error: bool = False) -> list:
|
||||||
"""Get all tools of an MCP server (no filtering).
|
"""Get all tools of an MCP server (no filtering).
|
||||||
|
|
||||||
For management UI to display tool list, supports viewing all tools and their enabled status.
|
For management UI to display tool list, supports viewing all tools and their enabled status.
|
||||||
@ -610,6 +615,7 @@ async def get_all_mcp_tools(server_name: str) -> list:
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
server_name: Server name
|
server_name: Server name
|
||||||
|
raise_on_error: Whether to raise an exception on error instead of returning empty list
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
List of all tools (unfiltered)
|
List of all tools (unfiltered)
|
||||||
@ -617,7 +623,15 @@ async def get_all_mcp_tools(server_name: str) -> list:
|
|||||||
config = MCP_SERVERS.get(server_name)
|
config = MCP_SERVERS.get(server_name)
|
||||||
if not config:
|
if not config:
|
||||||
logger.warning(f"MCP server '{server_name}' not found in cache")
|
logger.warning(f"MCP server '{server_name}' not found in cache")
|
||||||
|
if raise_on_error:
|
||||||
|
raise ValueError(f"MCP server '{server_name}' not found in cache")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
# Get all tools (no filtering, force refresh, no cache update)
|
# Get all tools (no filtering, force refresh, no cache update)
|
||||||
return await get_mcp_tools(server_name, disabled_tools=[], cache=False, force_refresh=True)
|
return await get_mcp_tools(
|
||||||
|
server_name,
|
||||||
|
disabled_tools=[],
|
||||||
|
cache=False,
|
||||||
|
force_refresh=True,
|
||||||
|
raise_on_error=raise_on_error,
|
||||||
|
)
|
||||||
|
|||||||
278
test/test_minimax_provider.py
Normal file
278
test/test_minimax_provider.py
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
"""
|
||||||
|
Unit tests for MiniMax provider configuration and model selection.
|
||||||
|
|
||||||
|
These tests validate MiniMax integration without requiring Docker services.
|
||||||
|
Run with: uv run python -m pytest test/test_minimax_provider.py -v
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import importlib.util
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# Helpers: Import static models module directly (bypass src.__init__)
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
_PROJECT_ROOT = Path(__file__).resolve().parent.parent
|
||||||
|
|
||||||
|
|
||||||
|
def _load_static_models():
|
||||||
|
"""Load src/config/static/models.py directly, bypassing src.__init__.py."""
|
||||||
|
models_path = _PROJECT_ROOT / "src" / "config" / "static" / "models.py"
|
||||||
|
spec = importlib.util.spec_from_file_location("_static_models", models_path)
|
||||||
|
mod = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(mod)
|
||||||
|
return mod
|
||||||
|
|
||||||
|
|
||||||
|
_models_mod = _load_static_models()
|
||||||
|
DEFAULT_CHAT_MODEL_PROVIDERS = _models_mod.DEFAULT_CHAT_MODEL_PROVIDERS
|
||||||
|
ChatModelProvider = _models_mod.ChatModelProvider
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# Unit Tests: Provider Configuration
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
|
||||||
|
class TestMiniMaxProviderConfig:
|
||||||
|
"""Verify MiniMax is correctly registered in default providers."""
|
||||||
|
|
||||||
|
def test_minimax_in_default_providers(self):
|
||||||
|
assert "minimax" in DEFAULT_CHAT_MODEL_PROVIDERS
|
||||||
|
|
||||||
|
def test_minimax_provider_type(self):
|
||||||
|
assert isinstance(DEFAULT_CHAT_MODEL_PROVIDERS["minimax"], ChatModelProvider)
|
||||||
|
|
||||||
|
def test_minimax_name(self):
|
||||||
|
assert DEFAULT_CHAT_MODEL_PROVIDERS["minimax"].name == "MiniMax"
|
||||||
|
|
||||||
|
def test_minimax_base_url(self):
|
||||||
|
assert DEFAULT_CHAT_MODEL_PROVIDERS["minimax"].base_url == "https://api.minimax.io/v1"
|
||||||
|
|
||||||
|
def test_minimax_env_var(self):
|
||||||
|
assert DEFAULT_CHAT_MODEL_PROVIDERS["minimax"].env == "MINIMAX_API_KEY"
|
||||||
|
|
||||||
|
def test_minimax_default_model(self):
|
||||||
|
assert DEFAULT_CHAT_MODEL_PROVIDERS["minimax"].default == "MiniMax-M2.7"
|
||||||
|
|
||||||
|
def test_minimax_models_list(self):
|
||||||
|
expected = ["MiniMax-M2.7", "MiniMax-M2.7-highspeed", "MiniMax-M2.5", "MiniMax-M2.5-highspeed"]
|
||||||
|
assert DEFAULT_CHAT_MODEL_PROVIDERS["minimax"].models == expected
|
||||||
|
|
||||||
|
def test_minimax_not_custom(self):
|
||||||
|
assert DEFAULT_CHAT_MODEL_PROVIDERS["minimax"].custom is False
|
||||||
|
|
||||||
|
def test_minimax_has_documentation_url(self):
|
||||||
|
assert DEFAULT_CHAT_MODEL_PROVIDERS["minimax"].url.startswith("https://")
|
||||||
|
|
||||||
|
def test_minimax_all_models_have_minimax_prefix(self):
|
||||||
|
for model in DEFAULT_CHAT_MODEL_PROVIDERS["minimax"].models:
|
||||||
|
assert model.startswith("MiniMax-"), f"Model {model} should start with 'MiniMax-'"
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# Unit Tests: Provider Serialization
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
|
||||||
|
class TestMiniMaxProviderSerialization:
|
||||||
|
"""Test that MiniMax provider config serializes correctly."""
|
||||||
|
|
||||||
|
def test_minimax_model_dump(self):
|
||||||
|
dumped = DEFAULT_CHAT_MODEL_PROVIDERS["minimax"].model_dump()
|
||||||
|
assert dumped["name"] == "MiniMax"
|
||||||
|
assert dumped["base_url"] == "https://api.minimax.io/v1"
|
||||||
|
assert dumped["env"] == "MINIMAX_API_KEY"
|
||||||
|
assert dumped["default"] == "MiniMax-M2.7"
|
||||||
|
assert len(dumped["models"]) == 4
|
||||||
|
assert dumped["custom"] is False
|
||||||
|
|
||||||
|
def test_minimax_model_dump_round_trip(self):
|
||||||
|
provider = DEFAULT_CHAT_MODEL_PROVIDERS["minimax"]
|
||||||
|
restored = ChatModelProvider(**provider.model_dump())
|
||||||
|
assert restored == provider
|
||||||
|
|
||||||
|
def test_minimax_model_dump_keys(self):
|
||||||
|
dumped = DEFAULT_CHAT_MODEL_PROVIDERS["minimax"].model_dump()
|
||||||
|
assert set(dumped.keys()) == {"name", "url", "base_url", "default", "env", "models", "custom"}
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# Unit Tests: Model Selection (standalone, no Docker deps)
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
|
||||||
|
class TestMiniMaxModelSelection:
|
||||||
|
"""Test model selection logic with MiniMax provider."""
|
||||||
|
|
||||||
|
def _build_select_model(self, providers_dict):
|
||||||
|
"""Build a standalone select_model function mirroring src/models/chat.py."""
|
||||||
|
|
||||||
|
class _Model:
|
||||||
|
def __init__(self, api_key, base_url, model_name):
|
||||||
|
self.api_key = api_key
|
||||||
|
self.base_url = base_url
|
||||||
|
self.model_name = model_name
|
||||||
|
|
||||||
|
def select_model(model_provider=None, model_name=None, model_spec=None):
|
||||||
|
if model_spec:
|
||||||
|
parts = model_spec.split("/", 1)
|
||||||
|
model_provider = model_provider or parts[0]
|
||||||
|
model_name = model_name or (parts[1] if len(parts) > 1 else "")
|
||||||
|
|
||||||
|
assert model_provider, "Model provider not specified"
|
||||||
|
info = providers_dict.get(model_provider)
|
||||||
|
if not info:
|
||||||
|
raise ValueError(f"Unknown model provider: {model_provider}")
|
||||||
|
model_name = model_name or info.default
|
||||||
|
return _Model(
|
||||||
|
api_key=os.environ.get(info.env, info.env),
|
||||||
|
base_url=info.base_url,
|
||||||
|
model_name=model_name,
|
||||||
|
)
|
||||||
|
|
||||||
|
return select_model
|
||||||
|
|
||||||
|
def test_select_model_minimax_default(self):
|
||||||
|
select_model = self._build_select_model({"minimax": DEFAULT_CHAT_MODEL_PROVIDERS["minimax"]})
|
||||||
|
with patch.dict(os.environ, {"MINIMAX_API_KEY": "test-key-123"}):
|
||||||
|
model = select_model("minimax", "MiniMax-M2.7")
|
||||||
|
assert model.model_name == "MiniMax-M2.7"
|
||||||
|
assert model.base_url == "https://api.minimax.io/v1"
|
||||||
|
|
||||||
|
def test_select_model_minimax_highspeed(self):
|
||||||
|
select_model = self._build_select_model({"minimax": DEFAULT_CHAT_MODEL_PROVIDERS["minimax"]})
|
||||||
|
with patch.dict(os.environ, {"MINIMAX_API_KEY": "test-key-123"}):
|
||||||
|
model = select_model("minimax", "MiniMax-M2.7-highspeed")
|
||||||
|
assert model.model_name == "MiniMax-M2.7-highspeed"
|
||||||
|
|
||||||
|
def test_select_model_minimax_uses_default(self):
|
||||||
|
select_model = self._build_select_model({"minimax": DEFAULT_CHAT_MODEL_PROVIDERS["minimax"]})
|
||||||
|
with patch.dict(os.environ, {"MINIMAX_API_KEY": "test-key-123"}):
|
||||||
|
model = select_model("minimax")
|
||||||
|
assert model.model_name == "MiniMax-M2.7"
|
||||||
|
|
||||||
|
def test_select_model_minimax_from_spec(self):
|
||||||
|
select_model = self._build_select_model({"minimax": DEFAULT_CHAT_MODEL_PROVIDERS["minimax"]})
|
||||||
|
with patch.dict(os.environ, {"MINIMAX_API_KEY": "test-key-123"}):
|
||||||
|
model = select_model(model_spec="minimax/MiniMax-M2.5")
|
||||||
|
assert model.model_name == "MiniMax-M2.5"
|
||||||
|
assert model.base_url == "https://api.minimax.io/v1"
|
||||||
|
|
||||||
|
def test_select_model_minimax_api_key_from_env(self):
|
||||||
|
select_model = self._build_select_model({"minimax": DEFAULT_CHAT_MODEL_PROVIDERS["minimax"]})
|
||||||
|
with patch.dict(os.environ, {"MINIMAX_API_KEY": "my-secret-key"}):
|
||||||
|
model = select_model("minimax", "MiniMax-M2.7")
|
||||||
|
assert model.api_key == "my-secret-key"
|
||||||
|
|
||||||
|
def test_select_model_unknown_provider_raises(self):
|
||||||
|
select_model = self._build_select_model({"minimax": DEFAULT_CHAT_MODEL_PROVIDERS["minimax"]})
|
||||||
|
with pytest.raises(ValueError, match="Unknown model provider"):
|
||||||
|
select_model("nonexistent", "some-model")
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# Unit Tests: LangChain OpenAI-compat Loading
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
|
||||||
|
class TestMiniMaxLangChainLoading:
|
||||||
|
"""Test ChatOpenAI instantiation for MiniMax (OpenAI-compat)."""
|
||||||
|
|
||||||
|
def test_langchain_openai_compat_for_minimax(self):
|
||||||
|
from langchain_openai import ChatOpenAI
|
||||||
|
from pydantic import SecretStr
|
||||||
|
|
||||||
|
minimax = DEFAULT_CHAT_MODEL_PROVIDERS["minimax"]
|
||||||
|
model = ChatOpenAI(
|
||||||
|
model="MiniMax-M2.7",
|
||||||
|
api_key=SecretStr("test-key-123"),
|
||||||
|
base_url=minimax.base_url,
|
||||||
|
stream_usage=True,
|
||||||
|
)
|
||||||
|
assert isinstance(model, ChatOpenAI)
|
||||||
|
assert model.model_name == "MiniMax-M2.7"
|
||||||
|
|
||||||
|
def test_langchain_minimax_highspeed(self):
|
||||||
|
from langchain_openai import ChatOpenAI
|
||||||
|
from pydantic import SecretStr
|
||||||
|
|
||||||
|
minimax = DEFAULT_CHAT_MODEL_PROVIDERS["minimax"]
|
||||||
|
model = ChatOpenAI(
|
||||||
|
model="MiniMax-M2.5-highspeed",
|
||||||
|
api_key=SecretStr("test-key-123"),
|
||||||
|
base_url=minimax.base_url,
|
||||||
|
)
|
||||||
|
assert isinstance(model, ChatOpenAI)
|
||||||
|
assert model.model_name == "MiniMax-M2.5-highspeed"
|
||||||
|
|
||||||
|
def test_langchain_minimax_base_url_correct(self):
|
||||||
|
from langchain_openai import ChatOpenAI
|
||||||
|
from pydantic import SecretStr
|
||||||
|
|
||||||
|
minimax = DEFAULT_CHAT_MODEL_PROVIDERS["minimax"]
|
||||||
|
model = ChatOpenAI(
|
||||||
|
model="MiniMax-M2.7",
|
||||||
|
api_key=SecretStr("test-key-123"),
|
||||||
|
base_url=minimax.base_url,
|
||||||
|
)
|
||||||
|
assert str(model.openai_api_base) == "https://api.minimax.io/v1"
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# Integration Tests: MiniMax API Connectivity
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.integration
|
||||||
|
class TestMiniMaxIntegration:
|
||||||
|
"""Integration tests that verify MiniMax API connectivity.
|
||||||
|
|
||||||
|
Require MINIMAX_API_KEY env var and network access to api.minimax.io.
|
||||||
|
Run with: uv run python -m pytest test/test_minimax_provider.py -m integration -v
|
||||||
|
"""
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def skip_without_api_key(self):
|
||||||
|
if not os.environ.get("MINIMAX_API_KEY"):
|
||||||
|
pytest.skip("MINIMAX_API_KEY not set")
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_minimax_chat_completion(self):
|
||||||
|
from openai import AsyncOpenAI
|
||||||
|
|
||||||
|
client = AsyncOpenAI(api_key=os.environ["MINIMAX_API_KEY"], base_url="https://api.minimax.io/v1")
|
||||||
|
response = await client.chat.completions.create(
|
||||||
|
model="MiniMax-M2.7", messages=[{"role": "user", "content": "Say 1."}]
|
||||||
|
)
|
||||||
|
assert response.choices[0].message.content is not None
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_minimax_streaming(self):
|
||||||
|
from openai import AsyncOpenAI
|
||||||
|
|
||||||
|
client = AsyncOpenAI(api_key=os.environ["MINIMAX_API_KEY"], base_url="https://api.minimax.io/v1")
|
||||||
|
chunks = []
|
||||||
|
response = await client.chat.completions.create(
|
||||||
|
model="MiniMax-M2.7", messages=[{"role": "user", "content": "Say hi."}], stream=True
|
||||||
|
)
|
||||||
|
async for chunk in response:
|
||||||
|
if chunk.choices and chunk.choices[0].delta.content:
|
||||||
|
chunks.append(chunk.choices[0].delta.content)
|
||||||
|
assert len(chunks) > 0
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_minimax_highspeed_model(self):
|
||||||
|
from openai import AsyncOpenAI
|
||||||
|
|
||||||
|
client = AsyncOpenAI(api_key=os.environ["MINIMAX_API_KEY"], base_url="https://api.minimax.io/v1")
|
||||||
|
response = await client.chat.completions.create(
|
||||||
|
model="MiniMax-M2.7-highspeed", messages=[{"role": "user", "content": "Say 1."}]
|
||||||
|
)
|
||||||
|
assert response.choices[0].message.content is not None
|
||||||
1
web/src/assets/providers/minimax-color.svg
Normal file
1
web/src/assets/providers/minimax-color.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Minimax</title><defs><linearGradient id="lobe-icons-minimax-fill" x1="0%" x2="100.182%" y1="50.057%" y2="50.057%"><stop offset="0%" stop-color="#E2167E"></stop><stop offset="100%" stop-color="#FE603C"></stop></linearGradient></defs><path d="M16.278 2c1.156 0 2.093.927 2.093 2.07v12.501a.74.74 0 00.744.709.74.74 0 00.743-.709V9.099a2.06 2.06 0 012.071-2.049A2.06 2.06 0 0124 9.1v6.561a.649.649 0 01-.652.645.649.649 0 01-.653-.645V9.1a.762.762 0 00-.766-.758.762.762 0 00-.766.758v7.472a2.037 2.037 0 01-2.048 2.026 2.037 2.037 0 01-2.048-2.026v-12.5a.785.785 0 00-.788-.753.785.785 0 00-.789.752l-.001 15.904A2.037 2.037 0 0113.441 22a2.037 2.037 0 01-2.048-2.026V18.04c0-.356.292-.645.652-.645.36 0 .652.289.652.645v1.934c0 .263.142.506.372.638.23.131.514.131.744 0a.734.734 0 00.372-.638V4.07c0-1.143.937-2.07 2.093-2.07zm-5.674 0c1.156 0 2.093.927 2.093 2.07v11.523a.648.648 0 01-.652.645.648.648 0 01-.652-.645V4.07a.785.785 0 00-.789-.78.785.785 0 00-.789.78v14.013a2.06 2.06 0 01-2.07 2.048 2.06 2.06 0 01-2.071-2.048V9.1a.762.762 0 00-.766-.758.762.762 0 00-.766.758v3.8a2.06 2.06 0 01-2.071 2.049A2.06 2.06 0 010 12.9v-1.378c0-.357.292-.646.652-.646.36 0 .653.29.653.646V12.9c0 .418.343.757.766.757s.766-.339.766-.757V9.099a2.06 2.06 0 012.07-2.048 2.06 2.06 0 012.071 2.048v8.984c0 .419.343.758.767.758.423 0 .766-.339.766-.758V4.07c0-1.143.937-2.07 2.093-2.07z" fill="url(#lobe-icons-minimax-fill)" fill-rule="nonzero"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
@ -8,6 +8,7 @@ import arkIcon from '@/assets/providers/doubao-color.svg'
|
|||||||
import openrouterIcon from '@/assets/providers/openrouter.svg'
|
import openrouterIcon from '@/assets/providers/openrouter.svg'
|
||||||
import defaultIcon from '@/assets/providers/default.png'
|
import defaultIcon from '@/assets/providers/default.png'
|
||||||
import modelscopeIcon from '@/assets/providers/modelscope-color.svg'
|
import modelscopeIcon from '@/assets/providers/modelscope-color.svg'
|
||||||
|
import minimaxIcon from '@/assets/providers/minimax-color.svg'
|
||||||
|
|
||||||
export const modelIcons = {
|
export const modelIcons = {
|
||||||
openai: openaiIcon,
|
openai: openaiIcon,
|
||||||
@ -20,5 +21,6 @@ export const modelIcons = {
|
|||||||
together: togetherIcon,
|
together: togetherIcon,
|
||||||
openrouter: openrouterIcon,
|
openrouter: openrouterIcon,
|
||||||
modelscope: modelscopeIcon,
|
modelscope: modelscopeIcon,
|
||||||
|
minimax: minimaxIcon,
|
||||||
default: defaultIcon // 添加默认图标
|
default: defaultIcon // 添加默认图标
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user