diff --git a/ui/desktop/src/components/settings/providers/ProviderGrid.tsx b/ui/desktop/src/components/settings/providers/ProviderGrid.tsx index 26397e471809..ee1e9e4aaac2 100644 --- a/ui/desktop/src/components/settings/providers/ProviderGrid.tsx +++ b/ui/desktop/src/components/settings/providers/ProviderGrid.tsx @@ -11,6 +11,7 @@ import { Plus } from 'lucide-react'; import { Dialog, DialogContent, DialogHeader, DialogTitle } from '../../ui/dialog'; import CustomProviderForm from './modal/subcomponents/forms/CustomProviderForm'; import { SwitchModelModal } from '../models/subcomponents/SwitchModelModal'; +import { useModelAndProvider } from '../../ModelAndProviderContext'; import type { View } from '../../../utils/navigationUtils'; const GridLayout = memo(function GridLayout({ children }: { children: React.ReactNode }) { @@ -65,10 +66,13 @@ function ProviderCards({ const [showCustomProviderModal, setShowCustomProviderModal] = useState(false); const [showSwitchModelModal, setShowSwitchModelModal] = useState(false); const [switchModelProvider, setSwitchModelProvider] = useState(null); + const [isActiveProvider, setIsActiveProvider] = useState(false); + const { getCurrentModelAndProvider } = useModelAndProvider(); const [editingProvider, setEditingProvider] = useState<{ id: string; config: DeclarativeProviderConfig; isEditable: boolean; + providerType: string; } | null>(null); const handleProviderLaunchWithModelSelection = useCallback((provider: ProviderDetails) => { @@ -92,14 +96,24 @@ function ProviderCards({ id: provider.name, config: result.data.config, isEditable: result.data.is_editable, + providerType: provider.provider_type, }); + + // Check if this is the active provider + try { + const providerModel = await getCurrentModelAndProvider(); + setIsActiveProvider(provider.name === providerModel.provider); + } catch { + setIsActiveProvider(false); + } + setShowCustomProviderModal(true); } } else { openModal(provider); } }, - [openModal] + [openModal, getCurrentModelAndProvider] ); const handleUpdateCustomProvider = useCallback( @@ -124,9 +138,26 @@ function ProviderCards({ [editingProvider, refreshProviders] ); + const handleDeleteCustomProvider = useCallback(async () => { + if (!editingProvider) return; + + const { removeCustomProvider } = await import('../../../api'); + await removeCustomProvider({ + path: { id: editingProvider.id }, + throwOnError: true, + }); + setShowCustomProviderModal(false); + setEditingProvider(null); + setIsActiveProvider(false); + if (refreshProviders) { + refreshProviders(); + } + }, [editingProvider, refreshProviders]); + const handleCloseModal = useCallback(() => { setShowCustomProviderModal(false); setEditingProvider(null); + setIsActiveProvider(false); }, []); const onCloseProviderConfig = useCallback(() => { @@ -178,8 +209,10 @@ function ProviderCards({ const providerCards = useMemo(() => { // providers needs to be an array const providersArray = Array.isArray(providers) ? providers : []; - // Sort providers alphabetically by name - const sortedProviders = [...providersArray].sort((a, b) => a.name.localeCompare(b.name)); + // Sort providers alphabetically by display name + const sortedProviders = [...providersArray].sort((a, b) => + a.metadata.display_name.localeCompare(b.metadata.display_name) + ); const cards = sortedProviders.map((provider) => ( {' '} diff --git a/ui/desktop/src/components/settings/providers/modal/subcomponents/forms/CustomProviderForm.tsx b/ui/desktop/src/components/settings/providers/modal/subcomponents/forms/CustomProviderForm.tsx index 99d3cabc1441..be252a1735d8 100644 --- a/ui/desktop/src/components/settings/providers/modal/subcomponents/forms/CustomProviderForm.tsx +++ b/ui/desktop/src/components/settings/providers/modal/subcomponents/forms/CustomProviderForm.tsx @@ -4,10 +4,13 @@ import { Select } from '../../../../../ui/Select'; import { Button } from '../../../../../ui/button'; import { SecureStorageNotice } from '../SecureStorageNotice'; import { UpdateCustomProviderRequest } from '../../../../../../api'; +import { Trash2, AlertTriangle } from 'lucide-react'; interface CustomProviderFormProps { onSubmit: (data: UpdateCustomProviderRequest) => void; onCancel: () => void; + onDelete?: () => Promise; + isActiveProvider?: boolean; initialData: UpdateCustomProviderRequest | null; isEditable?: boolean; } @@ -15,6 +18,8 @@ interface CustomProviderFormProps { export default function CustomProviderForm({ onSubmit, onCancel, + onDelete, + isActiveProvider = false, initialData, isEditable, }: CustomProviderFormProps) { @@ -26,6 +31,7 @@ export default function CustomProviderForm({ const [requiresApiKey, setRequiresApiKey] = useState(false); const [supportsStreaming, setSupportsStreaming] = useState(true); const [validationErrors, setValidationErrors] = useState>({}); + const [showDeleteConfirmation, setShowDeleteConfirmation] = useState(false); useEffect(() => { if (initialData) { @@ -257,12 +263,62 @@ export default function CustomProviderForm({ )} -
- - -
+ + {showDeleteConfirmation ? ( +
+ {isActiveProvider ? ( +
+

+ + + You cannot delete this provider while it's currently in use. Please switch to a + different model first. + +

+
+ ) : ( +
+

+ Are you sure you want to delete this custom provider? This will permanently remove + the provider and its stored API key. This action cannot be undone. +

+
+ )} +
+ + {!isActiveProvider && ( + + )} +
+
+ ) : ( +
+ {initialData && onDelete && ( + + )} + + +
+ )} ); }