From 0a8fa0e0010379813fd090e013c0eb268916f342 Mon Sep 17 00:00:00 2001 From: chengzhichao-xydt Date: Fri, 13 Mar 2026 00:10:38 +0800 Subject: [PATCH] Moonshot: respect explicit baseUrl for CN endpoint so platform.moonshot.cn keys authenticate (#33637) (#33696) * Moonshot: respect explicit baseUrl for CN endpoint so platform.moonshot.cn keys authenticate (#33637) * Moonshot: address review - remove dead constant, import canonical URLs (#33696) --- CHANGELOG.md | 1 + .../models-config.providers.moonshot.test.ts | 60 +++++++++++++++++++ src/agents/models-config.providers.ts | 18 +++++- 3 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 src/agents/models-config.providers.moonshot.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 025d607eb2b..aac8fd373b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ Docs: https://docs.openclaw.ai ### Fixes +- Moonshot CN API: respect explicit `baseUrl` (api.moonshot.cn) in implicit provider resolution so platform.moonshot.cn API keys authenticate correctly instead of returning HTTP 401. (#33637) Thanks @chengzhichao-xydt. - Cron/proactive delivery: keep isolated direct cron sends out of the write-ahead resend queue so transient-send retries do not replay duplicate proactive messages after restart. (#40646) Thanks @openperf and @vincentkoc. - TUI/chat log: reuse the active assistant message component for the same streaming run so `openclaw tui` no longer renders duplicate assistant replies. (#35364) Thanks @lisitan. - macOS/Reminders: add the missing `NSRemindersUsageDescription` to the bundled app so `apple-reminders` can trigger the system permission prompt from OpenClaw.app. (#8559) Thanks @dinakars777. diff --git a/src/agents/models-config.providers.moonshot.test.ts b/src/agents/models-config.providers.moonshot.test.ts new file mode 100644 index 00000000000..00e1f5949c6 --- /dev/null +++ b/src/agents/models-config.providers.moonshot.test.ts @@ -0,0 +1,60 @@ +import { mkdtempSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { describe, expect, it } from "vitest"; +import { + MOONSHOT_BASE_URL as MOONSHOT_AI_BASE_URL, + MOONSHOT_CN_BASE_URL, +} from "../commands/onboard-auth.models.js"; +import { captureEnv } from "../test-utils/env.js"; +import { resolveImplicitProviders } from "./models-config.providers.js"; + +describe("moonshot implicit provider (#33637)", () => { + it("uses explicit CN baseUrl when provided", async () => { + const agentDir = mkdtempSync(join(tmpdir(), "openclaw-test-")); + const envSnapshot = captureEnv(["MOONSHOT_API_KEY"]); + process.env.MOONSHOT_API_KEY = "sk-test-cn"; + + try { + const providers = await resolveImplicitProviders({ + agentDir, + explicitProviders: { + moonshot: { + baseUrl: MOONSHOT_CN_BASE_URL, + api: "openai-completions", + models: [ + { + id: "kimi-k2.5", + name: "Kimi K2.5", + reasoning: false, + input: ["text", "image"], + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 256000, + maxTokens: 8192, + }, + ], + }, + }, + }); + expect(providers?.moonshot).toBeDefined(); + expect(providers?.moonshot?.baseUrl).toBe(MOONSHOT_CN_BASE_URL); + expect(providers?.moonshot?.apiKey).toBeDefined(); + } finally { + envSnapshot.restore(); + } + }); + + it("defaults to .ai baseUrl when no explicit provider", async () => { + const agentDir = mkdtempSync(join(tmpdir(), "openclaw-test-")); + const envSnapshot = captureEnv(["MOONSHOT_API_KEY"]); + process.env.MOONSHOT_API_KEY = "sk-test"; + + try { + const providers = await resolveImplicitProviders({ agentDir }); + expect(providers?.moonshot).toBeDefined(); + expect(providers?.moonshot?.baseUrl).toBe(MOONSHOT_AI_BASE_URL); + } finally { + envSnapshot.restore(); + } + }); +}); diff --git a/src/agents/models-config.providers.ts b/src/agents/models-config.providers.ts index 411072f2d7a..d886264dfaf 100644 --- a/src/agents/models-config.providers.ts +++ b/src/agents/models-config.providers.ts @@ -610,6 +610,7 @@ function withApiKey( build: (params: { apiKey: string; discoveryApiKey?: string; + explicitProvider?: ProviderConfig; }) => ProviderConfig | Promise, ): ImplicitProviderLoader { return async (ctx) => { @@ -618,7 +619,11 @@ function withApiKey( return undefined; } return { - [providerKey]: await build({ apiKey, discoveryApiKey }), + [providerKey]: await build({ + apiKey, + discoveryApiKey, + explicitProvider: ctx.explicitProviders?.[providerKey], + }), }; }; } @@ -651,7 +656,16 @@ function mergeImplicitProviderSet( const SIMPLE_IMPLICIT_PROVIDER_LOADERS: ImplicitProviderLoader[] = [ withApiKey("minimax", async ({ apiKey }) => ({ ...buildMinimaxProvider(), apiKey })), - withApiKey("moonshot", async ({ apiKey }) => ({ ...buildMoonshotProvider(), apiKey })), + withApiKey("moonshot", async ({ apiKey, explicitProvider }) => { + const explicitBaseUrl = explicitProvider?.baseUrl; + return { + ...buildMoonshotProvider(), + ...(typeof explicitBaseUrl === "string" && explicitBaseUrl.trim() + ? { baseUrl: explicitBaseUrl.trim() } + : {}), + apiKey, + }; + }), withApiKey("kimi-coding", async ({ apiKey }) => ({ ...buildKimiCodingProvider(), apiKey })), withApiKey("synthetic", async ({ apiKey }) => ({ ...buildSyntheticProvider(), apiKey })), withApiKey("venice", async ({ apiKey }) => ({ ...(await buildVeniceProvider()), apiKey })),