From 35ad72e2410de5dce4aad5a75becbd3f2d09fb1b Mon Sep 17 00:00:00 2001 From: octo-patch <266937838+octo-patch@users.noreply.github.com> Date: Wed, 8 Jul 2026 08:57:33 +0000 Subject: [PATCH 1/7] Add MiniMax M3 to provider defaults --- uncommon_route/providers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uncommon_route/providers.py b/uncommon_route/providers.py index 338c3de..9e79eb9 100644 --- a/uncommon_route/providers.py +++ b/uncommon_route/providers.py @@ -48,7 +48,7 @@ } PROVIDER_MODELS: dict[str, list[str]] = { - "minimax": ["minimax/minimax-m2.5"], + "minimax": ["minimax/minimax-m2.5", "minimax/minimax-m3"], "deepseek": ["deepseek/deepseek-chat", "deepseek/deepseek-reasoner"], "openai": ["openai/gpt-4o-mini", "openai/gpt-4o", "openai/gpt-5.2", "openai/gpt-5.2-codex", "openai/o1-mini", "openai/o3", "openai/o4-mini"], "anthropic": ["anthropic/claude-haiku-4.5", "anthropic/claude-sonnet-4.6", "anthropic/claude-opus-4.6"], From 93dc418e1d49c29a002cbdeb0cc5d70023771762 Mon Sep 17 00:00:00 2001 From: octo-patch <266937838+octo-patch@users.noreply.github.com> Date: Sun, 12 Jul 2026 14:42:20 +0800 Subject: [PATCH 2/7] Add MiniMax M2.7 to provider defaults --- uncommon_route/providers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uncommon_route/providers.py b/uncommon_route/providers.py index 9e79eb9..d3cf13d 100644 --- a/uncommon_route/providers.py +++ b/uncommon_route/providers.py @@ -48,7 +48,7 @@ } PROVIDER_MODELS: dict[str, list[str]] = { - "minimax": ["minimax/minimax-m2.5", "minimax/minimax-m3"], + "minimax": ["minimax/minimax-m2.5", "minimax/minimax-m3", "minimax/minimax-m2.7"], "deepseek": ["deepseek/deepseek-chat", "deepseek/deepseek-reasoner"], "openai": ["openai/gpt-4o-mini", "openai/gpt-4o", "openai/gpt-5.2", "openai/gpt-5.2-codex", "openai/o1-mini", "openai/o3", "openai/o4-mini"], "anthropic": ["anthropic/claude-haiku-4.5", "anthropic/claude-sonnet-4.6", "anthropic/claude-opus-4.6"], From ec695e63e28cc42f9d62fc3cf64e9edccec7cfb7 Mon Sep 17 00:00:00 2001 From: octo-patch <266937838+octo-patch@users.noreply.github.com> Date: Mon, 13 Jul 2026 13:20:22 +0800 Subject: [PATCH 3/7] Document and test MiniMax provider defaults --- tests/test_providers.py | 6 +++++- uncommon_route/providers.py | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/test_providers.py b/tests/test_providers.py index 352f7e2..d32662b 100644 --- a/tests/test_providers.py +++ b/tests/test_providers.py @@ -41,7 +41,11 @@ def test_add_provider_with_plan(self) -> None: cfg = add_provider("minimax", "eyJ-test", plan="coding-plan") entry = cfg.providers["minimax"] assert entry.plan == "coding-plan" - assert "minimax/minimax-m2.5" in entry.models + assert entry.models == [ + "minimax/minimax-m2.5", + "minimax/minimax-m3", + "minimax/minimax-m2.7", + ] def test_add_provider_custom_url(self) -> None: cfg = add_provider("openai", "sk-openai", base_url="https://my-proxy.com/v1") diff --git a/uncommon_route/providers.py b/uncommon_route/providers.py index d3cf13d..236e1ad 100644 --- a/uncommon_route/providers.py +++ b/uncommon_route/providers.py @@ -12,7 +12,11 @@ "minimax": { "api_key": "eyJ...", "base_url": "https://api.minimax.io/v1", - "models": ["minimax/minimax-m2.5"], + "models": [ + "minimax/minimax-m2.5", + "minimax/minimax-m3", + "minimax/minimax-m2.7" + ], "plan": "coding-plan" }, "deepseek": { From 097c150fe4704c5cc4196be6699db82e4b9715ad Mon Sep 17 00:00:00 2001 From: octo-patch <266937838+octo-patch@users.noreply.github.com> Date: Tue, 14 Jul 2026 17:21:21 +0800 Subject: [PATCH 4/7] Document MiniMax regional endpoints --- README.md | 17 ++++++++++ tests/test_anthropic_compat.py | 57 ++++++++++++++++++++++++++-------- 2 files changed, 61 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 018d432..5606d53 100644 --- a/README.md +++ b/README.md @@ -256,6 +256,23 @@ uncommon-route provider add google AIza... uncommon-route serve ``` +MiniMax uses the global OpenAI-compatible endpoint by default. Pass the China +endpoint explicitly when needed: + +```bash +uncommon-route provider add minimax eyJ-... +uncommon-route provider add minimax eyJ-... --url https://api.minimaxi.com/v1 +``` + +| Region | OpenAI-compatible base URL | Anthropic-compatible base URL | +|---|---|---| +| Global | `https://api.minimax.io/v1` | `https://api.minimax.io/anthropic` | +| China | `https://api.minimaxi.com/v1` | `https://api.minimaxi.com/anthropic` | + +Provider configuration stores the OpenAI-compatible base URL. Anthropic +requests use the corresponding `/anthropic` base and append `/v1/messages` +internally. + > UncommonRoute doesn't automatically read `OPENAI_API_KEY` or `ANTHROPIC_API_KEY`. Use `init`, a saved connection, or one of the manual setup paths above. ### Routing Modes diff --git a/tests/test_anthropic_compat.py b/tests/test_anthropic_compat.py index a1d462d..dcf434a 100644 --- a/tests/test_anthropic_compat.py +++ b/tests/test_anthropic_compat.py @@ -1931,9 +1931,18 @@ def handler(request: httpx.Request) -> httpx.Response: finally: asyncio.run(async_client.aclose()) + @pytest.mark.parametrize( + ("base_url", "expected_url"), + [ + ("https://api.minimax.io/v1", "https://api.minimax.io/anthropic/v1/messages"), + ("https://api.minimaxi.com/v1", "https://api.minimaxi.com/anthropic/v1/messages"), + ], + ) def test_messages_use_minimax_anthropic_endpoint_for_direct_provider( self, monkeypatch: pytest.MonkeyPatch, + base_url: str, + expected_url: str, ) -> None: captured: dict[str, object] = {} @@ -1947,7 +1956,7 @@ def handler(request: httpx.Request) -> httpx.Response: "id": "msg_minimax_byok", "type": "message", "role": "assistant", - "model": "minimax/minimax-m2.1", + "model": "minimax/minimax-m3", "content": [{"type": "text", "text": "ok"}], "stop_reason": "end_turn", "stop_sequence": None, @@ -1964,32 +1973,41 @@ def handler(request: httpx.Request) -> httpx.Response: "minimax": ProviderEntry( name="minimax", api_key="mm-key-123", - base_url="https://api.minimax.io/v1", - models=["minimax/minimax-m2.1"], + base_url=base_url, + models=["minimax/minimax-m3"], ), }) app = create_app( - upstream="https://api.commonstack.ai/v1", + upstream="https://primary.example/v1", providers_config=providers, ) client = TestClient(app, raise_server_exceptions=False) resp = client.post("/v1/messages", json={ - "model": "minimax/minimax-m2.1", + "model": "minimax/minimax-m3", "max_tokens": 32, "messages": [{"role": "user", "content": "hello"}], }) assert resp.status_code == 200 - assert captured["url"] == "https://api.minimax.io/anthropic/v1/messages" + assert captured["url"] == expected_url headers = captured["headers"] assert isinstance(headers, dict) assert headers["x-api-key"] == "mm-key-123" finally: asyncio.run(async_client.aclose()) - def test_chat_completions_keep_openai_transport_for_minimax_models( + @pytest.mark.parametrize( + ("base_url", "expected_url"), + [ + ("https://api.minimax.io/v1", "https://api.minimax.io/v1/chat/completions"), + ("https://api.minimaxi.com/v1", "https://api.minimaxi.com/v1/chat/completions"), + ], + ) + def test_chat_completions_use_minimax_openai_endpoint_for_direct_provider( self, monkeypatch: pytest.MonkeyPatch, + base_url: str, + expected_url: str, ) -> None: captured: dict[str, object] = {} @@ -2003,7 +2021,7 @@ def handler(request: httpx.Request) -> httpx.Response: "id": "chatcmpl_minimax", "object": "chat.completion", "created": 1, - "model": "minimax/minimax-m2.1", + "model": "minimax/minimax-m3", "choices": [{ "index": 0, "message": {"role": "assistant", "content": "done"}, @@ -2016,13 +2034,23 @@ def handler(request: httpx.Request) -> httpx.Response: async_client = httpx.AsyncClient(transport=httpx.MockTransport(handler)) monkeypatch.setattr("uncommon_route.proxy._get_client", lambda: async_client) - monkeypatch.setenv("UNCOMMON_ROUTE_API_KEY", "env-key-123") try: - app = create_app(upstream="https://api.commonstack.ai/v1") + providers = ProvidersConfig(providers={ + "minimax": ProviderEntry( + name="minimax", + api_key="mm-key-123", + base_url=base_url, + models=["minimax/minimax-m3"], + ), + }) + app = create_app( + upstream="https://primary.example/v1", + providers_config=providers, + ) client = TestClient(app, raise_server_exceptions=False) resp = client.post("/v1/chat/completions", json={ - "model": "minimax/minimax-m2.1", + "model": "minimax/minimax-m3", "max_tokens": 32, "messages": [{"role": "user", "content": "hello"}], }) @@ -2031,10 +2059,13 @@ def handler(request: httpx.Request) -> httpx.Response: assert resp.headers["x-uncommon-route-requested-transport"] == "openai-chat" assert resp.headers["x-uncommon-route-transport"] == "openai-chat" assert resp.headers["x-uncommon-route-transport-source"] == "ingress-policy" - assert captured["url"] == "https://api.commonstack.ai/v1/chat/completions" + assert captured["url"] == expected_url + headers = captured["headers"] + assert isinstance(headers, dict) + assert headers["authorization"] == "Bearer mm-key-123" body = captured["body"] assert isinstance(body, dict) - assert body["model"] == "minimax/minimax-m2.1" + assert body["model"] == "minimax/minimax-m3" finally: asyncio.run(async_client.aclose()) From 7ed31d93297da5c7c3c69e351a37ed0f48a32050 Mon Sep 17 00:00:00 2001 From: octo-patch <266937838+octo-patch@users.noreply.github.com> Date: Tue, 14 Jul 2026 18:27:46 +0800 Subject: [PATCH 5/7] Translate MiniMax model IDs for direct requests --- tests/test_anthropic_compat.py | 9 ++++++--- tests/test_providers.py | 6 ++++++ uncommon_route/providers.py | 11 +++++++++++ uncommon_route/proxy.py | 5 ++++- 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/tests/test_anthropic_compat.py b/tests/test_anthropic_compat.py index dcf434a..519b1ee 100644 --- a/tests/test_anthropic_compat.py +++ b/tests/test_anthropic_compat.py @@ -1956,7 +1956,7 @@ def handler(request: httpx.Request) -> httpx.Response: "id": "msg_minimax_byok", "type": "message", "role": "assistant", - "model": "minimax/minimax-m3", + "model": "MiniMax-M3", "content": [{"type": "text", "text": "ok"}], "stop_reason": "end_turn", "stop_sequence": None, @@ -1993,6 +1993,9 @@ def handler(request: httpx.Request) -> httpx.Response: headers = captured["headers"] assert isinstance(headers, dict) assert headers["x-api-key"] == "mm-key-123" + body = captured["body"] + assert isinstance(body, dict) + assert body["model"] == "MiniMax-M3" finally: asyncio.run(async_client.aclose()) @@ -2021,7 +2024,7 @@ def handler(request: httpx.Request) -> httpx.Response: "id": "chatcmpl_minimax", "object": "chat.completion", "created": 1, - "model": "minimax/minimax-m3", + "model": "MiniMax-M3", "choices": [{ "index": 0, "message": {"role": "assistant", "content": "done"}, @@ -2065,7 +2068,7 @@ def handler(request: httpx.Request) -> httpx.Response: assert headers["authorization"] == "Bearer mm-key-123" body = captured["body"] assert isinstance(body, dict) - assert body["model"] == "minimax/minimax-m3" + assert body["model"] == "MiniMax-M3" finally: asyncio.run(async_client.aclose()) diff --git a/tests/test_providers.py b/tests/test_providers.py index d32662b..528a840 100644 --- a/tests/test_providers.py +++ b/tests/test_providers.py @@ -11,6 +11,7 @@ ProviderEntry, add_provider, load_providers, + resolve_upstream_model, remove_provider, select_preferred_model, ) @@ -51,6 +52,11 @@ def test_add_provider_custom_url(self) -> None: cfg = add_provider("openai", "sk-openai", base_url="https://my-proxy.com/v1") assert cfg.providers["openai"].base_url == "https://my-proxy.com/v1" + def test_resolve_minimax_upstream_model_ids(self) -> None: + assert resolve_upstream_model("minimax", "minimax/minimax-m3") == "MiniMax-M3" + assert resolve_upstream_model("minimax", "minimax/minimax-m2.7") == "MiniMax-M2.7" + assert resolve_upstream_model("minimax", "minimax/unknown") == "minimax/unknown" + def test_add_provider_custom_models(self) -> None: cfg = add_provider( "custom", diff --git a/uncommon_route/providers.py b/uncommon_route/providers.py index 236e1ad..8adfccd 100644 --- a/uncommon_route/providers.py +++ b/uncommon_route/providers.py @@ -61,6 +61,17 @@ "moonshot": ["moonshot/kimi-k2.5"], } +UPSTREAM_MODEL_IDS: dict[str, dict[str, str]] = { + "minimax": { + "minimax/minimax-m3": "MiniMax-M3", + "minimax/minimax-m2.7": "MiniMax-M2.7", + }, +} + + +def resolve_upstream_model(provider_name: str, model_id: str) -> str: + return UPSTREAM_MODEL_IDS.get(provider_name, {}).get(model_id, model_id) + @dataclass class ProviderEntry: diff --git a/uncommon_route/proxy.py b/uncommon_route/proxy.py index fc341aa..4ef4b0c 100644 --- a/uncommon_route/proxy.py +++ b/uncommon_route/proxy.py @@ -106,6 +106,7 @@ ProvidersConfig, add_provider, load_providers, + resolve_upstream_model, remove_provider, verify_key, ) @@ -4635,7 +4636,9 @@ def _prepare_attempt(model_name: str) -> dict[str, Any]: attempt_headers[_ORIGINAL_MODEL_HEADER] = requested_model resolved_model = model_name - if not attempt_provider_entry: + if attempt_provider_entry: + resolved_model = resolve_upstream_model(attempt_provider_entry.name, model_name) + else: resolved_model = _mapper.resolve(model_name) attempt_upstream_body["model"] = resolved_model From ea3d0e4cb991fb6f0a0db594e809a957cc0f0393 Mon Sep 17 00:00:00 2001 From: octo-patch <266937838+octo-patch@users.noreply.github.com> Date: Tue, 14 Jul 2026 22:40:22 +0800 Subject: [PATCH 6/7] Resolve MiniMax IDs for semantic requests --- tests/test_proxy.py | 31 +++++++++++++++++++++++++++++++ uncommon_route/proxy.py | 2 +- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/tests/test_proxy.py b/tests/test_proxy.py index 08427f0..9387d04 100644 --- a/tests/test_proxy.py +++ b/tests/test_proxy.py @@ -17,6 +17,7 @@ from uncommon_route.model_experience import InMemoryModelExperienceStorage, ModelExperienceStore from uncommon_route.providers import ProviderEntry, ProvidersConfig from uncommon_route.proxy import ( + UpstreamSemanticCompressor, _extract_current_message, _extract_prompt, _normalize_reasoning_content_chunk, @@ -77,6 +78,36 @@ def _build_test_mapper(*model_ids: str) -> ModelMapper: return mapper +class TestUpstreamSemanticCompressor: + def test_direct_minimax_provider_uses_upstream_model_id(self) -> None: + providers = ProvidersConfig(providers={ + "minimax": ProviderEntry( + name="minimax", + api_key="test-key", + base_url="https://api.minimax.io/v1", + models=["minimax/minimax-m3"], + ), + }) + compressor = UpstreamSemanticCompressor( + upstream_chat="https://primary.example/v1/chat/completions", + primary_api_key="primary-test-key", + providers_config=providers, + model_mapper=_build_test_mapper("openai/gpt-4o-mini"), + composition_policy=CompositionPolicy(), + ) + + resolved = compressor._resolve_request( + "minimax/minimax-m3", + httpx.Request("POST", "https://local.example/v1/chat/completions"), + ) + + assert resolved is not None + target_url, headers, upstream_model = resolved + assert target_url == "https://api.minimax.io/v1/chat/completions" + assert headers["authorization"] == "Bearer test-key" + assert upstream_model == "MiniMax-M3" + + class TestPromptExtraction: def test_extract_prompt_ignores_claude_code_wrapper_blocks(self) -> None: prompt, system_prompt, max_tokens = _extract_prompt({ diff --git a/uncommon_route/proxy.py b/uncommon_route/proxy.py index 4ef4b0c..de6f775 100644 --- a/uncommon_route/proxy.py +++ b/uncommon_route/proxy.py @@ -915,7 +915,7 @@ def _resolve_request(self, model_id: str, request: Request) -> tuple[str, dict[s provider_entry = self._providers.get_for_model(model_id) if provider_entry and provider_entry.base_url: target_chat_url = f"{provider_entry.base_url.rstrip('/')}/chat/completions" - upstream_model = model_id + upstream_model = resolve_upstream_model(provider_entry.name, model_id) elif self._upstream_chat: target_chat_url = self._upstream_chat upstream_model = self._mapper.resolve(model_id) From 801912f68b730da53bf799a92e35c293fffb098d Mon Sep 17 00:00:00 2001 From: octo-patch <266937838+octo-patch@users.noreply.github.com> Date: Tue, 14 Jul 2026 22:44:20 +0800 Subject: [PATCH 7/7] Keep semantic regression target-specific --- tests/test_proxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_proxy.py b/tests/test_proxy.py index 9387d04..1b7043b 100644 --- a/tests/test_proxy.py +++ b/tests/test_proxy.py @@ -92,7 +92,7 @@ def test_direct_minimax_provider_uses_upstream_model_id(self) -> None: upstream_chat="https://primary.example/v1/chat/completions", primary_api_key="primary-test-key", providers_config=providers, - model_mapper=_build_test_mapper("openai/gpt-4o-mini"), + model_mapper=ModelMapper("https://primary.example/v1"), composition_policy=CompositionPolicy(), )