refactor: align create agent model selection with edit agent

This commit is contained in:
defi-failure 2025-11-06 12:06:09 +08:00
parent 84f2281506
commit 84bf94e2ff
2 changed files with 88 additions and 50 deletions

View File

@ -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,
instructions: form.instructions,
configuration: form.configuration,
created_at: agent?.created_at ?? new Date().toISOString(),
updated_at: agent?.updated_at ?? new Date().toISOString()
}),
[form, agent?.id, agent?.created_at, agent?.updated_at]
) )
.map((model) => ({
value: model.id,
label: (
<OptionWrapper>
<Avatar src={getModelLogoById(model.id)} size={24} />
<span>{model.name}</span>
</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>

View File

@ -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">
<ModelAvatar model={model ? apiModelAdapter(model) : undefined} size={avatarSize} />
<span className="truncate text-[var(--color-text)]"> <span className="truncate text-[var(--color-text)]">
{model ? model.name : t('button.select_model')} {providerName ? ' | ' + providerName : ''} {model ? model.name : t('button.select_model')} {providerName ? ' | ' + providerName : ''}
</span> </span>
</div> </div>
<ChevronsUpDown size={14} color="var(--color-icon)" /> <ChevronsUpDown size={iconSize} color="var(--color-icon)" />
</div>
</Button> </Button>
) )
} }