mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-02 10:29:02 +08:00
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:
parent
adacb8c638
commit
287c96ea2e
@ -1,4 +1,5 @@
|
|||||||
import { loggerService } from '@logger'
|
import { loggerService } from '@logger'
|
||||||
|
import { isNewApiProvider } from '@renderer/config/providers'
|
||||||
import { Provider } from '@renderer/types'
|
import { Provider } from '@renderer/types'
|
||||||
|
|
||||||
import { AihubmixAPIClient } from './aihubmix/AihubmixAPIClient'
|
import { AihubmixAPIClient } from './aihubmix/AihubmixAPIClient'
|
||||||
@ -45,7 +46,7 @@ export class ApiClientFactory {
|
|||||||
return instance
|
return instance
|
||||||
}
|
}
|
||||||
|
|
||||||
if (provider.id === 'new-api') {
|
if (isNewApiProvider(provider)) {
|
||||||
logger.debug(`Creating NewAPIClient for provider: ${provider.id}`)
|
logger.debug(`Creating NewAPIClient for provider: ${provider.id}`)
|
||||||
instance = new NewAPIClient(provider) as BaseApiClient
|
instance = new NewAPIClient(provider) as BaseApiClient
|
||||||
return instance
|
return instance
|
||||||
|
|||||||
@ -67,7 +67,9 @@ vi.mock('@renderer/config/models', () => ({
|
|||||||
silicon: [],
|
silicon: [],
|
||||||
defaultModel: []
|
defaultModel: []
|
||||||
},
|
},
|
||||||
isOpenAIModel: vi.fn(() => false)
|
isOpenAIModel: vi.fn(() => false),
|
||||||
|
glm45FlashModel: {},
|
||||||
|
qwen38bModel: {}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
describe('ApiClientFactory', () => {
|
describe('ApiClientFactory', () => {
|
||||||
|
|||||||
@ -35,18 +35,8 @@ vi.mock('@renderer/config/models', () => ({
|
|||||||
findTokenLimit: vi.fn().mockReturnValue(4096),
|
findTokenLimit: vi.fn().mockReturnValue(4096),
|
||||||
isFunctionCallingModel: vi.fn().mockReturnValue(false),
|
isFunctionCallingModel: vi.fn().mockReturnValue(false),
|
||||||
DEFAULT_MAX_TOKENS: 4096,
|
DEFAULT_MAX_TOKENS: 4096,
|
||||||
qwen38bModel: {
|
qwen38bModel: {},
|
||||||
id: 'Qwen/Qwen3-8B',
|
glm45FlashModel: {}
|
||||||
name: 'Qwen3-8B',
|
|
||||||
provider: 'cherryai',
|
|
||||||
group: 'Qwen'
|
|
||||||
},
|
|
||||||
glm45FlashModel: {
|
|
||||||
id: 'glm-4.5-flash',
|
|
||||||
name: 'GLM-4.5-Flash',
|
|
||||||
provider: 'cherryai',
|
|
||||||
group: 'GLM-4.5'
|
|
||||||
}
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
vi.mock('@renderer/services/AssistantService', () => ({
|
vi.mock('@renderer/services/AssistantService', () => ({
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import {
|
|||||||
type ProviderSettingsMap
|
type ProviderSettingsMap
|
||||||
} from '@cherrystudio/ai-core/provider'
|
} from '@cherrystudio/ai-core/provider'
|
||||||
import { isOpenAIChatCompletionOnlyModel } from '@renderer/config/models'
|
import { isOpenAIChatCompletionOnlyModel } from '@renderer/config/models'
|
||||||
|
import { isNewApiProvider } from '@renderer/config/providers'
|
||||||
import {
|
import {
|
||||||
getAwsBedrockAccessKeyId,
|
getAwsBedrockAccessKeyId,
|
||||||
getAwsBedrockRegion,
|
getAwsBedrockRegion,
|
||||||
@ -65,7 +66,7 @@ function handleSpecialProviders(model: Model, provider: Provider): Provider {
|
|||||||
if (provider.id === 'aihubmix') {
|
if (provider.id === 'aihubmix') {
|
||||||
return aihubmixProviderCreator(model, provider)
|
return aihubmixProviderCreator(model, provider)
|
||||||
}
|
}
|
||||||
if (provider.id === 'new-api') {
|
if (isNewApiProvider(provider)) {
|
||||||
return newApiResolverCreator(model, provider)
|
return newApiResolverCreator(model, provider)
|
||||||
}
|
}
|
||||||
if (provider.id === 'vertexai') {
|
if (provider.id === 'vertexai') {
|
||||||
|
|||||||
@ -138,16 +138,6 @@ export const SYSTEM_PROVIDERS_CONFIG: Record<SystemProviderId, SystemProvider> =
|
|||||||
isSystem: true,
|
isSystem: true,
|
||||||
enabled: false
|
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: {
|
alayanew: {
|
||||||
id: 'alayanew',
|
id: 'alayanew',
|
||||||
name: 'AlayaNew',
|
name: 'AlayaNew',
|
||||||
@ -158,16 +148,6 @@ export const SYSTEM_PROVIDERS_CONFIG: Record<SystemProviderId, SystemProvider> =
|
|||||||
isSystem: true,
|
isSystem: true,
|
||||||
enabled: false
|
enabled: false
|
||||||
},
|
},
|
||||||
qiniu: {
|
|
||||||
id: 'qiniu',
|
|
||||||
name: 'Qiniu',
|
|
||||||
type: 'openai',
|
|
||||||
apiKey: '',
|
|
||||||
apiHost: 'https://api.qnaigc.com',
|
|
||||||
models: SYSTEM_MODELS.qiniu,
|
|
||||||
isSystem: true,
|
|
||||||
enabled: false
|
|
||||||
},
|
|
||||||
dmxapi: {
|
dmxapi: {
|
||||||
id: 'dmxapi',
|
id: 'dmxapi',
|
||||||
name: 'DMXAPI',
|
name: 'DMXAPI',
|
||||||
@ -178,6 +158,16 @@ export const SYSTEM_PROVIDERS_CONFIG: Record<SystemProviderId, SystemProvider> =
|
|||||||
isSystem: true,
|
isSystem: true,
|
||||||
enabled: false
|
enabled: false
|
||||||
},
|
},
|
||||||
|
aionly: {
|
||||||
|
id: 'aionly',
|
||||||
|
name: 'AIOnly',
|
||||||
|
type: 'openai',
|
||||||
|
apiKey: '',
|
||||||
|
apiHost: 'https://api.aiionly.com',
|
||||||
|
models: SYSTEM_MODELS.aionly,
|
||||||
|
isSystem: true,
|
||||||
|
enabled: false
|
||||||
|
},
|
||||||
burncloud: {
|
burncloud: {
|
||||||
id: 'burncloud',
|
id: 'burncloud',
|
||||||
name: 'BurnCloud',
|
name: 'BurnCloud',
|
||||||
@ -238,6 +228,26 @@ export const SYSTEM_PROVIDERS_CONFIG: Record<SystemProviderId, SystemProvider> =
|
|||||||
isSystem: true,
|
isSystem: true,
|
||||||
enabled: false
|
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: {
|
openrouter: {
|
||||||
id: 'openrouter',
|
id: 'openrouter',
|
||||||
name: 'OpenRouter',
|
name: 'OpenRouter',
|
||||||
@ -612,16 +622,6 @@ export const SYSTEM_PROVIDERS_CONFIG: Record<SystemProviderId, SystemProvider> =
|
|||||||
models: SYSTEM_MODELS['poe'],
|
models: SYSTEM_MODELS['poe'],
|
||||||
isSystem: true,
|
isSystem: true,
|
||||||
enabled: false
|
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
|
} as const
|
||||||
|
|
||||||
@ -1375,3 +1375,7 @@ const SUPPORT_GEMINI_NATIVE_WEB_SEARCH_PROVIDERS = ['gemini', 'vertexai'] as con
|
|||||||
export const isGeminiWebSearchProvider = (provider: Provider) => {
|
export const isGeminiWebSearchProvider = (provider: Provider) => {
|
||||||
return SUPPORT_GEMINI_NATIVE_WEB_SEARCH_PROVIDERS.some((id) => id === provider.id)
|
return SUPPORT_GEMINI_NATIVE_WEB_SEARCH_PROVIDERS.some((id) => id === provider.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const isNewApiProvider = (provider: Provider) => {
|
||||||
|
return ['new-api', 'cherryin'].includes(provider.id)
|
||||||
|
}
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import {
|
|||||||
isVisionModel,
|
isVisionModel,
|
||||||
isWebSearchModel
|
isWebSearchModel
|
||||||
} from '@renderer/config/models'
|
} from '@renderer/config/models'
|
||||||
|
import { isNewApiProvider } from '@renderer/config/providers'
|
||||||
import { useDynamicLabelWidth } from '@renderer/hooks/useDynamicLabelWidth'
|
import { useDynamicLabelWidth } from '@renderer/hooks/useDynamicLabelWidth'
|
||||||
import { Model, ModelCapability, ModelType, Provider } from '@renderer/types'
|
import { Model, ModelCapability, ModelType, Provider } from '@renderer/types'
|
||||||
import { getDefaultGroupName, getDifference, getUnion, uniqueObjectArray } from '@renderer/utils'
|
import { getDefaultGroupName, getDifference, getUnion, uniqueObjectArray } from '@renderer/utils'
|
||||||
@ -78,7 +79,7 @@ const ModelEditContent: FC<ModelEditContentProps & ModalProps> = ({ provider, mo
|
|||||||
id: formValues.id || model.id,
|
id: formValues.id || model.id,
|
||||||
name: formValues.name || model.name,
|
name: formValues.name || model.name,
|
||||||
group: formValues.group || model.group,
|
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,
|
capabilities: overrides?.capabilities ?? modelCapabilities,
|
||||||
supported_text_delta: overrides?.supported_text_delta ?? supportedTextDelta,
|
supported_text_delta: overrides?.supported_text_delta ?? supportedTextDelta,
|
||||||
pricing: {
|
pricing: {
|
||||||
@ -97,7 +98,7 @@ const ModelEditContent: FC<ModelEditContentProps & ModalProps> = ({ provider, mo
|
|||||||
id: values.id || model.id,
|
id: values.id || model.id,
|
||||||
name: values.name || model.name,
|
name: values.name || model.name,
|
||||||
group: values.group || model.group,
|
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,
|
capabilities: modelCapabilities,
|
||||||
supported_text_delta: supportedTextDelta,
|
supported_text_delta: supportedTextDelta,
|
||||||
pricing: {
|
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}>
|
<Modal title={t('models.edit')} footer={null} transitionName="animation-move-down" centered {...props}>
|
||||||
<Form
|
<Form
|
||||||
form={form}
|
form={form}
|
||||||
labelCol={{ flex: provider.id === 'new-api' ? labelWidth : '110px' }}
|
labelCol={{ flex: isNewApiProvider(provider) ? labelWidth : '110px' }}
|
||||||
labelAlign="left"
|
labelAlign="left"
|
||||||
colon={false}
|
colon={false}
|
||||||
style={{ marginTop: 15 }}
|
style={{ marginTop: 15 }}
|
||||||
@ -309,7 +310,7 @@ const ModelEditContent: FC<ModelEditContentProps & ModalProps> = ({ provider, mo
|
|||||||
tooltip={t('settings.models.add.group_name.tooltip')}>
|
tooltip={t('settings.models.add.group_name.tooltip')}>
|
||||||
<Input placeholder={t('settings.models.add.group_name.placeholder')} spellCheck={false} />
|
<Input placeholder={t('settings.models.add.group_name.placeholder')} spellCheck={false} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
{provider.id === 'new-api' && (
|
{isNewApiProvider(provider) && (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="endpointType"
|
name="endpointType"
|
||||||
label={t('settings.models.add.endpoint_type.label')}
|
label={t('settings.models.add.endpoint_type.label')}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import ModelIdWithTags from '@renderer/components/ModelIdWithTags'
|
|||||||
import CustomTag from '@renderer/components/Tags/CustomTag'
|
import CustomTag from '@renderer/components/Tags/CustomTag'
|
||||||
import { DynamicVirtualList } from '@renderer/components/VirtualList'
|
import { DynamicVirtualList } from '@renderer/components/VirtualList'
|
||||||
import { getModelLogo } from '@renderer/config/models'
|
import { getModelLogo } from '@renderer/config/models'
|
||||||
|
import { isNewApiProvider } from '@renderer/config/providers'
|
||||||
import FileItem from '@renderer/pages/files/FileItem'
|
import FileItem from '@renderer/pages/files/FileItem'
|
||||||
import NewApiBatchAddModelPopup from '@renderer/pages/settings/ProviderSettings/ModelList/NewApiBatchAddModelPopup'
|
import NewApiBatchAddModelPopup from '@renderer/pages/settings/ProviderSettings/ModelList/NewApiBatchAddModelPopup'
|
||||||
import { Model, Provider } from '@renderer/types'
|
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))
|
const wouldAddModels = models.filter((model) => !isModelInProvider(provider, model.id))
|
||||||
|
|
||||||
if (provider.id === 'new-api') {
|
if (isNewApiProvider(provider)) {
|
||||||
if (wouldAddModels.every(isValidNewApiModel)) {
|
if (wouldAddModels.every(isValidNewApiModel)) {
|
||||||
wouldAddModels.forEach(onAddModel)
|
wouldAddModels.forEach(onAddModel)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import {
|
|||||||
isWebSearchModel,
|
isWebSearchModel,
|
||||||
SYSTEM_MODELS
|
SYSTEM_MODELS
|
||||||
} from '@renderer/config/models'
|
} from '@renderer/config/models'
|
||||||
|
import { isNewApiProvider } from '@renderer/config/providers'
|
||||||
import { useProvider } from '@renderer/hooks/useProvider'
|
import { useProvider } from '@renderer/hooks/useProvider'
|
||||||
import NewApiAddModelPopup from '@renderer/pages/settings/ProviderSettings/ModelList/NewApiAddModelPopup'
|
import NewApiAddModelPopup from '@renderer/pages/settings/ProviderSettings/ModelList/NewApiAddModelPopup'
|
||||||
import NewApiBatchAddModelPopup from '@renderer/pages/settings/ProviderSettings/ModelList/NewApiBatchAddModelPopup'
|
import NewApiBatchAddModelPopup from '@renderer/pages/settings/ProviderSettings/ModelList/NewApiBatchAddModelPopup'
|
||||||
@ -129,7 +130,7 @@ const PopupContainer: React.FC<Props> = ({ providerId, resolve }) => {
|
|||||||
const onAddModel = useCallback(
|
const onAddModel = useCallback(
|
||||||
(model: Model) => {
|
(model: Model) => {
|
||||||
if (!isEmpty(model.name)) {
|
if (!isEmpty(model.name)) {
|
||||||
if (provider.id === 'new-api') {
|
if (isNewApiProvider(provider)) {
|
||||||
if (model.supported_endpoint_types && model.supported_endpoint_types.length > 0) {
|
if (model.supported_endpoint_types && model.supported_endpoint_types.length > 0) {
|
||||||
addModel({
|
addModel({
|
||||||
...model,
|
...model,
|
||||||
@ -160,7 +161,7 @@ const PopupContainer: React.FC<Props> = ({ providerId, resolve }) => {
|
|||||||
content: t('settings.models.manage.add_listed.confirm'),
|
content: t('settings.models.manage.add_listed.confirm'),
|
||||||
centered: true,
|
centered: true,
|
||||||
onOk: () => {
|
onOk: () => {
|
||||||
if (provider.id === 'new-api') {
|
if (isNewApiProvider(provider)) {
|
||||||
if (models.every(isValidNewApiModel)) {
|
if (models.every(isValidNewApiModel)) {
|
||||||
wouldAddModel.forEach(onAddModel)
|
wouldAddModel.forEach(onAddModel)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import CollapsibleSearchBar from '@renderer/components/CollapsibleSearchBar'
|
|||||||
import { LoadingIcon, StreamlineGoodHealthAndWellBeing } from '@renderer/components/Icons'
|
import { LoadingIcon, StreamlineGoodHealthAndWellBeing } from '@renderer/components/Icons'
|
||||||
import { HStack } from '@renderer/components/Layout'
|
import { HStack } from '@renderer/components/Layout'
|
||||||
import CustomTag from '@renderer/components/Tags/CustomTag'
|
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 { useProvider } from '@renderer/hooks/useProvider'
|
||||||
import { getProviderLabel } from '@renderer/i18n/label'
|
import { getProviderLabel } from '@renderer/i18n/label'
|
||||||
import { SettingHelpLink, SettingHelpText, SettingHelpTextRow, SettingSubtitle } from '@renderer/pages/settings'
|
import { SettingHelpLink, SettingHelpText, SettingHelpTextRow, SettingSubtitle } from '@renderer/pages/settings'
|
||||||
@ -86,7 +86,7 @@ const ModelList: React.FC<ModelListProps> = ({ providerId }) => {
|
|||||||
}, [provider.id])
|
}, [provider.id])
|
||||||
|
|
||||||
const onAddModel = useCallback(() => {
|
const onAddModel = useCallback(() => {
|
||||||
if (provider.id === 'new-api') {
|
if (isNewApiProvider(provider)) {
|
||||||
NewApiAddModelPopup.show({ title: t('settings.models.add.add_model'), provider })
|
NewApiAddModelPopup.show({ title: t('settings.models.add.add_model'), provider })
|
||||||
} else {
|
} else {
|
||||||
AddModelPopup.show({ title: t('settings.models.add.add_model'), provider })
|
AddModelPopup.show({ title: t('settings.models.add.add_model'), provider })
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { TopView } from '@renderer/components/TopView'
|
import { TopView } from '@renderer/components/TopView'
|
||||||
import { endpointTypeOptions } from '@renderer/config/endpointTypes'
|
import { endpointTypeOptions } from '@renderer/config/endpointTypes'
|
||||||
import { isNotSupportedTextDelta } from '@renderer/config/models'
|
import { isNotSupportedTextDelta } from '@renderer/config/models'
|
||||||
|
import { isNewApiProvider } from '@renderer/config/providers'
|
||||||
import { useDynamicLabelWidth } from '@renderer/hooks/useDynamicLabelWidth'
|
import { useDynamicLabelWidth } from '@renderer/hooks/useDynamicLabelWidth'
|
||||||
import { useProvider } from '@renderer/hooks/useProvider'
|
import { useProvider } from '@renderer/hooks/useProvider'
|
||||||
import { EndpointType, Model, Provider } from '@renderer/types'
|
import { EndpointType, Model, Provider } from '@renderer/types'
|
||||||
@ -60,7 +61,7 @@ const PopupContainer: React.FC<Props> = ({ title, provider, resolve, model, endp
|
|||||||
provider: provider.id,
|
provider: provider.id,
|
||||||
name: values.name ? values.name : id.toUpperCase(),
|
name: values.name ? values.name : id.toUpperCase(),
|
||||||
group: values.group ?? getDefaultGroupName(id),
|
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) })
|
addModel({ ...model, supported_text_delta: !isNotSupportedTextDelta(model) })
|
||||||
|
|||||||
@ -2495,6 +2495,7 @@ const migrateConfig = {
|
|||||||
'157': (state: RootState) => {
|
'157': (state: RootState) => {
|
||||||
try {
|
try {
|
||||||
addProvider(state, 'aionly')
|
addProvider(state, 'aionly')
|
||||||
|
state.llm.providers = moveProvider(state.llm.providers, 'aionly', 10)
|
||||||
|
|
||||||
const cherryinProvider = state.llm.providers.find((provider) => provider.id === 'cherryin')
|
const cherryinProvider = state.llm.providers.find((provider) => provider.id === 'cherryin')
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user