From 28dff9dfe38641c9ee3ab019ab798a34505f8abf Mon Sep 17 00:00:00 2001 From: SuYao Date: Wed, 26 Nov 2025 19:19:34 +0800 Subject: [PATCH] feat: add silicon provider support for Anthropic API compatibility (#11468) * feat: add silicon provider support for Anthropic API compatibility * fix: update handling of ANTHROPIC_BASE_URL for silicon provider compatibility * fix: update anthropicApiHost for silicon provider to use the correct endpoint * fix: remove silicon from CLAUDE_OFFICIAL_SUPPORTED_PROVIDERS * chore: add comment to clarify silicon model fallback logic in CLAUDE_OFFICIAL_SUPPORTED_PROVIDERS --- packages/shared/config/providers.ts | 48 +++++++++++++++++++ src/main/apiServer/utils/index.ts | 3 ++ src/renderer/src/config/providers.ts | 2 + src/renderer/src/pages/code/CodeToolsPage.tsx | 5 ++ src/renderer/src/pages/code/index.ts | 23 +++++++-- .../ProviderSettings/ProviderSetting.tsx | 5 +- src/renderer/src/store/index.ts | 2 +- src/renderer/src/store/migrate.ts | 22 +++++++++ 8 files changed, 105 insertions(+), 5 deletions(-) create mode 100644 packages/shared/config/providers.ts diff --git a/packages/shared/config/providers.ts b/packages/shared/config/providers.ts new file mode 100644 index 0000000000..f7744150e2 --- /dev/null +++ b/packages/shared/config/providers.ts @@ -0,0 +1,48 @@ +/** + * @fileoverview Shared provider configuration for Claude Code and Anthropic API compatibility + * + * This module defines which models from specific providers support the Anthropic API endpoint. + * Used by both the Code Tools page and the Anthropic SDK client. + */ + +/** + * Silicon provider models that support Anthropic API endpoint. + * These models can be used with Claude Code via the Anthropic-compatible API. + * + * @see https://docs.siliconflow.cn/cn/api-reference/chat-completions/messages + */ +export const SILICON_ANTHROPIC_COMPATIBLE_MODELS: readonly string[] = [ + // DeepSeek V3.1 series + 'Pro/deepseek-ai/DeepSeek-V3.1-Terminus', + 'deepseek-ai/DeepSeek-V3.1', + 'Pro/deepseek-ai/DeepSeek-V3.1', + // DeepSeek V3 series + 'deepseek-ai/DeepSeek-V3', + 'Pro/deepseek-ai/DeepSeek-V3', + // Moonshot/Kimi series + 'moonshotai/Kimi-K2-Instruct-0905', + 'Pro/moonshotai/Kimi-K2-Instruct-0905', + 'moonshotai/Kimi-Dev-72B', + // Baidu ERNIE + 'baidu/ERNIE-4.5-300B-A47B' +] + +/** + * Creates a Set for efficient lookup of silicon Anthropic-compatible model IDs. + */ +const SILICON_ANTHROPIC_COMPATIBLE_MODEL_SET = new Set(SILICON_ANTHROPIC_COMPATIBLE_MODELS) + +/** + * Checks if a model ID is compatible with Anthropic API on Silicon provider. + * + * @param modelId - The model ID to check + * @returns true if the model supports Anthropic API endpoint + */ +export function isSiliconAnthropicCompatibleModel(modelId: string): boolean { + return SILICON_ANTHROPIC_COMPATIBLE_MODEL_SET.has(modelId) +} + +/** + * Silicon provider's Anthropic API host URL. + */ +export const SILICON_ANTHROPIC_API_HOST = 'https://api.siliconflow.cn' diff --git a/src/main/apiServer/utils/index.ts b/src/main/apiServer/utils/index.ts index f9f751c559..e25b49e750 100644 --- a/src/main/apiServer/utils/index.ts +++ b/src/main/apiServer/utils/index.ts @@ -1,6 +1,7 @@ import { CacheService } from '@main/services/CacheService' import { loggerService } from '@main/services/LoggerService' import { reduxService } from '@main/services/ReduxService' +import { isSiliconAnthropicCompatibleModel } from '@shared/config/providers' import type { ApiModel, Model, Provider } from '@types' const logger = loggerService.withContext('ApiServerUtils') @@ -287,6 +288,8 @@ export const getProviderAnthropicModelChecker = (providerId: string): ((m: Model return (m: Model) => m.endpoint_type === 'anthropic' case 'aihubmix': return (m: Model) => m.id.includes('claude') + case 'silicon': + return (m: Model) => isSiliconAnthropicCompatibleModel(m.id) default: // allow all models when checker not configured return () => true diff --git a/src/renderer/src/config/providers.ts b/src/renderer/src/config/providers.ts index 1e91b93c13..4df11592ca 100644 --- a/src/renderer/src/config/providers.ts +++ b/src/renderer/src/config/providers.ts @@ -95,6 +95,7 @@ export const SYSTEM_PROVIDERS_CONFIG: Record = type: 'openai', apiKey: '', apiHost: 'https://api.siliconflow.cn', + anthropicApiHost: 'https://api.siliconflow.cn', models: SYSTEM_MODELS.silicon, isSystem: true, enabled: false @@ -168,6 +169,7 @@ export const SYSTEM_PROVIDERS_CONFIG: Record = type: 'openai', apiKey: '', apiHost: 'https://www.dmxapi.cn', + anthropicApiHost: 'https://www.dmxapi.cn', models: SYSTEM_MODELS.dmxapi, isSystem: true, enabled: false diff --git a/src/renderer/src/pages/code/CodeToolsPage.tsx b/src/renderer/src/pages/code/CodeToolsPage.tsx index 3d62a82d0e..fcb2dbf482 100644 --- a/src/renderer/src/pages/code/CodeToolsPage.tsx +++ b/src/renderer/src/pages/code/CodeToolsPage.tsx @@ -17,6 +17,7 @@ import type { EndpointType, Model } from '@renderer/types' import { getClaudeSupportedProviders } from '@renderer/utils/provider' import type { TerminalConfig } from '@shared/config/constant' import { codeTools, terminalApps } from '@shared/config/constant' +import { isSiliconAnthropicCompatibleModel } from '@shared/config/providers' import { Alert, Avatar, Button, Checkbox, Input, Popover, Select, Space, Tooltip } from 'antd' import { ArrowUpRight, Download, FolderOpen, HelpCircle, Terminal, X } from 'lucide-react' import type { FC } from 'react' @@ -81,6 +82,10 @@ const CodeToolsPage: FC = () => { if (m.supported_endpoint_types) { return m.supported_endpoint_types.includes('anthropic') } + // Special handling for silicon provider: only specific models support Anthropic API + if (m.provider === 'silicon') { + return isSiliconAnthropicCompatibleModel(m.id) + } return m.id.includes('claude') || CLAUDE_OFFICIAL_SUPPORTED_PROVIDERS.includes(m.provider) } diff --git a/src/renderer/src/pages/code/index.ts b/src/renderer/src/pages/code/index.ts index c0a00a02c5..6e66164f05 100644 --- a/src/renderer/src/pages/code/index.ts +++ b/src/renderer/src/pages/code/index.ts @@ -1,4 +1,4 @@ -import type { EndpointType, Model, Provider } from '@renderer/types' +import { type EndpointType, type Model, type Provider, SystemProviderIds } from '@renderer/types' import { codeTools } from '@shared/config/constant' export interface LaunchValidationResult { @@ -25,7 +25,18 @@ export const CLI_TOOLS = [ ] export const GEMINI_SUPPORTED_PROVIDERS = ['aihubmix', 'dmxapi', 'new-api', 'cherryin'] -export const CLAUDE_OFFICIAL_SUPPORTED_PROVIDERS = ['deepseek', 'moonshot', 'zhipu', 'dashscope', 'modelscope'] +export const CLAUDE_OFFICIAL_SUPPORTED_PROVIDERS = [ + 'deepseek', + 'moonshot', + 'zhipu', + 'dashscope', + 'modelscope', + 'minimax', + 'longcat', + SystemProviderIds.qiniu + // If silicon is in this list, the fallback logic above will return true for all silicon models, + // potentially bypassing the specific model filtering you added above. +] export const CLAUDE_SUPPORTED_PROVIDERS = [ 'aihubmix', 'dmxapi', @@ -79,6 +90,11 @@ export const getCodeToolsApiBaseUrl = (model: Model, type: EndpointType) => { anthropic: { api_base_url: 'https://api-inference.modelscope.cn' } + }, + minimax: { + anthropic: { + api_base_url: 'https://api.minimaxi.com/anthropic' + } } } @@ -125,7 +141,8 @@ export const generateToolEnvironment = ({ switch (tool) { case codeTools.claudeCode: - env.ANTHROPIC_BASE_URL = getCodeToolsApiBaseUrl(model, 'anthropic') || modelProvider.apiHost + env.ANTHROPIC_BASE_URL = + getCodeToolsApiBaseUrl(model, 'anthropic') || modelProvider.anthropicApiHost || modelProvider.apiHost env.ANTHROPIC_MODEL = model.id if (modelProvider.type === 'anthropic') { env.ANTHROPIC_API_KEY = apiKey diff --git a/src/renderer/src/pages/settings/ProviderSettings/ProviderSetting.tsx b/src/renderer/src/pages/settings/ProviderSettings/ProviderSetting.tsx index 6f46b8144b..b1cbb9be6c 100644 --- a/src/renderer/src/pages/settings/ProviderSettings/ProviderSetting.tsx +++ b/src/renderer/src/pages/settings/ProviderSettings/ProviderSetting.tsx @@ -82,7 +82,10 @@ const ANTHROPIC_COMPATIBLE_PROVIDER_IDS = [ SystemProviderIds.grok, SystemProviderIds.cherryin, SystemProviderIds.longcat, - SystemProviderIds.minimax + SystemProviderIds.minimax, + SystemProviderIds.silicon, + SystemProviderIds.qiniu, + SystemProviderIds.dmxapi ] as const type AnthropicCompatibleProviderId = (typeof ANTHROPIC_COMPATIBLE_PROVIDER_IDS)[number] diff --git a/src/renderer/src/store/index.ts b/src/renderer/src/store/index.ts index eec3b1e91c..5c562885bb 100644 --- a/src/renderer/src/store/index.ts +++ b/src/renderer/src/store/index.ts @@ -67,7 +67,7 @@ const persistedReducer = persistReducer( { key: 'cherry-studio', storage, - version: 178, + version: 179, blacklist: ['runtime', 'messages', 'messageBlocks', 'tabs', 'toolPermissions'], migrate }, diff --git a/src/renderer/src/store/migrate.ts b/src/renderer/src/store/migrate.ts index d375f1de81..4b2e4cef89 100644 --- a/src/renderer/src/store/migrate.ts +++ b/src/renderer/src/store/migrate.ts @@ -2884,6 +2884,28 @@ const migrateConfig = { logger.error('migrate 178 error', error as Error) return state } + }, + '179': (state: RootState) => { + try { + state.llm.providers.forEach((provider) => { + switch (provider.id) { + case SystemProviderIds.silicon: + provider.anthropicApiHost = 'https://api.siliconflow.cn' + break + case SystemProviderIds.qiniu: + provider.anthropicApiHost = 'https://api.qnaigc.com' + break + case SystemProviderIds.dmxapi: + provider.anthropicApiHost = provider.apiHost + break + } + }) + logger.info('migrate 179 success') + return state + } catch (error) { + logger.error('migrate 179 error', error as Error) + return state + } } }