From ae35d689ec75d2189165c2e6686a8d429d792dbf Mon Sep 17 00:00:00 2001 From: icarus Date: Sat, 20 Sep 2025 00:32:51 +0800 Subject: [PATCH] feat(AgentModal): add dynamic model options from API Fetch model options from API instead of using mocked data Display provider label for model options --- .../components/Popups/agent/AgentModal.tsx | 44 ++++++++++++++----- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/src/renderer/src/components/Popups/agent/AgentModal.tsx b/src/renderer/src/components/Popups/agent/AgentModal.tsx index 039595da1b..08481b4be1 100644 --- a/src/renderer/src/components/Popups/agent/AgentModal.tsx +++ b/src/renderer/src/components/Popups/agent/AgentModal.tsx @@ -18,7 +18,10 @@ import { } from '@heroui/react' import { loggerService } from '@logger' import ClaudeIcon from '@renderer/assets/images/models/claude.png' +import { getModelLogo } from '@renderer/config/models' import { useAgents } from '@renderer/hooks/agents/useAgents' +import { useModels } from '@renderer/hooks/agents/useModels' +import { getProviderLabel } from '@renderer/i18n/label' import { AddAgentForm, AgentEntity, AgentType, BaseAgentForm, isAgentType, UpdateAgentForm } from '@renderer/types' import { ChangeEvent, FormEvent, ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' @@ -27,19 +30,33 @@ import { ErrorBoundary } from '../../ErrorBoundary' const logger = loggerService.withContext('AddAgentPopup') -interface Option { +interface BaseOption { + type: 'type' | 'model' key: string label: string // img src avatar: string } -interface AgentTypeOption extends Option { +interface AgentTypeOption extends BaseOption { + type: 'type' key: AgentEntity['type'] name: AgentEntity['name'] } -type ModelOption = Option +// function isAgentTypeOption(option: BaseOption): option is AgentTypeOption { +// return option.type === 'type' +// } + +interface ModelOption extends BaseOption { + providerId?: string +} + +function isModelOption(option: BaseOption): option is ModelOption { + return option.type === 'model' +} + +type Option = AgentTypeOption | ModelOption const buildAgentForm = (existing?: AgentEntity): BaseAgentForm => ({ type: existing?.type ?? 'claude-code', @@ -84,6 +101,8 @@ export const AgentModal: React.FC = ({ agent, trigger, isOpen: _isOpen, o const loadingRef = useRef(false) // const { setTimeoutTimer } = useTimer() const { addAgent, updateAgent } = useAgents() + // hard-coded. We only support anthropic for now. + const { models } = useModels({ providerType: 'anthropic' }) const isEditing = (agent?: AgentEntity) => agent !== undefined const [form, setForm] = useState(() => buildAgentForm(agent)) @@ -107,7 +126,7 @@ export const AgentModal: React.FC = ({ agent, trigger, isOpen: _isOpen, o return (
- {option.label} + {option.label} {isModelOption(option) && option.providerId && `| ${getProviderLabel(option.providerId)}`}
) }, @@ -126,6 +145,7 @@ export const AgentModal: React.FC = ({ agent, trigger, isOpen: _isOpen, o () => [ { + type: 'type', key: 'claude-code', label: 'Claude Code', name: 'Claude Code', @@ -189,14 +209,14 @@ export const AgentModal: React.FC = ({ agent, trigger, isOpen: _isOpen, o const modelOptions = useMemo(() => { // mocked data. not final version - return [ - { - key: 'claude-4-sonnet', - label: 'Claude 4 Sonnet', - avatar: ClaudeIcon - } - ] satisfies ModelOption[] - }, []) + return (models ?? []).map((model) => ({ + type: 'model', + key: model.id, + label: model.name, + avatar: getModelLogo(model.id), + providerId: model.provider + })) satisfies ModelOption[] + }, [models]) const onModelChange = useCallback((e: ChangeEvent) => { setForm((prev) => ({