mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-26 03:31:24 +08:00
feat(provider): 支持在提供商设置中配置 service_tier 参数
将 service_tier 配置从全局设置迁移到提供商设置中,并添加相关 UI 和逻辑支持
This commit is contained in:
parent
02ea056cdd
commit
1b231aed1e
@ -6,10 +6,9 @@ import {
|
||||
isSupportFlexServiceTierModel
|
||||
} from '@renderer/config/models'
|
||||
import { REFERENCE_PROMPT } from '@renderer/config/prompts'
|
||||
import { isSupportServiceTierProviders } from '@renderer/config/providers'
|
||||
import { getLMStudioKeepAliveTime } from '@renderer/hooks/useLMStudio'
|
||||
import { getStoreSetting } from '@renderer/hooks/useSettings'
|
||||
import { getAssistantSettings } from '@renderer/services/AssistantService'
|
||||
import { SettingsState } from '@renderer/store/settings'
|
||||
import {
|
||||
Assistant,
|
||||
FileTypes,
|
||||
@ -21,6 +20,7 @@ import {
|
||||
MemoryItem,
|
||||
Model,
|
||||
OpenAIServiceTier,
|
||||
OpenAIServiceTiers,
|
||||
Provider,
|
||||
ToolCallResponse,
|
||||
WebSearchProviderResponse,
|
||||
@ -201,22 +201,30 @@ export abstract class BaseApiClient<
|
||||
return assistantSettings?.enableTopP ? assistantSettings?.topP : undefined
|
||||
}
|
||||
|
||||
// NOTE: 这个也许可以迁移到OpenAIBaseClient
|
||||
protected getServiceTier(model: Model) {
|
||||
if (!isOpenAIModel(model) || model.provider === 'github' || model.provider === 'copilot') {
|
||||
if (
|
||||
!isSupportServiceTierProviders(this.provider) ||
|
||||
!isOpenAIModel(model) ||
|
||||
model.provider === 'github' ||
|
||||
model.provider === 'copilot'
|
||||
) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const openAI = getStoreSetting('openAI') as SettingsState['openAI']
|
||||
let serviceTier = 'auto' as OpenAIServiceTier
|
||||
let serviceTier: OpenAIServiceTier = OpenAIServiceTiers.AUTO
|
||||
const serviceTierSetting = this.provider.serviceTier
|
||||
|
||||
if (openAI && openAI?.serviceTier === 'flex') {
|
||||
if (serviceTierSetting === OpenAIServiceTiers.FLEX) {
|
||||
if (isSupportFlexServiceTierModel(model)) {
|
||||
serviceTier = 'flex'
|
||||
serviceTier = OpenAIServiceTiers.FLEX
|
||||
} else {
|
||||
serviceTier = 'auto'
|
||||
serviceTier = OpenAIServiceTiers.AUTO
|
||||
}
|
||||
} else if (serviceTierSetting) {
|
||||
serviceTier = serviceTierSetting
|
||||
} else {
|
||||
serviceTier = openAI.serviceTier
|
||||
// undefined 时使用默认值 auto
|
||||
}
|
||||
|
||||
return serviceTier
|
||||
|
||||
@ -258,7 +258,6 @@ export const SYSTEM_PROVIDERS_CONFIG: Record<SystemProviderId, SystemProvider> =
|
||||
models: SYSTEM_MODELS.openai,
|
||||
isSystem: true,
|
||||
enabled: false,
|
||||
isSupportServiceTier: true,
|
||||
serviceTier: OpenAIServiceTiers.AUTO
|
||||
},
|
||||
'azure-openai': {
|
||||
@ -415,9 +414,7 @@ export const SYSTEM_PROVIDERS_CONFIG: Record<SystemProviderId, SystemProvider> =
|
||||
apiHost: 'https://api.groq.com/openai',
|
||||
models: SYSTEM_MODELS.groq,
|
||||
isSystem: true,
|
||||
enabled: false,
|
||||
isSupportServiceTier: true,
|
||||
serviceTier: OpenAIServiceTiers.DEFAULT
|
||||
enabled: false
|
||||
},
|
||||
together: {
|
||||
id: 'together',
|
||||
@ -1259,38 +1256,63 @@ export const PROVIDER_CONFIG = {
|
||||
}
|
||||
}
|
||||
|
||||
const NOT_SUPPORT_ARRAY_CONTENT_PROVIDERS = ['deepseek', 'baichuan', 'minimax', 'xirang']
|
||||
const NOT_SUPPORT_ARRAY_CONTENT_PROVIDERS = [
|
||||
'deepseek',
|
||||
'baichuan',
|
||||
'minimax',
|
||||
'xirang'
|
||||
] as const satisfies SystemProviderId[]
|
||||
|
||||
/**
|
||||
* 判断提供商是否支持 message 的 content 为数组类型。 Only for OpenAI Chat Completions API.
|
||||
*/
|
||||
export const isSupportArrayContentProvider = (provider: Provider) => {
|
||||
return provider.isNotSupportArrayContent !== true || !NOT_SUPPORT_ARRAY_CONTENT_PROVIDERS.includes(provider.id)
|
||||
return (
|
||||
provider.isNotSupportArrayContent !== true ||
|
||||
!NOT_SUPPORT_ARRAY_CONTENT_PROVIDERS.some((pid) => pid === provider.id)
|
||||
)
|
||||
}
|
||||
|
||||
const NOT_SUPPORT_DEVELOPER_ROLE_PROVIDERS = ['poe']
|
||||
const NOT_SUPPORT_DEVELOPER_ROLE_PROVIDERS = ['poe'] as const satisfies SystemProviderId[]
|
||||
|
||||
/**
|
||||
* 判断提供商是否支持 developer 作为 message role。 Only for OpenAI API.
|
||||
*/
|
||||
export const isSupportDeveloperRoleProvider = (provider: Provider) => {
|
||||
return provider.isNotSupportDeveloperRole !== true || !NOT_SUPPORT_DEVELOPER_ROLE_PROVIDERS.includes(provider.id)
|
||||
return (
|
||||
provider.isNotSupportDeveloperRole !== true ||
|
||||
!NOT_SUPPORT_DEVELOPER_ROLE_PROVIDERS.some((pid) => pid === provider.id)
|
||||
)
|
||||
}
|
||||
|
||||
const NOT_SUPPORT_STREAM_OPTIONS_PROVIDERS = ['mistral']
|
||||
const NOT_SUPPORT_STREAM_OPTIONS_PROVIDERS = ['mistral'] as const satisfies SystemProviderId[]
|
||||
|
||||
/**
|
||||
* 判断提供商是否支持 stream_options 参数。Only for OpenAI API.
|
||||
*/
|
||||
export const isSupportStreamOptionsProvider = (provider: Provider) => {
|
||||
return provider.isNotSupportStreamOptions !== true || !NOT_SUPPORT_STREAM_OPTIONS_PROVIDERS.includes(provider.id)
|
||||
return (
|
||||
provider.isNotSupportStreamOptions !== true ||
|
||||
!NOT_SUPPORT_STREAM_OPTIONS_PROVIDERS.some((pid) => pid === provider.id)
|
||||
)
|
||||
}
|
||||
|
||||
const SUPPORT_QWEN3_ENABLE_THINKING_PROVIDER = ['dashscope', 'modelscope']
|
||||
const SUPPORT_QWEN3_ENABLE_THINKING_PROVIDER = ['dashscope', 'modelscope'] as const satisfies SystemProviderId[]
|
||||
|
||||
/**
|
||||
* 判断提供商是否支持使用enable_thinking参数来控制Qwen3系列模型的思考。 Only for OpenAI Chat Completions API.
|
||||
*/
|
||||
export const isSupportQwen3EnableThinkingProvider = (provider: Provider) => {
|
||||
return SUPPORT_QWEN3_ENABLE_THINKING_PROVIDER.includes(provider.id)
|
||||
return SUPPORT_QWEN3_ENABLE_THINKING_PROVIDER.some((pid) => pid === provider.id)
|
||||
}
|
||||
|
||||
const NOT_SUPPORT_SERVICE_TIER_PROVIDERS = ['github', 'copilot'] as const satisfies SystemProviderId[]
|
||||
|
||||
/**
|
||||
* 判断提供商是否支持 service_tier 设置。 Only for OpenAI API.
|
||||
*/
|
||||
export const isSupportServiceTierProviders = (provider: Provider) => {
|
||||
return (
|
||||
provider.isNotSupportServiceTier !== true || !NOT_SUPPORT_SERVICE_TIER_PROVIDERS.some((pid) => pid === provider.id)
|
||||
)
|
||||
}
|
||||
|
||||
@ -3054,6 +3054,7 @@
|
||||
"auto": "自动",
|
||||
"default": "默认",
|
||||
"flex": "灵活",
|
||||
"priority": "优先",
|
||||
"tip": "指定用于处理请求的延迟层级",
|
||||
"title": "服务层级"
|
||||
},
|
||||
@ -3107,6 +3108,10 @@
|
||||
"label": "支持 Developer Message"
|
||||
},
|
||||
"label": "API 设置",
|
||||
"service_tier": {
|
||||
"help": "该提供商是否支持配置 service_tier 参数。开启后,可在对话页面的服务层级设置中调整该参数。(仅限OpenAI模型)",
|
||||
"label": "支持 service_tier"
|
||||
},
|
||||
"stream_options": {
|
||||
"help": "该提供商是否支持 stream_options 参数",
|
||||
"label": "支持 stream_options"
|
||||
|
||||
@ -3,11 +3,7 @@ import { HStack } from '@renderer/components/Layout'
|
||||
import Scrollbar from '@renderer/components/Scrollbar'
|
||||
import Selector from '@renderer/components/Selector'
|
||||
import { DEFAULT_CONTEXTCOUNT, DEFAULT_MAX_TOKENS, DEFAULT_TEMPERATURE } from '@renderer/config/constant'
|
||||
import {
|
||||
isOpenAIModel,
|
||||
isSupportedReasoningEffortOpenAIModel,
|
||||
isSupportFlexServiceTierModel
|
||||
} from '@renderer/config/models'
|
||||
import { isOpenAIModel } from '@renderer/config/models'
|
||||
import { translateLanguageOptions } from '@renderer/config/translate'
|
||||
import { useCodeStyle } from '@renderer/context/CodeStyleProvider'
|
||||
import { useTheme } from '@renderer/context/ThemeProvider'
|
||||
@ -168,11 +164,6 @@ const SettingsTab: FC<Props> = (props) => {
|
||||
const model = assistant.model || getDefaultModel()
|
||||
|
||||
const isOpenAI = isOpenAIModel(model)
|
||||
const isOpenAIReasoning =
|
||||
isSupportedReasoningEffortOpenAIModel(model) &&
|
||||
!model.id.includes('o1-pro') &&
|
||||
(provider.type === 'openai-response' || provider.id === 'aihubmix')
|
||||
const isOpenAIFlexServiceTier = isSupportFlexServiceTierModel(model)
|
||||
|
||||
return (
|
||||
<Container className="settings-tab">
|
||||
@ -300,8 +291,8 @@ const SettingsTab: FC<Props> = (props) => {
|
||||
</CollapsibleSettingGroup>
|
||||
{isOpenAI && (
|
||||
<OpenAISettingsGroup
|
||||
isOpenAIReasoning={isOpenAIReasoning}
|
||||
isSupportedFlexServiceTier={isOpenAIFlexServiceTier}
|
||||
model={model}
|
||||
providerId={provider.id}
|
||||
SettingGroup={SettingGroup}
|
||||
SettingRowTitleSmall={SettingRowTitleSmall}
|
||||
/>
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
import Selector from '@renderer/components/Selector'
|
||||
import { isSupportedReasoningEffortOpenAIModel, isSupportFlexServiceTierModel } from '@renderer/config/models'
|
||||
import { useProvider } from '@renderer/hooks/useProvider'
|
||||
import { SettingDivider, SettingRow } from '@renderer/pages/settings'
|
||||
import { CollapsibleSettingGroup } from '@renderer/pages/settings/SettingGroup'
|
||||
import { RootState, useAppDispatch } from '@renderer/store'
|
||||
import { setOpenAIServiceTier, setOpenAISummaryText } from '@renderer/store/settings'
|
||||
import { OpenAIServiceTier, OpenAISummaryText } from '@renderer/types'
|
||||
import { setOpenAISummaryText } from '@renderer/store/settings'
|
||||
import { Model, OpenAIServiceTier, OpenAISummaryText } from '@renderer/types'
|
||||
import { Tooltip } from 'antd'
|
||||
import { CircleHelp } from 'lucide-react'
|
||||
import { FC, useCallback, useEffect, useMemo } from 'react'
|
||||
@ -11,8 +13,8 @@ import { useTranslation } from 'react-i18next'
|
||||
import { useSelector } from 'react-redux'
|
||||
|
||||
interface Props {
|
||||
isOpenAIReasoning: boolean
|
||||
isSupportedFlexServiceTier: boolean
|
||||
model: Model
|
||||
providerId: string
|
||||
SettingGroup: FC<{ children: React.ReactNode }>
|
||||
SettingRowTitleSmall: FC<{ children: React.ReactNode }>
|
||||
}
|
||||
@ -24,17 +26,20 @@ const FALL_BACK_SERVICE_TIER: Record<OpenAIServiceTier, OpenAIServiceTier> = {
|
||||
priority: 'priority'
|
||||
}
|
||||
|
||||
const OpenAISettingsGroup: FC<Props> = ({
|
||||
isOpenAIReasoning,
|
||||
isSupportedFlexServiceTier,
|
||||
SettingGroup,
|
||||
SettingRowTitleSmall
|
||||
}) => {
|
||||
const OpenAISettingsGroup: FC<Props> = ({ model, providerId, SettingGroup, SettingRowTitleSmall }) => {
|
||||
const { t } = useTranslation()
|
||||
const { provider, updateProvider } = useProvider(providerId)
|
||||
const summaryText = useSelector((state: RootState) => state.settings.openAI.summaryText)
|
||||
const serviceTierMode = useSelector((state: RootState) => state.settings.openAI.serviceTier)
|
||||
const serviceTierMode = provider.serviceTier
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
const isOpenAIReasoning =
|
||||
isSupportedReasoningEffortOpenAIModel(model) &&
|
||||
!model.id.includes('o1-pro') &&
|
||||
(provider.type === 'openai-response' || provider.id === 'aihubmix')
|
||||
const isSupportServiceTier = !provider.isNotSupportServiceTier
|
||||
const isSupportedFlexServiceTier = isSupportFlexServiceTierModel(model)
|
||||
|
||||
const setSummaryText = useCallback(
|
||||
(value: OpenAISummaryText) => {
|
||||
dispatch(setOpenAISummaryText(value))
|
||||
@ -44,9 +49,9 @@ const OpenAISettingsGroup: FC<Props> = ({
|
||||
|
||||
const setServiceTierMode = useCallback(
|
||||
(value: OpenAIServiceTier) => {
|
||||
dispatch(setOpenAIServiceTier(value))
|
||||
updateProvider({ serviceTier: value })
|
||||
},
|
||||
[dispatch]
|
||||
[updateProvider]
|
||||
)
|
||||
|
||||
const summaryTextOptions = [
|
||||
@ -97,24 +102,31 @@ const OpenAISettingsGroup: FC<Props> = ({
|
||||
}
|
||||
}, [serviceTierMode, serviceTierOptions, setServiceTierMode])
|
||||
|
||||
if (!isOpenAIReasoning && !isSupportServiceTier) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<CollapsibleSettingGroup title={t('settings.openai.title')} defaultExpanded={true}>
|
||||
<SettingGroup>
|
||||
<SettingRow>
|
||||
<SettingRowTitleSmall>
|
||||
{t('settings.openai.service_tier.title')}{' '}
|
||||
<Tooltip title={t('settings.openai.service_tier.tip')}>
|
||||
<CircleHelp size={14} style={{ marginLeft: 4 }} color="var(--color-text-2)" />
|
||||
</Tooltip>
|
||||
</SettingRowTitleSmall>
|
||||
<Selector
|
||||
value={serviceTierMode}
|
||||
onChange={(value) => {
|
||||
setServiceTierMode(value as OpenAIServiceTier)
|
||||
}}
|
||||
options={serviceTierOptions}
|
||||
/>
|
||||
</SettingRow>
|
||||
{isSupportServiceTier && (
|
||||
<SettingRow>
|
||||
<SettingRowTitleSmall>
|
||||
{t('settings.openai.service_tier.title')}{' '}
|
||||
<Tooltip title={t('settings.openai.service_tier.tip')}>
|
||||
<CircleHelp size={14} style={{ marginLeft: 4 }} color="var(--color-text-2)" />
|
||||
</Tooltip>
|
||||
</SettingRowTitleSmall>
|
||||
<Selector
|
||||
value={serviceTierMode}
|
||||
onChange={(value) => {
|
||||
setServiceTierMode(value as OpenAIServiceTier)
|
||||
}}
|
||||
options={serviceTierOptions}
|
||||
placeholder={t('settings.openai.service_tier.auto')}
|
||||
/>
|
||||
</SettingRow>
|
||||
)}
|
||||
{isOpenAIReasoning && (
|
||||
<>
|
||||
<SettingDivider />
|
||||
|
||||
@ -59,6 +59,15 @@ const ApiOptionsSettings = ({ providerId }: Props) => {
|
||||
updateProviderTransition({ ...provider, isNotSupportArrayContent: !checked })
|
||||
},
|
||||
checked: !provider.isNotSupportArrayContent
|
||||
},
|
||||
{
|
||||
key: 'openai_service_tier',
|
||||
label: t('settings.provider.api.options.service_tier.label'),
|
||||
tip: t('settings.provider.api.options.service_tier.help'),
|
||||
onChange: (checked: boolean) => {
|
||||
updateProviderTransition({ ...provider, isNotSupportArrayContent: !checked })
|
||||
},
|
||||
checked: !provider.isNotSupportServiceTier
|
||||
}
|
||||
],
|
||||
[t, provider, updateProviderTransition]
|
||||
|
||||
@ -186,6 +186,7 @@ export interface SettingsState {
|
||||
// OpenAI
|
||||
openAI: {
|
||||
summaryText: OpenAISummaryText
|
||||
/** @deprecated 现在该设置迁移到Provider对象中 */
|
||||
serviceTier: OpenAIServiceTier
|
||||
}
|
||||
// Notification
|
||||
@ -759,9 +760,6 @@ const settingsSlice = createSlice({
|
||||
setOpenAISummaryText: (state, action: PayloadAction<OpenAISummaryText>) => {
|
||||
state.openAI.summaryText = action.payload
|
||||
},
|
||||
setOpenAIServiceTier: (state, action: PayloadAction<OpenAIServiceTier>) => {
|
||||
state.openAI.serviceTier = action.payload
|
||||
},
|
||||
setNotificationSettings: (state, action: PayloadAction<SettingsState['notification']>) => {
|
||||
state.notification = action.payload
|
||||
},
|
||||
@ -925,7 +923,6 @@ export const {
|
||||
setEnableBackspaceDeleteModel,
|
||||
setDisableHardwareAcceleration,
|
||||
setOpenAISummaryText,
|
||||
setOpenAIServiceTier,
|
||||
setNotificationSettings,
|
||||
// Local backup settings
|
||||
setLocalBackupDir,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user