mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-19 22:52:08 +08:00
refactor: align create agent model selection with edit agent
This commit is contained in:
parent
84f2281506
commit
84bf94e2ff
@ -3,14 +3,14 @@ import ClaudeIcon from '@renderer/assets/images/models/claude.png'
|
|||||||
import { ErrorBoundary } from '@renderer/components/ErrorBoundary'
|
import { ErrorBoundary } from '@renderer/components/ErrorBoundary'
|
||||||
import { TopView } from '@renderer/components/TopView'
|
import { TopView } from '@renderer/components/TopView'
|
||||||
import { permissionModeCards } from '@renderer/config/agent'
|
import { permissionModeCards } from '@renderer/config/agent'
|
||||||
import { agentModelFilter, getModelLogoById } from '@renderer/config/models'
|
|
||||||
import { useAgents } from '@renderer/hooks/agents/useAgents'
|
import { useAgents } from '@renderer/hooks/agents/useAgents'
|
||||||
import { useApiModels } from '@renderer/hooks/agents/useModels'
|
|
||||||
import { useUpdateAgent } from '@renderer/hooks/agents/useUpdateAgent'
|
import { useUpdateAgent } from '@renderer/hooks/agents/useUpdateAgent'
|
||||||
|
import SelectAgentBaseModelButton from '@renderer/pages/home/components/SelectAgentBaseModelButton'
|
||||||
import type {
|
import type {
|
||||||
AddAgentForm,
|
AddAgentForm,
|
||||||
AgentEntity,
|
AgentEntity,
|
||||||
AgentType,
|
AgentType,
|
||||||
|
ApiModel,
|
||||||
BaseAgentForm,
|
BaseAgentForm,
|
||||||
PermissionMode,
|
PermissionMode,
|
||||||
Tool,
|
Tool,
|
||||||
@ -65,7 +65,6 @@ const PopupContainer: React.FC<Props> = ({ agent, afterSubmit, resolve }) => {
|
|||||||
const loadingRef = useRef(false)
|
const loadingRef = useRef(false)
|
||||||
const { addAgent } = useAgents()
|
const { addAgent } = useAgents()
|
||||||
const { updateAgent } = useUpdateAgent()
|
const { updateAgent } = useUpdateAgent()
|
||||||
const { models } = useApiModels({ providerType: 'anthropic' })
|
|
||||||
const isEditing = (agent?: AgentWithTools) => agent !== undefined
|
const isEditing = (agent?: AgentWithTools) => agent !== undefined
|
||||||
|
|
||||||
const [form, setForm] = useState<BaseAgentForm>(() => buildAgentForm(agent))
|
const [form, setForm] = useState<BaseAgentForm>(() => buildAgentForm(agent))
|
||||||
@ -199,32 +198,26 @@ const PopupContainer: React.FC<Props> = ({ agent, afterSubmit, resolve }) => {
|
|||||||
}))
|
}))
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const modelOptions = useMemo(() => {
|
// Create a temporary agentBase object for SelectAgentBaseModelButton
|
||||||
return (models ?? [])
|
const tempAgentBase: AgentEntity = useMemo(
|
||||||
.filter((m) =>
|
() => ({
|
||||||
agentModelFilter({
|
id: agent?.id ?? 'temp-creating',
|
||||||
id: m.id,
|
type: form.type,
|
||||||
provider: m.provider || '',
|
name: form.name,
|
||||||
name: m.name,
|
model: form.model,
|
||||||
group: ''
|
accessible_paths: form.accessible_paths.length > 0 ? form.accessible_paths : ['/'],
|
||||||
})
|
allowed_tools: form.allowed_tools ?? [],
|
||||||
)
|
description: form.description,
|
||||||
.map((model) => ({
|
instructions: form.instructions,
|
||||||
value: model.id,
|
configuration: form.configuration,
|
||||||
label: (
|
created_at: agent?.created_at ?? new Date().toISOString(),
|
||||||
<OptionWrapper>
|
updated_at: agent?.updated_at ?? new Date().toISOString()
|
||||||
<Avatar src={getModelLogoById(model.id)} size={24} />
|
}),
|
||||||
<span>{model.name}</span>
|
[form, agent?.id, agent?.created_at, agent?.updated_at]
|
||||||
</OptionWrapper>
|
)
|
||||||
)
|
|
||||||
}))
|
|
||||||
}, [models])
|
|
||||||
|
|
||||||
const onModelChange = useCallback((value: string) => {
|
const handleModelSelect = useCallback(async (model: ApiModel) => {
|
||||||
setForm((prev) => ({
|
setForm((prev) => ({ ...prev, model: model.id }))
|
||||||
...prev,
|
|
||||||
model: value
|
|
||||||
}))
|
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const onCancel = () => {
|
const onCancel = () => {
|
||||||
@ -336,7 +329,7 @@ const PopupContainer: React.FC<Props> = ({ agent, afterSubmit, resolve }) => {
|
|||||||
afterClose={onClose}
|
afterClose={onClose}
|
||||||
transitionName="animation-move-down"
|
transitionName="animation-move-down"
|
||||||
centered
|
centered
|
||||||
width={680}
|
width={500}
|
||||||
footer={null}>
|
footer={null}>
|
||||||
<StyledForm onSubmit={onSubmit}>
|
<StyledForm onSubmit={onSubmit}>
|
||||||
<FormContent>
|
<FormContent>
|
||||||
@ -363,17 +356,20 @@ const PopupContainer: React.FC<Props> = ({ agent, afterSubmit, resolve }) => {
|
|||||||
<Label>
|
<Label>
|
||||||
{t('common.model')} <RequiredMark>*</RequiredMark>
|
{t('common.model')} <RequiredMark>*</RequiredMark>
|
||||||
</Label>
|
</Label>
|
||||||
<Select
|
<SelectAgentBaseModelButton
|
||||||
value={form.model || undefined}
|
agentBase={tempAgentBase}
|
||||||
onChange={onModelChange}
|
onSelect={handleModelSelect}
|
||||||
options={modelOptions}
|
fontSize={14}
|
||||||
placeholder={t('common.placeholders.select.model')}
|
avatarSize={24}
|
||||||
style={{ width: '100%' }}
|
iconSize={16}
|
||||||
showSearch
|
buttonStyle={{
|
||||||
filterOption={(input, option) => {
|
padding: '8px 12px',
|
||||||
const label = option?.label as any
|
width: '100%',
|
||||||
return label?.props?.children?.[1]?.toLowerCase().includes(input.toLowerCase()) || false
|
border: '1px solid var(--color-border)',
|
||||||
|
borderRadius: 6,
|
||||||
|
height: 'auto'
|
||||||
}}
|
}}
|
||||||
|
containerClassName="flex items-center justify-between w-full"
|
||||||
/>
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
||||||
|
|||||||
@ -4,25 +4,56 @@ import { agentModelFilter } from '@renderer/config/models'
|
|||||||
import { useApiModel } from '@renderer/hooks/agents/useModel'
|
import { useApiModel } from '@renderer/hooks/agents/useModel'
|
||||||
import { getProviderNameById } from '@renderer/services/ProviderService'
|
import { getProviderNameById } from '@renderer/services/ProviderService'
|
||||||
import type { AgentBaseWithId, ApiModel } from '@renderer/types'
|
import type { AgentBaseWithId, ApiModel } from '@renderer/types'
|
||||||
|
import { isAgentSessionEntity } from '@renderer/types'
|
||||||
import { isAgentEntity } from '@renderer/types'
|
import { isAgentEntity } from '@renderer/types'
|
||||||
import { getModelFilterByAgentType } from '@renderer/utils/agentSession'
|
import { getModelFilterByAgentType } from '@renderer/utils/agentSession'
|
||||||
import { apiModelAdapter } from '@renderer/utils/model'
|
import { apiModelAdapter } from '@renderer/utils/model'
|
||||||
|
import type { ButtonProps } from 'antd'
|
||||||
import { Button } from 'antd'
|
import { Button } from 'antd'
|
||||||
import { ChevronsUpDown } from 'lucide-react'
|
import { ChevronsUpDown } from 'lucide-react'
|
||||||
import type { FC } from 'react'
|
import type { CSSProperties, FC } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
agentBase: AgentBaseWithId
|
agentBase: AgentBaseWithId
|
||||||
onSelect: (model: ApiModel) => Promise<void>
|
onSelect: (model: ApiModel) => Promise<void>
|
||||||
isDisabled?: boolean
|
isDisabled?: boolean
|
||||||
|
/** Custom className for the button */
|
||||||
|
className?: string
|
||||||
|
/** Custom inline styles for the button (merged with default styles) */
|
||||||
|
buttonStyle?: CSSProperties
|
||||||
|
/** Custom button size */
|
||||||
|
buttonSize?: ButtonProps['size']
|
||||||
|
/** Custom avatar size */
|
||||||
|
avatarSize?: number
|
||||||
|
/** Custom font size */
|
||||||
|
fontSize?: number
|
||||||
|
/** Custom icon size */
|
||||||
|
iconSize?: number
|
||||||
|
/** Custom className for the inner container (e.g., for justify-between) */
|
||||||
|
containerClassName?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const SelectAgentBaseModelButton: FC<Props> = ({ agentBase: agent, onSelect, isDisabled }) => {
|
const SelectAgentBaseModelButton: FC<Props> = ({
|
||||||
|
agentBase: agent,
|
||||||
|
onSelect,
|
||||||
|
isDisabled,
|
||||||
|
className,
|
||||||
|
buttonStyle,
|
||||||
|
buttonSize = 'small',
|
||||||
|
avatarSize = 20,
|
||||||
|
fontSize = 12,
|
||||||
|
iconSize = 14,
|
||||||
|
containerClassName
|
||||||
|
}) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const model = useApiModel({ id: agent?.model })
|
const model = useApiModel({ id: agent?.model })
|
||||||
|
|
||||||
const apiFilter = isAgentEntity(agent) ? getModelFilterByAgentType(agent.type) : undefined
|
const apiFilter = isAgentEntity(agent)
|
||||||
|
? getModelFilterByAgentType(agent.type)
|
||||||
|
: isAgentSessionEntity(agent)
|
||||||
|
? getModelFilterByAgentType(agent.agent_type)
|
||||||
|
: undefined
|
||||||
|
|
||||||
if (!agent) return null
|
if (!agent) return null
|
||||||
|
|
||||||
@ -35,20 +66,31 @@ const SelectAgentBaseModelButton: FC<Props> = ({ agentBase: agent, onSelect, isD
|
|||||||
|
|
||||||
const providerName = model?.provider ? getProviderNameById(model.provider) : model?.provider_name
|
const providerName = model?.provider ? getProviderNameById(model.provider) : model?.provider_name
|
||||||
|
|
||||||
|
// Merge default styles with custom styles
|
||||||
|
const mergedStyle: CSSProperties = {
|
||||||
|
borderRadius: 20,
|
||||||
|
fontSize,
|
||||||
|
padding: 2,
|
||||||
|
...buttonStyle
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
size={buttonSize}
|
||||||
type="text"
|
type="text"
|
||||||
style={{ borderRadius: 20, fontSize: 12, padding: 2 }}
|
className={className}
|
||||||
|
style={mergedStyle}
|
||||||
onClick={onSelectModel}
|
onClick={onSelectModel}
|
||||||
disabled={isDisabled}>
|
disabled={isDisabled}>
|
||||||
<div className="flex items-center gap-1.5 overflow-x-hidden">
|
<div className={containerClassName || 'flex w-full items-center gap-1.5'}>
|
||||||
<ModelAvatar model={model ? apiModelAdapter(model) : undefined} size={20} />
|
<div className="flex flex-1 items-center gap-1.5 overflow-x-hidden">
|
||||||
<span className="truncate text-[var(--color-text)]">
|
<ModelAvatar model={model ? apiModelAdapter(model) : undefined} size={avatarSize} />
|
||||||
{model ? model.name : t('button.select_model')} {providerName ? ' | ' + providerName : ''}
|
<span className="truncate text-[var(--color-text)]">
|
||||||
</span>
|
{model ? model.name : t('button.select_model')} {providerName ? ' | ' + providerName : ''}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<ChevronsUpDown size={iconSize} color="var(--color-icon)" />
|
||||||
</div>
|
</div>
|
||||||
<ChevronsUpDown size={14} color="var(--color-icon)" />
|
|
||||||
</Button>
|
</Button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user