'use client'; import React, { useState, useMemo } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { type Model, type ManualModel, type GroupSettings, } from '@/lib/api/schemas/models'; import { AdminService, type AdminModelGroup } from '@/lib/api/services/admin'; type ModelGroup = AdminModelGroup; import { AddModelForm } from '@/components/AddModelForm'; import { EditModelForm } from '@/components/EditModelForm'; import { EditGroupForm } from '@/components/EditGroupForm'; import { CollectModelsDialog } from '@/components/CollectModelsDialog'; import { formatCost } from '@/lib/services/costValidation'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from '@/components/ui/card'; import { Skeleton } from '@/components/ui/skeleton'; import { Checkbox } from '@/components/ui/checkbox'; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from '@/components/ui/alert-dialog'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, DropdownMenuSeparator, } from '@/components/ui/dropdown-menu'; import { Cpu, Zap, Edit3, Users, MoreVertical, Key, Globe, RefreshCw, Trash2, AlertTriangle, CheckSquare, Square, Ban, CheckCircle, } from 'lucide-react'; import { toast } from 'sonner'; import { cn } from '@/lib/utils'; import { sortModels, groupAndSortModelsByProvider, } from '@/lib/utils/modelSort'; interface ModelSelectorProps { filterProvider?: string; groupData?: ModelGroup; showProviderActions?: boolean; filteredModels?: Model[]; showDeleteAllButton?: boolean; } export function ModelSelector({ filterProvider, groupData, filteredModels: propFilteredModels, showDeleteAllButton = false, }: ModelSelectorProps) { const [selectedModelId, setSelectedModelId] = useState(''); const [, setHoveredModelId] = useState(null); const [isAddFormOpen, setIsAddFormOpen] = useState(false); const [editingModel, setEditingModel] = useState(null); const [editingGroup, setEditingGroup] = useState<{ provider: string; models: Model[]; groupData: ModelGroup; } | null>(null); const [isCollectDialogOpen, setIsCollectDialogOpen] = useState(false); // Bulk selection state const [selectedModels, setSelectedModels] = useState>(new Set()); const [bulkDeleteDialogOpen, setBulkDeleteDialogOpen] = useState(false); const [deleteAllDialogOpen, setDeleteAllDialogOpen] = useState(false); const [ bulkApplyGroupSettingsDialogOpen, setBulkApplyGroupSettingsDialogOpen, ] = useState(false); // Search and filter state const [filteredModels] = useState([]); const queryClient = useQueryClient(); // Fetch models and groups const { data: modelsData, isLoading: isLoadingModels, error: modelsError, refetch: refetchModels, } = useQuery({ queryKey: ['models-with-providers'], queryFn: () => AdminService.getModelsWithProviders(), refetchOnWindowFocus: false, }); const { models = [], groups = [] } = modelsData || {}; // Filter models by provider if specified const providerFilteredModels = useMemo(() => { if (!filterProvider) return models; return models.filter((model) => model.provider === filterProvider); }, [models, filterProvider]); // Bulk deletion mutations const bulkDeleteMutation = useMutation({ mutationFn: async (modelIds: string[]) => { const selectedModelsList = models.filter((m) => modelIds.includes(m.id)); const modelsByProvider = selectedModelsList.reduce< Record >((acc, model) => { const providerId = model.provider_id || 'unknown'; if (!acc[providerId]) { acc[providerId] = []; } acc[providerId].push(model); return acc; }, {}); let totalDeleted = 0; for (const [providerId, providerModels] of Object.entries( modelsByProvider )) { if (providerId === 'unknown') { continue; } const modelFullNames = providerModels.map((m) => m.id); const result = await AdminService.deleteModels( modelFullNames, providerId ); totalDeleted += result.deleted_count; } return { deleted_count: totalDeleted, message: 'Models deleted' }; }, onSuccess: (data) => { toast.success( `Successfully deleted ${data.deleted_count} model overrides` ); setSelectedModels(new Set()); queryClient.invalidateQueries({ queryKey: ['models-with-providers'] }); queryClient.invalidateQueries({ queryKey: ['all-provider-models'] }); queryClient.invalidateQueries({ queryKey: ['upstream-providers'] }); }, onError: (error) => { toast.error(`Failed to delete model overrides: ${error.message}`); }, }); // Bulk soft deletion mutations (disable models) const bulkSoftDeleteMutation = useMutation({ mutationFn: async (modelIds: string[]) => { const selectedModelsList = models.filter((m) => modelIds.includes(m.id)); const modelsByProvider = selectedModelsList.reduce< Record >((acc, model) => { const providerId = model.provider_id || 'unknown'; if (!acc[providerId]) { acc[providerId] = []; } acc[providerId].push(model); return acc; }, {}); let totalDisabled = 0; for (const [providerId, providerModels] of Object.entries( modelsByProvider )) { if (providerId === 'unknown') { continue; } for (const model of providerModels) { const providerIdNum = parseInt(providerId); try { const existingModel = await AdminService.getProviderModel( providerIdNum, model.id ); await AdminService.updateProviderModel(providerIdNum, model.id, { ...existingModel, enabled: false, }); totalDisabled++; } catch (fetchError: unknown) { const error = fetchError as { message?: string; status?: number }; if (error.message?.includes('404') || error.status === 404) { const newOverride = { id: model.id, name: model.name, description: model.description || '', created: Math.floor(Date.now() / 1000), context_length: model.contextLength || 4096, architecture: { modality: model.modelType || 'text', input_modalities: [model.modelType || 'text'], output_modalities: [model.modelType || 'text'], tokenizer: '', instruct_type: null, }, pricing: { prompt: model.input_cost, completion: model.output_cost, request: 0, image: 0, web_search: 0, internal_reasoning: 0, }, per_request_limits: null, top_provider: null, upstream_provider_id: providerIdNum, enabled: false, }; await AdminService.createProviderModel( providerIdNum, newOverride ); totalDisabled++; } } } } return { deleted_count: totalDisabled, message: 'Models disabled' }; }, onSuccess: (data) => { toast.success(`Successfully disabled ${data.deleted_count} models`); setSelectedModels(new Set()); queryClient.invalidateQueries({ queryKey: ['models-with-providers'] }); queryClient.invalidateQueries({ queryKey: ['all-provider-models'] }); queryClient.invalidateQueries({ queryKey: ['upstream-providers'] }); }, onError: (error) => { toast.error(`Failed to disable models: ${error.message}`); }, }); // Bulk apply group settings mutation const bulkApplyGroupSettingsMutation = useMutation({ mutationFn: async (modelIds: string[]) => { if (!groupData) throw new Error('No group data available'); const updates: { api_key?: string; url?: string } = {}; // Set api_key to empty string to remove individual API keys (will use group key) updates.api_key = ''; // Use group URL if available, otherwise use default if (groupData.group_url) { updates.url = groupData.group_url; } return AdminService.bulkUpdateModels(modelIds, updates); }, onSuccess: (data) => { toast.success( `Successfully applied group settings to ${data.updated_count} models` ); if (data.errors.length > 0) { toast.warning(`${data.errors.length} models had errors during update`); console.warn('Bulk update errors:', data.errors); } setSelectedModels(new Set()); queryClient.invalidateQueries({ queryKey: ['models-with-providers'] }); queryClient.invalidateQueries({ queryKey: ['all-provider-models'] }); queryClient.invalidateQueries({ queryKey: ['upstream-providers'] }); }, onError: (error) => { toast.error(`Failed to apply group settings: ${error.message}`); }, }); const deleteAllMutation = useMutation({ mutationFn: () => AdminService.deleteAllModels(), onSuccess: (data) => { toast.success( `Successfully permanently deleted ${data.deleted_count} models` ); setSelectedModels(new Set()); queryClient.invalidateQueries({ queryKey: ['models-with-providers'] }); queryClient.invalidateQueries({ queryKey: ['all-provider-models'] }); queryClient.invalidateQueries({ queryKey: ['upstream-providers'] }); }, onError: (error) => { toast.error(`Failed to permanently delete all models: ${error.message}`); }, }); const deleteByProviderMutation = useMutation({ mutationFn: (providerId: string) => AdminService.deleteModelsByProvider(providerId), onSuccess: (data) => { toast.success( `Successfully permanently deleted ${data.deleted_count} models from provider` ); setSelectedModels(new Set()); queryClient.invalidateQueries({ queryKey: ['models-with-providers'] }); queryClient.invalidateQueries({ queryKey: ['all-provider-models'] }); queryClient.invalidateQueries({ queryKey: ['upstream-providers'] }); }, onError: (error) => { toast.error( `Failed to permanently delete provider models: ${error.message}` ); }, }); // Restore model mutation (enable model) const restoreMutation = useMutation({ mutationFn: async (modelId: string) => { const model = models.find((m) => m.id === modelId); if (!model) { throw new Error('Model not found'); } if (!model.provider_id) { throw new Error('Provider ID not available for this model'); } const providerId = parseInt(model.provider_id); const existingModel = await AdminService.getProviderModel( providerId, model.id ); await AdminService.updateProviderModel(providerId, model.id, { ...existingModel, enabled: true, }); return { restored_count: 1 }; }, onSuccess: () => { toast.success('Model enabled successfully'); queryClient.invalidateQueries({ queryKey: ['models-with-providers'] }); queryClient.invalidateQueries({ queryKey: ['all-provider-models'] }); queryClient.invalidateQueries({ queryKey: ['upstream-providers'] }); }, onError: (error) => { toast.error(`Failed to enable model: ${error.message}`); }, }); // Bulk restore mutation (enable models) const bulkRestoreMutation = useMutation({ mutationFn: async (modelIds: string[]) => { const selectedModelsList = models.filter((m) => modelIds.includes(m.id)); const modelsByProvider = selectedModelsList.reduce< Record >((acc, model) => { const providerId = model.provider_id || 'unknown'; if (!acc[providerId]) { acc[providerId] = []; } acc[providerId].push(model); return acc; }, {}); let totalEnabled = 0; for (const [providerId, providerModels] of Object.entries( modelsByProvider )) { if (providerId === 'unknown') { continue; } for (const model of providerModels) { const providerIdNum = parseInt(providerId); try { const existingModel = await AdminService.getProviderModel( providerIdNum, model.id ); await AdminService.updateProviderModel(providerIdNum, model.id, { ...existingModel, enabled: true, }); totalEnabled++; } catch (error) { console.error(`Failed to enable model ${model.full_name}:`, error); } } } return { restored_count: totalEnabled, message: 'Models enabled' }; }, onSuccess: (data) => { toast.success(`Successfully enabled ${data.restored_count} models`); setSelectedModels(new Set()); queryClient.invalidateQueries({ queryKey: ['models-with-providers'] }); queryClient.invalidateQueries({ queryKey: ['all-provider-models'] }); queryClient.invalidateQueries({ queryKey: ['upstream-providers'] }); }, onError: (error) => { toast.error(`Failed to enable models: ${error.message}`); }, }); // Group models by provider for better organization (only if not filtering) const groupedModels = useMemo(() => { const modelsToGroup = propFilteredModels && propFilteredModels.length > 0 ? propFilteredModels : filteredModels.length > 0 ? filteredModels : providerFilteredModels; if (filterProvider) { const sortedModels = sortModels(modelsToGroup); return { [filterProvider]: sortedModels }; } if (!modelsToGroup) return {}; return groupAndSortModelsByProvider(modelsToGroup); }, [ providerFilteredModels, filteredModels, propFilteredModels, filterProvider, ]); // Create a map of provider names to group data const groupDataMap = useMemo(() => { return new Map(groups.map((group) => [group.provider, group])); }, [groups]); // Group settings for compatibility const groupSettings = useMemo(() => { const settings: Record< string, { group_api_key?: string; group_url?: string } > = {}; groups.forEach((group) => { settings[group.provider] = { group_api_key: group.group_api_key, group_url: group.group_url, }; }); return settings; }, [groups]); // Utility function to get effective API key for a model const getEffectiveApiKey = (model: Model): string | undefined => { if (model.api_key) { return model.api_key; // Individual key takes precedence } return groupSettings[model.provider]?.group_api_key; // Fallback to group key }; // Utility function to check if model has truly individual settings different from group const hasIndividualSettings = (model: Model): boolean => { const groupData = groupDataMap.get(model.provider); // Check if API key is different from group const hasIndividualApiKey = !!( model.api_key && typeof model.api_key === 'string' && model.api_key !== groupData?.group_api_key ); // Check if URL is different from group (excluding relative paths like "/v1/chat/completions") const hasIndividualUrl = !!( model.url && typeof model.url === 'string' && !model.url.startsWith('/') && model.url !== groupData?.group_url ); return hasIndividualApiKey || hasIndividualUrl; }; // Handle manual model addition const handleAddModel = async (newModel: ManualModel) => { try { // Find or create the provider group let providerGroup = groups.find((g) => g.provider === newModel.provider); if (!providerGroup) { providerGroup = await AdminService.createModelGroup({ provider: newModel.provider, group_api_key: undefined, }); } const modelData = { name: newModel.name, full_name: newModel.name, input_cost: newModel.input_cost, output_cost: newModel.output_cost, provider: newModel.provider, modelType: newModel.modelType, description: newModel.description || undefined, contextLength: newModel.contextLength, }; await AdminService.createModel(modelData); await refetchModels(); toast.success(`Model "${newModel.name}" added successfully!`); } catch (error) { console.error('Error adding model:', error); toast.error('Failed to add model. Please try again.'); throw error; // Re-throw to let the form handle the error } }; // Handle model update const handleModelUpdate = async () => { await refetchModels(); setEditingModel(null); }; // Handle group update const handleGroupUpdate = async ( oldProvider: string, updatedData: GroupSettings ) => { try { const groupData = groupDataMap.get(oldProvider); if (groupData) { await AdminService.updateModelGroup(groupData.id, { provider: updatedData.provider, group_api_key: updatedData.group_api_key || undefined, group_url: updatedData.group_url || undefined, }); await refetchModels(); setEditingGroup(null); toast.success('Group updated successfully!'); } } catch (error) { console.error('Error updating group:', error); toast.error('Failed to update group. Please try again.'); } }; // Handle model collection success const handleCollectSuccess = async () => { await refetchModels(); setIsCollectDialogOpen(false); }; // Handle provider-specific refresh using group credentials const handleProviderRefresh = async (provider: string) => { const groupData = groupDataMap.get(provider); if (!groupData?.id) { toast.error('No group configuration found for this provider'); return; } try { const response = await AdminService.refreshModels({ provider_id: groupData.id, }); toast.info(response.message); await refetchModels(); queryClient.invalidateQueries({ queryKey: ['models-with-providers'] }); queryClient.invalidateQueries({ queryKey: ['all-provider-models'] }); queryClient.invalidateQueries({ queryKey: ['upstream-providers'] }); } catch (error: unknown) { console.error('Error refreshing provider models:', error); const errorMessage = error instanceof Error ? error.message : 'Unknown error'; toast.error(`Failed to refresh ${provider} models: ${errorMessage}`); } }; const getModelTypeIcon = (modelType: string) => { switch (modelType.toLowerCase()) { case 'text': return ; case 'embedding': return ; case 'image': return ; default: return ; } }; const getModelTypeColor = (modelType: string) => { switch (modelType.toLowerCase()) { case 'text': return 'bg-blue-100 text-blue-800'; case 'embedding': return 'bg-purple-100 text-purple-800'; case 'image': return 'bg-green-100 text-green-800'; case 'audio': return 'bg-orange-100 text-orange-800'; case 'multimodal': return 'bg-pink-100 text-pink-800'; default: return 'bg-gray-100 text-gray-800'; } }; // Bulk selection utilities const toggleModelSelection = (modelId: string) => { const newSelected = new Set(selectedModels); if (newSelected.has(modelId)) { newSelected.delete(modelId); } else { newSelected.add(modelId); } setSelectedModels(newSelected); }; const selectAllModels = () => { const modelsToSelect = propFilteredModels && propFilteredModels.length > 0 ? propFilteredModels : filteredModels.length > 0 ? filteredModels : providerFilteredModels; setSelectedModels(new Set(modelsToSelect.map((m) => m.id))); }; const deselectAllModels = () => { setSelectedModels(new Set()); }; const selectProviderModels = (provider: string) => { const modelsToFilter = propFilteredModels && propFilteredModels.length > 0 ? propFilteredModels : filteredModels.length > 0 ? filteredModels : providerFilteredModels; const providerModelIds = modelsToFilter .filter((m) => m.provider === provider) .map((m) => m.id); const newSelected = new Set(selectedModels); providerModelIds.forEach((id) => newSelected.add(id)); setSelectedModels(newSelected); }; // Bulk operation handlers const handleBulkDelete = () => { setBulkDeleteDialogOpen(true); }; const confirmBulkDelete = () => { bulkDeleteMutation.mutate(Array.from(selectedModels)); setBulkDeleteDialogOpen(false); }; const handleBulkSoftDelete = () => { bulkSoftDeleteMutation.mutate(Array.from(selectedModels)); }; const confirmBulkApplyGroupSettings = () => { bulkApplyGroupSettingsMutation.mutate(Array.from(selectedModels)); setBulkApplyGroupSettingsDialogOpen(false); }; const handleDeleteAll = () => { setDeleteAllDialogOpen(true); }; const confirmDeleteAll = () => { deleteAllMutation.mutate(); setDeleteAllDialogOpen(false); }; const handleDeleteByProvider = (provider: string) => { const group = groupDataMap.get(provider); if (group?.id) { deleteByProviderMutation.mutate(group.id); } }; // Individual model deletion handler const handleDeleteModel = async (modelId: string) => { const model = models.find((m) => m.id === modelId); if (!model) { toast.error('Model not found'); return; } if (!model.provider_id) { toast.error('Provider ID not available for this model'); return; } try { const providerId = parseInt(model.provider_id); await AdminService.deleteProviderModel(providerId, model.full_name); toast.success('Model override deleted successfully'); queryClient.invalidateQueries({ queryKey: ['models-with-providers'] }); queryClient.invalidateQueries({ queryKey: ['all-provider-models'] }); queryClient.invalidateQueries({ queryKey: ['upstream-providers'] }); } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; toast.error(`Failed to delete model override: ${errorMessage}`); } }; // Individual model soft deletion handler (disable model) const handleSoftDeleteModel = async (modelId: string) => { const model = models.find((m) => m.id === modelId); if (!model) { toast.error('Model not found'); return; } if (!model.provider_id) { toast.error('Provider ID not available for this model'); return; } try { const providerId = parseInt(model.provider_id); try { const existingModel = await AdminService.getProviderModel( providerId, model.id ); await AdminService.updateProviderModel(providerId, model.full_name, { ...existingModel, enabled: false, }); toast.success('Model disabled successfully'); } catch (fetchError: unknown) { const error = fetchError as { message?: string; status?: number }; if (error.message?.includes('404') || error.status === 404) { const newOverride = { id: model.id, name: model.name, description: model.description || '', created: Math.floor(Date.now() / 1000), context_length: model.contextLength || 4096, architecture: { modality: model.modelType || 'text', input_modalities: [model.modelType || 'text'], output_modalities: [model.modelType || 'text'], tokenizer: '', instruct_type: null, }, pricing: { prompt: model.input_cost, completion: model.output_cost, request: 0, image: 0, web_search: 0, internal_reasoning: 0, }, per_request_limits: null, top_provider: null, upstream_provider_id: providerId, enabled: false, }; await AdminService.createProviderModel(providerId, newOverride); toast.success('Model disabled successfully'); } else { throw fetchError; } } queryClient.invalidateQueries({ queryKey: ['models-with-providers'] }); queryClient.invalidateQueries({ queryKey: ['all-provider-models'] }); queryClient.invalidateQueries({ queryKey: ['upstream-providers'] }); } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; toast.error(`Failed to disable model: ${errorMessage}`); } }; if (isLoadingModels) { return (
); } if (modelsError) { return (

Error loading models. Please try again later.

); } return (
{showDeleteAllButton && ( )} {/* Model Management Actions */} {/* Bulk selection actions */} {selectedModels.size > 0 && ( <>
{selectedModels.size} selected {(() => { const modelsToFilter = propFilteredModels && propFilteredModels.length > 0 ? propFilteredModels : filteredModels.length > 0 ? filteredModels : providerFilteredModels; const selectedSoftDeletedModels = modelsToFilter.filter( (m) => selectedModels.has(m.id) && m.soft_deleted ); return ( selectedSoftDeletedModels.length > 0 && ( ) ); })()} )}
{/* Provider Groups or Filtered Models */} {Object.entries(groupedModels).map(([provider, providerModels]) => { if (filterProvider && provider !== filterProvider) return null; const groupData = groupDataMap.get(provider); const allProviderSelected = providerModels.every((m) => selectedModels.has(m.id) ); const someProviderSelected = providerModels.some((m) => selectedModels.has(m.id) ); return ( {!filterProvider && (
{ if (input && 'indeterminate' in input) { (input as HTMLInputElement).indeterminate = someProviderSelected && !allProviderSelected; } }} onCheckedChange={(checked) => { if (checked) { selectProviderModels(provider); } else { const providerModelIds = providerModels.map( (m) => m.id ); const newSelected = new Set(selectedModels); providerModelIds.forEach((id) => newSelected.delete(id) ); setSelectedModels(newSelected); } }} />
{provider} ({providerModels.length} models) {groupData?.group_url ? ( {groupData.group_url} ) : ( 'Using default endpoint' )} {groupData?.group_api_key && ( Group API Key )}
{ if (groupData) { setEditingGroup({ provider, models: providerModels, groupData, }); } }} > Edit Group selectProviderModels(provider)} > Select All handleProviderRefresh(provider)} > Refresh Models handleDeleteByProvider(provider)} className='text-destructive focus:text-destructive' > Delete All Models Permanently
)}
{providerModels.map((model) => ( setHoveredModelId(model.id)} onMouseLeave={() => setHoveredModelId(null)} >
toggleModelSelection(model.id)} onClick={(e) => e.stopPropagation()} className='mt-1 flex-shrink-0' />
{getModelTypeIcon(model.modelType)}

{model.name}

{model.full_name !== model.name && (

{model.full_name}

)}
{ e.stopPropagation(); setEditingModel(model); }} > Edit Model {model.soft_deleted ? ( { e.stopPropagation(); restoreMutation.mutate(model.id); }} className='text-green-600 focus:text-green-700' > Enable Model ) : ( { e.stopPropagation(); handleSoftDeleteModel(model.id); }} className='text-orange-600 focus:text-orange-700' > Disable Model )} { e.stopPropagation(); handleDeleteModel(model.id); }} className='text-destructive focus:text-destructive' > Delete
setSelectedModelId(model.id)} >
{model.modelType} {model.api_key_type === 'remote' && ( Remote )} {model.api_key_type !== 'remote' && ( Database )} {model.is_free && ( Free )} {!getEffectiveApiKey(model) && ( No API Key )} {getEffectiveApiKey(model) && !hasIndividualSettings(model) && ( Group Key )} {hasIndividualSettings(model) && ( Individual Key )} {model.soft_deleted && ( Disabled )}
{!model.is_free && (
Input: {formatCost(model.input_cost)}/1M tokens
Output: {formatCost(model.output_cost)}/1M tokens
)} {model.description && (

{model.description}

)}
))}
); })} {/* Forms and Dialogs */} setIsAddFormOpen(false)} /> {editingModel && ( setEditingModel(null)} isOpen={!!editingModel} /> )} {editingGroup && ( setEditingGroup(null)} isOpen={!!editingGroup} /> )} setIsCollectDialogOpen(false)} onSuccess={handleCollectSuccess} /> {/* Confirmation Dialogs */} Permanently Delete Selected Models Are you sure you want to permanently delete {selectedModels.size}{' '} selected models? This will completely remove them from the database and cannot be undone. Cancel Delete Models Apply Group Settings to{' '} {selectedModels.size === filteredModels.length && filterProvider ? `All Models in ${filterProvider}` : 'Selected Models'} {selectedModels.size === filteredModels.length && filterProvider ? (

Are you sure you want to force ALL {selectedModels.size}{' '} models in the {filterProvider} group to use group settings?

⚠️ This will override individual model configurations:

) : (

Are you sure you want to apply the group settings to{' '} {selectedModels.size} selected models?

)}
  • Remove individual API keys (models will use the group API key)
  • {groupData?.group_url && (
  • Set the URL to:{' '} {groupData.group_url}
  • )}
  • Force models to use group configurations
  • {selectedModels.size === filteredModels.length && filterProvider && (
  • This will affect ALL models in this provider group
  • )}
Cancel {selectedModels.size === filteredModels.length && filterProvider ? `Force All ${selectedModels.size} Models` : 'Apply Group Settings'}
Permanently Delete All Models Are you sure you want to permanently delete ALL models? This will completely remove all {models.length} models from the database. This action cannot be undone. Cancel Delete All Models
); }