fix(models): default MiniMax to /anthropic
This commit is contained in:
@@ -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).
|
- Installer UX: add `--install-method git|npm` and auto-detect source checkouts (prompt to update git checkout vs migrate to npm).
|
||||||
|
|
||||||
### Fixes
|
### 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/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.
|
- Gateway/Auth: send invalid connect responses before closing the handshake; stabilize invalid-connect auth test.
|
||||||
- Doctor: surface plugin diagnostics in the report.
|
- Doctor: surface plugin diagnostics in the report.
|
||||||
|
|||||||
@@ -112,8 +112,8 @@ OpenAI/Anthropic‑compatible proxies.
|
|||||||
|
|
||||||
MiniMax is configured via `models.providers` because it uses custom endpoints:
|
MiniMax is configured via `models.providers` because it uses custom endpoints:
|
||||||
|
|
||||||
- MiniMax Cloud (OpenAI‑compatible): `--auth-choice minimax-cloud`
|
- MiniMax (Anthropic‑compatible): `--auth-choice minimax-cloud`
|
||||||
- MiniMax API (Anthropic‑compatible): `--auth-choice minimax-api`
|
- `--auth-choice minimax-api` is a legacy alias.
|
||||||
- Auth: `MINIMAX_API_KEY`
|
- Auth: `MINIMAX_API_KEY`
|
||||||
|
|
||||||
See [/providers/minimax](/providers/minimax) for setup details, model options, and config snippets.
|
See [/providers/minimax](/providers/minimax) for setup details, model options, and config snippets.
|
||||||
|
|||||||
@@ -1830,7 +1830,7 @@ Use MiniMax's Anthropic-compatible API directly without LM Studio:
|
|||||||
```
|
```
|
||||||
|
|
||||||
Notes:
|
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)
|
- 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.
|
- Pricing is a placeholder; MiniMax doesn't publish public rates. Override in `models.json` for accurate cost tracking.
|
||||||
|
|
||||||
|
|||||||
@@ -27,52 +27,15 @@ MiniMax highlights these improvements in M2.1:
|
|||||||
|
|
||||||
## Choose a setup
|
## 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:
|
Configure via CLI:
|
||||||
- Run `clawdbot configure`
|
- Run `clawdbot configure`
|
||||||
- Select **Model/auth**
|
- Select **Model/auth**
|
||||||
- Choose **MiniMax M2.1 (minimax.io)**
|
- 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
|
```json5
|
||||||
{
|
{
|
||||||
env: { MINIMAX_API_KEY: "sk-..." },
|
env: { MINIMAX_API_KEY: "sk-..." },
|
||||||
@@ -93,22 +56,36 @@ Configure via CLI:
|
|||||||
cost: { input: 15, output: 60, cacheRead: 2, cacheWrite: 10 },
|
cost: { input: 15, output: 60, cacheRead: 2, cacheWrite: 10 },
|
||||||
contextWindow: 200000,
|
contextWindow: 200000,
|
||||||
maxTokens: 8192
|
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",
|
id: "MiniMax-M2.1",
|
||||||
name: "MiniMax M2.1 Lightning",
|
name: "MiniMax M2.1",
|
||||||
reasoning: false,
|
reasoning: false,
|
||||||
input: ["text"],
|
input: ["text"],
|
||||||
cost: { input: 15, output: 60, cacheRead: 2, cacheWrite: 10 },
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
||||||
contextWindow: 200000,
|
|
||||||
maxTokens: 8192
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "MiniMax-M2",
|
|
||||||
name: "MiniMax M2",
|
|
||||||
reasoning: true,
|
|
||||||
input: ["text"],
|
|
||||||
cost: { input: 15, output: 60, cacheRead: 2, cacheWrite: 10 },
|
|
||||||
contextWindow: 200000,
|
contextWindow: 200000,
|
||||||
maxTokens: 8192
|
maxTokens: 8192
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
- **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).
|
- **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.
|
- **API key**: stores the key for you.
|
||||||
- **MiniMax M2.1 (minimax.io)**: config is auto‑written for the OpenAI-compatible `/v1` endpoint.
|
- **MiniMax M2.1 (minimax.io)**: config is auto‑written for the Anthropic-compatible `/anthropic` endpoint.
|
||||||
- **MiniMax API (platform.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.
|
- **MiniMax M2.1 (LM Studio)**: config is auto‑written for the LM Studio endpoint.
|
||||||
- More detail: [MiniMax](/providers/minimax)
|
- More detail: [MiniMax](/providers/minimax)
|
||||||
- **Skip**: no auth configured yet.
|
- **Skip**: no auth configured yet.
|
||||||
|
|||||||
@@ -172,12 +172,11 @@ export function buildAuthChoiceOptions(params: {
|
|||||||
label: "OpenCode Zen (multi-model proxy)",
|
label: "OpenCode Zen (multi-model proxy)",
|
||||||
hint: "Claude, GPT, Gemini via opencode.ai/zen",
|
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({
|
options.push({
|
||||||
value: "minimax-api",
|
value: "minimax-cloud",
|
||||||
label: "MiniMax API (platform.minimax.io)",
|
label: "MiniMax M2.1 (minimax.io) — Anthropic-compatible",
|
||||||
});
|
});
|
||||||
|
options.push({ value: "minimax", label: "Minimax M2.1 (LM Studio)" });
|
||||||
if (params.includeSkip) {
|
if (params.includeSkip) {
|
||||||
options.push({ value: "skip", label: "Skip for now" });
|
options.push({ value: "skip", label: "Skip for now" });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,6 +104,68 @@ describe("applyAuthChoice", () => {
|
|||||||
expect(parsed.profiles?.["minimax:default"]?.key).toBe("sk-minimax-test");
|
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 () => {
|
it("does not override the default model when selecting opencode-zen without setDefaultModel", async () => {
|
||||||
tempStateDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-auth-"));
|
tempStateDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-auth-"));
|
||||||
process.env.CLAWDBOT_STATE_DIR = tempStateDir;
|
process.env.CLAWDBOT_STATE_DIR = tempStateDir;
|
||||||
|
|||||||
@@ -40,15 +40,12 @@ import {
|
|||||||
applyMinimaxApiConfig,
|
applyMinimaxApiConfig,
|
||||||
applyMinimaxApiProviderConfig,
|
applyMinimaxApiProviderConfig,
|
||||||
applyMinimaxConfig,
|
applyMinimaxConfig,
|
||||||
applyMinimaxHostedConfig,
|
|
||||||
applyMinimaxHostedProviderConfig,
|
|
||||||
applyMinimaxProviderConfig,
|
applyMinimaxProviderConfig,
|
||||||
applyOpencodeZenConfig,
|
applyOpencodeZenConfig,
|
||||||
applyOpencodeZenProviderConfig,
|
applyOpencodeZenProviderConfig,
|
||||||
applyOpenrouterConfig,
|
applyOpenrouterConfig,
|
||||||
applyOpenrouterProviderConfig,
|
applyOpenrouterProviderConfig,
|
||||||
applyZaiConfig,
|
applyZaiConfig,
|
||||||
MINIMAX_HOSTED_MODEL_REF,
|
|
||||||
OPENROUTER_DEFAULT_MODEL_REF,
|
OPENROUTER_DEFAULT_MODEL_REF,
|
||||||
setAnthropicApiKey,
|
setAnthropicApiKey,
|
||||||
setGeminiApiKey,
|
setGeminiApiKey,
|
||||||
@@ -727,33 +724,10 @@ export async function applyAuthChoice(params: {
|
|||||||
provider: "anthropic",
|
provider: "anthropic",
|
||||||
mode: "api_key",
|
mode: "api_key",
|
||||||
});
|
});
|
||||||
} else if (params.authChoice === "minimax-cloud") {
|
} else if (
|
||||||
const key = await params.prompter.text({
|
params.authChoice === "minimax-cloud" ||
|
||||||
message: "Enter MiniMax API key",
|
params.authChoice === "minimax-api"
|
||||||
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") {
|
|
||||||
const key = await params.prompter.text({
|
const key = await params.prompter.text({
|
||||||
message: "Enter MiniMax API key",
|
message: "Enter MiniMax API key",
|
||||||
validate: (value) => (value?.trim() ? undefined : "Required"),
|
validate: (value) => (value?.trim() ? undefined : "Required"),
|
||||||
@@ -771,6 +745,14 @@ export async function applyAuthChoice(params: {
|
|||||||
agentModelOverride = "minimax/MiniMax-M2.1";
|
agentModelOverride = "minimax/MiniMax-M2.1";
|
||||||
await noteAgentModel("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") {
|
} else if (params.authChoice === "opencode-zen") {
|
||||||
await params.prompter.note(
|
await params.prompter.note(
|
||||||
[
|
[
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ import {
|
|||||||
applyAuthProfileConfig,
|
applyAuthProfileConfig,
|
||||||
applyMinimaxApiConfig,
|
applyMinimaxApiConfig,
|
||||||
applyMinimaxConfig,
|
applyMinimaxConfig,
|
||||||
applyMinimaxHostedConfig,
|
|
||||||
applyOpencodeZenConfig,
|
applyOpencodeZenConfig,
|
||||||
applyOpenrouterConfig,
|
applyOpenrouterConfig,
|
||||||
applyZaiConfig,
|
applyZaiConfig,
|
||||||
@@ -285,26 +284,7 @@ export async function runNonInteractiveOnboarding(
|
|||||||
mode: "api_key",
|
mode: "api_key",
|
||||||
});
|
});
|
||||||
nextConfig = applyOpenrouterConfig(nextConfig);
|
nextConfig = applyOpenrouterConfig(nextConfig);
|
||||||
} else if (authChoice === "minimax-cloud") {
|
} else if (authChoice === "minimax-cloud" || authChoice === "minimax-api") {
|
||||||
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") {
|
|
||||||
const resolved = await resolveNonInteractiveApiKey({
|
const resolved = await resolveNonInteractiveApiKey({
|
||||||
provider: "minimax",
|
provider: "minimax",
|
||||||
cfg: baseConfig,
|
cfg: baseConfig,
|
||||||
|
|||||||
Reference in New Issue
Block a user