fix(models): default MiniMax to /anthropic

This commit is contained in:
Peter Steinberger
2026-01-12 05:09:59 +00:00
parent b4a2cf8382
commit f5c851e11e
9 changed files with 112 additions and 112 deletions

View File

@@ -33,6 +33,7 @@
- Installer UX: add `--install-method git|npm` and auto-detect source checkouts (prompt to update git checkout vs migrate to npm).
### Fixes
- Models/Onboarding: configure MiniMax (minimax.io) via Anthropic-compatible `/anthropic` endpoint by default (keep `minimax-api` as a legacy alias).
- Gateway/WebChat: include handshake validation details in the WebSocket close reason for easier debugging.
- Gateway/Auth: send invalid connect responses before closing the handshake; stabilize invalid-connect auth test.
- Doctor: surface plugin diagnostics in the report.

View File

@@ -112,8 +112,8 @@ OpenAI/Anthropiccompatible proxies.
MiniMax is configured via `models.providers` because it uses custom endpoints:
- MiniMax Cloud (OpenAIcompatible): `--auth-choice minimax-cloud`
- MiniMax API (Anthropiccompatible): `--auth-choice minimax-api`
- MiniMax (Anthropiccompatible): `--auth-choice minimax-cloud`
- `--auth-choice minimax-api` is a legacy alias.
- Auth: `MINIMAX_API_KEY`
See [/providers/minimax](/providers/minimax) for setup details, model options, and config snippets.

View File

@@ -1830,7 +1830,7 @@ Use MiniMax's Anthropic-compatible API directly without LM Studio:
```
Notes:
- Set `MINIMAX_API_KEY` environment variable or use `clawdbot onboard --auth-choice minimax-api`
- Set `MINIMAX_API_KEY` environment variable or use `clawdbot onboard --auth-choice minimax-cloud`
- Available models: `MiniMax-M2.1` (default), `MiniMax-M2.1-lightning` (~100 tps), `MiniMax-M2` (reasoning)
- Pricing is a placeholder; MiniMax doesn't publish public rates. Override in `models.json` for accurate cost tracking.

View File

@@ -27,52 +27,15 @@ MiniMax highlights these improvements in M2.1:
## Choose a setup
### Option A: MiniMax Cloud (OpenAI-compatible `/v1`)
### Option A: MiniMax (Anthropic-compatible `/anthropic`) — recommended
**Best for:** hosted MiniMax with OpenAI-compatible API.
**Best for:** hosted MiniMax with Anthropic-compatible API.
Configure via CLI:
- Run `clawdbot configure`
- Select **Model/auth**
- Choose **MiniMax M2.1 (minimax.io)**
```json5
{
env: { MINIMAX_API_KEY: "sk-..." },
agents: { defaults: { model: { primary: "minimax/MiniMax-M2.1" } } },
models: {
mode: "merge",
providers: {
minimax: {
baseUrl: "https://api.minimax.io/v1",
apiKey: "${MINIMAX_API_KEY}",
api: "openai-completions",
models: [
{
id: "MiniMax-M2.1",
name: "MiniMax M2.1",
reasoning: false,
input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 200000,
maxTokens: 8192
}
]
}
}
}
}
```
### Option B: MiniMax API (Anthropic-compatible `/anthropic`)
**Best for:** MiniMax's Anthropic-compatible API (platform.minimax.io).
Configure via CLI:
- Run `clawdbot configure`
- Select **Model/auth**
- Choose **MiniMax API (platform.minimax.io)**
```json5
{
env: { MINIMAX_API_KEY: "sk-..." },
@@ -93,22 +56,36 @@ Configure via CLI:
cost: { input: 15, output: 60, cacheRead: 2, cacheWrite: 10 },
contextWindow: 200000,
maxTokens: 8192
},
}
]
}
}
}
}
```
### Option B: MiniMax OpenAI-compatible `/v1` (manual)
**Best for:** setups that require OpenAI-compatible payloads.
```json5
{
env: { MINIMAX_API_KEY: "sk-..." },
agents: { defaults: { model: { primary: "minimax/MiniMax-M2.1" } } },
models: {
mode: "merge",
providers: {
minimax: {
baseUrl: "https://api.minimax.io/v1",
apiKey: "${MINIMAX_API_KEY}",
api: "openai-completions",
models: [
{
id: "MiniMax-M2.1-lightning",
name: "MiniMax M2.1 Lightning",
id: "MiniMax-M2.1",
name: "MiniMax M2.1",
reasoning: false,
input: ["text"],
cost: { input: 15, output: 60, cacheRead: 2, cacheWrite: 10 },
contextWindow: 200000,
maxTokens: 8192
},
{
id: "MiniMax-M2",
name: "MiniMax M2",
reasoning: true,
input: ["text"],
cost: { input: 15, output: 60, cacheRead: 2, cacheWrite: 10 },
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 200000,
maxTokens: 8192
}

View File

@@ -80,8 +80,7 @@ Tip: `--json` does **not** imply non-interactive mode. Use `--non-interactive` (
- **OpenAI API key**: uses `OPENAI_API_KEY` if present or prompts for a key, then saves it to `~/.clawdbot/.env` so launchd can read it.
- **OpenCode Zen (multi-model proxy)**: prompts for `OPENCODE_API_KEY` (or `OPENCODE_ZEN_API_KEY`, get it at https://opencode.ai/auth).
- **API key**: stores the key for you.
- **MiniMax M2.1 (minimax.io)**: config is autowritten for the OpenAI-compatible `/v1` endpoint.
- **MiniMax API (platform.minimax.io)**: config is autowritten for the Anthropic-compatible `/anthropic` endpoint.
- **MiniMax M2.1 (minimax.io)**: config is autowritten for the Anthropic-compatible `/anthropic` endpoint.
- **MiniMax M2.1 (LM Studio)**: config is autowritten for the LM Studio endpoint.
- More detail: [MiniMax](/providers/minimax)
- **Skip**: no auth configured yet.

View File

@@ -172,12 +172,11 @@ export function buildAuthChoiceOptions(params: {
label: "OpenCode Zen (multi-model proxy)",
hint: "Claude, GPT, Gemini via opencode.ai/zen",
});
options.push({ value: "minimax-cloud", label: "MiniMax M2.1 (minimax.io)" });
options.push({ value: "minimax", label: "Minimax M2.1 (LM Studio)" });
options.push({
value: "minimax-api",
label: "MiniMax API (platform.minimax.io)",
value: "minimax-cloud",
label: "MiniMax M2.1 (minimax.io) — Anthropic-compatible",
});
options.push({ value: "minimax", label: "Minimax M2.1 (LM Studio)" });
if (params.includeSkip) {
options.push({ value: "skip", label: "Skip for now" });
}

View File

@@ -104,6 +104,68 @@ describe("applyAuthChoice", () => {
expect(parsed.profiles?.["minimax:default"]?.key).toBe("sk-minimax-test");
});
it("configures MiniMax (minimax-cloud) via the Anthropic-compatible endpoint", async () => {
tempStateDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-auth-"));
process.env.CLAWDBOT_STATE_DIR = tempStateDir;
process.env.CLAWDBOT_AGENT_DIR = path.join(tempStateDir, "agent");
process.env.PI_CODING_AGENT_DIR = process.env.CLAWDBOT_AGENT_DIR;
const text = vi.fn().mockResolvedValue("sk-minimax-test");
const select: WizardPrompter["select"] = vi.fn(
async (params) => params.options[0]?.value as never,
);
const multiselect: WizardPrompter["multiselect"] = vi.fn(async () => []);
const prompter: WizardPrompter = {
intro: vi.fn(noopAsync),
outro: vi.fn(noopAsync),
note: vi.fn(noopAsync),
select,
multiselect,
text,
confirm: vi.fn(async () => false),
progress: vi.fn(() => ({ update: noop, stop: noop })),
};
const runtime: RuntimeEnv = {
log: vi.fn(),
error: vi.fn(),
exit: vi.fn((code: number) => {
throw new Error(`exit:${code}`);
}),
};
const result = await applyAuthChoice({
authChoice: "minimax-cloud",
config: {},
prompter,
runtime,
setDefaultModel: true,
});
expect(text).toHaveBeenCalledWith(
expect.objectContaining({ message: "Enter MiniMax API key" }),
);
expect(result.config.models?.providers?.minimax).toMatchObject({
baseUrl: "https://api.minimax.io/anthropic",
api: "anthropic-messages",
});
expect(result.config.agents?.defaults?.model).toMatchObject({
primary: "minimax/MiniMax-M2.1",
});
const authProfilePath = path.join(
tempStateDir,
"agents",
"main",
"agent",
"auth-profiles.json",
);
const raw = await fs.readFile(authProfilePath, "utf8");
const parsed = JSON.parse(raw) as {
profiles?: Record<string, { key?: string }>;
};
expect(parsed.profiles?.["minimax:default"]?.key).toBe("sk-minimax-test");
});
it("does not override the default model when selecting opencode-zen without setDefaultModel", async () => {
tempStateDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-auth-"));
process.env.CLAWDBOT_STATE_DIR = tempStateDir;

View File

@@ -40,15 +40,12 @@ import {
applyMinimaxApiConfig,
applyMinimaxApiProviderConfig,
applyMinimaxConfig,
applyMinimaxHostedConfig,
applyMinimaxHostedProviderConfig,
applyMinimaxProviderConfig,
applyOpencodeZenConfig,
applyOpencodeZenProviderConfig,
applyOpenrouterConfig,
applyOpenrouterProviderConfig,
applyZaiConfig,
MINIMAX_HOSTED_MODEL_REF,
OPENROUTER_DEFAULT_MODEL_REF,
setAnthropicApiKey,
setGeminiApiKey,
@@ -727,33 +724,10 @@ export async function applyAuthChoice(params: {
provider: "anthropic",
mode: "api_key",
});
} else if (params.authChoice === "minimax-cloud") {
const key = await params.prompter.text({
message: "Enter MiniMax API key",
validate: (value) => (value?.trim() ? undefined : "Required"),
});
await setMinimaxApiKey(String(key).trim(), params.agentDir);
nextConfig = applyAuthProfileConfig(nextConfig, {
profileId: "minimax:default",
provider: "minimax",
mode: "api_key",
});
if (params.setDefaultModel) {
nextConfig = applyMinimaxHostedConfig(nextConfig);
} else {
nextConfig = applyMinimaxHostedProviderConfig(nextConfig);
agentModelOverride = MINIMAX_HOSTED_MODEL_REF;
await noteAgentModel(MINIMAX_HOSTED_MODEL_REF);
}
} else if (params.authChoice === "minimax") {
if (params.setDefaultModel) {
nextConfig = applyMinimaxConfig(nextConfig);
} else {
nextConfig = applyMinimaxProviderConfig(nextConfig);
agentModelOverride = "lmstudio/minimax-m2.1-gs32";
await noteAgentModel("lmstudio/minimax-m2.1-gs32");
}
} else if (params.authChoice === "minimax-api") {
} else if (
params.authChoice === "minimax-cloud" ||
params.authChoice === "minimax-api"
) {
const key = await params.prompter.text({
message: "Enter MiniMax API key",
validate: (value) => (value?.trim() ? undefined : "Required"),
@@ -771,6 +745,14 @@ export async function applyAuthChoice(params: {
agentModelOverride = "minimax/MiniMax-M2.1";
await noteAgentModel("minimax/MiniMax-M2.1");
}
} else if (params.authChoice === "minimax") {
if (params.setDefaultModel) {
nextConfig = applyMinimaxConfig(nextConfig);
} else {
nextConfig = applyMinimaxProviderConfig(nextConfig);
agentModelOverride = "lmstudio/minimax-m2.1-gs32";
await noteAgentModel("lmstudio/minimax-m2.1-gs32");
}
} else if (params.authChoice === "opencode-zen") {
await params.prompter.note(
[

View File

@@ -34,7 +34,6 @@ import {
applyAuthProfileConfig,
applyMinimaxApiConfig,
applyMinimaxConfig,
applyMinimaxHostedConfig,
applyOpencodeZenConfig,
applyOpenrouterConfig,
applyZaiConfig,
@@ -285,26 +284,7 @@ export async function runNonInteractiveOnboarding(
mode: "api_key",
});
nextConfig = applyOpenrouterConfig(nextConfig);
} else if (authChoice === "minimax-cloud") {
const resolved = await resolveNonInteractiveApiKey({
provider: "minimax",
cfg: baseConfig,
flagValue: opts.minimaxApiKey,
flagName: "--minimax-api-key",
envVar: "MINIMAX_API_KEY",
runtime,
});
if (!resolved) return;
if (resolved.source !== "profile") {
await setMinimaxApiKey(resolved.key);
}
nextConfig = applyAuthProfileConfig(nextConfig, {
profileId: "minimax:default",
provider: "minimax",
mode: "api_key",
});
nextConfig = applyMinimaxHostedConfig(nextConfig);
} else if (authChoice === "minimax-api") {
} else if (authChoice === "minimax-cloud" || authChoice === "minimax-api") {
const resolved = await resolveNonInteractiveApiKey({
provider: "minimax",
cfg: baseConfig,