feat: add Top-P parameter toggle with default enabled state and improved UI styling (#8137)

* feat: add Top-P parameter toggle with default enabled state and improved UI styling

* fix: resolve undefined enableTopP issue in ppio models by using getAssistantSettings

* refactor(api): Unify getTopP method to BaseApiClient

* feat(settings): adjust layout of Top-P setting in assistant model settings

* feat: add temperature parameter toggle control with UI and multi-language support

* fix: Fix lint error

* fix: Sort these imports

* style(settings): refactor model settings layout and styles

* chore: yarn sync:i18n
This commit is contained in:
lihqi 2025-07-28 21:27:31 +08:00 committed by GitHub
parent c4182a950f
commit 536aa68389
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 246 additions and 164 deletions

View File

@ -8,6 +8,7 @@ import {
import { REFERENCE_PROMPT } from '@renderer/config/prompts'
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,
@ -185,11 +186,19 @@ export abstract class BaseApiClient<
}
public getTemperature(assistant: Assistant, model: Model): number | undefined {
return isNotSupportTemperatureAndTopP(model) ? undefined : assistant.settings?.temperature
if (isNotSupportTemperatureAndTopP(model)) {
return undefined
}
const assistantSettings = getAssistantSettings(assistant)
return assistantSettings?.enableTemperature ? assistantSettings?.temperature : undefined
}
public getTopP(assistant: Assistant, model: Model): number | undefined {
return isNotSupportTemperatureAndTopP(model) ? undefined : assistant.settings?.topP
if (isNotSupportTemperatureAndTopP(model)) {
return undefined
}
const assistantSettings = getAssistantSettings(assistant)
return assistantSettings?.enableTopP ? assistantSettings?.topP : undefined
}
protected getServiceTier(model: Model) {

View File

@ -138,14 +138,14 @@ export class AnthropicAPIClient extends BaseApiClient<
if (assistant.settings?.reasoning_effort && isClaudeReasoningModel(model)) {
return undefined
}
return assistant.settings?.temperature
return super.getTemperature(assistant, model)
}
override getTopP(assistant: Assistant, model: Model): number | undefined {
if (assistant.settings?.reasoning_effort && isClaudeReasoningModel(model)) {
return undefined
}
return assistant.settings?.topP
return super.getTopP(assistant, model)
}
/**

View File

@ -1,7 +1,6 @@
import { loggerService } from '@logger'
import {
isClaudeReasoningModel,
isNotSupportTemperatureAndTopP,
isOpenAIReasoningModel,
isSupportedModel,
isSupportedReasoningEffortOpenAIModel
@ -172,23 +171,17 @@ export abstract class OpenAIBaseClient<
}
override getTemperature(assistant: Assistant, model: Model): number | undefined {
if (
isNotSupportTemperatureAndTopP(model) ||
(assistant.settings?.reasoning_effort && isClaudeReasoningModel(model))
) {
if (assistant.settings?.reasoning_effort && isClaudeReasoningModel(model)) {
return undefined
}
return assistant.settings?.temperature
return super.getTemperature(assistant, model)
}
override getTopP(assistant: Assistant, model: Model): number | undefined {
if (
isNotSupportTemperatureAndTopP(model) ||
(assistant.settings?.reasoning_effort && isClaudeReasoningModel(model))
) {
if (assistant.settings?.reasoning_effort && isClaudeReasoningModel(model)) {
return undefined
}
return assistant.settings?.topP
return super.getTopP(assistant, model)
}
/**

View File

@ -465,7 +465,7 @@
},
"top_p": {
"label": "Top-P",
"tip": "默认值为 1值越小AI 生成的内容越单调也越容易理解值越大AI 回复的词汇围越大,越多样化"
"tip": "默认值为 1值越小AI 生成的内容越单调也越容易理解值越大AI 回复的词汇围越大,越多样化"
}
},
"suggestions": {

View File

@ -68,6 +68,7 @@ const SettingsTab: FC<Props> = (props) => {
const { themeNames } = useCodeStyle()
const [temperature, setTemperature] = useState(assistant?.settings?.temperature ?? DEFAULT_TEMPERATURE)
const [enableTemperature, setEnableTemperature] = useState(assistant?.settings?.enableTemperature ?? true)
const [contextCount, setContextCount] = useState(assistant?.settings?.contextCount ?? DEFAULT_CONTEXTCOUNT)
const [enableMaxTokens, setEnableMaxTokens] = useState(assistant?.settings?.enableMaxTokens ?? false)
const [maxTokens, setMaxTokens] = useState(assistant?.settings?.maxTokens ?? 0)
@ -154,6 +155,7 @@ const SettingsTab: FC<Props> = (props) => {
useEffect(() => {
setTemperature(assistant?.settings?.temperature ?? DEFAULT_TEMPERATURE)
setEnableTemperature(assistant?.settings?.enableTemperature ?? true)
setContextCount(assistant?.settings?.contextCount ?? DEFAULT_CONTEXTCOUNT)
setEnableMaxTokens(assistant?.settings?.enableMaxTokens ?? false)
setMaxTokens(assistant?.settings?.maxTokens ?? DEFAULT_MAX_TOKENS)
@ -193,19 +195,32 @@ const SettingsTab: FC<Props> = (props) => {
<Tooltip title={t('chat.settings.temperature.tip')}>
<CircleHelp size={14} style={{ marginLeft: 4 }} color="var(--color-text-2)" />
</Tooltip>
<Switch
size="small"
style={{ marginLeft: 'auto' }}
checked={enableTemperature}
onChange={(enabled) => {
setEnableTemperature(enabled)
onUpdateAssistantSettings({ enableTemperature: enabled })
}}
/>
</Row>
<Row align="middle" gutter={10}>
<Col span={23}>
<Slider
min={0}
max={2}
onChange={setTemperature}
onChangeComplete={onTemperatureChange}
value={typeof temperature === 'number' ? temperature : 0}
step={0.1}
/>
</Col>
</Row>
{enableTemperature ? (
<Row align="middle" gutter={10}>
<Col span={23}>
<Slider
min={0}
max={2}
onChange={setTemperature}
onChangeComplete={onTemperatureChange}
value={typeof temperature === 'number' ? temperature : 0}
step={0.1}
/>
</Col>
</Row>
) : (
<SettingDivider />
)}
<Row align="middle">
<SettingRowTitleSmall>{t('chat.settings.context_count.label')}</SettingRowTitleSmall>
<Tooltip title={t('chat.settings.context_count.tip')}>

View File

@ -29,9 +29,11 @@ const AssistantModelSettings: FC<Props> = ({ assistant, updateAssistant, updateA
const [toolUseMode, setToolUseMode] = useState(assistant?.settings?.toolUseMode ?? 'prompt')
const [defaultModel, setDefaultModel] = useState(assistant?.defaultModel)
const [topP, setTopP] = useState(assistant?.settings?.topP ?? 1)
const [enableTopP, setEnableTopP] = useState(assistant?.settings?.enableTopP ?? true)
const [customParameters, setCustomParameters] = useState<AssistantSettingCustomParameters[]>(
assistant?.settings?.customParameters ?? []
)
const [enableTemperature, setEnableTemperature] = useState(assistant?.settings?.enableTemperature ?? true)
const customParametersRef = useRef(customParameters)
@ -151,20 +153,24 @@ const AssistantModelSettings: FC<Props> = ({ assistant, updateAssistant, updateA
const onReset = () => {
setTemperature(DEFAULT_TEMPERATURE)
setEnableTemperature(true)
setContextCount(DEFAULT_CONTEXTCOUNT)
setEnableMaxTokens(false)
setMaxTokens(0)
setStreamOutput(true)
setTopP(1)
setEnableTopP(true)
setCustomParameters([])
setToolUseMode('prompt')
updateAssistantSettings({
temperature: DEFAULT_TEMPERATURE,
enableTemperature: true,
contextCount: DEFAULT_CONTEXTCOUNT,
enableMaxTokens: false,
maxTokens: 0,
streamOutput: true,
topP: 1,
enableTopP: true,
customParameters: [],
toolUseMode: 'prompt'
})
@ -226,86 +232,103 @@ const AssistantModelSettings: FC<Props> = ({ assistant, updateAssistant, updateA
</HStack>
</HStack>
<Divider style={{ margin: '10px 0' }} />
<Row align="middle">
<Col span={20}>
<SettingRow style={{ minHeight: 30 }}>
<HStack alignItems="center">
<Label>
{t('chat.settings.temperature.label')}
<Tooltip title={t('chat.settings.temperature.tip')}>
<QuestionIcon />
</Tooltip>
</Label>
</Col>
<Col span={4}>
<EditableNumber
min={0}
max={2}
step={0.01}
precision={2}
value={temperature}
onChange={(value) => {
if (value !== null) {
setTemperature(value)
setTimeout(() => updateAssistantSettings({ temperature: value }), 500)
}
}}
style={{ width: '100%' }}
/>
</Col>
</Row>
<Row align="middle" gutter={24}>
<Col span={24}>
<Slider
min={0}
max={2}
onChange={setTemperature}
onChangeComplete={onTemperatureChange}
value={typeof temperature === 'number' ? temperature : 0}
marks={{ 0: '0', 0.7: '0.7', 2: '2' }}
step={0.01}
/>
</Col>
</Row>
</HStack>
<Switch
checked={enableTemperature}
onChange={(enabled) => {
setEnableTemperature(enabled)
updateAssistantSettings({ enableTemperature: enabled })
}}
/>
</SettingRow>
{enableTemperature && (
<Row align="middle" gutter={12}>
<Col span={20}>
<Slider
min={0}
max={2}
onChange={setTemperature}
onChangeComplete={onTemperatureChange}
value={typeof temperature === 'number' ? temperature : 0}
marks={{ 0: '0', 0.7: '0.7', 2: '2' }}
step={0.01}
/>
</Col>
<Col span={4}>
<EditableNumber
min={0}
max={2}
step={0.01}
value={temperature}
changeOnBlur
onChange={(value) => {
if (!isNull(value)) {
setTemperature(value)
setTimeout(() => updateAssistantSettings({ temperature: value }), 500)
}
}}
style={{ width: '100%' }}
/>
</Col>
</Row>
)}
<Divider style={{ margin: '10px 0' }} />
<Row align="middle">
<Col span={20}>
<Label>
{t('chat.settings.top_p.label')}
<Tooltip title={t('chat.settings.top_p.tip')}>
<QuestionIcon />
</Tooltip>
</Label>
</Col>
<Col span={4}>
<EditableNumber
min={0}
max={1}
step={0.01}
value={topP}
changeOnBlur
onChange={(value) => {
if (!isNull(value)) {
setTopP(value)
setTimeout(() => updateAssistantSettings({ topP: value }), 500)
}
}}
style={{ width: '100%' }}
/>
</Col>
</Row>
<Row align="middle" gutter={24}>
<Col span={24}>
<Slider
min={0}
max={1}
onChange={setTopP}
onChangeComplete={onTopPChange}
value={typeof topP === 'number' ? topP : 1}
marks={{ 0: '0', 1: '1' }}
step={0.01}
/>
</Col>
</Row>
<SettingRow style={{ minHeight: 30 }}>
<HStack alignItems="center">
<Label>{t('chat.settings.top_p.label')}</Label>
<Tooltip title={t('chat.settings.top_p.tip')}>
<QuestionIcon />
</Tooltip>
</HStack>
<Switch
checked={enableTopP}
onChange={(enabled) => {
setEnableTopP(enabled)
updateAssistantSettings({ enableTopP: enabled })
}}
/>
</SettingRow>
{enableTopP && (
<Row align="middle" gutter={12}>
<Col span={20}>
<Slider
min={0}
max={1}
onChange={setTopP}
onChangeComplete={onTopPChange}
value={typeof topP === 'number' ? topP : 1}
marks={{ 0: '0', 1: '1' }}
step={0.01}
/>
</Col>
<Col span={4}>
<EditableNumber
min={0}
max={1}
step={0.01}
value={topP}
changeOnBlur
onChange={(value) => {
if (!isNull(value)) {
setTopP(value)
setTimeout(() => updateAssistantSettings({ topP: value }), 500)
}
}}
style={{ width: '100%' }}
/>
</Col>
</Row>
)}
<Divider style={{ margin: '10px 0' }} />
<Row align="middle">

View File

@ -13,15 +13,17 @@ import { Dispatch, FC, SetStateAction, useState } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
import { SettingContainer, SettingSubtitle } from '..'
import { SettingContainer, SettingRow, SettingSubtitle } from '..'
const AssistantSettings: FC = () => {
const { defaultAssistant, updateDefaultAssistant } = useDefaultAssistant()
const [temperature, setTemperature] = useState(defaultAssistant.settings?.temperature ?? DEFAULT_TEMPERATURE)
const [enableTemperature, setEnableTemperature] = useState(defaultAssistant.settings?.enableTemperature ?? true)
const [contextCount, setContextCount] = useState(defaultAssistant.settings?.contextCount ?? DEFAULT_CONTEXTCOUNT)
const [enableMaxTokens, setEnableMaxTokens] = useState(defaultAssistant?.settings?.enableMaxTokens ?? false)
const [maxTokens, setMaxTokens] = useState(defaultAssistant?.settings?.maxTokens ?? 0)
const [topP, setTopP] = useState(defaultAssistant.settings?.topP ?? 1)
const [enableTopP, setEnableTopP] = useState(defaultAssistant.settings?.enableTopP ?? true)
const [emoji, setEmoji] = useState(defaultAssistant.emoji || getLeadingEmoji(defaultAssistant.name) || '')
const [name, setName] = useState(
defaultAssistant.name.replace(getLeadingEmoji(defaultAssistant.name) || '', '').trim()
@ -36,11 +38,13 @@ const AssistantSettings: FC = () => {
settings: {
...defaultAssistant.settings,
temperature: settings.temperature ?? temperature,
enableTemperature: settings.enableTemperature ?? enableTemperature,
contextCount: settings.contextCount ?? contextCount,
enableMaxTokens: settings.enableMaxTokens ?? enableMaxTokens,
maxTokens: settings.maxTokens ?? maxTokens,
streamOutput: settings.streamOutput ?? true,
topP: settings.topP ?? topP
topP: settings.topP ?? topP,
enableTopP: settings.enableTopP ?? enableTopP
}
})
}
@ -61,20 +65,24 @@ const AssistantSettings: FC = () => {
const onReset = () => {
setTemperature(DEFAULT_TEMPERATURE)
setEnableTemperature(true)
setContextCount(DEFAULT_CONTEXTCOUNT)
setEnableMaxTokens(false)
setMaxTokens(0)
setTopP(1)
setEnableTopP(true)
updateDefaultAssistant({
...defaultAssistant,
settings: {
...defaultAssistant.settings,
temperature: DEFAULT_TEMPERATURE,
enableTemperature: true,
contextCount: DEFAULT_CONTEXTCOUNT,
enableMaxTokens: false,
maxTokens: DEFAULT_MAX_TOKENS,
streamOutput: true,
topP: 1
topP: 1,
enableTopP: true
}
})
}
@ -96,9 +104,11 @@ const AssistantSettings: FC = () => {
}
return (
<SettingContainer style={{ height: 'auto', background: 'transparent', padding: 0 }} theme={theme}>
<SettingContainer
style={{ height: 'auto', background: 'transparent', padding: `0 0 12px 0`, gap: 12 }}
theme={theme}>
<SettingSubtitle style={{ marginTop: 0 }}>{t('common.name')}</SettingSubtitle>
<HStack gap={8} alignItems="center" style={{ margin: '10px 0' }}>
<HStack gap={8} alignItems="center">
<Popover content={<EmojiPicker onEmojiClick={handleEmojiSelect} />} arrow>
<EmojiButtonWrapper>
<Button style={{ fontSize: 20, padding: '4px', minWidth: '30px', height: '30px' }}>{emoji}</Button>
@ -129,84 +139,108 @@ const AssistantSettings: FC = () => {
style={{ flex: 1 }}
/>
</HStack>
<SettingSubtitle>{t('common.prompt')}</SettingSubtitle>
<SettingSubtitle style={{ marginTop: 0 }}>{t('common.prompt')}</SettingSubtitle>
<TextArea
rows={4}
placeholder={t('common.assistant') + t('common.prompt')}
value={defaultAssistant.prompt}
onChange={(e) => updateDefaultAssistant({ ...defaultAssistant, prompt: e.target.value })}
style={{ margin: '10px 0' }}
spellCheck={false}
/>
<SettingSubtitle
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between'
justifyContent: 'space-between',
marginTop: 0
}}>
{t('settings.assistant.model_params')}
<Button onClick={onReset} style={{ width: 81 }}>
{t('chat.settings.reset')}
</Button>
</SettingSubtitle>
<Row align="middle">
<Label>{t('chat.settings.temperature.label')}</Label>
<Tooltip title={t('chat.settings.temperature.tip')}>
<QuestionIcon />
</Tooltip>
</Row>
<Row align="middle" style={{ marginBottom: 10 }} gutter={20}>
<Col span={19}>
<Slider
min={0}
max={2}
onChange={setTemperature}
onChangeComplete={onTemperatureChange}
value={typeof temperature === 'number' ? temperature : 0}
marks={{ 0: '0', 0.7: '0.7', 2: '2' }}
step={0.01}
/>
</Col>
<Col span={5}>
<InputNumber
min={0}
max={2}
step={0.01}
value={temperature}
onChange={onTemperatureChange}
style={{ width: '100%' }}
/>
</Col>
</Row>
<Row align="middle">
<Label>{t('chat.settings.top_p.label')}</Label>
<Tooltip title={t('chat.settings.top_p.tip')}>
<QuestionIcon />
</Tooltip>
</Row>
<Row align="middle" style={{ marginBottom: 10 }} gutter={20}>
<Col span={19}>
<Slider
min={0}
max={1}
onChange={setTopP}
onChangeComplete={onTopPChange}
value={typeof topP === 'number' ? topP : 1}
marks={{ 0: '0', 0.5: '0.5', 1: '1' }}
step={0.01}
/>
</Col>
<Col span={5}>
<InputNumber min={0} max={1} step={0.01} value={topP} onChange={onTopPChange} style={{ width: '100%' }} />
</Col>
</Row>
<SettingRow>
<HStack alignItems="center">
<Label>{t('chat.settings.temperature.label')}</Label>
<Tooltip title={t('chat.settings.temperature.tip')}>
<QuestionIcon />
</Tooltip>
</HStack>
<Switch
style={{ marginLeft: 10 }}
checked={enableTemperature}
onChange={(enabled) => {
setEnableTemperature(enabled)
onUpdateAssistantSettings({ enableTemperature: enabled })
}}
/>
</SettingRow>
{enableTemperature && (
<Row align="middle" gutter={12}>
<Col span={20}>
<Slider
min={0}
max={2}
onChange={setTemperature}
onChangeComplete={onTemperatureChange}
value={typeof temperature === 'number' ? temperature : 0}
marks={{ 0: '0', 0.7: '0.7', 2: '2' }}
step={0.01}
/>
</Col>
<Col span={4}>
<InputNumber
min={0}
max={2}
step={0.01}
value={temperature}
onChange={onTemperatureChange}
style={{ width: '100%' }}
/>
</Col>
</Row>
)}
<SettingRow>
<HStack alignItems="center">
<Label>{t('chat.settings.top_p.label')}</Label>
<Tooltip title={t('chat.settings.top_p.tip')}>
<QuestionIcon />
</Tooltip>
</HStack>
<Switch
style={{ marginLeft: 10 }}
checked={enableTopP}
onChange={(enabled) => {
setEnableTopP(enabled)
onUpdateAssistantSettings({ enableTopP: enabled })
}}
/>
</SettingRow>
{enableTopP && (
<Row align="middle" gutter={12}>
<Col span={20}>
<Slider
min={0}
max={1}
onChange={setTopP}
onChangeComplete={onTopPChange}
value={typeof topP === 'number' ? topP : 1}
marks={{ 0: '0', 0.5: '0.5', 1: '1' }}
step={0.01}
/>
</Col>
<Col span={4}>
<InputNumber min={0} max={1} step={0.01} value={topP} onChange={onTopPChange} style={{ width: '100%' }} />
</Col>
</Row>
)}
<Row align="middle">
<Label>{t('chat.settings.context_count.label')}</Label>
<Tooltip title={t('chat.settings.context_count.tip')}>
<QuestionIcon />
</Tooltip>
</Row>
<Row align="middle" style={{ marginBottom: 10 }} gutter={20}>
<Row align="middle" gutter={20}>
<Col span={19}>
<Slider
min={0}
@ -229,7 +263,7 @@ const AssistantSettings: FC = () => {
/>
</Col>
</Row>
<Flex justify="space-between" align="center" style={{ marginBottom: 10 }}>
<Flex justify="space-between" align="center">
<HStack alignItems="center">
<Label>{t('chat.settings.max_tokens.label')}</Label>
<Tooltip title={t('chat.settings.max_tokens.tip')}>

View File

@ -32,11 +32,13 @@ export function getDefaultAssistant(): Assistant {
regularPhrases: [], // Added regularPhrases
settings: {
temperature: DEFAULT_TEMPERATURE,
enableTemperature: true,
contextCount: DEFAULT_CONTEXTCOUNT,
enableMaxTokens: false,
maxTokens: 0,
streamOutput: true,
topP: 1,
enableTopP: true,
toolUseMode: 'prompt',
customParameters: []
}
@ -125,7 +127,9 @@ export const getAssistantSettings = (assistant: Assistant): AssistantSettings =>
return {
contextCount: contextCount === MAX_CONTEXT_COUNT ? UNLIMITED_CONTEXT_COUNT : contextCount,
temperature: assistant?.settings?.temperature ?? DEFAULT_TEMPERATURE,
enableTemperature: assistant?.settings?.enableTemperature ?? true,
topP: assistant?.settings?.topP ?? 1,
enableTopP: assistant?.settings?.enableTopP ?? true,
enableMaxTokens: assistant?.settings?.enableMaxTokens ?? false,
maxTokens: getAssistantMaxTokens(),
streamOutput: assistant?.settings?.streamOutput ?? true,
@ -155,11 +159,13 @@ export async function createAssistantFromAgent(agent: Agent) {
regularPhrases: agent.regularPhrases || [], // Ensured regularPhrases
settings: agent.settings || {
temperature: DEFAULT_TEMPERATURE,
enableTemperature: true,
contextCount: DEFAULT_CONTEXTCOUNT,
enableMaxTokens: false,
maxTokens: 0,
streamOutput: true,
topP: 1,
enableTopP: true,
toolUseMode: 'prompt',
customParameters: []
}

View File

@ -62,11 +62,13 @@ export const EFFORT_RATIO: EffortRatio = {
}
export type AssistantSettings = {
contextCount: number
maxTokens?: number
enableMaxTokens?: boolean
temperature: number
enableTemperature?: boolean
topP: number
maxTokens: number | undefined
enableMaxTokens: boolean
enableTopP?: boolean
contextCount: number
streamOutput: boolean
defaultModel?: Model
customParameters?: AssistantSettingCustomParameters[]