mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-06 05:51:15 +08:00
feat(minimax): add m3 model support (#88860)
This commit is contained in:
committed by
GitHub
parent
9919e4601f
commit
0369672691
@@ -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
|
||||
|
||||
@@ -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.
|
||||
</Accordion>
|
||||
<Accordion title="MiniMax">
|
||||
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.
|
||||
</Accordion>
|
||||
<Accordion title="NVIDIA">
|
||||
Model ids use a `nvidia/<vendor>/<model>` namespace (for example `nvidia/nvidia/nemotron-...` alongside `nvidia/moonshotai/kimi-k2.5`); pickers preserve the literal `<provider>/<model-id>` 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`
|
||||
|
||||
@@ -645,14 +645,14 @@ Interactive custom-provider onboarding infers image input for common vision mode
|
||||
<Accordion title="Local models (LM Studio)">
|
||||
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.
|
||||
</Accordion>
|
||||
<Accordion title="MiniMax M2.7 (direct)">
|
||||
<Accordion title="MiniMax M3 (direct)">
|
||||
```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`.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Moonshot AI (Kimi)">
|
||||
|
||||
@@ -215,7 +215,7 @@ troubleshooting, see the main [FAQ](/help/faq).
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title='Why do I see "Unknown model: minimax/MiniMax-M2.7"?'>
|
||||
<Accordion title='Why do I see "Unknown model: minimax/MiniMax-M3"?'>
|
||||
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" },
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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):
|
||||
|
||||
|
||||
@@ -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.
|
||||
</Tabs>
|
||||
|
||||
<Note>
|
||||
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`.
|
||||
</Note>
|
||||
|
||||
<Tip>
|
||||
@@ -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.
|
||||
</Warning>
|
||||
|
||||
<Note>
|
||||
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`.
|
||||
</Note>
|
||||
|
||||
</Tab>
|
||||
@@ -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.
|
||||
|
||||
<Note>
|
||||
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/<model>`
|
||||
- OAuth setup: `minimax-portal/<model>`
|
||||
- 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`
|
||||
|
||||
<Tip>
|
||||
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
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title='"Unknown model: minimax/MiniMax-M2.7"'>
|
||||
<Accordion title='"Unknown model: minimax/MiniMax-M3"'>
|
||||
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:
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -182,7 +182,7 @@ What you set:
|
||||
More detail: [Cloudflare AI Gateway](/providers/cloudflare-ai-gateway).
|
||||
</Accordion>
|
||||
<Accordion title="MiniMax">
|
||||
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).
|
||||
</Accordion>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"]);
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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", () => {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 <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 <key>",
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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 = [
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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" },
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
@@ -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",
|
||||
]);
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user