mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-26 20:12:38 +08:00
feat(agent): add advanced configuration settings
This commit is contained in:
parent
60c85b651f
commit
9c679ede20
@ -25,6 +25,7 @@ import { useApiModels } from '@renderer/hooks/agents/useModels'
|
||||
import { useUpdateAgent } from '@renderer/hooks/agents/useUpdateAgent'
|
||||
import {
|
||||
AddAgentForm,
|
||||
AgentConfigurationSchema,
|
||||
AgentEntity,
|
||||
AgentType,
|
||||
BaseAgentForm,
|
||||
@ -58,7 +59,8 @@ const buildAgentForm = (existing?: AgentWithTools): BaseAgentForm => ({
|
||||
model: existing?.model ?? 'claude-4-sonnet',
|
||||
accessible_paths: existing?.accessible_paths ? [...existing.accessible_paths] : [],
|
||||
allowed_tools: existing?.allowed_tools ? [...existing.allowed_tools] : [],
|
||||
mcps: existing?.mcps ? [...existing.mcps] : []
|
||||
mcps: existing?.mcps ? [...existing.mcps] : [],
|
||||
configuration: AgentConfigurationSchema.parse(existing?.configuration ?? {})
|
||||
})
|
||||
|
||||
interface BaseProps {
|
||||
@ -321,7 +323,8 @@ export const AgentModal: React.FC<Props> = ({ agent, trigger, isOpen: _isOpen, o
|
||||
instructions: form.instructions,
|
||||
model: form.model,
|
||||
accessible_paths: [...form.accessible_paths],
|
||||
allowed_tools: [...form.allowed_tools]
|
||||
allowed_tools: [...form.allowed_tools],
|
||||
configuration: form.configuration ? { ...form.configuration } : undefined
|
||||
} satisfies UpdateAgentForm
|
||||
|
||||
updateAgent(updatePayload)
|
||||
@ -334,7 +337,8 @@ export const AgentModal: React.FC<Props> = ({ agent, trigger, isOpen: _isOpen, o
|
||||
instructions: form.instructions,
|
||||
model: form.model,
|
||||
accessible_paths: [...form.accessible_paths],
|
||||
allowed_tools: [...form.allowed_tools]
|
||||
allowed_tools: [...form.allowed_tools],
|
||||
configuration: form.configuration ? { ...form.configuration } : undefined
|
||||
} satisfies AddAgentForm
|
||||
addAgent(newAgent)
|
||||
logger.debug('Added agent', newAgent)
|
||||
@ -353,6 +357,7 @@ export const AgentModal: React.FC<Props> = ({ agent, trigger, isOpen: _isOpen, o
|
||||
form.instructions,
|
||||
form.accessible_paths,
|
||||
form.allowed_tools,
|
||||
form.configuration,
|
||||
agent,
|
||||
onClose,
|
||||
t,
|
||||
|
||||
@ -80,7 +80,27 @@
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"advance": {
|
||||
"maxTurns": {
|
||||
"description": "Define how many request/response cycles the agent may complete automatically.",
|
||||
"helper": "Higher values enable longer autonomous runs; lower values keep sessions short.",
|
||||
"label": "Conversation turn limit"
|
||||
},
|
||||
"permissionMode": {
|
||||
"description": "Control how the agent handles actions that require approval.",
|
||||
"label": "Permission mode",
|
||||
"options": {
|
||||
"acceptEdits": "Accept edits automatically",
|
||||
"bypassPermissions": "Bypass permission checks",
|
||||
"default": "Default (ask before continuing)",
|
||||
"plan": "Planning mode (requires plan approval)"
|
||||
},
|
||||
"placeholder": "Choose a permission behavior"
|
||||
},
|
||||
"title": "Advanced Settings"
|
||||
},
|
||||
"essential": "Essential Settings",
|
||||
"mcps": "MCP Servers",
|
||||
"prompt": "Prompt Settings"
|
||||
},
|
||||
"type": {
|
||||
|
||||
@ -80,6 +80,25 @@
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"advance": {
|
||||
"maxTurns": {
|
||||
"description": "设定代理自动执行的请求/回复轮次数。",
|
||||
"helper": "数值越高可自主运行越久;数值越低更易控制。",
|
||||
"label": "会话轮次数上限"
|
||||
},
|
||||
"permissionMode": {
|
||||
"description": "控制代理在需要授权时的处理方式。",
|
||||
"label": "权限模式",
|
||||
"options": {
|
||||
"acceptEdits": "自动接受编辑",
|
||||
"bypassPermissions": "跳过权限检查",
|
||||
"default": "默认(继续前询问)",
|
||||
"plan": "规划模式(需审批计划)"
|
||||
},
|
||||
"placeholder": "选择权限模式"
|
||||
},
|
||||
"title": "高级设置"
|
||||
},
|
||||
"essential": "基础设置",
|
||||
"mcps": "MCP 服务器",
|
||||
"prompt": "提示词设置"
|
||||
|
||||
@ -80,7 +80,27 @@
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"advance": {
|
||||
"maxTurns": {
|
||||
"description": "設定代理自動執行的請求/回覆輪次数。",
|
||||
"helper": "數值越高可自動運行越久;數值越低更容易掌控。",
|
||||
"label": "會話輪次上限"
|
||||
},
|
||||
"permissionMode": {
|
||||
"description": "控制代理在需要授權時的處理方式。",
|
||||
"label": "權限模式",
|
||||
"options": {
|
||||
"acceptEdits": "自動接受編輯",
|
||||
"bypassPermissions": "略過權限檢查",
|
||||
"default": "預設(繼續前先詢問)",
|
||||
"plan": "規劃模式(需核准計畫)"
|
||||
},
|
||||
"placeholder": "選擇權限模式"
|
||||
},
|
||||
"title": "進階設定"
|
||||
},
|
||||
"essential": "必要設定",
|
||||
"mcps": "MCP 伺服器",
|
||||
"prompt": "提示設定"
|
||||
},
|
||||
"type": {
|
||||
|
||||
@ -80,7 +80,27 @@
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"advance": {
|
||||
"maxTurns": {
|
||||
"description": "[to be translated]:设定代理自动执行的请求/回复轮次数。",
|
||||
"helper": "[to be translated]:数值越高可自主运行越久;数值越低更易控制。",
|
||||
"label": "[to be translated]:会话轮次数上限"
|
||||
},
|
||||
"permissionMode": {
|
||||
"description": "[to be translated]:控制代理在需要授权时的处理方式。",
|
||||
"label": "[to be translated]:权限模式",
|
||||
"options": {
|
||||
"acceptEdits": "[to be translated]:自动接受编辑",
|
||||
"bypassPermissions": "[to be translated]:跳过权限检查",
|
||||
"default": "[to be translated]:默认(继续前询问)",
|
||||
"plan": "[to be translated]:规划模式(需审批计划)"
|
||||
},
|
||||
"placeholder": "[to be translated]:选择权限模式"
|
||||
},
|
||||
"title": "[to be translated]:高级设置"
|
||||
},
|
||||
"essential": "Βασικές Ρυθμίσεις",
|
||||
"mcps": "[to be translated]:MCP 服务器",
|
||||
"prompt": "Ρυθμίσεις Προτροπής"
|
||||
},
|
||||
"type": {
|
||||
|
||||
@ -80,7 +80,27 @@
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"advance": {
|
||||
"maxTurns": {
|
||||
"description": "[to be translated]:设定代理自动执行的请求/回复轮次数。",
|
||||
"helper": "[to be translated]:数值越高可自主运行越久;数值越低更易控制。",
|
||||
"label": "[to be translated]:会话轮次数上限"
|
||||
},
|
||||
"permissionMode": {
|
||||
"description": "[to be translated]:控制代理在需要授权时的处理方式。",
|
||||
"label": "[to be translated]:权限模式",
|
||||
"options": {
|
||||
"acceptEdits": "[to be translated]:自动接受编辑",
|
||||
"bypassPermissions": "[to be translated]:跳过权限检查",
|
||||
"default": "[to be translated]:默认(继续前询问)",
|
||||
"plan": "[to be translated]:规划模式(需审批计划)"
|
||||
},
|
||||
"placeholder": "[to be translated]:选择权限模式"
|
||||
},
|
||||
"title": "[to be translated]:高级设置"
|
||||
},
|
||||
"essential": "Configuraciones esenciales",
|
||||
"mcps": "[to be translated]:MCP 服务器",
|
||||
"prompt": "Configuración de indicaciones"
|
||||
},
|
||||
"type": {
|
||||
|
||||
@ -80,7 +80,27 @@
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"advance": {
|
||||
"maxTurns": {
|
||||
"description": "[to be translated]:设定代理自动执行的请求/回复轮次数。",
|
||||
"helper": "[to be translated]:数值越高可自主运行越久;数值越低更易控制。",
|
||||
"label": "[to be translated]:会话轮次数上限"
|
||||
},
|
||||
"permissionMode": {
|
||||
"description": "[to be translated]:控制代理在需要授权时的处理方式。",
|
||||
"label": "[to be translated]:权限模式",
|
||||
"options": {
|
||||
"acceptEdits": "[to be translated]:自动接受编辑",
|
||||
"bypassPermissions": "[to be translated]:跳过权限检查",
|
||||
"default": "[to be translated]:默认(继续前询问)",
|
||||
"plan": "[to be translated]:规划模式(需审批计划)"
|
||||
},
|
||||
"placeholder": "[to be translated]:选择权限模式"
|
||||
},
|
||||
"title": "[to be translated]:高级设置"
|
||||
},
|
||||
"essential": "Paramètres essentiels",
|
||||
"mcps": "[to be translated]:MCP 服务器",
|
||||
"prompt": "Paramètres de l'invite"
|
||||
},
|
||||
"type": {
|
||||
|
||||
@ -80,7 +80,27 @@
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"advance": {
|
||||
"maxTurns": {
|
||||
"description": "[to be translated]:设定代理自动执行的请求/回复轮次数。",
|
||||
"helper": "[to be translated]:数值越高可自主运行越久;数值越低更易控制。",
|
||||
"label": "[to be translated]:会话轮次数上限"
|
||||
},
|
||||
"permissionMode": {
|
||||
"description": "[to be translated]:控制代理在需要授权时的处理方式。",
|
||||
"label": "[to be translated]:权限模式",
|
||||
"options": {
|
||||
"acceptEdits": "[to be translated]:自动接受编辑",
|
||||
"bypassPermissions": "[to be translated]:跳过权限检查",
|
||||
"default": "[to be translated]:默认(继续前询问)",
|
||||
"plan": "[to be translated]:规划模式(需审批计划)"
|
||||
},
|
||||
"placeholder": "[to be translated]:选择权限模式"
|
||||
},
|
||||
"title": "[to be translated]:高级设置"
|
||||
},
|
||||
"essential": "必須設定",
|
||||
"mcps": "[to be translated]:MCP 服务器",
|
||||
"prompt": "プロンプト設定"
|
||||
},
|
||||
"type": {
|
||||
|
||||
@ -80,7 +80,27 @@
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"advance": {
|
||||
"maxTurns": {
|
||||
"description": "[to be translated]:设定代理自动执行的请求/回复轮次数。",
|
||||
"helper": "[to be translated]:数值越高可自主运行越久;数值越低更易控制。",
|
||||
"label": "[to be translated]:会话轮次数上限"
|
||||
},
|
||||
"permissionMode": {
|
||||
"description": "[to be translated]:控制代理在需要授权时的处理方式。",
|
||||
"label": "[to be translated]:权限模式",
|
||||
"options": {
|
||||
"acceptEdits": "[to be translated]:自动接受编辑",
|
||||
"bypassPermissions": "[to be translated]:跳过权限检查",
|
||||
"default": "[to be translated]:默认(继续前询问)",
|
||||
"plan": "[to be translated]:规划模式(需审批计划)"
|
||||
},
|
||||
"placeholder": "[to be translated]:选择权限模式"
|
||||
},
|
||||
"title": "[to be translated]:高级设置"
|
||||
},
|
||||
"essential": "Configurações Essenciais",
|
||||
"mcps": "[to be translated]:MCP 服务器",
|
||||
"prompt": "Configurações de Prompt"
|
||||
},
|
||||
"type": {
|
||||
|
||||
@ -80,7 +80,27 @@
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"advance": {
|
||||
"maxTurns": {
|
||||
"description": "[to be translated]:设定代理自动执行的请求/回复轮次数。",
|
||||
"helper": "[to be translated]:数值越高可自主运行越久;数值越低更易控制。",
|
||||
"label": "[to be translated]:会话轮次数上限"
|
||||
},
|
||||
"permissionMode": {
|
||||
"description": "[to be translated]:控制代理在需要授权时的处理方式。",
|
||||
"label": "[to be translated]:权限模式",
|
||||
"options": {
|
||||
"acceptEdits": "[to be translated]:自动接受编辑",
|
||||
"bypassPermissions": "[to be translated]:跳过权限检查",
|
||||
"default": "[to be translated]:默认(继续前询问)",
|
||||
"plan": "[to be translated]:规划模式(需审批计划)"
|
||||
},
|
||||
"placeholder": "[to be translated]:选择权限模式"
|
||||
},
|
||||
"title": "[to be translated]:高级设置"
|
||||
},
|
||||
"essential": "Основные настройки",
|
||||
"mcps": "[to be translated]:MCP 服务器",
|
||||
"prompt": "Настройки подсказки"
|
||||
},
|
||||
"type": {
|
||||
|
||||
@ -0,0 +1,162 @@
|
||||
import { Input, Select, SelectItem, Tooltip } from '@heroui/react'
|
||||
import type { Selection } from '@react-types/shared'
|
||||
import {
|
||||
AgentConfiguration,
|
||||
AgentConfigurationSchema,
|
||||
GetAgentResponse,
|
||||
PermissionMode,
|
||||
PermissionModeSchema,
|
||||
UpdateAgentForm
|
||||
} from '@renderer/types'
|
||||
import { Info } from 'lucide-react'
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import { SettingsContainer, SettingsItem, SettingsTitle } from './shared'
|
||||
|
||||
const permissionModeKeyMap: Record<PermissionMode, string> = {
|
||||
default: 'agent.settings.advance.permissionMode.options.default',
|
||||
acceptEdits: 'agent.settings.advance.permissionMode.options.acceptEdits',
|
||||
bypassPermissions: 'agent.settings.advance.permissionMode.options.bypassPermissions',
|
||||
plan: 'agent.settings.advance.permissionMode.options.plan'
|
||||
}
|
||||
|
||||
const permissionModeFallback: Record<PermissionMode, string> = {
|
||||
default: 'Default (ask before continuing)',
|
||||
acceptEdits: 'Accept edits automatically',
|
||||
bypassPermissions: 'Bypass permission checks',
|
||||
plan: 'Planning mode (requires plan approval)'
|
||||
}
|
||||
|
||||
type AgentConfigurationState = AgentConfiguration & Record<string, unknown>
|
||||
|
||||
interface AgentAdvanceSettingsProps {
|
||||
agent: GetAgentResponse | undefined | null
|
||||
updateAgent: (form: UpdateAgentForm) => Promise<void> | void
|
||||
}
|
||||
|
||||
const defaultConfiguration = AgentConfigurationSchema.parse({}) as AgentConfigurationState
|
||||
|
||||
export const AgentAdvanceSettings: React.FC<AgentAdvanceSettingsProps> = ({ agent, updateAgent }) => {
|
||||
const { t } = useTranslation()
|
||||
const [configuration, setConfiguration] = useState<AgentConfigurationState>(defaultConfiguration)
|
||||
const [maxTurnsInput, setMaxTurnsInput] = useState<string>(String(defaultConfiguration.max_turns))
|
||||
|
||||
useEffect(() => {
|
||||
if (!agent) {
|
||||
setConfiguration(defaultConfiguration)
|
||||
setMaxTurnsInput(String(defaultConfiguration.max_turns))
|
||||
return
|
||||
}
|
||||
const parsed = AgentConfigurationSchema.parse(agent.configuration ?? {}) as AgentConfigurationState
|
||||
setConfiguration(parsed)
|
||||
setMaxTurnsInput(String(parsed.max_turns))
|
||||
}, [agent])
|
||||
|
||||
const permissionOptions = useMemo(
|
||||
() =>
|
||||
PermissionModeSchema.options.map((mode) => ({
|
||||
key: mode,
|
||||
label: t(permissionModeKeyMap[mode], permissionModeFallback[mode])
|
||||
})) satisfies { key: PermissionMode; label: string }[],
|
||||
[t]
|
||||
)
|
||||
|
||||
const handlePermissionChange = useCallback(
|
||||
(keys: Selection) => {
|
||||
if (!agent || keys === 'all') return
|
||||
const [first] = Array.from(keys)
|
||||
if (!first) return
|
||||
const nextMode = first as PermissionMode
|
||||
setConfiguration((prev) => {
|
||||
if (prev.permission_mode === nextMode) {
|
||||
return prev
|
||||
}
|
||||
const next = { ...prev, permission_mode: nextMode } as AgentConfigurationState
|
||||
updateAgent({ id: agent.id, configuration: next } satisfies UpdateAgentForm)
|
||||
return next
|
||||
})
|
||||
},
|
||||
[agent, updateAgent]
|
||||
)
|
||||
|
||||
const commitMaxTurns = useCallback(() => {
|
||||
if (!agent) return
|
||||
const parsedValue = Number.parseInt(maxTurnsInput, 10)
|
||||
if (!Number.isFinite(parsedValue)) {
|
||||
setMaxTurnsInput(String(configuration.max_turns))
|
||||
return
|
||||
}
|
||||
const sanitized = Math.max(1, parsedValue)
|
||||
if (sanitized === configuration.max_turns) {
|
||||
setMaxTurnsInput(String(configuration.max_turns))
|
||||
return
|
||||
}
|
||||
const next = { ...configuration, max_turns: sanitized } as AgentConfigurationState
|
||||
setConfiguration(next)
|
||||
setMaxTurnsInput(String(sanitized))
|
||||
updateAgent({ id: agent.id, configuration: next } satisfies UpdateAgentForm)
|
||||
}, [agent, configuration, maxTurnsInput, updateAgent])
|
||||
|
||||
if (!agent) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<SettingsContainer>
|
||||
<SettingsItem>
|
||||
<SettingsTitle
|
||||
actions={
|
||||
<Tooltip content={t('agent.settings.advance.permissionMode.description')} placement="right">
|
||||
<Info size={16} className="text-foreground-400" />
|
||||
</Tooltip>
|
||||
}>
|
||||
{t('agent.settings.advance.permissionMode.label')}
|
||||
</SettingsTitle>
|
||||
<Select
|
||||
aria-label={t('agent.settings.advance.permissionMode.label')}
|
||||
selectionMode="single"
|
||||
selectedKeys={[configuration.permission_mode]}
|
||||
onSelectionChange={handlePermissionChange}
|
||||
className="max-w-md"
|
||||
placeholder={t('agent.settings.advance.permissionMode.placeholder')}>
|
||||
{permissionOptions.map((option) => (
|
||||
<SelectItem key={option.key} textValue={option.label}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
</SettingsItem>
|
||||
<SettingsItem divider={false}>
|
||||
<SettingsTitle
|
||||
actions={
|
||||
<Tooltip content={t('agent.settings.advance.maxTurns.description')} placement="right">
|
||||
<Info size={16} className="text-foreground-400" />
|
||||
</Tooltip>
|
||||
}>
|
||||
{t('agent.settings.advance.maxTurns.label')}
|
||||
</SettingsTitle>
|
||||
<div className="flex max-w-md flex-col gap-2">
|
||||
<Input
|
||||
type="number"
|
||||
min={1}
|
||||
value={maxTurnsInput}
|
||||
onValueChange={setMaxTurnsInput}
|
||||
onBlur={commitMaxTurns}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter') {
|
||||
commitMaxTurns()
|
||||
}
|
||||
}}
|
||||
aria-label={t('agent.settings.advance.maxTurns.label')}
|
||||
/>
|
||||
<span className="text-foreground-500 text-xs">
|
||||
{t('agent.settings.advance.maxTurns.helper')}
|
||||
</span>
|
||||
</div>
|
||||
</SettingsItem>
|
||||
</SettingsContainer>
|
||||
)
|
||||
}
|
||||
|
||||
export default AgentAdvanceSettings
|
||||
@ -7,6 +7,7 @@ import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import styled from 'styled-components'
|
||||
|
||||
import AgentAdvanceSettings from './AgentAdvanceSettings'
|
||||
import AgentEssentialSettings from './AgentEssentialSettings'
|
||||
import AgentMCPSettings from './AgentMCPSettings'
|
||||
import AgentPromptSettings from './AgentPromptSettings'
|
||||
@ -21,7 +22,7 @@ interface AgentSettingPopupParams extends AgentSettingPopupShowParams {
|
||||
resolve: () => void
|
||||
}
|
||||
|
||||
type AgentSettingPopupTab = 'essential' | 'prompt' | 'mcps' | 'session-mcps'
|
||||
type AgentSettingPopupTab = 'essential' | 'prompt' | 'mcps' | 'advance' | 'session-mcps'
|
||||
|
||||
const AgentSettingPopupContainer: React.FC<AgentSettingPopupParams> = ({ tab, agentId, resolve }) => {
|
||||
const [open, setOpen] = useState(true)
|
||||
@ -56,6 +57,10 @@ const AgentSettingPopupContainer: React.FC<AgentSettingPopupParams> = ({ tab, ag
|
||||
{
|
||||
key: 'mcps',
|
||||
label: t('agent.settings.mcps', 'MCP Servers')
|
||||
},
|
||||
{
|
||||
key: 'advance',
|
||||
label: t('agent.settings.advance.title', 'Advanced Settings')
|
||||
}
|
||||
] as const satisfies { key: AgentSettingPopupTab; label: string }[]
|
||||
).filter(Boolean)
|
||||
@ -87,6 +92,7 @@ const AgentSettingPopupContainer: React.FC<AgentSettingPopupParams> = ({ tab, ag
|
||||
{menu === 'essential' && <AgentEssentialSettings agent={agent} update={updateAgent} />}
|
||||
{menu === 'prompt' && <AgentPromptSettings agent={agent} update={updateAgent} />}
|
||||
{menu === 'mcps' && <AgentMCPSettings agent={agent} updateAgent={updateAgent} />}
|
||||
{menu === 'advance' && <AgentAdvanceSettings agent={agent} updateAgent={updateAgent} />}
|
||||
</Settings>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -169,6 +169,7 @@ export type BaseAgentForm = {
|
||||
accessible_paths: string[]
|
||||
allowed_tools: string[]
|
||||
mcps?: string[]
|
||||
configuration?: AgentConfiguration
|
||||
}
|
||||
|
||||
export type AddAgentForm = Omit<BaseAgentForm, 'id'> & { id?: never }
|
||||
|
||||
Loading…
Reference in New Issue
Block a user