feat: add tool use mode setting to default assistant settings (#11943)

* feat: add tool use mode setting to default assistant settings

- Add toolUseMode selector (prompt/function) to DefaultAssistantSettings
- Add dividers between model parameter sections for better UI
- Reduce slider margins for compact layout
- Add migration (v185) to reset toolUseMode to 'function' for existing users

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: reset toolUseMode for all assistants during migration

- Update migration logic to reset toolUseMode to 'function' for all assistants with a 'prompt' setting.
- Ensure compatibility with function calling models by checking model type before resetting.

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
亢奋猫 2025-12-17 15:37:11 +08:00 committed by GitHub
parent bfeef7ef91
commit 782f8496e0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 57 additions and 10 deletions

View File

@ -2,13 +2,14 @@ import { CloseCircleFilled, QuestionCircleOutlined } from '@ant-design/icons'
import EmojiPicker from '@renderer/components/EmojiPicker'
import { ResetIcon } from '@renderer/components/Icons'
import { HStack } from '@renderer/components/Layout'
import Selector from '@renderer/components/Selector'
import { TopView } from '@renderer/components/TopView'
import { DEFAULT_CONTEXTCOUNT, DEFAULT_MAX_TOKENS, DEFAULT_TEMPERATURE } from '@renderer/config/constant'
import { useTheme } from '@renderer/context/ThemeProvider'
import { useDefaultAssistant } from '@renderer/hooks/useAssistant'
import type { AssistantSettings as AssistantSettingsType } from '@renderer/types'
import { getLeadingEmoji, modalConfirm } from '@renderer/utils'
import { Button, Col, Flex, Input, InputNumber, Modal, Popover, Row, Slider, Switch, Tooltip } from 'antd'
import { Button, Col, Divider, Flex, Input, InputNumber, Modal, Popover, Row, Slider, Switch, Tooltip } from 'antd'
import TextArea from 'antd/es/input/TextArea'
import type { Dispatch, FC, SetStateAction } from 'react'
import { useState } from 'react'
@ -26,6 +27,9 @@ const AssistantSettings: FC = () => {
const [maxTokens, setMaxTokens] = useState(defaultAssistant?.settings?.maxTokens ?? 0)
const [topP, setTopP] = useState(defaultAssistant.settings?.topP ?? 1)
const [enableTopP, setEnableTopP] = useState(defaultAssistant.settings?.enableTopP ?? false)
const [toolUseMode, setToolUseMode] = useState<AssistantSettingsType['toolUseMode']>(
defaultAssistant.settings?.toolUseMode ?? 'function'
)
const [emoji, setEmoji] = useState(defaultAssistant.emoji || getLeadingEmoji(defaultAssistant.name) || '')
const [name, setName] = useState(
defaultAssistant.name.replace(getLeadingEmoji(defaultAssistant.name) || '', '').trim()
@ -46,7 +50,8 @@ const AssistantSettings: FC = () => {
maxTokens: settings.maxTokens ?? maxTokens,
streamOutput: settings.streamOutput ?? true,
topP: settings.topP ?? topP,
enableTopP: settings.enableTopP ?? enableTopP
enableTopP: settings.enableTopP ?? enableTopP,
toolUseMode: settings.toolUseMode ?? toolUseMode
}
})
}
@ -73,6 +78,7 @@ const AssistantSettings: FC = () => {
setMaxTokens(0)
setTopP(1)
setEnableTopP(false)
setToolUseMode('function')
updateDefaultAssistant({
...defaultAssistant,
settings: {
@ -84,7 +90,8 @@ const AssistantSettings: FC = () => {
maxTokens: DEFAULT_MAX_TOKENS,
streamOutput: true,
topP: 1,
enableTopP: false
enableTopP: false,
toolUseMode: 'function'
}
})
}
@ -107,10 +114,9 @@ const AssistantSettings: FC = () => {
return (
<SettingContainer
style={{ height: 'auto', background: 'transparent', padding: `0 0 12px 0`, gap: 12 }}
style={{ height: 'auto', background: 'transparent', padding: `0 0 12px 0`, gap: 10 }}
theme={theme}>
<SettingSubtitle style={{ marginTop: 0 }}>{t('common.name')}</SettingSubtitle>
<HStack gap={8} alignItems="center">
<HStack gap={8} alignItems="center" mt={10}>
<Popover content={<EmojiPicker onEmojiClick={handleEmojiSelect} />} arrow trigger="click">
<EmojiButtonWrapper>
<Button style={{ fontSize: 20, padding: '4px', minWidth: '30px', height: '30px' }}>{emoji}</Button>
@ -161,6 +167,7 @@ const AssistantSettings: FC = () => {
<Button type="text" onClick={onReset} icon={<ResetIcon size={16} />} />
</Tooltip>
</SettingSubtitle>
<Divider style={{ margin: '2px 0' }} />
<SettingRow>
<HStack alignItems="center">
<Label>{t('chat.settings.temperature.label')}</Label>
@ -178,7 +185,7 @@ const AssistantSettings: FC = () => {
/>
</SettingRow>
{enableTemperature && (
<Row align="middle" gutter={12}>
<Row align="middle" gutter={12} style={{ marginTop: -5, marginBottom: -10 }}>
<Col span={20}>
<Slider
min={0}
@ -202,6 +209,7 @@ const AssistantSettings: FC = () => {
</Col>
</Row>
)}
<Divider style={{ margin: '2px 0' }} />
<SettingRow>
<HStack alignItems="center">
<Label>{t('chat.settings.top_p.label')}</Label>
@ -219,7 +227,7 @@ const AssistantSettings: FC = () => {
/>
</SettingRow>
{enableTopP && (
<Row align="middle" gutter={12}>
<Row align="middle" gutter={12} style={{ marginTop: -5, marginBottom: -10 }}>
<Col span={20}>
<Slider
min={0}
@ -236,13 +244,14 @@ const AssistantSettings: FC = () => {
</Col>
</Row>
)}
<Divider style={{ margin: '2px 0' }} />
<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" gutter={20}>
<Row align="middle" gutter={20} style={{ marginTop: -5, marginBottom: -10 }}>
<Col span={19}>
<Slider
min={0}
@ -265,6 +274,7 @@ const AssistantSettings: FC = () => {
/>
</Col>
</Row>
<Divider style={{ margin: '2px 0' }} />
<Flex justify="space-between" align="center">
<HStack alignItems="center">
<Label>{t('chat.settings.max_tokens.label')}</Label>
@ -308,6 +318,22 @@ const AssistantSettings: FC = () => {
</Col>
</Row>
)}
<Divider style={{ margin: '2px 0' }} />
<SettingRow>
<Label>{t('assistants.settings.tool_use_mode.label')}</Label>
<Selector
value={toolUseMode}
options={[
{ label: t('assistants.settings.tool_use_mode.prompt'), value: 'prompt' },
{ label: t('assistants.settings.tool_use_mode.function'), value: 'function' }
]}
onChange={(value) => {
setToolUseMode(value)
onUpdateAssistantSettings({ toolUseMode: value })
}}
size={14}
/>
</SettingRow>
</SettingContainer>
)
}

View File

@ -67,7 +67,7 @@ const persistedReducer = persistReducer(
{
key: 'cherry-studio',
storage,
version: 184,
version: 185,
blacklist: ['runtime', 'messages', 'messageBlocks', 'tabs', 'toolPermissions'],
migrate
},

View File

@ -3017,6 +3017,27 @@ const migrateConfig = {
logger.error('migrate 184 error', error as Error)
return state
}
},
'185': (state: RootState) => {
try {
// Reset toolUseMode to function for default assistant
if (state.assistants.defaultAssistant.settings?.toolUseMode) {
state.assistants.defaultAssistant.settings.toolUseMode = 'function'
}
// Reset toolUseMode to function for assistants
state.assistants.assistants.forEach((assistant) => {
if (assistant.settings?.toolUseMode === 'prompt') {
if (assistant.model && isFunctionCallingModel(assistant.model)) {
assistant.settings.toolUseMode = 'function'
}
}
})
logger.info('migrate 185 success')
return state
} catch (error) {
logger.error('migrate 185 error', error as Error)
return state
}
}
}