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
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -714,7 +714,8 @@ Edit `deepcode_config.json` and fill in at least one provider key. Inline string
"providers": {
"openai": { "apiKey": "your_openai_api_key" },
"anthropic": { "apiKey": "${ANTHROPIC_API_KEY}" },
"gemini": { "apiKey": "" }
"gemini": { "apiKey": "" },
"minimax": { "apiKey": "${MINIMAX_API_KEY}" }
}
}
```
Expand Down
1 change: 1 addition & 0 deletions core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ class ProvidersConfig(_Base):
gemini: ProviderConfig = Field(default_factory=ProviderConfig)
zhipu: ProviderConfig = Field(default_factory=ProviderConfig)
dashscope: ProviderConfig = Field(default_factory=ProviderConfig)
minimax: ProviderConfig = Field(default_factory=ProviderConfig)
vllm: ProviderConfig = Field(default_factory=ProviderConfig)
ollama: ProviderConfig = Field(default_factory=ProviderConfig)

Expand Down
8 changes: 8 additions & 0 deletions core/providers/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,14 @@ def label(self) -> str:
backend="openai_compat",
is_local=True,
),
ProviderSpec(
name="minimax",
keywords=("minimax", "abab"),
env_key="MINIMAX_API_KEY",
display_name="MiniMax",
backend="openai_compat",
default_api_base="https://api.minimax.io/v1",
),
ProviderSpec(
name="ollama",
keywords=("ollama", "nemotron"),
Expand Down
3 changes: 3 additions & 0 deletions deepcode_config.json.example
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@
"dashscope": {
"apiKey": ""
},
"minimax": {
"apiKey": "${MINIMAX_API_KEY}"
},
"ollama": {
"apiBase": "http://localhost:11434/v1"
},
Expand Down
1 change: 1 addition & 0 deletions nanobot/nanobot/config/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ class ProvidersConfig(BaseModel):
groq: ProviderConfig = Field(default_factory=ProviderConfig)
zhipu: ProviderConfig = Field(default_factory=ProviderConfig)
dashscope: ProviderConfig = Field(default_factory=ProviderConfig) # 阿里云通义千问
minimax: ProviderConfig = Field(default_factory=ProviderConfig) # MiniMax
vllm: ProviderConfig = Field(default_factory=ProviderConfig)
gemini: ProviderConfig = Field(default_factory=ProviderConfig)
moonshot: ProviderConfig = Field(default_factory=ProviderConfig)
Expand Down
17 changes: 17 additions & 0 deletions nanobot/nanobot/providers/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,23 @@ def label(self) -> str:
strip_model_prefix=False,
model_overrides=(("kimi-k2.5", {"temperature": 1.0}),),
),
# MiniMax: OpenAI-compatible API. Needs "openai/" prefix for LiteLLM.
ProviderSpec(
name="minimax",
keywords=("minimax", "abab"),
env_key="MINIMAX_API_KEY",
display_name="MiniMax",
litellm_prefix="openai",
skip_prefixes=("openai/", "minimax/"),
env_extras=(),
is_gateway=False,
is_local=False,
detect_by_key_prefix="",
detect_by_base_keyword="minimax",
default_api_base="https://api.minimax.io/v1",
strip_model_prefix=False,
model_overrides=(),
),
# === Local deployment (matched by config key, NOT by api_base) =========
# vLLM / any OpenAI-compatible local server.
# Detected when config key is "vllm" (provider_name="vllm").
Expand Down
155 changes: 155 additions & 0 deletions tests/minimax_provider_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
"""Tests for MiniMax (minimax) provider integration."""

from __future__ import annotations

import sys
from pathlib import Path

import pytest

ROOT = Path(__file__).resolve().parents[1]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))

from core.providers.registry import ( # noqa: E402
PROVIDERS,
find_by_model,
find_by_name,
)


# ---------------------------------------------------------------------------
# Core registry tests
# ---------------------------------------------------------------------------


class TestCoreRegistry:
"""Tests for the core provider registry."""

def test_find_by_name(self):
spec = find_by_name("minimax")
assert spec is not None
assert spec.name == "minimax"
assert spec.display_name == "MiniMax"

def test_env_key(self):
spec = find_by_name("minimax")
assert spec is not None
assert spec.env_key == "MINIMAX_API_KEY"

def test_backend_is_openai_compat(self):
spec = find_by_name("minimax")
assert spec is not None
assert spec.backend == "openai_compat"

def test_default_api_base(self):
spec = find_by_name("minimax")
assert spec is not None
assert spec.default_api_base == "https://api.minimax.io/v1"

def test_find_by_model_minimax(self):
spec = find_by_model("minimax/MiniMax-M2.7")
assert spec is not None
assert spec.name == "minimax"

def test_find_by_model_abab(self):
spec = find_by_model("abab-7")
assert spec is not None
assert spec.name == "minimax"

def test_not_gateway_or_local(self):
spec = find_by_name("minimax")
assert spec is not None
assert spec.is_gateway is False
assert spec.is_local is False

def test_provider_in_registry_list(self):
names = [s.name for s in PROVIDERS]
assert "minimax" in names


# ---------------------------------------------------------------------------
# Config model tests
# ---------------------------------------------------------------------------


class TestConfigModel:
"""Tests for the config model with the new provider field."""

def test_providers_config_has_minimax_field(self):
from core.config import ProvidersConfig

cfg = ProvidersConfig()
assert hasattr(cfg, "minimax")
assert cfg.minimax.api_key is None

def test_providers_config_with_api_key(self):
from core.config import ProvidersConfig, ProviderConfig

cfg = ProvidersConfig(minimax=ProviderConfig(api_key="test-key"))
assert cfg.minimax.api_key == "test-key"

def test_providers_config_with_custom_base(self):
from core.config import ProvidersConfig, ProviderConfig

cfg = ProvidersConfig(
minimax=ProviderConfig(
api_key="test-key",
api_base="https://api.minimaxi.com/v1",
)
)
assert cfg.minimax.api_base == "https://api.minimaxi.com/v1"


# ---------------------------------------------------------------------------
# Nanobot registry tests
# ---------------------------------------------------------------------------


class TestNanobotRegistry:
"""Tests for the nanobot provider registry."""

@pytest.fixture(autouse=True)
def _setup_nanobot_path(self):
nanobot_root = ROOT / "nanobot"
if str(nanobot_root) not in sys.path:
sys.path.insert(0, str(nanobot_root))

def test_find_by_name(self):
nanobot_reg = pytest.importorskip("nanobot.providers.registry")
spec = nanobot_reg.find_by_name("minimax")
assert spec is not None
assert spec.name == "minimax"
assert spec.display_name == "MiniMax"

def test_litellm_prefix(self):
nanobot_reg = pytest.importorskip("nanobot.providers.registry")
spec = nanobot_reg.find_by_name("minimax")
assert spec is not None
assert spec.litellm_prefix == "openai"

def test_detect_by_base_keyword(self):
nanobot_reg = pytest.importorskip("nanobot.providers.registry")
spec = nanobot_reg.find_by_name("minimax")
assert spec is not None
assert spec.detect_by_base_keyword == "minimax"


# ---------------------------------------------------------------------------
# Nanobot config schema tests
# ---------------------------------------------------------------------------


class TestNanobotConfigSchema:
"""Tests for the nanobot config schema."""

@pytest.fixture(autouse=True)
def _setup_nanobot_path(self):
nanobot_root = ROOT / "nanobot"
if str(nanobot_root) not in sys.path:
sys.path.insert(0, str(nanobot_root))

def test_providers_config_has_minimax_field(self):
schema = pytest.importorskip("nanobot.config.schema")
cfg = schema.ProvidersConfig()
assert hasattr(cfg, "minimax")