From f5c851e11ea8516e1de17dd33079caa6c9803ce2 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 12 Jan 2026 05:09:59 +0000 Subject: [PATCH] fix(models): default MiniMax to /anthropic --- CHANGELOG.md | 1 + docs/concepts/model-providers.md | 4 +- docs/gateway/configuration.md | 2 +- docs/providers/minimax.md | 81 +++++++++---------------- docs/start/wizard.md | 3 +- src/commands/auth-choice-options.ts | 7 +-- src/commands/auth-choice.test.ts | 62 +++++++++++++++++++ src/commands/auth-choice.ts | 42 ++++--------- src/commands/onboard-non-interactive.ts | 22 +------ 9 files changed, 112 insertions(+), 112 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ce252fb7..d4112d107 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. diff --git a/docs/concepts/model-providers.md b/docs/concepts/model-providers.md index 58f2f4a60..944840111 100644 --- a/docs/concepts/model-providers.md +++ b/docs/concepts/model-providers.md @@ -112,8 +112,8 @@ OpenAI/Anthropic‑compatible proxies. MiniMax is configured via `models.providers` because it uses custom endpoints: -- MiniMax Cloud (OpenAI‑compatible): `--auth-choice minimax-cloud` -- MiniMax API (Anthropic‑compatible): `--auth-choice minimax-api` +- MiniMax (Anthropic‑compatible): `--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. diff --git a/docs/gateway/configuration.md b/docs/gateway/configuration.md index 17781dd03..680e65028 100644 --- a/docs/gateway/configuration.md +++ b/docs/gateway/configuration.md @@ -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. diff --git a/docs/providers/minimax.md b/docs/providers/minimax.md index c5bd1f118..3d5857e76 100644 --- a/docs/providers/minimax.md +++ b/docs/providers/minimax.md @@ -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 } diff --git a/docs/start/wizard.md b/docs/start/wizard.md index 873f9737d..cc98f22e6 100644 --- a/docs/start/wizard.md +++ b/docs/start/wizard.md @@ -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 auto‑written for the OpenAI-compatible `/v1` endpoint. - - **MiniMax API (platform.minimax.io)**: config is auto‑written for the Anthropic-compatible `/anthropic` endpoint. + - **MiniMax M2.1 (minimax.io)**: config is auto‑written for the Anthropic-compatible `/anthropic` endpoint. - **MiniMax M2.1 (LM Studio)**: config is auto‑written for the LM Studio endpoint. - More detail: [MiniMax](/providers/minimax) - **Skip**: no auth configured yet. diff --git a/src/commands/auth-choice-options.ts b/src/commands/auth-choice-options.ts index dbc27d08b..dffc8e69a 100644 --- a/src/commands/auth-choice-options.ts +++ b/src/commands/auth-choice-options.ts @@ -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" }); } diff --git a/src/commands/auth-choice.test.ts b/src/commands/auth-choice.test.ts index c1ba77fb5..b20a1e9e1 100644 --- a/src/commands/auth-choice.test.ts +++ b/src/commands/auth-choice.test.ts @@ -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; + }; + 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; diff --git a/src/commands/auth-choice.ts b/src/commands/auth-choice.ts index 65f6b8451..7a1f57535 100644 --- a/src/commands/auth-choice.ts +++ b/src/commands/auth-choice.ts @@ -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( [ diff --git a/src/commands/onboard-non-interactive.ts b/src/commands/onboard-non-interactive.ts index 7a2d5d4a7..a11c0dc47 100644 --- a/src/commands/onboard-non-interactive.ts +++ b/src/commands/onboard-non-interactive.ts @@ -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,