import { ChevronDown, PlusCircle, Ban } from 'lucide-react'; import { useEffect } from 'react'; import type { ChatModelConfig } from '@/api'; import { Button } from '@/components/ui/button'; import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; import { useAvailableModels } from '@/hooks/useAvailableModels'; import { useTranslation } from '@/i18n/useI18n.ts'; interface Props { value?: ChatModelConfig | null; /** * Called when the user selects a model, or — when `allowClear` is true — * clears the selection (in which case `null` is emitted). */ onChange?: (value: ChatModelConfig | null) => void; onAddCredential?: () => void; refetchTrigger?: number; /** Override the trigger label shown when no model is selected. */ placeholder?: string; /** * When true, append a "clear selection" item to the dropdown that emits * `null` via `onChange`. Used by the fallback selector. */ allowClear?: boolean; /** Override the label of the "clear selection" item. */ clearLabel?: string; } export function LlmSelect({ value, onChange, onAddCredential, refetchTrigger, placeholder, allowClear = false, clearLabel, }: Props) { const { groups, loading, refetch } = useAvailableModels(); const { t } = useTranslation(); const hasOptions = Object.keys(groups).length > 0; useEffect(() => { if (refetchTrigger !== undefined && refetchTrigger > 0) refetch(); }, [refetchTrigger, refetch]); const handleSelect = (type: string, credentialId: string, model: string) => { onChange?.({ type, credential_id: credentialId, model, parameters: {} }); }; const displayLabel = value?.model ? value.model : loading ? t('llm-select.loading') : (placeholder ?? t('llm-select.placeholder')); return ( {!loading && !hasOptions ? (

{t('llm-select.empty.title')}

{t('llm-select.empty.description')}

) : ( Object.entries(groups).map(([type, items], idx) => { const isSingle = items.length === 1; return ( {idx > 0 && } {type.replace(/_credential$/, '')} {isSingle ? items[0].models.map((m) => ( handleSelect( type, items[0].credential.id, m.name, ) } > {m.label} )) : items.map(({ credential, models }) => { const credName = (credential.data.name as string) || credential.id.slice(0, 8); return ( {credName} {models.map((m) => ( handleSelect( type, credential.id, m.name, ) } > {m.label} ))} ); })} ); }) )} {allowClear && ( onChange?.(null)} disabled={!value}> {clearLabel ?? t('llm-select.clear')} )} {t('llm-select.addCredential')}
); }