diff --git a/.github/workflows/openclaw-live-and-e2e-checks-reusable.yml b/.github/workflows/openclaw-live-and-e2e-checks-reusable.yml
index ede7ef5f6417..a6bd4bcac3d9 100644
--- a/.github/workflows/openclaw-live-and-e2e-checks-reusable.yml
+++ b/.github/workflows/openclaw-live-and-e2e-checks-reusable.yml
@@ -1953,7 +1953,7 @@ jobs:
profiles: stable full
- suite_id: native-live-src-gateway-profiles-minimax
label: Native live gateway profiles MiniMax
- command: OPENCLAW_LIVE_GATEWAY_PROVIDERS=minimax,minimax-portal OPENCLAW_LIVE_GATEWAY_MODELS=minimax/MiniMax-M2.7,minimax-portal/MiniMax-M2.7 OPENCLAW_LIVE_GATEWAY_MAX_MODELS=2 node .release-harness/scripts/test-live-shard.mjs native-live-src-gateway-profiles
+ command: OPENCLAW_LIVE_GATEWAY_PROVIDERS=minimax,minimax-portal OPENCLAW_LIVE_GATEWAY_MODELS=minimax/MiniMax-M3,minimax-portal/MiniMax-M3 OPENCLAW_LIVE_GATEWAY_MAX_MODELS=2 node .release-harness/scripts/test-live-shard.mjs native-live-src-gateway-profiles
timeout_minutes: 60
profile_env_only: false
profiles: stable full
@@ -2252,7 +2252,7 @@ jobs:
profiles: stable full
- suite_id: live-gateway-minimax-docker
label: Docker live gateway MiniMax
- command: OPENCLAW_LIVE_GATEWAY_PROVIDERS=minimax,minimax-portal OPENCLAW_LIVE_GATEWAY_MODELS=minimax/MiniMax-M2.7,minimax-portal/MiniMax-M2.7 OPENCLAW_LIVE_GATEWAY_MAX_MODELS=1 OPENCLAW_LIVE_GATEWAY_STEP_TIMEOUT_MS=90000 OPENCLAW_LIVE_GATEWAY_MODEL_TIMEOUT_MS=180000 OPENCLAW_LIVE_DOCKER_REPO_ROOT="$GITHUB_WORKSPACE" timeout --foreground --kill-after=30s 35m bash .release-harness/scripts/test-live-gateway-models-docker.sh
+ command: OPENCLAW_LIVE_GATEWAY_PROVIDERS=minimax,minimax-portal OPENCLAW_LIVE_GATEWAY_MODELS=minimax/MiniMax-M3,minimax-portal/MiniMax-M3 OPENCLAW_LIVE_GATEWAY_MAX_MODELS=1 OPENCLAW_LIVE_GATEWAY_STEP_TIMEOUT_MS=90000 OPENCLAW_LIVE_GATEWAY_MODEL_TIMEOUT_MS=180000 OPENCLAW_LIVE_DOCKER_REPO_ROOT="$GITHUB_WORKSPACE" timeout --foreground --kill-after=30s 35m bash .release-harness/scripts/test-live-gateway-models-docker.sh
timeout_minutes: 40
profile_env_only: false
profiles: stable full
diff --git a/docs/concepts/model-providers.md b/docs/concepts/model-providers.md
index 021210d06ec4..8af22bee223e 100644
--- a/docs/concepts/model-providers.md
+++ b/docs/concepts/model-providers.md
@@ -303,7 +303,7 @@ See [/providers/kilocode](/providers/kilocode) for setup details.
| Hugging Face Inference | `huggingface` | `HUGGINGFACE_HUB_TOKEN` or `HF_TOKEN` | `huggingface/deepseek-ai/DeepSeek-R1` |
| Kilo Gateway | `kilocode` | `KILOCODE_API_KEY` | `kilocode/kilo/auto` |
| Kimi Coding | `kimi` | `KIMI_API_KEY` or `KIMICODE_API_KEY` | `kimi/kimi-for-coding` |
-| MiniMax | `minimax` / `minimax-portal` | `MINIMAX_API_KEY` / `MINIMAX_OAUTH_TOKEN` | `minimax/MiniMax-M2.7` |
+| MiniMax | `minimax` / `minimax-portal` | `MINIMAX_API_KEY` / `MINIMAX_OAUTH_TOKEN` | `minimax/MiniMax-M3` |
| Mistral | `mistral` | `MISTRAL_API_KEY` | `mistral/mistral-large-latest` |
| Moonshot | `moonshot` | `MOONSHOT_API_KEY` | `moonshot/kimi-k2.6` |
| NVIDIA | `nvidia` | `NVIDIA_API_KEY` | `nvidia/nvidia/nemotron-3-super-120b-a12b` |
@@ -331,7 +331,7 @@ See [/providers/kilocode](/providers/kilocode) for setup details.
Gemini-backed refs follow the same proxy-Gemini sanitation path; `kilocode/kilo/auto` and other proxy-reasoning-unsupported refs skip proxy reasoning injection.
- API-key onboarding writes explicit text-only M2.7 chat model definitions; image understanding stays on the plugin-owned `MiniMax-VL-01` media provider.
+ API-key onboarding writes explicit M3 and M2.7 chat model definitions; image understanding stays on the plugin-owned `MiniMax-VL-01` media provider.
Model ids use a `nvidia//` namespace (for example `nvidia/nvidia/nemotron-...` alongside `nvidia/moonshotai/kimi-k2.5`); pickers preserve the literal `/` composition while the canonical key sent to the API stays single-prefixed.
@@ -537,7 +537,7 @@ On MiniMax's Anthropic-compatible streaming path, OpenClaw disables thinking by
Plugin-owned capability split:
-- Text/chat defaults stay on `minimax/MiniMax-M2.7`
+- Text/chat defaults stay on `minimax/MiniMax-M3`
- Image generation is `minimax/image-01` or `minimax-portal/image-01`
- Image understanding is plugin-owned `MiniMax-VL-01` on both MiniMax auth paths
- Web search stays on provider id `minimax`
diff --git a/docs/gateway/config-tools.md b/docs/gateway/config-tools.md
index 1dadc87d1d4e..472b2d284ee7 100644
--- a/docs/gateway/config-tools.md
+++ b/docs/gateway/config-tools.md
@@ -645,14 +645,14 @@ Interactive custom-provider onboarding infers image input for common vision mode
See [Local Models](/gateway/local-models). TL;DR: run a large local model via LM Studio Responses API on serious hardware; keep hosted models merged for fallback.
-
+
```json5
{
agents: {
defaults: {
- model: { primary: "minimax/MiniMax-M2.7" },
+ model: { primary: "minimax/MiniMax-M3" },
models: {
- "minimax/MiniMax-M2.7": { alias: "Minimax" },
+ "minimax/MiniMax-M3": { alias: "Minimax" },
},
},
},
@@ -665,12 +665,12 @@ Interactive custom-provider onboarding infers image input for common vision mode
api: "anthropic-messages",
models: [
{
- id: "MiniMax-M2.7",
- name: "MiniMax M2.7",
+ id: "MiniMax-M3",
+ name: "MiniMax M3",
reasoning: true,
- input: ["text"],
- cost: { input: 0.3, output: 1.2, cacheRead: 0.06, cacheWrite: 0.375 },
- contextWindow: 204800,
+ input: ["text", "image"],
+ cost: { input: 0.6, output: 2.4, cacheRead: 0.12, cacheWrite: 0 },
+ contextWindow: 1000000,
maxTokens: 131072,
},
],
@@ -680,7 +680,7 @@ Interactive custom-provider onboarding infers image input for common vision mode
}
```
- Set `MINIMAX_API_KEY`. Shortcuts: `openclaw onboard --auth-choice minimax-global-api` or `openclaw onboard --auth-choice minimax-cn-api`. The model catalog defaults to M2.7 only. On the Anthropic-compatible streaming path, OpenClaw disables MiniMax thinking by default unless you explicitly set `thinking` yourself. `/fast on` or `params.fastMode: true` rewrites `MiniMax-M2.7` to `MiniMax-M2.7-highspeed`.
+ Set `MINIMAX_API_KEY`. Shortcuts: `openclaw onboard --auth-choice minimax-global-api` or `openclaw onboard --auth-choice minimax-cn-api`. The model catalog defaults to M3 and also includes the M2.7 variants. On the Anthropic-compatible streaming path, OpenClaw disables MiniMax thinking by default unless you explicitly set `thinking` yourself. `/fast on` or `params.fastMode: true` rewrites `MiniMax-M2.7` to `MiniMax-M2.7-highspeed`.
diff --git a/docs/help/faq-models.md b/docs/help/faq-models.md
index e2dfcc31c51a..791b2c1236f4 100644
--- a/docs/help/faq-models.md
+++ b/docs/help/faq-models.md
@@ -215,7 +215,7 @@ troubleshooting, see the main [FAQ](/help/faq).
-
+
This means the **provider isn't configured** (no MiniMax provider config or auth
profile was found), so the model can't be resolved.
@@ -227,8 +227,9 @@ troubleshooting, see the main [FAQ](/help/faq).
(`MINIMAX_API_KEY` for `minimax`, `MINIMAX_OAUTH_TOKEN` or stored MiniMax
OAuth for `minimax-portal`).
3. Use the exact model id (case-sensitive) for your auth path:
- `minimax/MiniMax-M2.7` or `minimax/MiniMax-M2.7-highspeed` for API-key
- setup, or `minimax-portal/MiniMax-M2.7` /
+ `minimax/MiniMax-M3`, `minimax/MiniMax-M2.7`, or
+ `minimax/MiniMax-M2.7-highspeed` for API-key setup, or
+ `minimax-portal/MiniMax-M3`, `minimax-portal/MiniMax-M2.7`, or
`minimax-portal/MiniMax-M2.7-highspeed` for OAuth setup.
4. Run:
@@ -253,9 +254,9 @@ troubleshooting, see the main [FAQ](/help/faq).
env: { MINIMAX_API_KEY: "sk-...", OPENAI_API_KEY: "sk-..." },
agents: {
defaults: {
- model: { primary: "minimax/MiniMax-M2.7" },
+ model: { primary: "minimax/MiniMax-M3" },
models: {
- "minimax/MiniMax-M2.7": { alias: "minimax" },
+ "minimax/MiniMax-M3": { alias: "minimax" },
"openai/gpt-5.5": { alias: "gpt" },
},
},
diff --git a/docs/help/testing-live.md b/docs/help/testing-live.md
index de42bba84070..a5d0ca913d94 100644
--- a/docs/help/testing-live.md
+++ b/docs/help/testing-live.md
@@ -73,7 +73,7 @@ Live tests are split into two layers so we can isolate failures:
- `pnpm test:live` (or `OPENCLAW_LIVE_TEST=1` if invoking Vitest directly)
- Set `OPENCLAW_LIVE_MODELS=modern`, `small`, or `all` (alias for modern) to actually run this suite; otherwise it skips to keep `pnpm test:live` focused on gateway smoke
- How to select models:
- - `OPENCLAW_LIVE_MODELS=modern` to run the modern allowlist (Opus/Sonnet 4.6+, GPT-5.2 + Codex, Gemini 3, DeepSeek V4, GLM 4.7, MiniMax M2.7, Grok 4.3)
+ - `OPENCLAW_LIVE_MODELS=modern` to run the modern allowlist (Opus/Sonnet 4.6+, GPT-5.2 + Codex, Gemini 3, DeepSeek V4, GLM 4.7, MiniMax M3, Grok 4.3)
- `OPENCLAW_LIVE_MODELS=small` to run the constrained small-model allowlist (Qwen 8B/9B local-compatible routes, Ollama Gemma, OpenRouter Qwen/GLM, and Z.AI GLM)
- `OPENCLAW_LIVE_MODELS=all` is an alias for the modern allowlist
- or `OPENCLAW_LIVE_MODELS="openai/gpt-5.5,anthropic/claude-opus-4-6,..."` (comma allowlist)
@@ -109,7 +109,7 @@ Live tests are split into two layers so we can isolate failures:
- How to enable:
- `pnpm test:live` (or `OPENCLAW_LIVE_TEST=1` if invoking Vitest directly)
- How to select models:
- - Default: modern allowlist (Opus/Sonnet 4.6+, GPT-5.2 + Codex, Gemini 3, DeepSeek V4, GLM 4.7, MiniMax M2.7, Grok 4.3)
+ - Default: modern allowlist (Opus/Sonnet 4.6+, GPT-5.2 + Codex, Gemini 3, DeepSeek V4, GLM 4.7, MiniMax M3, Grok 4.3)
- `OPENCLAW_LIVE_GATEWAY_MODELS=all` is an alias for the modern allowlist
- Or set `OPENCLAW_LIVE_GATEWAY_MODELS="provider/model"` (or comma list) to narrow
- Modern/all gateway sweeps default to a curated high-signal cap; set `OPENCLAW_LIVE_GATEWAY_MAX_MODELS=0` for an exhaustive modern sweep or a positive number for a smaller cap.
@@ -351,7 +351,7 @@ Narrow, explicit allowlists are fastest and least flaky:
- `OPENCLAW_LIVE_GATEWAY_MODELS="openai/gpt-5.5" pnpm test:live src/gateway/gateway-models.profiles.live.test.ts`
- Tool calling across several providers:
- - `OPENCLAW_LIVE_GATEWAY_MODELS="openai/gpt-5.5,anthropic/claude-opus-4-6,google/gemini-3-flash-preview,deepseek/deepseek-v4-flash,zai/glm-5.1,minimax/MiniMax-M2.7" pnpm test:live src/gateway/gateway-models.profiles.live.test.ts`
+ - `OPENCLAW_LIVE_GATEWAY_MODELS="openai/gpt-5.5,anthropic/claude-opus-4-6,google/gemini-3-flash-preview,deepseek/deepseek-v4-flash,zai/glm-5.1,minimax/MiniMax-M3" pnpm test:live src/gateway/gateway-models.profiles.live.test.ts`
- Google focus (Gemini API key + Antigravity):
- Gemini (API key): `OPENCLAW_LIVE_GATEWAY_MODELS="google/gemini-3-flash-preview" pnpm test:live src/gateway/gateway-models.profiles.live.test.ts`
@@ -385,10 +385,10 @@ This is the "common models" run we expect to keep working:
- Google (Antigravity): `google-antigravity/claude-opus-4-6-thinking` and `google-antigravity/gemini-3-flash`
- DeepSeek: `deepseek/deepseek-v4-flash` and `deepseek/deepseek-v4-pro`
- Z.AI (GLM): `zai/glm-5.1`
-- MiniMax: `minimax/MiniMax-M2.7`
+- MiniMax: `minimax/MiniMax-M3`
Run gateway smoke with tools + image:
-`OPENCLAW_LIVE_GATEWAY_MODELS="openai/gpt-5.5,anthropic/claude-opus-4-6,google/gemini-3.1-pro-preview,google/gemini-3-flash-preview,google-antigravity/claude-opus-4-6-thinking,google-antigravity/gemini-3-flash,deepseek/deepseek-v4-flash,zai/glm-5.1,minimax/MiniMax-M2.7" pnpm test:live src/gateway/gateway-models.profiles.live.test.ts`
+`OPENCLAW_LIVE_GATEWAY_MODELS="openai/gpt-5.5,anthropic/claude-opus-4-6,google/gemini-3.1-pro-preview,google/gemini-3-flash-preview,google-antigravity/claude-opus-4-6-thinking,google-antigravity/gemini-3-flash,deepseek/deepseek-v4-flash,zai/glm-5.1,minimax/MiniMax-M3" pnpm test:live src/gateway/gateway-models.profiles.live.test.ts`
### Baseline: tool calling (Read + optional Exec)
@@ -399,7 +399,7 @@ Pick at least one per provider family:
- Google: `google/gemini-3-flash-preview` (or `google/gemini-3.1-pro-preview`)
- DeepSeek: `deepseek/deepseek-v4-flash`
- Z.AI (GLM): `zai/glm-5.1`
-- MiniMax: `minimax/MiniMax-M2.7`
+- MiniMax: `minimax/MiniMax-M3`
Optional additional coverage (nice to have):
diff --git a/docs/providers/minimax.md b/docs/providers/minimax.md
index 83d3d353918f..ca871408dd3c 100644
--- a/docs/providers/minimax.md
+++ b/docs/providers/minimax.md
@@ -6,7 +6,7 @@ read_when:
title: "MiniMax"
---
-OpenClaw's MiniMax provider defaults to **MiniMax M2.7**.
+OpenClaw's MiniMax provider defaults to **MiniMax M3**.
MiniMax also provides:
@@ -26,7 +26,8 @@ Provider split:
| Model | Type | Description |
| ------------------------ | ---------------- | ---------------------------------------- |
-| `MiniMax-M2.7` | Chat (reasoning) | Default hosted reasoning model |
+| `MiniMax-M3` | Chat (reasoning) | Default hosted reasoning model |
+| `MiniMax-M2.7` | Chat (reasoning) | Previous hosted reasoning model |
| `MiniMax-M2.7-highspeed` | Chat (reasoning) | Faster M2.7 reasoning tier |
| `MiniMax-VL-01` | Vision | Image understanding model |
| `image-01` | Image generation | Text-to-image and image-to-image editing |
@@ -79,7 +80,7 @@ Choose your preferred auth method and follow the setup steps.
- OAuth setups use the `minimax-portal` provider id. Model refs follow the form `minimax-portal/MiniMax-M2.7`.
+ OAuth setups use the `minimax-portal` provider id. Model refs follow the form `minimax-portal/MiniMax-M3`.
@@ -131,7 +132,7 @@ Choose your preferred auth method and follow the setup steps.
```json5
{
env: { MINIMAX_API_KEY: "sk-..." },
- agents: { defaults: { model: { primary: "minimax/MiniMax-M2.7" } } },
+ agents: { defaults: { model: { primary: "minimax/MiniMax-M3" } } },
models: {
mode: "merge",
providers: {
@@ -140,6 +141,15 @@ Choose your preferred auth method and follow the setup steps.
apiKey: "${MINIMAX_API_KEY}",
api: "anthropic-messages",
models: [
+ {
+ id: "MiniMax-M3",
+ name: "MiniMax M3",
+ reasoning: true,
+ input: ["text", "image"],
+ cost: { input: 0.6, output: 2.4, cacheRead: 0.12, cacheWrite: 0 },
+ contextWindow: 1000000,
+ maxTokens: 131072,
+ },
{
id: "MiniMax-M2.7",
name: "MiniMax M2.7",
@@ -170,7 +180,7 @@ Choose your preferred auth method and follow the setup steps.
- API-key setups use the `minimax` provider id. Model refs follow the form `minimax/MiniMax-M2.7`.
+ API-key setups use the `minimax` provider id. Model refs follow the form `minimax/MiniMax-M3`.
@@ -243,9 +253,10 @@ through the CN endpoint; the default global endpoint is
`https://api.minimax.io`.
When onboarding or API-key setup writes explicit `models.providers.minimax`
-entries, OpenClaw materializes `MiniMax-M2.7` and
-`MiniMax-M2.7-highspeed` as text-only chat models. Image understanding is
-exposed separately through the plugin-owned `MiniMax-VL-01` media provider.
+entries, OpenClaw materializes `MiniMax-M3`, `MiniMax-M2.7`, and
+`MiniMax-M2.7-highspeed` as chat models. M3 advertises text and image input;
+image understanding remains exposed separately through the plugin-owned
+`MiniMax-VL-01` media provider.
See [Image Generation](/tools/image-generation) for shared tool parameters, provider selection, and failover behavior.
@@ -353,7 +364,7 @@ catalog:
| `minimax-portal` | `MiniMax-VL-01` |
That is why automatic media routing can use MiniMax image understanding even
-when the bundled text-provider catalog still shows text-only M2.7 chat refs.
+when the bundled text-provider catalog also includes M3 image-capable chat refs.
### Web search
@@ -437,12 +448,12 @@ See [MiniMax Search](/tools/minimax-search) for full web search configuration an
- Model refs follow the auth path:
- API-key setup: `minimax/`
- OAuth setup: `minimax-portal/`
-- Default chat model: `MiniMax-M2.7`
-- Alternate chat model: `MiniMax-M2.7-highspeed`
-- Onboarding and direct API-key setup write text-only model definitions for both M2.7 variants
+- Default chat model: `MiniMax-M3`
+- Alternate chat models: `MiniMax-M2.7`, `MiniMax-M2.7-highspeed`
+- Onboarding and direct API-key setup write model definitions for M3 and both M2.7 variants
- Image understanding uses the plugin-owned `MiniMax-VL-01` media provider
- Update pricing values in `models.json` if you need exact cost tracking
-- Use `openclaw models list` to confirm the current provider id, then switch with `openclaw models set minimax/MiniMax-M2.7` or `openclaw models set minimax-portal/MiniMax-M2.7`
+- Use `openclaw models list` to confirm the current provider id, then switch with `openclaw models set minimax/MiniMax-M3` or `openclaw models set minimax-portal/MiniMax-M3`
Referral link for MiniMax Coding Plan (10% off): [MiniMax Coding Plan](https://platform.minimax.io/subscribe/coding-plan?code=DbXJTRClnb&source=link)
@@ -455,7 +466,7 @@ See [Model providers](/concepts/model-providers) for provider rules.
## Troubleshooting
-
+
This usually means the **MiniMax provider is not configured** (no matching provider entry and no MiniMax auth profile/env key found). A fix for this detection is in **2026.1.12**. Fix by:
- Upgrading to **2026.1.12** (or run from source `main`), then restarting the gateway.
@@ -465,8 +476,8 @@ See [Model providers](/concepts/model-providers) for provider rules.
Make sure the model id is **case-sensitive**:
- - API-key path: `minimax/MiniMax-M2.7` or `minimax/MiniMax-M2.7-highspeed`
- - OAuth path: `minimax-portal/MiniMax-M2.7` or `minimax-portal/MiniMax-M2.7-highspeed`
+ - API-key path: `minimax/MiniMax-M3`, `minimax/MiniMax-M2.7`, or `minimax/MiniMax-M2.7-highspeed`
+ - OAuth path: `minimax-portal/MiniMax-M3`, `minimax-portal/MiniMax-M2.7`, or `minimax-portal/MiniMax-M2.7-highspeed`
Then recheck with:
diff --git a/docs/reference/wizard.md b/docs/reference/wizard.md
index f385567bc2bb..6a2fa4a89cbf 100644
--- a/docs/reference/wizard.md
+++ b/docs/reference/wizard.md
@@ -47,7 +47,7 @@ For a high-level overview, see [Onboarding (CLI)](/start/wizard).
- More detail: [Vercel AI Gateway](/providers/vercel-ai-gateway)
- **Cloudflare AI Gateway**: prompts for Account ID, Gateway ID, and `CLOUDFLARE_AI_GATEWAY_API_KEY`.
- More detail: [Cloudflare AI Gateway](/providers/cloudflare-ai-gateway)
- - **MiniMax**: config is auto-written; hosted default is `MiniMax-M2.7`.
+ - **MiniMax**: config is auto-written; hosted default is `MiniMax-M3`.
API-key setup uses `minimax/...`, and OAuth setup uses
`minimax-portal/...`.
- More detail: [MiniMax](/providers/minimax)
diff --git a/docs/start/wizard-cli-reference.md b/docs/start/wizard-cli-reference.md
index 29ee25327dc4..7b04eb98d9ec 100644
--- a/docs/start/wizard-cli-reference.md
+++ b/docs/start/wizard-cli-reference.md
@@ -182,7 +182,7 @@ What you set:
More detail: [Cloudflare AI Gateway](/providers/cloudflare-ai-gateway).
- Config is auto-written. Hosted default is `MiniMax-M2.7`; API-key setup uses
+ Config is auto-written. Hosted default is `MiniMax-M3`; API-key setup uses
`minimax/...`, and OAuth setup uses `minimax-portal/...`.
More detail: [MiniMax](/providers/minimax).
diff --git a/extensions/minimax/index.test.ts b/extensions/minimax/index.test.ts
index 4f2c6415f878..f3b8a821ff71 100644
--- a/extensions/minimax/index.test.ts
+++ b/extensions/minimax/index.test.ts
@@ -6,7 +6,7 @@ import {
registerProviderPlugin,
requireRegisteredProvider,
} from "openclaw/plugin-sdk/plugin-test-runtime";
-import { describe, expect, it, vi } from "vitest";
+import { afterEach, describe, expect, it, vi } from "vitest";
import { registerMinimaxProviders } from "./provider-registration.js";
import { createMiniMaxWebSearchProvider } from "./src/minimax-web-search-provider.js";
@@ -26,6 +26,10 @@ const minimaxProviderPlugin = {
},
};
+afterEach(() => {
+ vi.unstubAllEnvs();
+});
+
describe("minimax provider hooks", () => {
it("declares CN provider auth aliases in the manifest", () => {
const pluginJson = JSON.parse(
@@ -91,7 +95,7 @@ describe("minimax provider hooks", () => {
hint: "Global endpoint - api.minimax.io",
choiceId: "minimax-global-api",
groupId: "minimax",
- groupHint: "M2.7 (recommended)",
+ groupHint: "M3 (recommended)",
},
{
id: "api-cn",
@@ -99,7 +103,7 @@ describe("minimax provider hooks", () => {
hint: "CN endpoint - api.minimaxi.com",
choiceId: "minimax-cn-api",
groupId: "minimax",
- groupHint: "M2.7 (recommended)",
+ groupHint: "M3 (recommended)",
},
]);
@@ -119,7 +123,7 @@ describe("minimax provider hooks", () => {
hint: "Global endpoint - api.minimax.io",
choiceId: "minimax-global-oauth",
groupId: "minimax",
- groupHint: "M2.7 (recommended)",
+ groupHint: "M3 (recommended)",
},
{
id: "oauth-cn",
@@ -127,7 +131,7 @@ describe("minimax provider hooks", () => {
hint: "CN endpoint - api.minimaxi.com",
choiceId: "minimax-cn-oauth",
groupId: "minimax",
- groupHint: "M2.7 (recommended)",
+ groupHint: "M3 (recommended)",
},
]);
});
@@ -173,7 +177,7 @@ describe("minimax provider hooks", () => {
});
});
- it("keeps M2.7 on the Anthropic Messages route used by the empty-history guard", async () => {
+ it("lists M3 on the Anthropic Messages route used by the empty-history guard", async () => {
const { providers } = await registerProviderPlugin({
plugin: minimaxProviderPlugin,
id: "minimax",
@@ -193,13 +197,55 @@ describe("minimax provider hooks", () => {
expect(provider?.api).toBe("anthropic-messages");
expect(provider?.authHeader).toBe(true);
expect(provider?.baseUrl).toBe("https://api.minimax.io/anthropic");
- const model = provider?.models.find((entry: { id?: string }) => entry.id === "MiniMax-M2.7");
- expect(model?.id).toBe("MiniMax-M2.7");
- expect(model?.input).toEqual(["text"]);
- expect(model?.name).toBe("MiniMax M2.7");
+ const model = provider?.models.find((entry: { id?: string }) => entry.id === "MiniMax-M3");
+ expect(model?.id).toBe("MiniMax-M3");
+ expect(model?.input).toEqual(["text", "image"]);
+ expect(model?.name).toBe("MiniMax M3");
expect(model?.reasoning).toBe(true);
});
+ it("resolves M3 through the dynamic model hook before agent discovery", async () => {
+ const { providers } = await registerProviderPlugin({
+ plugin: minimaxProviderPlugin,
+ id: "minimax",
+ name: "MiniMax Provider",
+ });
+ const apiProvider = requireRegisteredProvider(providers, "minimax");
+
+ const model = apiProvider.resolveDynamicModel?.({
+ provider: "minimax",
+ modelId: "MiniMax-M3",
+ providerConfig: {},
+ } as never);
+
+ expect(model).toMatchObject({
+ provider: "minimax",
+ id: "MiniMax-M3",
+ api: "anthropic-messages",
+ baseUrl: "https://api.minimax.io/anthropic",
+ input: ["text", "image"],
+ contextWindow: 1_000_000,
+ });
+ });
+
+ it("keeps MINIMAX_API_HOST endpoint overrides on dynamic M3 resolution", async () => {
+ vi.stubEnv("MINIMAX_API_HOST", "https://api.minimaxi.com");
+ const { providers } = await registerProviderPlugin({
+ plugin: minimaxProviderPlugin,
+ id: "minimax",
+ name: "MiniMax Provider",
+ });
+ const apiProvider = requireRegisteredProvider(providers, "minimax");
+
+ const model = apiProvider.resolveDynamicModel?.({
+ provider: "minimax",
+ modelId: "MiniMax-M3",
+ providerConfig: {},
+ } as never);
+
+ expect(model?.baseUrl).toBe("https://api.minimaxi.com/anthropic");
+ });
+
it("owns fast-mode stream wrapping for MiniMax transports", async () => {
const { providers } = await registerProviderPlugin({
plugin: minimaxProviderPlugin,
diff --git a/extensions/minimax/model-definitions.test.ts b/extensions/minimax/model-definitions.test.ts
index cf014420fbf4..cb7a39e9d29d 100644
--- a/extensions/minimax/model-definitions.test.ts
+++ b/extensions/minimax/model-definitions.test.ts
@@ -7,40 +7,43 @@ import {
MINIMAX_API_COST,
MINIMAX_API_HIGHSPEED_COST,
MINIMAX_HOSTED_MODEL_ID,
+ MINIMAX_M27_API_COST,
MINIMAX_M25_API_COST,
MINIMAX_M25_API_HIGHSPEED_COST,
+ MINIMAX_M3_CONTEXT_WINDOW,
} from "./model-definitions.js";
describe("minimax model definitions", () => {
- it("uses M2.7 as default hosted model", () => {
- expect(MINIMAX_HOSTED_MODEL_ID).toBe("MiniMax-M2.7");
+ it("uses M3 as default hosted model", () => {
+ expect(MINIMAX_HOSTED_MODEL_ID).toBe("MiniMax-M3");
});
- it("uses the higher upstream MiniMax context and token defaults", () => {
+ it("uses the current upstream MiniMax context, token, and pricing defaults", () => {
+ expect(MINIMAX_M3_CONTEXT_WINDOW).toBe(1_000_000);
expect(DEFAULT_MINIMAX_CONTEXT_WINDOW).toBe(204800);
expect(DEFAULT_MINIMAX_MAX_TOKENS).toBe(131072);
expect(MINIMAX_API_COST).toEqual({
- input: 0.3,
- output: 1.2,
- cacheRead: 0.06,
- cacheWrite: 0.375,
+ input: 0.6,
+ output: 2.4,
+ cacheRead: 0.12,
+ cacheWrite: 0,
});
});
- it("builds catalog model with name and reasoning from catalog for M2.7", () => {
+ it("builds catalog model with M3 metadata from the catalog", () => {
const model = buildMinimaxModelDefinition({
- id: "MiniMax-M2.7",
+ id: "MiniMax-M3",
cost: MINIMAX_API_COST,
- contextWindow: DEFAULT_MINIMAX_CONTEXT_WINDOW,
+ contextWindow: MINIMAX_M3_CONTEXT_WINDOW,
maxTokens: DEFAULT_MINIMAX_MAX_TOKENS,
});
expect(model).toEqual({
- contextWindow: DEFAULT_MINIMAX_CONTEXT_WINDOW,
+ contextWindow: MINIMAX_M3_CONTEXT_WINDOW,
cost: MINIMAX_API_COST,
- id: "MiniMax-M2.7",
- input: ["text"],
+ id: "MiniMax-M3",
+ input: ["text", "image"],
maxTokens: DEFAULT_MINIMAX_MAX_TOKENS,
- name: "MiniMax M2.7",
+ name: "MiniMax M3",
reasoning: true,
});
});
@@ -63,12 +66,12 @@ describe("minimax model definitions", () => {
});
});
- it("builds API model definition with standard cost for M2.7", () => {
- const model = buildMinimaxApiModelDefinition("MiniMax-M2.7");
+ it("builds API model definition with standard cost for M3", () => {
+ const model = buildMinimaxApiModelDefinition("MiniMax-M3");
expect(model.cost).toEqual(MINIMAX_API_COST);
- expect(model.contextWindow).toBe(DEFAULT_MINIMAX_CONTEXT_WINDOW);
+ expect(model.contextWindow).toBe(MINIMAX_M3_CONTEXT_WINDOW);
expect(model.maxTokens).toBe(DEFAULT_MINIMAX_MAX_TOKENS);
- expect(model.input).toEqual(["text"]);
+ expect(model.input).toEqual(["text", "image"]);
});
it("falls back to generated name for unknown model id", () => {
@@ -77,6 +80,13 @@ describe("minimax model definitions", () => {
expect(model.reasoning).toBe(false);
});
+ it("keeps M2.7 on its existing price and text-only metadata", () => {
+ const model = buildMinimaxApiModelDefinition("MiniMax-M2.7");
+ expect(model.input).toEqual(["text"]);
+ expect(model.cost).toEqual(MINIMAX_M27_API_COST);
+ expect(model.contextWindow).toBe(DEFAULT_MINIMAX_CONTEXT_WINDOW);
+ });
+
it("keeps M2.7 text-only on the Anthropic-compatible chat path", () => {
const model = buildMinimaxApiModelDefinition("MiniMax-M2.7");
expect(model.input).toEqual(["text"]);
diff --git a/extensions/minimax/model-definitions.ts b/extensions/minimax/model-definitions.ts
index 2ce490c10cb2..55288d41978b 100644
--- a/extensions/minimax/model-definitions.ts
+++ b/extensions/minimax/model-definitions.ts
@@ -7,9 +7,16 @@ export const MINIMAX_CN_API_BASE_URL = "https://api.minimaxi.com/anthropic";
export const MINIMAX_HOSTED_MODEL_ID = MINIMAX_DEFAULT_MODEL_ID;
export const MINIMAX_HOSTED_MODEL_REF = `minimax/${MINIMAX_HOSTED_MODEL_ID}`;
export const DEFAULT_MINIMAX_CONTEXT_WINDOW = 204800;
+export const MINIMAX_M3_CONTEXT_WINDOW = 1_000_000;
export const DEFAULT_MINIMAX_MAX_TOKENS = 131072;
export const MINIMAX_API_COST = {
+ input: 0.6,
+ output: 2.4,
+ cacheRead: 0.12,
+ cacheWrite: 0,
+};
+export const MINIMAX_M27_API_COST = {
input: 0.3,
output: 1.2,
cacheRead: 0.06,
@@ -49,6 +56,9 @@ export const MINIMAX_LM_STUDIO_COST = {
type MinimaxCatalogId = keyof typeof MINIMAX_TEXT_MODEL_CATALOG;
export function resolveMinimaxApiCost(modelId: string): ModelDefinitionConfig["cost"] {
+ if (modelId === "MiniMax-M2.7") {
+ return MINIMAX_M27_API_COST;
+ }
if (modelId === "MiniMax-M2.5-highspeed") {
return MINIMAX_M25_API_HIGHSPEED_COST;
}
@@ -74,7 +84,7 @@ export function buildMinimaxModelDefinition(params: {
id: params.id,
name: params.name ?? catalog?.name ?? `MiniMax ${params.id}`,
reasoning: params.reasoning ?? catalog?.reasoning ?? false,
- input: ["text"],
+ input: [...(catalog?.input ?? ["text"])],
cost: params.cost,
contextWindow: params.contextWindow,
maxTokens: params.maxTokens,
@@ -85,7 +95,9 @@ export function buildMinimaxApiModelDefinition(modelId: string): ModelDefinition
return buildMinimaxModelDefinition({
id: modelId,
cost: resolveMinimaxApiCost(modelId),
- contextWindow: DEFAULT_MINIMAX_CONTEXT_WINDOW,
+ contextWindow:
+ MINIMAX_TEXT_MODEL_CATALOG[modelId as MinimaxCatalogId]?.contextWindow ??
+ DEFAULT_MINIMAX_CONTEXT_WINDOW,
maxTokens: DEFAULT_MINIMAX_MAX_TOKENS,
});
}
diff --git a/extensions/minimax/onboard.test.ts b/extensions/minimax/onboard.test.ts
index 90139fd17be0..054bbda5cf68 100644
--- a/extensions/minimax/onboard.test.ts
+++ b/extensions/minimax/onboard.test.ts
@@ -14,16 +14,16 @@ describe("minimax onboard", () => {
baseUrl: "https://api.minimax.io/anthropic",
api: "anthropic-messages",
authHeader: true,
- models: [buildMinimaxApiModelDefinition("MiniMax-M2.7")],
+ models: [buildMinimaxApiModelDefinition("MiniMax-M3")],
});
- expect(cfg.agents?.defaults?.models?.["minimax/MiniMax-M2.7"]).toEqual({
+ expect(cfg.agents?.defaults?.models?.["minimax/MiniMax-M3"]).toEqual({
alias: "Minimax",
});
- expect(cfg.agents?.defaults?.model).toEqual({ primary: "minimax/MiniMax-M2.7" });
+ expect(cfg.agents?.defaults?.model).toEqual({ primary: "minimax/MiniMax-M3" });
});
- it("keeps reasoning enabled for MiniMax-M2.7", () => {
- const cfg = applyMinimaxApiConfig({}, "MiniMax-M2.7");
+ it("keeps reasoning enabled for MiniMax-M3", () => {
+ const cfg = applyMinimaxApiConfig({}, "MiniMax-M3");
expect(cfg.models?.providers?.minimax?.models[0]?.reasoning).toBe(true);
});
@@ -73,7 +73,7 @@ describe("minimax onboard", () => {
legacyApi: "openai-completions",
});
expect(provider?.authHeader).toBe(true);
- expect(provider?.models.map((m) => m.id)).toEqual(["old-model", "MiniMax-M2.7"]);
+ expect(provider?.models.map((m) => m.id)).toEqual(["old-model", "MiniMax-M3"]);
});
it("preserves other providers when adding minimax", () => {
diff --git a/extensions/minimax/onboard.ts b/extensions/minimax/onboard.ts
index b4779f43fd57..4bad3b7325b9 100644
--- a/extensions/minimax/onboard.ts
+++ b/extensions/minimax/onboard.ts
@@ -9,6 +9,7 @@ import {
MINIMAX_API_BASE_URL,
MINIMAX_CN_API_BASE_URL,
} from "./model-definitions.js";
+import { MINIMAX_DEFAULT_MODEL_ID } from "./provider-models.js";
type MinimaxApiProviderConfigParams = {
providerId: string;
@@ -61,7 +62,7 @@ function applyMinimaxApiConfigWithBaseUrl(
export function applyMinimaxApiProviderConfig(
cfg: OpenClawConfig,
- modelId = "MiniMax-M2.7",
+ modelId = MINIMAX_DEFAULT_MODEL_ID,
): OpenClawConfig {
return applyMinimaxApiProviderConfigWithBaseUrl(cfg, {
providerId: "minimax",
@@ -72,7 +73,7 @@ export function applyMinimaxApiProviderConfig(
export function applyMinimaxApiConfig(
cfg: OpenClawConfig,
- modelId = "MiniMax-M2.7",
+ modelId = MINIMAX_DEFAULT_MODEL_ID,
): OpenClawConfig {
return applyMinimaxApiConfigWithBaseUrl(cfg, {
providerId: "minimax",
@@ -83,7 +84,7 @@ export function applyMinimaxApiConfig(
export function applyMinimaxApiProviderConfigCn(
cfg: OpenClawConfig,
- modelId = "MiniMax-M2.7",
+ modelId = MINIMAX_DEFAULT_MODEL_ID,
): OpenClawConfig {
return applyMinimaxApiProviderConfigWithBaseUrl(cfg, {
providerId: "minimax",
@@ -94,7 +95,7 @@ export function applyMinimaxApiProviderConfigCn(
export function applyMinimaxApiConfigCn(
cfg: OpenClawConfig,
- modelId = "MiniMax-M2.7",
+ modelId = MINIMAX_DEFAULT_MODEL_ID,
): OpenClawConfig {
return applyMinimaxApiConfigWithBaseUrl(cfg, {
providerId: "minimax",
diff --git a/extensions/minimax/openclaw.plugin.json b/extensions/minimax/openclaw.plugin.json
index 9d072b294434..f1965b3247db 100644
--- a/extensions/minimax/openclaw.plugin.json
+++ b/extensions/minimax/openclaw.plugin.json
@@ -34,7 +34,7 @@
"choiceHint": "Global endpoint - api.minimax.io",
"groupId": "minimax",
"groupLabel": "MiniMax",
- "groupHint": "M2.7 (recommended)"
+ "groupHint": "M3 (recommended)"
},
{
"provider": "minimax",
@@ -45,7 +45,7 @@
"choiceHint": "Global endpoint - api.minimax.io",
"groupId": "minimax",
"groupLabel": "MiniMax",
- "groupHint": "M2.7 (recommended)",
+ "groupHint": "M3 (recommended)",
"optionKey": "minimaxApiKey",
"cliFlag": "--minimax-api-key",
"cliOption": "--minimax-api-key ",
@@ -59,7 +59,7 @@
"choiceHint": "CN endpoint - api.minimaxi.com",
"groupId": "minimax",
"groupLabel": "MiniMax",
- "groupHint": "M2.7 (recommended)"
+ "groupHint": "M3 (recommended)"
},
{
"provider": "minimax",
@@ -70,7 +70,7 @@
"choiceHint": "CN endpoint - api.minimaxi.com",
"groupId": "minimax",
"groupLabel": "MiniMax",
- "groupHint": "M2.7 (recommended)",
+ "groupHint": "M3 (recommended)",
"optionKey": "minimaxApiKey",
"cliFlag": "--minimax-api-key",
"cliOption": "--minimax-api-key ",
diff --git a/extensions/minimax/provider-catalog.ts b/extensions/minimax/provider-catalog.ts
index 2a5faeddf651..91e15e07d0ba 100644
--- a/extensions/minimax/provider-catalog.ts
+++ b/extensions/minimax/provider-catalog.ts
@@ -3,14 +3,13 @@ import type {
ModelProviderConfig,
} from "openclaw/plugin-sdk/provider-model-shared";
import {
- DEFAULT_MINIMAX_CONTEXT_WINDOW,
DEFAULT_MINIMAX_MAX_TOKENS,
MINIMAX_API_BASE_URL,
resolveMinimaxApiCost,
} from "./model-definitions.js";
import { MINIMAX_TEXT_MODEL_CATALOG, MINIMAX_TEXT_MODEL_ORDER } from "./provider-models.js";
-function resolveMinimaxCatalogBaseUrl(env: NodeJS.ProcessEnv = process.env): string {
+export function resolveMinimaxCatalogBaseUrl(env: NodeJS.ProcessEnv = process.env): string {
const rawHost = env.MINIMAX_API_HOST?.trim();
if (!rawHost) {
return MINIMAX_API_BASE_URL;
@@ -34,6 +33,7 @@ function buildMinimaxModel(params: {
reasoning: boolean;
input: ModelDefinitionConfig["input"];
cost: ModelDefinitionConfig["cost"];
+ contextWindow: number;
}): ModelDefinitionConfig {
return {
id: params.id,
@@ -41,7 +41,7 @@ function buildMinimaxModel(params: {
reasoning: params.reasoning,
input: params.input,
cost: params.cost,
- contextWindow: DEFAULT_MINIMAX_CONTEXT_WINDOW,
+ contextWindow: params.contextWindow,
maxTokens: DEFAULT_MINIMAX_MAX_TOKENS,
};
}
@@ -50,9 +50,11 @@ function buildMinimaxTextModel(params: {
id: string;
name: string;
reasoning: boolean;
+ input: ModelDefinitionConfig["input"];
cost: ModelDefinitionConfig["cost"];
+ contextWindow: number;
}): ModelDefinitionConfig {
- return buildMinimaxModel({ ...params, input: ["text"] });
+ return buildMinimaxModel(params);
}
function buildMinimaxCatalog(): ModelDefinitionConfig[] {
@@ -62,7 +64,9 @@ function buildMinimaxCatalog(): ModelDefinitionConfig[] {
id,
name: model.name,
reasoning: model.reasoning,
+ input: [...model.input],
cost: resolveMinimaxApiCost(id),
+ contextWindow: model.contextWindow,
});
});
}
diff --git a/extensions/minimax/provider-contract-api.ts b/extensions/minimax/provider-contract-api.ts
index dd7bead4f835..13b6c687d365 100644
--- a/extensions/minimax/provider-contract-api.ts
+++ b/extensions/minimax/provider-contract-api.ts
@@ -4,7 +4,7 @@ const noopAuth = async () => ({ profiles: [] });
const wizardGroup = {
groupId: "minimax",
groupLabel: "MiniMax",
- groupHint: "M2.7 (recommended)",
+ groupHint: "M3 (recommended)",
} as const;
export function createMinimaxProvider(): ProviderPlugin {
diff --git a/extensions/minimax/provider-models.ts b/extensions/minimax/provider-models.ts
index b4a026deda51..691c4d32e0e1 100644
--- a/extensions/minimax/provider-models.ts
+++ b/extensions/minimax/provider-models.ts
@@ -1,20 +1,40 @@
import { matchesExactOrPrefix } from "openclaw/plugin-sdk/provider-model-shared";
-export const MINIMAX_DEFAULT_MODEL_ID = "MiniMax-M2.7";
+export const MINIMAX_DEFAULT_MODEL_ID = "MiniMax-M3";
export const MINIMAX_DEFAULT_MODEL_REF = `minimax/${MINIMAX_DEFAULT_MODEL_ID}`;
-export const MINIMAX_TEXT_MODEL_ORDER = ["MiniMax-M2.7", "MiniMax-M2.7-highspeed"] as const;
+export const MINIMAX_TEXT_MODEL_ORDER = [
+ "MiniMax-M3",
+ "MiniMax-M2.7",
+ "MiniMax-M2.7-highspeed",
+] as const;
export const MINIMAX_TEXT_MODEL_CATALOG = {
- "MiniMax-M2.7": { name: "MiniMax M2.7", reasoning: true },
- "MiniMax-M2.7-highspeed": { name: "MiniMax M2.7 Highspeed", reasoning: true },
+ "MiniMax-M3": {
+ name: "MiniMax M3",
+ reasoning: true,
+ input: ["text", "image"],
+ contextWindow: 1_000_000,
+ },
+ "MiniMax-M2.7": {
+ name: "MiniMax M2.7",
+ reasoning: true,
+ input: ["text"],
+ contextWindow: 204800,
+ },
+ "MiniMax-M2.7-highspeed": {
+ name: "MiniMax M2.7 Highspeed",
+ reasoning: true,
+ input: ["text"],
+ contextWindow: 204800,
+ },
} as const;
export const MINIMAX_TEXT_MODEL_REFS = MINIMAX_TEXT_MODEL_ORDER.map(
(modelId) => `minimax/${modelId}`,
);
-const MINIMAX_MODERN_MODEL_MATCHERS = ["minimax-m2.7"] as const;
+const MINIMAX_MODERN_MODEL_MATCHERS = ["minimax-m3", "minimax-m2.7"] as const;
export function isMiniMaxModernModelId(modelId: string): boolean {
return matchesExactOrPrefix(modelId, MINIMAX_MODERN_MODEL_MATCHERS);
diff --git a/extensions/minimax/provider-registration.ts b/extensions/minimax/provider-registration.ts
index 7ac15403be14..965964947bdb 100644
--- a/extensions/minimax/provider-registration.ts
+++ b/extensions/minimax/provider-registration.ts
@@ -5,6 +5,8 @@ import type {
ProviderAuthContext,
ProviderAuthResult,
ProviderCatalogContext,
+ ProviderResolveDynamicModelContext,
+ ProviderRuntimeModel,
} from "openclaw/plugin-sdk/plugin-entry";
import {
MINIMAX_OAUTH_MARKER,
@@ -14,14 +16,27 @@ import {
import { buildOauthProviderAuthResult } from "openclaw/plugin-sdk/provider-auth";
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth-api-key";
import type { ProviderPlugin } from "openclaw/plugin-sdk/provider-model-shared";
-import { buildProviderReplayFamilyHooks } from "openclaw/plugin-sdk/provider-model-shared";
+import {
+ buildProviderReplayFamilyHooks,
+ normalizeModelCompat,
+} from "openclaw/plugin-sdk/provider-model-shared";
import { MINIMAX_FAST_MODE_STREAM_HOOKS } from "openclaw/plugin-sdk/provider-stream-family";
import { fetchMinimaxUsage } from "openclaw/plugin-sdk/provider-usage";
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
-import { isMiniMaxModernModelId, MINIMAX_DEFAULT_MODEL_ID } from "./api.js";
+import {
+ isMiniMaxModernModelId,
+ MINIMAX_DEFAULT_MODEL_ID,
+ MINIMAX_TEXT_MODEL_CATALOG,
+ MINIMAX_TEXT_MODEL_ORDER,
+} from "./api.js";
+import { DEFAULT_MINIMAX_MAX_TOKENS, resolveMinimaxApiCost } from "./model-definitions.js";
import type { MiniMaxRegion } from "./oauth.js";
import { applyMinimaxApiConfig, applyMinimaxApiConfigCn } from "./onboard.js";
-import { buildMinimaxPortalProvider, buildMinimaxProvider } from "./provider-catalog.js";
+import {
+ buildMinimaxPortalProvider,
+ buildMinimaxProvider,
+ resolveMinimaxCatalogBaseUrl,
+} from "./provider-catalog.js";
const API_PROVIDER_ID = "minimax";
const PORTAL_PROVIDER_ID = "minimax-portal";
@@ -38,7 +53,7 @@ const MINIMAX_USAGE_ENV_VAR_KEYS = [
const MINIMAX_WIZARD_GROUP = {
groupId: "minimax",
groupLabel: "MiniMax",
- groupHint: "M2.7 (recommended)",
+ groupHint: "M3 (recommended)",
} as const;
const HYBRID_ANTHROPIC_OPENAI_REPLAY_HOOKS = buildProviderReplayFamilyHooks({
family: "hybrid-anthropic-openai",
@@ -86,6 +101,35 @@ function buildPortalProviderCatalog(params: { baseUrl: string; apiKey: string })
};
}
+function findMinimaxCatalogModel(modelId: string) {
+ const normalizedModelId = modelId.trim().toLowerCase();
+ const catalogId = MINIMAX_TEXT_MODEL_ORDER.find((id) => id.toLowerCase() === normalizedModelId);
+ return catalogId ? { id: catalogId, model: MINIMAX_TEXT_MODEL_CATALOG[catalogId] } : undefined;
+}
+
+function resolveMinimaxDynamicModel(params: {
+ providerId: string;
+ ctx: ProviderResolveDynamicModelContext;
+}): ProviderRuntimeModel | undefined {
+ const catalogModel = findMinimaxCatalogModel(params.ctx.modelId);
+ if (!catalogModel) {
+ return undefined;
+ }
+ return normalizeModelCompat({
+ id: catalogModel.id,
+ name: catalogModel.model.name,
+ provider: params.providerId,
+ api: "anthropic-messages",
+ baseUrl:
+ normalizeOptionalString(params.ctx.providerConfig?.baseUrl) ?? resolveMinimaxCatalogBaseUrl(),
+ reasoning: catalogModel.model.reasoning,
+ input: [...catalogModel.model.input],
+ cost: resolveMinimaxApiCost(catalogModel.id),
+ contextWindow: catalogModel.model.contextWindow,
+ maxTokens: DEFAULT_MINIMAX_MAX_TOKENS,
+ });
+}
+
function resolveApiCatalog(ctx: ProviderCatalogContext) {
const apiKey = ctx.resolveProviderApiKey(API_PROVIDER_ID).apiKey;
if (!apiKey) {
@@ -165,6 +209,7 @@ function createOAuthHandler(region: MiniMaxRegion) {
agents: {
defaults: {
models: {
+ [portalModelRef("MiniMax-M3")]: { alias: "minimax-m3" },
[portalModelRef("MiniMax-M2.7")]: { alias: "minimax-m2.7" },
[portalModelRef("MiniMax-M2.7-highspeed")]: {
alias: "minimax-m2.7-highspeed",
@@ -267,6 +312,7 @@ export function buildMinimaxApiProviderPlugin(): ProviderPlugin {
return apiKey ? { token: apiKey } : null;
},
...MINIMAX_PROVIDER_HOOKS,
+ resolveDynamicModel: (ctx) => resolveMinimaxDynamicModel({ providerId: API_PROVIDER_ID, ctx }),
isModernModelRef: ({ modelId }) => isMiniMaxModernModelId(modelId),
fetchUsageSnapshot: async (ctx) =>
await fetchMinimaxUsage(ctx.token, ctx.timeoutMs, ctx.fetchFn, {
@@ -292,6 +338,8 @@ export function buildMinimaxPortalProviderPlugin(): ProviderPlugin {
},
auth: [createMinimaxOAuthMethod("global"), createMinimaxOAuthMethod("cn")],
...MINIMAX_PROVIDER_HOOKS,
+ resolveDynamicModel: (ctx) =>
+ resolveMinimaxDynamicModel({ providerId: PORTAL_PROVIDER_ID, ctx }),
isModernModelRef: ({ modelId }) => isMiniMaxModernModelId(modelId),
};
}
diff --git a/src/agents/live-model-filter.ts b/src/agents/live-model-filter.ts
index 1ebb444f69c5..a8342f0b1027 100644
--- a/src/agents/live-model-filter.ts
+++ b/src/agents/live-model-filter.ts
@@ -19,7 +19,7 @@ const HIGH_SIGNAL_LIVE_MODEL_PRIORITY = [
"anthropic/claude-opus-4-6",
"deepseek/deepseek-v4-flash",
"deepseek/deepseek-v4-pro",
- "minimax/minimax-m2.7",
+ "minimax/minimax-m3",
"openai/gpt-5.5",
"openrouter/openai/gpt-5.2-chat",
"openrouter/minimax/minimax-m2.7",
@@ -28,7 +28,7 @@ const HIGH_SIGNAL_LIVE_MODEL_PRIORITY = [
"xai/grok-4.3",
"zai/glm-5.1",
"fireworks/accounts/fireworks/models/glm-5p1",
- "minimax-portal/minimax-m2.7",
+ "minimax-portal/minimax-m3",
] as const;
const SMALL_LIVE_MODEL_PRIORITY = [
diff --git a/src/agents/minimax-docs.test.ts b/src/agents/minimax-docs.test.ts
index 52e1b16730a0..3f7a09e07c80 100644
--- a/src/agents/minimax-docs.test.ts
+++ b/src/agents/minimax-docs.test.ts
@@ -13,7 +13,7 @@ const minimaxDoc = fs.readFileSync(path.join(repoRoot, "docs/providers/minimax.m
describe("MiniMax docs sync", () => {
it("keeps the live-testing guide on the current MiniMax default", () => {
- expect(testingLiveDoc).toContain("MiniMax M2.7");
+ expect(testingLiveDoc).toContain("MiniMax M3");
expect(testingLiveDoc).toContain(MINIMAX_DEFAULT_MODEL_REF);
});
diff --git a/src/agents/minimax.live.test.ts b/src/agents/minimax.live.test.ts
index 990f6876a9fd..0da92eebf45f 100644
--- a/src/agents/minimax.live.test.ts
+++ b/src/agents/minimax.live.test.ts
@@ -8,7 +8,7 @@ import {
const MINIMAX_KEY = process.env.MINIMAX_API_KEY ?? "";
const MINIMAX_BASE_URL = process.env.MINIMAX_BASE_URL?.trim() || "https://api.minimax.io/anthropic";
-const MINIMAX_MODEL = process.env.MINIMAX_MODEL?.trim() || "MiniMax-M2.7";
+const MINIMAX_MODEL = process.env.MINIMAX_MODEL?.trim() || "MiniMax-M3";
const LIVE = isLiveTestEnabled(["MINIMAX_LIVE_TEST"]);
const describeLive = LIVE && MINIMAX_KEY ? describe : describe.skip;
@@ -36,10 +36,10 @@ describeLive("minimax live", () => {
provider: "minimax",
baseUrl: MINIMAX_BASE_URL,
reasoning: false,
- input: ["text"],
+ input: ["text", "image"],
// Pricing: placeholder values (per 1M tokens, multiplied by 1000 for display)
cost: { input: 15, output: 60, cacheRead: 2, cacheWrite: 10 },
- contextWindow: 200000,
+ contextWindow: 1_000_000,
maxTokens: 8192,
};
const probeResult = await runMinimaxTextProbe(model, 128);
diff --git a/src/agents/model-compat.test.ts b/src/agents/model-compat.test.ts
index 5ef586bf8830..0bdcaab3de7f 100644
--- a/src/agents/model-compat.test.ts
+++ b/src/agents/model-compat.test.ts
@@ -524,6 +524,7 @@ describe("isHighSignalLiveModelRef", () => {
expect(
isHighSignalLiveModelRef({ provider: "openrouter", id: "minimax/minimax-m2.1:free" }),
).toBe(false);
+ expect(isHighSignalLiveModelRef({ provider: "minimax", id: "MiniMax-M3" })).toBe(true);
expect(isHighSignalLiveModelRef({ provider: "minimax", id: "MiniMax-M2.7" })).toBe(true);
expect(isHighSignalLiveModelRef({ provider: "openrouter", id: "minimax/minimax-m2.7" })).toBe(
true,
@@ -656,7 +657,7 @@ describe("isPrioritizedHighSignalLiveModelRef", () => {
{ provider: "anthropic", id: "claude-opus-4-6" },
{ provider: "deepseek", id: "deepseek-v4-flash" },
{ provider: "deepseek", id: "deepseek-v4-pro" },
- { provider: "minimax", id: "minimax-m2.7" },
+ { provider: "minimax", id: "minimax-m3" },
{ provider: "openai", id: "gpt-5.5" },
{ provider: "openrouter", id: "openai/gpt-5.2-chat" },
{ provider: "openrouter", id: "minimax/minimax-m2.7" },
@@ -665,7 +666,7 @@ describe("isPrioritizedHighSignalLiveModelRef", () => {
{ provider: "xai", id: "grok-4.3" },
{ provider: "zai", id: "glm-5.1" },
{ provider: "fireworks", id: "accounts/fireworks/models/glm-5p1" },
- { provider: "minimax-portal", id: "minimax-m2.7" },
+ { provider: "minimax-portal", id: "minimax-m3" },
]);
});
});
@@ -733,7 +734,7 @@ describe("selectHighSignalLiveItems", () => {
{ provider: "openai", id: "gpt-5.5" },
{ provider: "deepseek", id: "deepseek-v4-flash" },
{ provider: "deepseek", id: "deepseek-v4-pro" },
- { provider: "minimax", id: "minimax-m2.7" },
+ { provider: "minimax", id: "minimax-m3" },
];
expect(
@@ -746,7 +747,7 @@ describe("selectHighSignalLiveItems", () => {
).toEqual([
{ provider: "deepseek", id: "deepseek-v4-flash" },
{ provider: "deepseek", id: "deepseek-v4-pro" },
- { provider: "minimax", id: "minimax-m2.7" },
+ { provider: "minimax", id: "minimax-m3" },
]);
});
diff --git a/src/agents/models-config.providers.minimax.test.ts b/src/agents/models-config.providers.minimax.test.ts
index cffdcdd582ee..de2bf0f7fcd1 100644
--- a/src/agents/models-config.providers.minimax.test.ts
+++ b/src/agents/models-config.providers.minimax.test.ts
@@ -2,6 +2,15 @@ import { describe, expect, it } from "vitest";
function buildMinimaxCatalog() {
return [
+ {
+ id: "MiniMax-M3",
+ cost: {
+ input: 0.6,
+ output: 2.4,
+ cacheRead: 0.12,
+ cacheWrite: 0,
+ },
+ },
{
id: "MiniMax-M2.7",
cost: {
@@ -30,10 +39,12 @@ describe("minimax provider catalog", () => {
"minimax-portal": { models: buildMinimaxCatalog() },
};
expect(providers?.minimax?.models?.map((model) => model.id)).toEqual([
+ "MiniMax-M3",
"MiniMax-M2.7",
"MiniMax-M2.7-highspeed",
]);
expect(providers?.["minimax-portal"]?.models?.map((model) => model.id)).toEqual([
+ "MiniMax-M3",
"MiniMax-M2.7",
"MiniMax-M2.7-highspeed",
]);
diff --git a/src/plugin-sdk/test-helpers/provider-discovery-contract.ts b/src/plugin-sdk/test-helpers/provider-discovery-contract.ts
index 1c89c31f5280..306045cd0e9b 100644
--- a/src/plugin-sdk/test-helpers/provider-discovery-contract.ts
+++ b/src/plugin-sdk/test-helpers/provider-discovery-contract.ts
@@ -717,6 +717,7 @@ export function describeMinimaxProviderDiscoveryContract(
apiKey: "minimax-key",
});
const ids = providerModelIds(provider);
+ expect(ids).toContain("MiniMax-M3");
expect(ids).toContain("MiniMax-M2.7");
expect(ids).toContain("MiniMax-M2.7-highspeed");
});
diff --git a/test/scripts/package-acceptance-workflow.test.ts b/test/scripts/package-acceptance-workflow.test.ts
index e9faf6de7a75..9f3745d810c9 100644
--- a/test/scripts/package-acceptance-workflow.test.ts
+++ b/test/scripts/package-acceptance-workflow.test.ts
@@ -255,9 +255,7 @@ describe("package acceptance workflow", () => {
expect(crabboxConfig.jobs?.changed?.command).toContain(
"commit -q --no-gpg-sign -m remote-check-tree",
);
- expect(crabboxConfig.jobs?.changed?.command).toContain(
- "env CI=1 corepack pnpm check --timed",
- );
+ expect(crabboxConfig.jobs?.changed?.command).toContain("env CI=1 corepack pnpm check --timed");
expect(crabboxConfig.ssh?.user).toBe("crabbox");
expect(crabboxConfig.ssh?.port).toBe("22");
});
@@ -655,7 +653,7 @@ describe("package artifact reuse", () => {
"OPENCLAW_LIVE_GATEWAY_MODELS=google/gemini-3.1-pro-preview node .release-harness/scripts/test-live-shard.mjs native-live-src-gateway-profiles",
);
expect(workflow).toContain(
- "OPENCLAW_LIVE_GATEWAY_MODELS=minimax/MiniMax-M2.7,minimax-portal/MiniMax-M2.7 OPENCLAW_LIVE_GATEWAY_MAX_MODELS=2",
+ "OPENCLAW_LIVE_GATEWAY_MODELS=minimax/MiniMax-M3,minimax-portal/MiniMax-M3 OPENCLAW_LIVE_GATEWAY_MAX_MODELS=2",
);
expect(workflow).toMatch(
/suite_id: native-live-src-gateway-profiles-fireworks[\s\S]*?timeout_minutes: 30[\s\S]*?advisory: true/u,