From 5482acd43d4c896c510885ecec26c10b77279ce8 Mon Sep 17 00:00:00 2001 From: Octopus Date: Mon, 13 Apr 2026 18:17:29 +0800 Subject: [PATCH] fix(providers): add Moonshot (Global) provider for api.moonshot.ai endpoint (#839) Co-authored-by: octo-patch --- electron/shared/providers/registry.ts | 31 +++++++++++++++++++++++++++ electron/utils/openclaw-auth.ts | 31 ++++++++++++++++----------- electron/utils/provider-keys.ts | 1 + src/assets/providers/index.ts | 1 + src/lib/providers.ts | 3 +++ 5 files changed, 54 insertions(+), 13 deletions(-) diff --git a/electron/shared/providers/registry.ts b/electron/shared/providers/registry.ts index 1c6b65efa..aa483e372 100644 --- a/electron/shared/providers/registry.ts +++ b/electron/shared/providers/registry.ts @@ -144,6 +144,37 @@ export const PROVIDER_DEFINITIONS: ProviderDefinition[] = [ ], }, }, + { + id: 'moonshot-global', + name: 'Moonshot (Global)', + icon: '🌙', + placeholder: 'sk-...', + model: 'Kimi', + requiresApiKey: true, + defaultBaseUrl: 'https://api.moonshot.ai/v1', + defaultModelId: 'kimi-k2.5', + category: 'official', + envVar: 'MOONSHOT_GLOBAL_API_KEY', + supportedAuthModes: ['api_key'], + defaultAuthMode: 'api_key', + supportsMultipleAccounts: true, + providerConfig: { + baseUrl: 'https://api.moonshot.ai/v1', + api: 'openai-completions', + apiKeyEnv: 'MOONSHOT_GLOBAL_API_KEY', + models: [ + { + id: 'kimi-k2.5', + name: 'Kimi K2.5', + reasoning: false, + input: ['text'], + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 256000, + maxTokens: 8192, + }, + ], + }, + }, { id: 'siliconflow', name: 'SiliconFlow (CN)', diff --git a/electron/utils/openclaw-auth.ts b/electron/utils/openclaw-auth.ts index 8b322b37b..7a53776e3 100644 --- a/electron/utils/openclaw-auth.ts +++ b/electron/utils/openclaw-auth.ts @@ -21,6 +21,7 @@ import { } from './provider-registry'; import { OPENCLAW_PROVIDER_KEY_MOONSHOT, + OPENCLAW_PROVIDER_KEY_MOONSHOT_GLOBAL, isOAuthProviderType, isOpenClawOAuthPluginProviderKey, } from './provider-keys'; @@ -849,14 +850,16 @@ function removeLegacyMoonshotKimiSearchConfig(config: Record): function upsertMoonshotWebSearchConfig( config: Record, + providerKey: string, + baseUrl: string, legacyKimi?: Record, ): void { const plugins = isPlainRecord(config.plugins) ? config.plugins : (Array.isArray(config.plugins) ? { load: [...config.plugins] } : {}); const entries = isPlainRecord(plugins.entries) ? plugins.entries : {}; - const moonshot = isPlainRecord(entries[OPENCLAW_PROVIDER_KEY_MOONSHOT]) - ? entries[OPENCLAW_PROVIDER_KEY_MOONSHOT] as Record + const moonshot = isPlainRecord(entries[providerKey]) + ? entries[providerKey] as Record : {}; const moonshotConfig = isPlainRecord(moonshot.config) ? moonshot.config as Record : {}; const currentWebSearch = isPlainRecord(moonshotConfig.webSearch) @@ -865,25 +868,27 @@ function upsertMoonshotWebSearchConfig( const nextWebSearch = { ...(legacyKimi || {}), ...currentWebSearch }; delete nextWebSearch.apiKey; - nextWebSearch.baseUrl = 'https://api.moonshot.cn/v1'; + nextWebSearch.baseUrl = baseUrl; moonshotConfig.webSearch = nextWebSearch; moonshot.config = moonshotConfig; - entries[OPENCLAW_PROVIDER_KEY_MOONSHOT] = moonshot; + entries[providerKey] = moonshot; plugins.entries = entries; config.plugins = plugins; } function ensureMoonshotKimiWebSearchCnBaseUrl(config: Record, provider: string): void { - if (provider !== OPENCLAW_PROVIDER_KEY_MOONSHOT) return; + if (provider === OPENCLAW_PROVIDER_KEY_MOONSHOT) { + const tools = isPlainRecord(config.tools) ? config.tools : null; + const web = tools && isPlainRecord(tools.web) ? tools.web : null; + const search = web && isPlainRecord(web.search) ? web.search : null; + const legacyKimi = search && isPlainRecord(search.kimi) ? search.kimi : undefined; - const tools = isPlainRecord(config.tools) ? config.tools : null; - const web = tools && isPlainRecord(tools.web) ? tools.web : null; - const search = web && isPlainRecord(web.search) ? web.search : null; - const legacyKimi = search && isPlainRecord(search.kimi) ? search.kimi : undefined; - - upsertMoonshotWebSearchConfig(config, legacyKimi); - removeLegacyMoonshotKimiSearchConfig(config); + upsertMoonshotWebSearchConfig(config, OPENCLAW_PROVIDER_KEY_MOONSHOT, 'https://api.moonshot.cn/v1', legacyKimi); + removeLegacyMoonshotKimiSearchConfig(config); + } else if (provider === OPENCLAW_PROVIDER_KEY_MOONSHOT_GLOBAL) { + upsertMoonshotWebSearchConfig(config, OPENCLAW_PROVIDER_KEY_MOONSHOT_GLOBAL, 'https://api.moonshot.ai/v1'); + } } /** @@ -1461,7 +1466,7 @@ export async function sanitizeOpenClawConfig(): Promise { const hadLegacyKimi = Boolean(legacyKimi); if (legacyKimi) { - upsertMoonshotWebSearchConfig(config, legacyKimi); + upsertMoonshotWebSearchConfig(config, OPENCLAW_PROVIDER_KEY_MOONSHOT, 'https://api.moonshot.cn/v1', legacyKimi); removeLegacyMoonshotKimiSearchConfig(config); modified = true; console.log('[sanitize] Migrated legacy "tools.web.search.kimi" to "plugins.entries.moonshot.config.webSearch"'); diff --git a/electron/utils/provider-keys.ts b/electron/utils/provider-keys.ts index 390db5f57..ffa6facaa 100644 --- a/electron/utils/provider-keys.ts +++ b/electron/utils/provider-keys.ts @@ -2,6 +2,7 @@ const MULTI_INSTANCE_PROVIDER_TYPES = new Set(['custom', 'ollama']); export const OPENCLAW_PROVIDER_KEY_MINIMAX = 'minimax-portal'; export const OPENCLAW_PROVIDER_KEY_MOONSHOT = 'moonshot'; +export const OPENCLAW_PROVIDER_KEY_MOONSHOT_GLOBAL = 'moonshot-global'; export const OAUTH_PROVIDER_TYPES = ['minimax-portal', 'minimax-portal-cn'] as const; export const OPENCLAW_OAUTH_PLUGIN_PROVIDER_KEYS = [ OPENCLAW_PROVIDER_KEY_MINIMAX, diff --git a/src/assets/providers/index.ts b/src/assets/providers/index.ts index 70aaa090a..3c9572dc4 100644 --- a/src/assets/providers/index.ts +++ b/src/assets/providers/index.ts @@ -17,6 +17,7 @@ export const providerIcons: Record = { openrouter, ark, moonshot, + 'moonshot-global': moonshot, siliconflow, 'minimax-portal': minimaxPortal, 'minimax-portal-cn': minimaxPortal, diff --git a/src/lib/providers.ts b/src/lib/providers.ts index 74a570727..e3d4849d7 100644 --- a/src/lib/providers.ts +++ b/src/lib/providers.ts @@ -13,6 +13,7 @@ export const PROVIDER_TYPES = [ 'openrouter', 'ark', 'moonshot', + 'moonshot-global', 'siliconflow', 'minimax-portal', 'minimax-portal-cn', @@ -29,6 +30,7 @@ export const BUILTIN_PROVIDER_TYPES = [ 'openrouter', 'ark', 'moonshot', + 'moonshot-global', 'siliconflow', 'minimax-portal', 'minimax-portal-cn', @@ -172,6 +174,7 @@ export const PROVIDER_TYPE_INFO: ProviderTypeInfo[] = [ { id: 'openrouter', name: 'OpenRouter', icon: '🌐', placeholder: 'sk-or-v1-...', model: 'Multi-Model', requiresApiKey: true, showModelId: true, modelIdPlaceholder: 'openai/gpt-5.4', defaultModelId: 'openai/gpt-5.4', docsUrl: 'https://openrouter.ai/models' }, { id: 'minimax-portal-cn', name: 'MiniMax (CN)', icon: '☁️', placeholder: 'sk-...', model: 'MiniMax', requiresApiKey: false, isOAuth: true, supportsApiKey: true, defaultModelId: 'MiniMax-M2.7', showModelId: true, showModelIdInDevModeOnly: true, modelIdPlaceholder: 'MiniMax-M2.7', apiKeyUrl: 'https://platform.minimaxi.com/' }, { id: 'moonshot', name: 'Moonshot (CN)', icon: '🌙', placeholder: 'sk-...', model: 'Kimi', requiresApiKey: true, defaultBaseUrl: 'https://api.moonshot.cn/v1', defaultModelId: 'kimi-k2.5', docsUrl: 'https://platform.moonshot.cn/' }, + { id: 'moonshot-global', name: 'Moonshot (Global)', icon: '🌙', placeholder: 'sk-...', model: 'Kimi', requiresApiKey: true, defaultBaseUrl: 'https://api.moonshot.ai/v1', defaultModelId: 'kimi-k2.5', docsUrl: 'https://platform.moonshot.ai/' }, { id: 'siliconflow', name: 'SiliconFlow (CN)', icon: '🌊', placeholder: 'sk-...', model: 'Multi-Model', requiresApiKey: true, defaultBaseUrl: 'https://api.siliconflow.cn/v1', showModelId: true, showModelIdInDevModeOnly: true, modelIdPlaceholder: 'deepseek-ai/DeepSeek-V3', defaultModelId: 'deepseek-ai/DeepSeek-V3', docsUrl: 'https://docs.siliconflow.cn/cn/userguide/introduction' }, { id: 'minimax-portal', name: 'MiniMax (Global)', icon: '☁️', placeholder: 'sk-...', model: 'MiniMax', requiresApiKey: false, isOAuth: true, supportsApiKey: true, defaultModelId: 'MiniMax-M2.7', showModelId: true, showModelIdInDevModeOnly: true, modelIdPlaceholder: 'MiniMax-M2.7', apiKeyUrl: 'https://platform.minimax.io' }, { id: 'modelstudio', name: 'Model Studio', icon: '☁️', placeholder: 'sk-...', model: 'Qwen', requiresApiKey: true, defaultBaseUrl: 'https://coding.dashscope.aliyuncs.com/v1', showBaseUrl: true, defaultModelId: 'qwen3.5-plus', showModelId: true, showModelIdInDevModeOnly: true, modelIdPlaceholder: 'qwen3.5-plus', apiKeyUrl: 'https://bailian.console.aliyun.com/', hidden: true },