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.
This commit is contained in:
kangfenmao 2025-09-24 16:02:26 +08:00
parent adacb8c638
commit 287c96ea2e
11 changed files with 58 additions and 55 deletions

View File

@ -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

View File

@ -67,7 +67,9 @@ vi.mock('@renderer/config/models', () => ({
silicon: [],
defaultModel: []
},
isOpenAIModel: vi.fn(() => false)
isOpenAIModel: vi.fn(() => false),
glm45FlashModel: {},
qwen38bModel: {}
}))
describe('ApiClientFactory', () => {

View File

@ -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', () => ({

View File

@ -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') {

View File

@ -138,16 +138,6 @@ export const SYSTEM_PROVIDERS_CONFIG: Record<SystemProviderId, SystemProvider> =
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<SystemProviderId, SystemProvider> =
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<SystemProviderId, SystemProvider> =
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<SystemProviderId, SystemProvider> =
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<SystemProviderId, SystemProvider> =
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)
}

View File

@ -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<ModelEditContentProps & ModalProps> = ({ 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<ModelEditContentProps & ModalProps> = ({ 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<ModelEditContentProps & ModalProps> = ({ provider, mo
<Modal title={t('models.edit')} footer={null} transitionName="animation-move-down" centered {...props}>
<Form
form={form}
labelCol={{ flex: provider.id === 'new-api' ? labelWidth : '110px' }}
labelCol={{ flex: isNewApiProvider(provider) ? labelWidth : '110px' }}
labelAlign="left"
colon={false}
style={{ marginTop: 15 }}
@ -309,7 +310,7 @@ const ModelEditContent: FC<ModelEditContentProps & ModalProps> = ({ provider, mo
tooltip={t('settings.models.add.group_name.tooltip')}>
<Input placeholder={t('settings.models.add.group_name.placeholder')} spellCheck={false} />
</Form.Item>
{provider.id === 'new-api' && (
{isNewApiProvider(provider) && (
<Form.Item
name="endpointType"
label={t('settings.models.add.endpoint_type.label')}

View File

@ -3,6 +3,7 @@ import ModelIdWithTags from '@renderer/components/ModelIdWithTags'
import CustomTag from '@renderer/components/Tags/CustomTag'
import { DynamicVirtualList } from '@renderer/components/VirtualList'
import { getModelLogo } from '@renderer/config/models'
import { isNewApiProvider } from '@renderer/config/providers'
import FileItem from '@renderer/pages/files/FileItem'
import NewApiBatchAddModelPopup from '@renderer/pages/settings/ProviderSettings/ModelList/NewApiBatchAddModelPopup'
import { Model, Provider } from '@renderer/types'
@ -91,7 +92,7 @@ const ManageModelsList: React.FC<ManageModelsListProps> = ({ 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 {

View File

@ -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<Props> = ({ 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<Props> = ({ 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 {

View File

@ -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<ModelListProps> = ({ 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 })

View File

@ -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<Props> = ({ 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) })

View File

@ -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')