From 7856b202a72f0f05a9b6529c2906e9dcb38470f4 Mon Sep 17 00:00:00 2001 From: octo-patch <266937838+octo-patch@users.noreply.github.com> Date: Sun, 12 Jul 2026 15:44:09 +0800 Subject: [PATCH 1/4] Fix MiniMax Anthropic SDK endpoints --- src/cli/__tests__/model-providers.test.ts | 66 +++++++++++++++++++++++ src/cli/bootstrap/model-wizard.tsx | 13 ++--- src/cli/model-providers.ts | 31 ++++++++++- 3 files changed, 102 insertions(+), 8 deletions(-) create mode 100644 src/cli/__tests__/model-providers.test.ts diff --git a/src/cli/__tests__/model-providers.test.ts b/src/cli/__tests__/model-providers.test.ts new file mode 100644 index 0000000..910d3c9 --- /dev/null +++ b/src/cli/__tests__/model-providers.test.ts @@ -0,0 +1,66 @@ +import Anthropic from "@anthropic-ai/sdk"; +import { describe, expect, test } from "bun:test"; + +import { MODEL_PROVIDERS } from "../model-providers"; + +const MINIMAX_ANTHROPIC_PROVIDERS = [ + { + id: "minimax_anthropic_cn", + baseURL: "https://api.minimaxi.com/anthropic", + }, + { + id: "minimax_anthropic_global", + baseURL: "https://api.minimax.io/anthropic", + }, +] as const; + +const MINIMAX_MODEL_IDS = ["MiniMax-M3", "MiniMax-M2.7"] as const; + +describe("MiniMax Anthropic providers", () => { + test("use SDK-compatible base URLs", () => { + for (const expected of MINIMAX_ANTHROPIC_PROVIDERS) { + expect(MODEL_PROVIDERS.find(({ id }) => id === expected.id)).toMatchObject({ + ...expected, + providerType: "anthropic", + defaultModelName: "MiniMax-M3", + }); + } + }); + + for (const provider of MINIMAX_ANTHROPIC_PROVIDERS) { + for (const model of MINIMAX_MODEL_IDS) { + test(`${provider.id} sends ${model} to one /v1/messages path`, async () => { + let requestURL = ""; + const client = new Anthropic({ + apiKey: "test-api-key", + baseURL: provider.baseURL, + fetch: async (input) => { + requestURL = typeof input === "string" ? input : input instanceof URL ? input.href : input.url; + return new Response( + JSON.stringify({ + id: "msg_test", + type: "message", + role: "assistant", + model, + content: [], + stop_reason: "end_turn", + stop_sequence: null, + usage: { input_tokens: 1, output_tokens: 1 }, + }), + { headers: { "content-type": "application/json" } }, + ); + }, + }); + + await client.messages.create({ + model, + max_tokens: 1, + messages: [{ role: "user", content: "test" }], + }); + + expect(requestURL).toBe(`${provider.baseURL}/v1/messages`); + expect(requestURL.match(/\/v1\/messages/g)).toHaveLength(1); + }); + } + } +}); diff --git a/src/cli/bootstrap/model-wizard.tsx b/src/cli/bootstrap/model-wizard.tsx index 5b9bd61..d1c5475 100644 --- a/src/cli/bootstrap/model-wizard.tsx +++ b/src/cli/bootstrap/model-wizard.tsx @@ -27,8 +27,11 @@ type ModelWizardProps = { function ModelWizard({ onComplete, onAbort }: ModelWizardProps) { const [step, setStep] = useState("provider"); const [providerIndex, setProviderIndex] = useState(0); + const selectedProvider = MODEL_PROVIDERS[providerIndex]!; const [apiKey, setApiKey] = useState(""); const [modelName, setModelName] = useState(""); + const suggestedModelName = selectedProvider.defaultModelName ?? ""; + const effectiveModelName = modelName.trim() || suggestedModelName; const [customBaseURL, setCustomBaseURL] = useState(""); const [pendingEntry, setPendingEntry] = useState(null); @@ -72,11 +75,9 @@ function ModelWizard({ onComplete, onAbort }: ModelWizardProps) { { isActive: step === "provider" }, ); - const selectedProvider = MODEL_PROVIDERS[providerIndex]!; - const finishWithBaseURL = (url: string) => { setCustomBaseURL(url); - const entry = buildModelEntry(url, apiKey, modelName, selectedProvider.providerType); + const entry = buildModelEntry(url, apiKey, effectiveModelName, selectedProvider.providerType); setPendingEntry(entry); setStep("confirm"); }; @@ -85,7 +86,7 @@ function ModelWizard({ onComplete, onAbort }: ModelWizardProps) { if (!selectedProvider.baseURL) { setStep("baseURL"); } else { - const entry = buildModelEntry(selectedProvider.baseURL, apiKey, modelName, selectedProvider.providerType); + const entry = buildModelEntry(selectedProvider.baseURL, apiKey, effectiveModelName, selectedProvider.providerType); setPendingEntry(entry); setStep("confirm"); } @@ -142,12 +143,12 @@ function ModelWizard({ onComplete, onAbort }: ModelWizardProps) { if (step === "modelName") { return ( - Enter a model name + Enter a model name{suggestedModelName ? ` (default: ${suggestedModelName})` : ""} Model: diff --git a/src/cli/model-providers.ts b/src/cli/model-providers.ts index 8b14b70..ea376af 100644 --- a/src/cli/model-providers.ts +++ b/src/cli/model-providers.ts @@ -5,6 +5,7 @@ export type ModelProviderConfig = { id: string; baseURL: string; providerType: ProviderType; + defaultModelName?: string; }; export const MODEL_PROVIDERS: ModelProviderConfig[] = [ @@ -18,8 +19,34 @@ export const MODEL_PROVIDERS: ModelProviderConfig[] = [ providerType: "openai", }, { label: "Qwen (Aliyun)", id: "qwen", baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1", providerType: "openai" }, - { label: "Minimax (Domestic)", id: "minimax_cn", baseURL: "https://api.minimaxi.com/v1", providerType: "openai" }, - { label: "Minimax (Global)", id: "minimax_global", baseURL: "https://api.minimax.io/v1", providerType: "openai" }, + { + label: "Minimax (Domestic)", + id: "minimax_cn", + baseURL: "https://api.minimaxi.com/v1", + providerType: "openai", + defaultModelName: "MiniMax-M3", + }, + { + label: "Minimax (Global)", + id: "minimax_global", + baseURL: "https://api.minimax.io/v1", + providerType: "openai", + defaultModelName: "MiniMax-M3", + }, + { + label: "Minimax Anthropic (Domestic)", + id: "minimax_anthropic_cn", + baseURL: "https://api.minimaxi.com/anthropic", + providerType: "anthropic", + defaultModelName: "MiniMax-M3", + }, + { + label: "Minimax Anthropic (Global)", + id: "minimax_anthropic_global", + baseURL: "https://api.minimax.io/anthropic", + providerType: "anthropic", + defaultModelName: "MiniMax-M3", + }, { label: "GLM (Zhipu AI)", id: "glm", baseURL: "https://open.bigmodel.cn/api/paas/v4", providerType: "openai" }, { label: "Kimi (Moonshot)", id: "kimi", baseURL: "https://api.moonshot.cn/v1", providerType: "openai" }, { label: "DeepSeek (OpenAI compatible)", id: "deepseek", baseURL: "https://api.deepseek.com/v1", providerType: "openai" }, From b2d17a6b20983a26619e27a02aab45e6a4fda8df Mon Sep 17 00:00:00 2001 From: octo-patch <266937838+octo-patch@users.noreply.github.com> Date: Mon, 13 Jul 2026 14:40:52 +0800 Subject: [PATCH 2/4] Fix MiniMax thinking defaults --- src/cli/__tests__/model-providers.test.ts | 75 ++++++++++++++++++++++- src/cli/index.tsx | 8 +-- src/cli/model-providers.ts | 43 ++++++++++--- 3 files changed, 110 insertions(+), 16 deletions(-) diff --git a/src/cli/__tests__/model-providers.test.ts b/src/cli/__tests__/model-providers.test.ts index 910d3c9..d0fccc2 100644 --- a/src/cli/__tests__/model-providers.test.ts +++ b/src/cli/__tests__/model-providers.test.ts @@ -1,14 +1,30 @@ import Anthropic from "@anthropic-ai/sdk"; import { describe, expect, test } from "bun:test"; +import { OpenAI } from "openai"; -import { MODEL_PROVIDERS } from "../model-providers"; +import { getDefaultModelOptions, MODEL_PROVIDERS } from "../model-providers"; + +const MINIMAX_OPENAI_PROVIDERS = [ + { + label: "MiniMax (Domestic)", + id: "minimax_cn", + baseURL: "https://api.minimaxi.com/v1", + }, + { + label: "MiniMax (Global)", + id: "minimax_global", + baseURL: "https://api.minimax.io/v1", + }, +] as const; const MINIMAX_ANTHROPIC_PROVIDERS = [ { + label: "MiniMax Anthropic (Domestic)", id: "minimax_anthropic_cn", baseURL: "https://api.minimaxi.com/anthropic", }, { + label: "MiniMax Anthropic (Global)", id: "minimax_anthropic_global", baseURL: "https://api.minimax.io/anthropic", }, @@ -16,8 +32,15 @@ const MINIMAX_ANTHROPIC_PROVIDERS = [ const MINIMAX_MODEL_IDS = ["MiniMax-M3", "MiniMax-M2.7"] as const; -describe("MiniMax Anthropic providers", () => { - test("use SDK-compatible base URLs", () => { +describe("MiniMax providers", () => { + test("use current endpoints and defaults", () => { + for (const expected of MINIMAX_OPENAI_PROVIDERS) { + expect(MODEL_PROVIDERS.find(({ id }) => id === expected.id)).toMatchObject({ + ...expected, + providerType: "openai", + defaultModelName: "MiniMax-M3", + }); + } for (const expected of MINIMAX_ANTHROPIC_PROVIDERS) { expect(MODEL_PROVIDERS.find(({ id }) => id === expected.id)).toMatchObject({ ...expected, @@ -27,6 +50,52 @@ describe("MiniMax Anthropic providers", () => { } }); + test("use API-compatible thinking defaults", () => { + expect(getDefaultModelOptions("MiniMax-M3")).toEqual({ + max_tokens: 16 * 1024, + thinking: { type: "adaptive" }, + }); + expect(getDefaultModelOptions("MiniMax-M2.7")).toEqual({ max_tokens: 16 * 1024 }); + expect(getDefaultModelOptions("claude-sonnet-4-5")).toEqual({ + max_tokens: 16 * 1024, + thinking: { type: "enabled" }, + }); + }); + + for (const provider of MINIMAX_OPENAI_PROVIDERS) { + for (const model of MINIMAX_MODEL_IDS) { + test(`${provider.id} sends ${model} to one /chat/completions path`, async () => { + let requestURL = ""; + const client = new OpenAI({ + apiKey: "test-api-key", + baseURL: provider.baseURL, + fetch: async (input) => { + requestURL = typeof input === "string" ? input : input instanceof URL ? input.href : input.url; + return new Response( + JSON.stringify({ + id: "chatcmpl_test", + object: "chat.completion", + created: 0, + model, + choices: [{ index: 0, message: { role: "assistant", content: "ok" }, finish_reason: "stop" }], + usage: { prompt_tokens: 1, completion_tokens: 1, total_tokens: 2 }, + }), + { headers: { "content-type": "application/json" } }, + ); + }, + }); + + await client.chat.completions.create({ + model, + messages: [{ role: "user", content: "test" }], + }); + + expect(requestURL).toBe(`${provider.baseURL}/chat/completions`); + expect(requestURL.match(/\/chat\/completions/g)).toHaveLength(1); + }); + } + } + for (const provider of MINIMAX_ANTHROPIC_PROVIDERS) { for (const model of MINIMAX_MODEL_IDS) { test(`${provider.id} sends ${model} to one /v1/messages path`, async () => { diff --git a/src/cli/index.tsx b/src/cli/index.tsx index 006c6f4..e76bcb6 100644 --- a/src/cli/index.tsx +++ b/src/cli/index.tsx @@ -13,6 +13,7 @@ import { OpenAIModelProvider } from "@/community/openai"; import type { ModelProvider } from "@/foundation"; import { Model } from "@/foundation"; +import { getDefaultModelOptions } from "./model-providers"; import { App } from "./tui"; import { loadAvailableCommands, type SlashCommand } from "./tui/command-registry"; import { AgentLoopProvider } from "./tui/hooks/use-agent-loop"; @@ -54,12 +55,7 @@ if (args.length > 0) { }); } - const model = new Model(entry.name, provider, { - max_tokens: 16 * 1024, - thinking: { - type: "enabled", - }, - }); + const model = new Model(entry.name, provider, getDefaultModelOptions(entry.name)); const skillsDirs = [ join(process.cwd(), "skills"), diff --git a/src/cli/model-providers.ts b/src/cli/model-providers.ts index ea376af..50a2c86 100644 --- a/src/cli/model-providers.ts +++ b/src/cli/model-providers.ts @@ -8,40 +8,64 @@ export type ModelProviderConfig = { defaultModelName?: string; }; +export function getDefaultModelOptions(modelName: string): { + max_tokens: number; + thinking?: { type: "adaptive" | "enabled" }; +} { + const options = { max_tokens: 16 * 1024 }; + if (modelName === "MiniMax-M3") { + return { ...options, thinking: { type: "adaptive" } }; + } + if (modelName.startsWith("MiniMax-M2")) { + return options; + } + return { ...options, thinking: { type: "enabled" } }; +} + export const MODEL_PROVIDERS: ModelProviderConfig[] = [ { label: "Anthropic (Claude)", id: "anthropic", baseURL: "https://api.anthropic.com", providerType: "anthropic" }, { label: "OpenAI", id: "openai", baseURL: "https://api.openai.com/v1", providerType: "openai" }, - { label: "Volcengine - General", id: "volcengine", baseURL: "https://ark.cn-beijing.volces.com/api/v3", providerType: "openai" }, + { + label: "Volcengine - General", + id: "volcengine", + baseURL: "https://ark.cn-beijing.volces.com/api/v3", + providerType: "openai", + }, { label: "Volcengine - Coding Plan", id: "volcengine_coding_plan", baseURL: "https://ark.cn-beijing.volces.com/api/coding/v3", providerType: "openai", }, - { label: "Qwen (Aliyun)", id: "qwen", baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1", providerType: "openai" }, { - label: "Minimax (Domestic)", + label: "Qwen (Aliyun)", + id: "qwen", + baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1", + providerType: "openai", + }, + { + label: "MiniMax (Domestic)", id: "minimax_cn", baseURL: "https://api.minimaxi.com/v1", providerType: "openai", defaultModelName: "MiniMax-M3", }, { - label: "Minimax (Global)", + label: "MiniMax (Global)", id: "minimax_global", baseURL: "https://api.minimax.io/v1", providerType: "openai", defaultModelName: "MiniMax-M3", }, { - label: "Minimax Anthropic (Domestic)", + label: "MiniMax Anthropic (Domestic)", id: "minimax_anthropic_cn", baseURL: "https://api.minimaxi.com/anthropic", providerType: "anthropic", defaultModelName: "MiniMax-M3", }, { - label: "Minimax Anthropic (Global)", + label: "MiniMax Anthropic (Global)", id: "minimax_anthropic_global", baseURL: "https://api.minimax.io/anthropic", providerType: "anthropic", @@ -49,6 +73,11 @@ export const MODEL_PROVIDERS: ModelProviderConfig[] = [ }, { label: "GLM (Zhipu AI)", id: "glm", baseURL: "https://open.bigmodel.cn/api/paas/v4", providerType: "openai" }, { label: "Kimi (Moonshot)", id: "kimi", baseURL: "https://api.moonshot.cn/v1", providerType: "openai" }, - { label: "DeepSeek (OpenAI compatible)", id: "deepseek", baseURL: "https://api.deepseek.com/v1", providerType: "openai" }, + { + label: "DeepSeek (OpenAI compatible)", + id: "deepseek", + baseURL: "https://api.deepseek.com/v1", + providerType: "openai", + }, { label: "Other", id: "other", baseURL: "", providerType: "openai" }, ]; From ee8b3f73f7be190a4f583b0c425cf027431c4b5a Mon Sep 17 00:00:00 2001 From: octo-patch <266937838+octo-patch@users.noreply.github.com> Date: Mon, 13 Jul 2026 14:42:11 +0800 Subject: [PATCH 3/4] Keep provider changes scoped --- src/cli/model-providers.ts | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/src/cli/model-providers.ts b/src/cli/model-providers.ts index 50a2c86..778c6cb 100644 --- a/src/cli/model-providers.ts +++ b/src/cli/model-providers.ts @@ -25,24 +25,14 @@ export function getDefaultModelOptions(modelName: string): { export const MODEL_PROVIDERS: ModelProviderConfig[] = [ { label: "Anthropic (Claude)", id: "anthropic", baseURL: "https://api.anthropic.com", providerType: "anthropic" }, { label: "OpenAI", id: "openai", baseURL: "https://api.openai.com/v1", providerType: "openai" }, - { - label: "Volcengine - General", - id: "volcengine", - baseURL: "https://ark.cn-beijing.volces.com/api/v3", - providerType: "openai", - }, + { label: "Volcengine - General", id: "volcengine", baseURL: "https://ark.cn-beijing.volces.com/api/v3", providerType: "openai" }, { label: "Volcengine - Coding Plan", id: "volcengine_coding_plan", baseURL: "https://ark.cn-beijing.volces.com/api/coding/v3", providerType: "openai", }, - { - label: "Qwen (Aliyun)", - id: "qwen", - baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1", - providerType: "openai", - }, + { label: "Qwen (Aliyun)", id: "qwen", baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1", providerType: "openai" }, { label: "MiniMax (Domestic)", id: "minimax_cn", @@ -73,11 +63,6 @@ export const MODEL_PROVIDERS: ModelProviderConfig[] = [ }, { label: "GLM (Zhipu AI)", id: "glm", baseURL: "https://open.bigmodel.cn/api/paas/v4", providerType: "openai" }, { label: "Kimi (Moonshot)", id: "kimi", baseURL: "https://api.moonshot.cn/v1", providerType: "openai" }, - { - label: "DeepSeek (OpenAI compatible)", - id: "deepseek", - baseURL: "https://api.deepseek.com/v1", - providerType: "openai", - }, + { label: "DeepSeek (OpenAI compatible)", id: "deepseek", baseURL: "https://api.deepseek.com/v1", providerType: "openai" }, { label: "Other", id: "other", baseURL: "", providerType: "openai" }, ]; From f3f89d69272eeb9290a02dcd6e50a251a0257d98 Mon Sep 17 00:00:00 2001 From: octo-patch <266937838+octo-patch@users.noreply.github.com> Date: Tue, 14 Jul 2026 19:40:14 +0800 Subject: [PATCH 4/4] Fix protocol-specific MiniMax defaults --- src/cli/__tests__/model-providers.test.ts | 8 +++++--- src/cli/bootstrap/model-wizard.tsx | 2 +- src/cli/index.tsx | 2 +- src/cli/model-providers.ts | 4 ++-- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/cli/__tests__/model-providers.test.ts b/src/cli/__tests__/model-providers.test.ts index d0fccc2..85c1432 100644 --- a/src/cli/__tests__/model-providers.test.ts +++ b/src/cli/__tests__/model-providers.test.ts @@ -51,12 +51,14 @@ describe("MiniMax providers", () => { }); test("use API-compatible thinking defaults", () => { - expect(getDefaultModelOptions("MiniMax-M3")).toEqual({ + expect(getDefaultModelOptions("MiniMax-M3", "openai")).toEqual({ max_tokens: 16 * 1024, thinking: { type: "adaptive" }, }); - expect(getDefaultModelOptions("MiniMax-M2.7")).toEqual({ max_tokens: 16 * 1024 }); - expect(getDefaultModelOptions("claude-sonnet-4-5")).toEqual({ + expect(getDefaultModelOptions("MiniMax-M3", "anthropic")).toEqual({ max_tokens: 16 * 1024 }); + expect(getDefaultModelOptions("MiniMax-M2.7", "openai")).toEqual({ max_tokens: 16 * 1024 }); + expect(getDefaultModelOptions("MiniMax-M2.7", "anthropic")).toEqual({ max_tokens: 16 * 1024 }); + expect(getDefaultModelOptions("custom-model", "anthropic")).toEqual({ max_tokens: 16 * 1024, thinking: { type: "enabled" }, }); diff --git a/src/cli/bootstrap/model-wizard.tsx b/src/cli/bootstrap/model-wizard.tsx index d1c5475..6e0f022 100644 --- a/src/cli/bootstrap/model-wizard.tsx +++ b/src/cli/bootstrap/model-wizard.tsx @@ -148,7 +148,7 @@ function ModelWizard({ onComplete, onAbort }: ModelWizardProps) { Model: diff --git a/src/cli/index.tsx b/src/cli/index.tsx index e76bcb6..50fde83 100644 --- a/src/cli/index.tsx +++ b/src/cli/index.tsx @@ -55,7 +55,7 @@ if (args.length > 0) { }); } - const model = new Model(entry.name, provider, getDefaultModelOptions(entry.name)); + const model = new Model(entry.name, provider, getDefaultModelOptions(entry.name, entry.provider)); const skillsDirs = [ join(process.cwd(), "skills"), diff --git a/src/cli/model-providers.ts b/src/cli/model-providers.ts index 778c6cb..37cb384 100644 --- a/src/cli/model-providers.ts +++ b/src/cli/model-providers.ts @@ -8,13 +8,13 @@ export type ModelProviderConfig = { defaultModelName?: string; }; -export function getDefaultModelOptions(modelName: string): { +export function getDefaultModelOptions(modelName: string, providerType: ProviderType): { max_tokens: number; thinking?: { type: "adaptive" | "enabled" }; } { const options = { max_tokens: 16 * 1024 }; if (modelName === "MiniMax-M3") { - return { ...options, thinking: { type: "adaptive" } }; + return providerType === "openai" ? { ...options, thinking: { type: "adaptive" } } : options; } if (modelName.startsWith("MiniMax-M2")) { return options;