From 287c96ea2e02850ad2b44865a929667dc17ba829 Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Wed, 24 Sep 2025 16:02:26 +0800 Subject: [PATCH] feat: implement isNewApiProvider utility for provider identification - Added isNewApiProvider function to streamline checks for 'new-api' and 'cherryin' providers. - Updated ApiClientFactory, providerConfig, and various components to utilize isNewApiProvider for improved readability and maintainability. - Refactored conditional checks across multiple files to replace direct string comparisons with the new utility function. --- .../aiCore/legacy/clients/ApiClientFactory.ts | 3 +- .../__tests__/ApiClientFactory.test.ts | 4 +- .../index.clientCompatibilityTypes.test.ts | 14 +--- .../src/aiCore/provider/providerConfig.ts | 3 +- src/renderer/src/config/providers.ts | 64 ++++++++++--------- .../EditModelPopup/ModelEditContent.tsx | 9 +-- .../ModelList/ManageModelsList.tsx | 3 +- .../ModelList/ManageModelsPopup.tsx | 5 +- .../ProviderSettings/ModelList/ModelList.tsx | 4 +- .../ModelList/NewApiAddModelPopup.tsx | 3 +- src/renderer/src/store/migrate.ts | 1 + 11 files changed, 58 insertions(+), 55 deletions(-) diff --git a/src/renderer/src/aiCore/legacy/clients/ApiClientFactory.ts b/src/renderer/src/aiCore/legacy/clients/ApiClientFactory.ts index b38ab59537..f7b4a80f46 100644 --- a/src/renderer/src/aiCore/legacy/clients/ApiClientFactory.ts +++ b/src/renderer/src/aiCore/legacy/clients/ApiClientFactory.ts @@ -1,4 +1,5 @@ import { loggerService } from '@logger' +import { isNewApiProvider } from '@renderer/config/providers' import { Provider } from '@renderer/types' import { AihubmixAPIClient } from './aihubmix/AihubmixAPIClient' @@ -45,7 +46,7 @@ export class ApiClientFactory { return instance } - if (provider.id === 'new-api') { + if (isNewApiProvider(provider)) { logger.debug(`Creating NewAPIClient for provider: ${provider.id}`) instance = new NewAPIClient(provider) as BaseApiClient return instance diff --git a/src/renderer/src/aiCore/legacy/clients/__tests__/ApiClientFactory.test.ts b/src/renderer/src/aiCore/legacy/clients/__tests__/ApiClientFactory.test.ts index 081469516b..550486afb2 100644 --- a/src/renderer/src/aiCore/legacy/clients/__tests__/ApiClientFactory.test.ts +++ b/src/renderer/src/aiCore/legacy/clients/__tests__/ApiClientFactory.test.ts @@ -67,7 +67,9 @@ vi.mock('@renderer/config/models', () => ({ silicon: [], defaultModel: [] }, - isOpenAIModel: vi.fn(() => false) + isOpenAIModel: vi.fn(() => false), + glm45FlashModel: {}, + qwen38bModel: {} })) describe('ApiClientFactory', () => { diff --git a/src/renderer/src/aiCore/legacy/clients/__tests__/index.clientCompatibilityTypes.test.ts b/src/renderer/src/aiCore/legacy/clients/__tests__/index.clientCompatibilityTypes.test.ts index d70d9c58f9..dd85730c36 100644 --- a/src/renderer/src/aiCore/legacy/clients/__tests__/index.clientCompatibilityTypes.test.ts +++ b/src/renderer/src/aiCore/legacy/clients/__tests__/index.clientCompatibilityTypes.test.ts @@ -35,18 +35,8 @@ vi.mock('@renderer/config/models', () => ({ findTokenLimit: vi.fn().mockReturnValue(4096), isFunctionCallingModel: vi.fn().mockReturnValue(false), DEFAULT_MAX_TOKENS: 4096, - qwen38bModel: { - id: 'Qwen/Qwen3-8B', - name: 'Qwen3-8B', - provider: 'cherryai', - group: 'Qwen' - }, - glm45FlashModel: { - id: 'glm-4.5-flash', - name: 'GLM-4.5-Flash', - provider: 'cherryai', - group: 'GLM-4.5' - } + qwen38bModel: {}, + glm45FlashModel: {} })) vi.mock('@renderer/services/AssistantService', () => ({ diff --git a/src/renderer/src/aiCore/provider/providerConfig.ts b/src/renderer/src/aiCore/provider/providerConfig.ts index eaaef15211..b91dad9cf7 100644 --- a/src/renderer/src/aiCore/provider/providerConfig.ts +++ b/src/renderer/src/aiCore/provider/providerConfig.ts @@ -6,6 +6,7 @@ import { type ProviderSettingsMap } from '@cherrystudio/ai-core/provider' import { isOpenAIChatCompletionOnlyModel } from '@renderer/config/models' +import { isNewApiProvider } from '@renderer/config/providers' import { getAwsBedrockAccessKeyId, getAwsBedrockRegion, @@ -65,7 +66,7 @@ function handleSpecialProviders(model: Model, provider: Provider): Provider { if (provider.id === 'aihubmix') { return aihubmixProviderCreator(model, provider) } - if (provider.id === 'new-api') { + if (isNewApiProvider(provider)) { return newApiResolverCreator(model, provider) } if (provider.id === 'vertexai') { diff --git a/src/renderer/src/config/providers.ts b/src/renderer/src/config/providers.ts index 80ea9bdf7a..3b8821905a 100644 --- a/src/renderer/src/config/providers.ts +++ b/src/renderer/src/config/providers.ts @@ -138,16 +138,6 @@ export const SYSTEM_PROVIDERS_CONFIG: Record = isSystem: true, enabled: false }, - ppio: { - id: 'ppio', - name: 'PPIO', - type: 'openai', - apiKey: '', - apiHost: 'https://api.ppinfra.com/v3/openai/', - models: SYSTEM_MODELS.ppio, - isSystem: true, - enabled: false - }, alayanew: { id: 'alayanew', name: 'AlayaNew', @@ -158,16 +148,6 @@ export const SYSTEM_PROVIDERS_CONFIG: Record = isSystem: true, enabled: false }, - qiniu: { - id: 'qiniu', - name: 'Qiniu', - type: 'openai', - apiKey: '', - apiHost: 'https://api.qnaigc.com', - models: SYSTEM_MODELS.qiniu, - isSystem: true, - enabled: false - }, dmxapi: { id: 'dmxapi', name: 'DMXAPI', @@ -178,6 +158,16 @@ export const SYSTEM_PROVIDERS_CONFIG: Record = isSystem: true, enabled: false }, + aionly: { + id: 'aionly', + name: 'AIOnly', + type: 'openai', + apiKey: '', + apiHost: 'https://api.aiionly.com', + models: SYSTEM_MODELS.aionly, + isSystem: true, + enabled: false + }, burncloud: { id: 'burncloud', name: 'BurnCloud', @@ -238,6 +228,26 @@ export const SYSTEM_PROVIDERS_CONFIG: Record = isSystem: true, enabled: false }, + ppio: { + id: 'ppio', + name: 'PPIO', + type: 'openai', + apiKey: '', + apiHost: 'https://api.ppinfra.com/v3/openai/', + models: SYSTEM_MODELS.ppio, + isSystem: true, + enabled: false + }, + qiniu: { + id: 'qiniu', + name: 'Qiniu', + type: 'openai', + apiKey: '', + apiHost: 'https://api.qnaigc.com', + models: SYSTEM_MODELS.qiniu, + isSystem: true, + enabled: false + }, openrouter: { id: 'openrouter', name: 'OpenRouter', @@ -612,16 +622,6 @@ export const SYSTEM_PROVIDERS_CONFIG: Record = models: SYSTEM_MODELS['poe'], isSystem: true, enabled: false - }, - aionly: { - id: 'aionly', - name: 'AIOnly', - type: 'openai', - apiKey: '', - apiHost: 'https://api.aiionly.com', - models: SYSTEM_MODELS.aionly, - isSystem: true, - enabled: false } } as const @@ -1375,3 +1375,7 @@ const SUPPORT_GEMINI_NATIVE_WEB_SEARCH_PROVIDERS = ['gemini', 'vertexai'] as con export const isGeminiWebSearchProvider = (provider: Provider) => { return SUPPORT_GEMINI_NATIVE_WEB_SEARCH_PROVIDERS.some((id) => id === provider.id) } + +export const isNewApiProvider = (provider: Provider) => { + return ['new-api', 'cherryin'].includes(provider.id) +} diff --git a/src/renderer/src/pages/settings/ProviderSettings/EditModelPopup/ModelEditContent.tsx b/src/renderer/src/pages/settings/ProviderSettings/EditModelPopup/ModelEditContent.tsx index b09e44e43c..fed30fddb3 100644 --- a/src/renderer/src/pages/settings/ProviderSettings/EditModelPopup/ModelEditContent.tsx +++ b/src/renderer/src/pages/settings/ProviderSettings/EditModelPopup/ModelEditContent.tsx @@ -17,6 +17,7 @@ import { isVisionModel, isWebSearchModel } from '@renderer/config/models' +import { isNewApiProvider } from '@renderer/config/providers' import { useDynamicLabelWidth } from '@renderer/hooks/useDynamicLabelWidth' import { Model, ModelCapability, ModelType, Provider } from '@renderer/types' import { getDefaultGroupName, getDifference, getUnion, uniqueObjectArray } from '@renderer/utils' @@ -78,7 +79,7 @@ const ModelEditContent: FC = ({ provider, mo id: formValues.id || model.id, name: formValues.name || model.name, group: formValues.group || model.group, - endpoint_type: provider.id === 'new-api' ? formValues.endpointType : model.endpoint_type, + endpoint_type: isNewApiProvider(provider) ? formValues.endpointType : model.endpoint_type, capabilities: overrides?.capabilities ?? modelCapabilities, supported_text_delta: overrides?.supported_text_delta ?? supportedTextDelta, pricing: { @@ -97,7 +98,7 @@ const ModelEditContent: FC = ({ provider, mo id: values.id || model.id, name: values.name || model.name, group: values.group || model.group, - endpoint_type: provider.id === 'new-api' ? values.endpointType : model.endpoint_type, + endpoint_type: isNewApiProvider(provider) ? values.endpointType : model.endpoint_type, capabilities: modelCapabilities, supported_text_delta: supportedTextDelta, pricing: { @@ -247,7 +248,7 @@ const ModelEditContent: FC = ({ provider, mo
= ({ provider, mo tooltip={t('settings.models.add.group_name.tooltip')}> - {provider.id === 'new-api' && ( + {isNewApiProvider(provider) && ( = ({ modelGroups, provid // 添加整组 const wouldAddModels = models.filter((model) => !isModelInProvider(provider, model.id)) - if (provider.id === 'new-api') { + if (isNewApiProvider(provider)) { if (wouldAddModels.every(isValidNewApiModel)) { wouldAddModels.forEach(onAddModel) } else { diff --git a/src/renderer/src/pages/settings/ProviderSettings/ModelList/ManageModelsPopup.tsx b/src/renderer/src/pages/settings/ProviderSettings/ModelList/ManageModelsPopup.tsx index 036894dbee..706cd8bfce 100644 --- a/src/renderer/src/pages/settings/ProviderSettings/ModelList/ManageModelsPopup.tsx +++ b/src/renderer/src/pages/settings/ProviderSettings/ModelList/ManageModelsPopup.tsx @@ -13,6 +13,7 @@ import { isWebSearchModel, SYSTEM_MODELS } from '@renderer/config/models' +import { isNewApiProvider } from '@renderer/config/providers' import { useProvider } from '@renderer/hooks/useProvider' import NewApiAddModelPopup from '@renderer/pages/settings/ProviderSettings/ModelList/NewApiAddModelPopup' import NewApiBatchAddModelPopup from '@renderer/pages/settings/ProviderSettings/ModelList/NewApiBatchAddModelPopup' @@ -129,7 +130,7 @@ const PopupContainer: React.FC = ({ providerId, resolve }) => { const onAddModel = useCallback( (model: Model) => { if (!isEmpty(model.name)) { - if (provider.id === 'new-api') { + if (isNewApiProvider(provider)) { if (model.supported_endpoint_types && model.supported_endpoint_types.length > 0) { addModel({ ...model, @@ -160,7 +161,7 @@ const PopupContainer: React.FC = ({ providerId, resolve }) => { content: t('settings.models.manage.add_listed.confirm'), centered: true, onOk: () => { - if (provider.id === 'new-api') { + if (isNewApiProvider(provider)) { if (models.every(isValidNewApiModel)) { wouldAddModel.forEach(onAddModel) } else { diff --git a/src/renderer/src/pages/settings/ProviderSettings/ModelList/ModelList.tsx b/src/renderer/src/pages/settings/ProviderSettings/ModelList/ModelList.tsx index 58468f09bb..2d2af3788a 100644 --- a/src/renderer/src/pages/settings/ProviderSettings/ModelList/ModelList.tsx +++ b/src/renderer/src/pages/settings/ProviderSettings/ModelList/ModelList.tsx @@ -2,7 +2,7 @@ import CollapsibleSearchBar from '@renderer/components/CollapsibleSearchBar' import { LoadingIcon, StreamlineGoodHealthAndWellBeing } from '@renderer/components/Icons' import { HStack } from '@renderer/components/Layout' import CustomTag from '@renderer/components/Tags/CustomTag' -import { PROVIDER_URLS } from '@renderer/config/providers' +import { isNewApiProvider, PROVIDER_URLS } from '@renderer/config/providers' import { useProvider } from '@renderer/hooks/useProvider' import { getProviderLabel } from '@renderer/i18n/label' import { SettingHelpLink, SettingHelpText, SettingHelpTextRow, SettingSubtitle } from '@renderer/pages/settings' @@ -86,7 +86,7 @@ const ModelList: React.FC = ({ providerId }) => { }, [provider.id]) const onAddModel = useCallback(() => { - if (provider.id === 'new-api') { + if (isNewApiProvider(provider)) { NewApiAddModelPopup.show({ title: t('settings.models.add.add_model'), provider }) } else { AddModelPopup.show({ title: t('settings.models.add.add_model'), provider }) diff --git a/src/renderer/src/pages/settings/ProviderSettings/ModelList/NewApiAddModelPopup.tsx b/src/renderer/src/pages/settings/ProviderSettings/ModelList/NewApiAddModelPopup.tsx index 4e81b39aa2..8812a5926d 100644 --- a/src/renderer/src/pages/settings/ProviderSettings/ModelList/NewApiAddModelPopup.tsx +++ b/src/renderer/src/pages/settings/ProviderSettings/ModelList/NewApiAddModelPopup.tsx @@ -1,6 +1,7 @@ import { TopView } from '@renderer/components/TopView' import { endpointTypeOptions } from '@renderer/config/endpointTypes' import { isNotSupportedTextDelta } from '@renderer/config/models' +import { isNewApiProvider } from '@renderer/config/providers' import { useDynamicLabelWidth } from '@renderer/hooks/useDynamicLabelWidth' import { useProvider } from '@renderer/hooks/useProvider' import { EndpointType, Model, Provider } from '@renderer/types' @@ -60,7 +61,7 @@ const PopupContainer: React.FC = ({ title, provider, resolve, model, endp provider: provider.id, name: values.name ? values.name : id.toUpperCase(), group: values.group ?? getDefaultGroupName(id), - endpoint_type: provider.id === 'new-api' ? values.endpointType : undefined + endpoint_type: isNewApiProvider(provider) ? values.endpointType : undefined } addModel({ ...model, supported_text_delta: !isNotSupportedTextDelta(model) }) diff --git a/src/renderer/src/store/migrate.ts b/src/renderer/src/store/migrate.ts index 8063f7ec92..e26a382fc9 100644 --- a/src/renderer/src/store/migrate.ts +++ b/src/renderer/src/store/migrate.ts @@ -2495,6 +2495,7 @@ const migrateConfig = { '157': (state: RootState) => { try { addProvider(state, 'aionly') + state.llm.providers = moveProvider(state.llm.providers, 'aionly', 10) const cherryinProvider = state.llm.providers.find((provider) => provider.id === 'cherryin')