mirror of
https://github.com/farcasclaudiu/openclaw.git
synced 2026-06-22 05:01:43 +03:00
fix: wire minimax-api-key-cn onboarding (#15191) (thanks @liuy)
This commit is contained in:
+1
-1
@@ -82,5 +82,5 @@ USER.md
|
|||||||
/memory/
|
/memory/
|
||||||
.agent/*.json
|
.agent/*.json
|
||||||
!.agent/workflows/
|
!.agent/workflows/
|
||||||
local/
|
/local/
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ Docs: https://docs.openclaw.ai
|
|||||||
- Skills: remove duplicate `local-places` Google Places skill/proxy and keep `goplaces` as the single supported Google Places path.
|
- Skills: remove duplicate `local-places` Google Places skill/proxy and keep `goplaces` as the single supported Google Places path.
|
||||||
- Agents: add pre-prompt context diagnostics (`messages`, `systemPromptChars`, `promptChars`, provider/model, session file) before embedded runner prompt calls to improve overflow debugging. (#8930) Thanks @Glucksberg.
|
- Agents: add pre-prompt context diagnostics (`messages`, `systemPromptChars`, `promptChars`, provider/model, session file) before embedded runner prompt calls to improve overflow debugging. (#8930) Thanks @Glucksberg.
|
||||||
- Onboarding/Providers: add first-class Hugging Face Inference provider support (provider wiring, onboarding auth choice/API key flow, and default-model selection), and preserve Hugging Face auth intent in auth-choice remapping (`tokenProvider=huggingface` with `authChoice=apiKey`) while skipping env-override prompts when an explicit token is provided. (#13472) Thanks @Josephrp.
|
- Onboarding/Providers: add first-class Hugging Face Inference provider support (provider wiring, onboarding auth choice/API key flow, and default-model selection), and preserve Hugging Face auth intent in auth-choice remapping (`tokenProvider=huggingface` with `authChoice=apiKey`) while skipping env-override prompts when an explicit token is provided. (#13472) Thanks @Josephrp.
|
||||||
|
- Onboarding/Providers: add `minimax-api-key-cn` auth choice for the MiniMax China API endpoint. (#15191) Thanks @liuy.
|
||||||
|
|
||||||
### Breaking
|
### Breaking
|
||||||
|
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ describe("buildAuthChoiceOptions", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(options.some((opt) => opt.value === "minimax-api")).toBe(true);
|
expect(options.some((opt) => opt.value === "minimax-api")).toBe(true);
|
||||||
|
expect(options.some((opt) => opt.value === "minimax-api-key-cn")).toBe(true);
|
||||||
expect(options.some((opt) => opt.value === "minimax-api-lightning")).toBe(true);
|
expect(options.some((opt) => opt.value === "minimax-api-lightning")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ const AUTH_CHOICE_GROUP_DEFS: {
|
|||||||
value: "minimax",
|
value: "minimax",
|
||||||
label: "MiniMax",
|
label: "MiniMax",
|
||||||
hint: "M2.5 (recommended)",
|
hint: "M2.5 (recommended)",
|
||||||
choices: ["minimax-portal", "minimax-api", "minimax-api-lightning"],
|
choices: ["minimax-portal", "minimax-api", "minimax-api-key-cn", "minimax-api-lightning"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: "moonshot",
|
value: "moonshot",
|
||||||
@@ -286,6 +286,11 @@ const BASE_AUTH_CHOICE_OPTIONS: ReadonlyArray<AuthChoiceOption> = [
|
|||||||
hint: "Claude, GPT, Gemini via opencode.ai/zen",
|
hint: "Claude, GPT, Gemini via opencode.ai/zen",
|
||||||
},
|
},
|
||||||
{ value: "minimax-api", label: "MiniMax M2.5" },
|
{ value: "minimax-api", label: "MiniMax M2.5" },
|
||||||
|
{
|
||||||
|
value: "minimax-api-key-cn",
|
||||||
|
label: "MiniMax M2.5 (CN)",
|
||||||
|
hint: "China endpoint (api.minimaxi.com)",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
value: "minimax-api-lightning",
|
value: "minimax-api-lightning",
|
||||||
label: "MiniMax M2.5 Lightning",
|
label: "MiniMax M2.5 Lightning",
|
||||||
|
|||||||
@@ -6,7 +6,11 @@ import type { RuntimeEnv } from "../runtime.js";
|
|||||||
import type { WizardPrompter } from "../wizard/prompts.js";
|
import type { WizardPrompter } from "../wizard/prompts.js";
|
||||||
import type { AuthChoice } from "./onboard-types.js";
|
import type { AuthChoice } from "./onboard-types.js";
|
||||||
import { applyAuthChoice, resolvePreferredProviderForAuthChoice } from "./auth-choice.js";
|
import { applyAuthChoice, resolvePreferredProviderForAuthChoice } from "./auth-choice.js";
|
||||||
import { ZAI_CODING_CN_BASE_URL, ZAI_CODING_GLOBAL_BASE_URL } from "./onboard-auth.js";
|
import {
|
||||||
|
MINIMAX_CN_API_BASE_URL,
|
||||||
|
ZAI_CODING_CN_BASE_URL,
|
||||||
|
ZAI_CODING_GLOBAL_BASE_URL,
|
||||||
|
} from "./onboard-auth.js";
|
||||||
|
|
||||||
vi.mock("../providers/github-copilot-auth.js", () => ({
|
vi.mock("../providers/github-copilot-auth.js", () => ({
|
||||||
githubCopilotLoginCommand: vi.fn(async () => {}),
|
githubCopilotLoginCommand: vi.fn(async () => {}),
|
||||||
@@ -209,6 +213,60 @@ describe("applyAuthChoice", () => {
|
|||||||
expect(parsed.profiles?.["minimax:default"]?.key).toBe("sk-minimax-test");
|
expect(parsed.profiles?.["minimax:default"]?.key).toBe("sk-minimax-test");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("prompts and writes MiniMax API key when selecting minimax-api-key-cn", async () => {
|
||||||
|
tempStateDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-auth-"));
|
||||||
|
process.env.OPENCLAW_STATE_DIR = tempStateDir;
|
||||||
|
process.env.OPENCLAW_AGENT_DIR = path.join(tempStateDir, "agent");
|
||||||
|
process.env.PI_CODING_AGENT_DIR = process.env.OPENCLAW_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-api-key-cn",
|
||||||
|
config: {},
|
||||||
|
prompter,
|
||||||
|
runtime,
|
||||||
|
setDefaultModel: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(text).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({ message: "Enter MiniMax China API key" }),
|
||||||
|
);
|
||||||
|
expect(result.config.auth?.profiles?.["minimax:default"]).toMatchObject({
|
||||||
|
provider: "minimax",
|
||||||
|
mode: "api_key",
|
||||||
|
});
|
||||||
|
expect(result.config.models?.providers?.minimax?.baseUrl).toBe(MINIMAX_CN_API_BASE_URL);
|
||||||
|
|
||||||
|
const authProfilePath = authProfilePathFor(requireAgentDir());
|
||||||
|
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("prompts and writes Synthetic API key when selecting synthetic-api-key", async () => {
|
it("prompts and writes Synthetic API key when selecting synthetic-api-key", async () => {
|
||||||
tempStateDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-auth-"));
|
tempStateDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-auth-"));
|
||||||
process.env.OPENCLAW_STATE_DIR = tempStateDir;
|
process.env.OPENCLAW_STATE_DIR = tempStateDir;
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ const PREFERRED_PROVIDER_BY_AUTH_CHOICE: Partial<Record<AuthChoice, string>> = {
|
|||||||
"copilot-proxy": "copilot-proxy",
|
"copilot-proxy": "copilot-proxy",
|
||||||
"minimax-cloud": "minimax",
|
"minimax-cloud": "minimax",
|
||||||
"minimax-api": "minimax",
|
"minimax-api": "minimax",
|
||||||
|
"minimax-api-key-cn": "minimax",
|
||||||
"minimax-api-lightning": "minimax",
|
"minimax-api-lightning": "minimax",
|
||||||
minimax: "lmstudio",
|
minimax: "lmstudio",
|
||||||
"opencode-zen": "opencode",
|
"opencode-zen": "opencode",
|
||||||
|
|||||||
@@ -155,6 +155,72 @@ async function expectApiKeyProfile(params: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe("onboard (non-interactive): provider auth", () => {
|
describe("onboard (non-interactive): provider auth", () => {
|
||||||
|
it("stores MiniMax API key and uses global baseUrl by default", async () => {
|
||||||
|
await withOnboardEnv("openclaw-onboard-minimax-", async ({ configPath, runtime }) => {
|
||||||
|
await runNonInteractive(
|
||||||
|
{
|
||||||
|
nonInteractive: true,
|
||||||
|
authChoice: "minimax-api",
|
||||||
|
minimaxApiKey: "sk-minimax-test",
|
||||||
|
skipHealth: true,
|
||||||
|
skipChannels: true,
|
||||||
|
skipSkills: true,
|
||||||
|
json: true,
|
||||||
|
},
|
||||||
|
runtime,
|
||||||
|
);
|
||||||
|
|
||||||
|
const cfg = await readJsonFile<{
|
||||||
|
auth?: { profiles?: Record<string, { provider?: string; mode?: string }> };
|
||||||
|
agents?: { defaults?: { model?: { primary?: string } } };
|
||||||
|
models?: { providers?: Record<string, { baseUrl?: string }> };
|
||||||
|
}>(configPath);
|
||||||
|
|
||||||
|
expect(cfg.auth?.profiles?.["minimax:default"]?.provider).toBe("minimax");
|
||||||
|
expect(cfg.auth?.profiles?.["minimax:default"]?.mode).toBe("api_key");
|
||||||
|
expect(cfg.models?.providers?.minimax?.baseUrl).toBe("https://api.minimax.io/anthropic");
|
||||||
|
expect(cfg.agents?.defaults?.model?.primary).toBe("minimax/MiniMax-M2.5");
|
||||||
|
await expectApiKeyProfile({
|
||||||
|
profileId: "minimax:default",
|
||||||
|
provider: "minimax",
|
||||||
|
key: "sk-minimax-test",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, 60_000);
|
||||||
|
|
||||||
|
it("supports MiniMax CN API endpoint auth choice", async () => {
|
||||||
|
await withOnboardEnv("openclaw-onboard-minimax-cn-", async ({ configPath, runtime }) => {
|
||||||
|
await runNonInteractive(
|
||||||
|
{
|
||||||
|
nonInteractive: true,
|
||||||
|
authChoice: "minimax-api-key-cn",
|
||||||
|
minimaxApiKey: "sk-minimax-test",
|
||||||
|
skipHealth: true,
|
||||||
|
skipChannels: true,
|
||||||
|
skipSkills: true,
|
||||||
|
json: true,
|
||||||
|
},
|
||||||
|
runtime,
|
||||||
|
);
|
||||||
|
|
||||||
|
const cfg = await readJsonFile<{
|
||||||
|
auth?: { profiles?: Record<string, { provider?: string; mode?: string }> };
|
||||||
|
agents?: { defaults?: { model?: { primary?: string } } };
|
||||||
|
models?: { providers?: Record<string, { baseUrl?: string }> };
|
||||||
|
}>(configPath);
|
||||||
|
|
||||||
|
expect(cfg.auth?.profiles?.["minimax:default"]?.provider).toBe("minimax");
|
||||||
|
expect(cfg.auth?.profiles?.["minimax:default"]?.mode).toBe("api_key");
|
||||||
|
expect(cfg.models?.providers?.minimax?.baseUrl).toBe("https://api.minimaxi.com/anthropic");
|
||||||
|
expect(cfg.agents?.defaults?.model?.primary).toBe("minimax/MiniMax-M2.5");
|
||||||
|
await expectApiKeyProfile({
|
||||||
|
profileId: "minimax:default",
|
||||||
|
provider: "minimax",
|
||||||
|
key: "sk-minimax-test",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, 60_000);
|
||||||
|
|
||||||
it("stores Z.AI API key and uses global baseUrl by default", async () => {
|
it("stores Z.AI API key and uses global baseUrl by default", async () => {
|
||||||
await withOnboardEnv("openclaw-onboard-zai-", async ({ configPath, runtime }) => {
|
await withOnboardEnv("openclaw-onboard-zai-", async ({ configPath, runtime }) => {
|
||||||
await runNonInteractive(
|
await runNonInteractive(
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
applyQianfanConfig,
|
applyQianfanConfig,
|
||||||
applyKimiCodeConfig,
|
applyKimiCodeConfig,
|
||||||
applyMinimaxApiConfig,
|
applyMinimaxApiConfig,
|
||||||
|
applyMinimaxApiConfigCn,
|
||||||
applyMinimaxConfig,
|
applyMinimaxConfig,
|
||||||
applyMoonshotConfig,
|
applyMoonshotConfig,
|
||||||
applyMoonshotConfigCn,
|
applyMoonshotConfigCn,
|
||||||
@@ -570,6 +571,7 @@ export async function applyNonInteractiveAuthChoice(params: {
|
|||||||
if (
|
if (
|
||||||
authChoice === "minimax-cloud" ||
|
authChoice === "minimax-cloud" ||
|
||||||
authChoice === "minimax-api" ||
|
authChoice === "minimax-api" ||
|
||||||
|
authChoice === "minimax-api-key-cn" ||
|
||||||
authChoice === "minimax-api-lightning"
|
authChoice === "minimax-api-lightning"
|
||||||
) {
|
) {
|
||||||
const resolved = await resolveNonInteractiveApiKey({
|
const resolved = await resolveNonInteractiveApiKey({
|
||||||
@@ -592,8 +594,10 @@ export async function applyNonInteractiveAuthChoice(params: {
|
|||||||
mode: "api_key",
|
mode: "api_key",
|
||||||
});
|
});
|
||||||
const modelId =
|
const modelId =
|
||||||
authChoice === "minimax-api-lightning" ? "MiniMax-M2.1-lightning" : "MiniMax-M2.1";
|
authChoice === "minimax-api-lightning" ? "MiniMax-M2.5-Lightning" : "MiniMax-M2.5";
|
||||||
return applyMinimaxApiConfig(nextConfig, modelId);
|
return authChoice === "minimax-api-key-cn"
|
||||||
|
? applyMinimaxApiConfigCn(nextConfig, modelId)
|
||||||
|
: applyMinimaxApiConfig(nextConfig, modelId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (authChoice === "minimax") {
|
if (authChoice === "minimax") {
|
||||||
|
|||||||
Reference in New Issue
Block a user