diff --git a/hermes_cli/auth.py b/hermes_cli/auth.py index 3a80780f..293f91e0 100644 --- a/hermes_cli/auth.py +++ b/hermes_cli/auth.py @@ -145,7 +145,7 @@ PROVIDER_REGISTRY: Dict[str, ProviderConfig] = { id="minimax", name="MiniMax", auth_type="api_key", - inference_base_url="https://api.minimax.io/v1", + inference_base_url="https://api.minimax.io/anthropic", api_key_env_vars=("MINIMAX_API_KEY",), base_url_env_var="MINIMAX_BASE_URL", ), @@ -168,7 +168,7 @@ PROVIDER_REGISTRY: Dict[str, ProviderConfig] = { id="minimax-cn", name="MiniMax (China)", auth_type="api_key", - inference_base_url="https://api.minimaxi.com/v1", + inference_base_url="https://api.minimaxi.com/anthropic", api_key_env_vars=("MINIMAX_CN_API_KEY",), base_url_env_var="MINIMAX_CN_BASE_URL", ), diff --git a/hermes_cli/runtime_provider.py b/hermes_cli/runtime_provider.py index ed9e2854..b00db5cf 100644 --- a/hermes_cli/runtime_provider.py +++ b/hermes_cli/runtime_provider.py @@ -387,6 +387,12 @@ def resolve_runtime_provider( # (e.g. https://api.minimax.io/anthropic, https://dashscope.../anthropic) elif base_url.rstrip("/").endswith("/anthropic"): api_mode = "anthropic_messages" + # MiniMax providers always use Anthropic Messages API. + # Auto-correct stale /v1 URLs (from old .env or config) to /anthropic. + elif provider in ("minimax", "minimax-cn"): + api_mode = "anthropic_messages" + if base_url.rstrip("/").endswith("/v1"): + base_url = base_url.rstrip("/")[:-3] + "/anthropic" return { "provider": provider, "api_mode": api_mode, diff --git a/tests/test_api_key_providers.py b/tests/test_api_key_providers.py index 1bb91eef..95d18bdd 100644 --- a/tests/test_api_key_providers.py +++ b/tests/test_api_key_providers.py @@ -92,8 +92,8 @@ class TestProviderRegistry: assert PROVIDER_REGISTRY["copilot-acp"].inference_base_url == "acp://copilot" assert PROVIDER_REGISTRY["zai"].inference_base_url == "https://api.z.ai/api/paas/v4" assert PROVIDER_REGISTRY["kimi-coding"].inference_base_url == "https://api.moonshot.ai/v1" - assert PROVIDER_REGISTRY["minimax"].inference_base_url == "https://api.minimax.io/v1" - assert PROVIDER_REGISTRY["minimax-cn"].inference_base_url == "https://api.minimaxi.com/v1" + assert PROVIDER_REGISTRY["minimax"].inference_base_url == "https://api.minimax.io/anthropic" + assert PROVIDER_REGISTRY["minimax-cn"].inference_base_url == "https://api.minimaxi.com/anthropic" assert PROVIDER_REGISTRY["ai-gateway"].inference_base_url == "https://ai-gateway.vercel.sh/v1" assert PROVIDER_REGISTRY["kilocode"].inference_base_url == "https://api.kilo.ai/api/gateway" @@ -399,14 +399,14 @@ class TestResolveApiKeyProviderCredentials: creds = resolve_api_key_provider_credentials("minimax") assert creds["provider"] == "minimax" assert creds["api_key"] == "mm-secret-key" - assert creds["base_url"] == "https://api.minimax.io/v1" + assert creds["base_url"] == "https://api.minimax.io/anthropic" def test_resolve_minimax_cn_with_key(self, monkeypatch): monkeypatch.setenv("MINIMAX_CN_API_KEY", "mmcn-secret-key") creds = resolve_api_key_provider_credentials("minimax-cn") assert creds["provider"] == "minimax-cn" assert creds["api_key"] == "mmcn-secret-key" - assert creds["base_url"] == "https://api.minimaxi.com/v1" + assert creds["base_url"] == "https://api.minimaxi.com/anthropic" def test_resolve_ai_gateway_with_key(self, monkeypatch): monkeypatch.setenv("AI_GATEWAY_API_KEY", "gw-secret-key") diff --git a/tests/test_runtime_provider_resolution.py b/tests/test_runtime_provider_resolution.py index 4789287c..6a50db79 100644 --- a/tests/test_runtime_provider_resolution.py +++ b/tests/test_runtime_provider_resolution.py @@ -479,8 +479,8 @@ def test_api_key_provider_explicit_api_mode_config(monkeypatch): assert resolved["api_mode"] == "anthropic_messages" -def test_api_key_provider_default_url_stays_chat_completions(monkeypatch): - """API-key providers with default /v1 URL should stay on chat_completions.""" +def test_minimax_default_url_uses_anthropic_messages(monkeypatch): + """MiniMax with default /anthropic URL should auto-detect anthropic_messages mode.""" monkeypatch.setattr(rp, "resolve_provider", lambda *a, **k: "minimax") monkeypatch.setattr(rp, "_get_model_config", lambda: {}) monkeypatch.setenv("MINIMAX_API_KEY", "test-minimax-key") @@ -488,9 +488,50 @@ def test_api_key_provider_default_url_stays_chat_completions(monkeypatch): resolved = rp.resolve_runtime_provider(requested="minimax") + assert resolved["provider"] == "minimax" + assert resolved["api_mode"] == "anthropic_messages" + assert resolved["base_url"] == "https://api.minimax.io/anthropic" + + +def test_minimax_stale_v1_url_auto_corrected(monkeypatch): + """MiniMax with stale /v1 base URL should be auto-corrected to /anthropic.""" + monkeypatch.setattr(rp, "resolve_provider", lambda *a, **k: "minimax") + monkeypatch.setattr(rp, "_get_model_config", lambda: {}) + monkeypatch.setenv("MINIMAX_API_KEY", "test-minimax-key") + monkeypatch.setenv("MINIMAX_BASE_URL", "https://api.minimax.io/v1") + + resolved = rp.resolve_runtime_provider(requested="minimax") + + assert resolved["provider"] == "minimax" + assert resolved["api_mode"] == "anthropic_messages" + assert resolved["base_url"] == "https://api.minimax.io/anthropic" + + +def test_minimax_cn_stale_v1_url_auto_corrected(monkeypatch): + """MiniMax-CN with stale /v1 base URL should be auto-corrected to /anthropic.""" + monkeypatch.setattr(rp, "resolve_provider", lambda *a, **k: "minimax-cn") + monkeypatch.setattr(rp, "_get_model_config", lambda: {}) + monkeypatch.setenv("MINIMAX_CN_API_KEY", "test-minimax-cn-key") + monkeypatch.setenv("MINIMAX_CN_BASE_URL", "https://api.minimaxi.com/v1") + + resolved = rp.resolve_runtime_provider(requested="minimax-cn") + + assert resolved["provider"] == "minimax-cn" + assert resolved["api_mode"] == "anthropic_messages" + assert resolved["base_url"] == "https://api.minimaxi.com/anthropic" + + +def test_minimax_explicit_api_mode_respected(monkeypatch): + """Explicit api_mode config should override MiniMax auto-detection.""" + monkeypatch.setattr(rp, "resolve_provider", lambda *a, **k: "minimax") + monkeypatch.setattr(rp, "_get_model_config", lambda: {"api_mode": "chat_completions"}) + monkeypatch.setenv("MINIMAX_API_KEY", "test-minimax-key") + monkeypatch.delenv("MINIMAX_BASE_URL", raising=False) + + resolved = rp.resolve_runtime_provider(requested="minimax") + assert resolved["provider"] == "minimax" assert resolved["api_mode"] == "chat_completions" - assert resolved["base_url"] == "https://api.minimax.io/v1" def test_named_custom_provider_anthropic_api_mode(monkeypatch):