// ======================================== // Model Pool List Component // ======================================== // Display model pools as cards with search, filter, and actions import { useState, useMemo } from 'react'; import { useIntl } from 'react-intl'; import { Search, Plus, Edit, Trash2, Layers, Zap, CheckCircle2, XCircle, MoreVertical, } from 'lucide-react'; import { Card } from '@/components/ui/Card'; import { Button } from '@/components/ui/Button'; import { Input } from '@/components/ui/Input'; import { Badge } from '@/components/ui/Badge'; import { Switch } from '@/components/ui/Switch'; import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator } from '@/components/ui/Dropdown'; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from '@/components/ui/AlertDialog'; import { useModelPools, useDeleteModelPool, useUpdateModelPool, } from '@/hooks/useApiSettings'; import { useNotifications } from '@/hooks/useNotifications'; import type { ModelPoolConfig, ModelPoolType } from '@/lib/api'; import { cn } from '@/lib/utils'; // ========== Types ========== export interface ModelPoolListProps { onAddPool: () => void; onEditPool: (poolId: string) => void; } type FilterType = 'all' | 'embedding' | 'llm' | 'reranker'; // ========== Helper Components ========== interface PoolCardProps { pool: ModelPoolConfig; onEdit: () => void; onDelete: () => void; onToggleEnabled: (enabled: boolean) => void; isDeleting: boolean; isToggling: boolean; } function PoolCard({ pool, onEdit, onDelete, onToggleEnabled, isDeleting, isToggling, }: PoolCardProps) { const { formatMessage } = useIntl(); // Get model type badge const getModelTypeBadge = () => { const variantMap = { embedding: 'success' as const, llm: 'default' as const, reranker: 'info' as const, }; const labelMap = { embedding: 'apiSettings.modelPools.embedding', llm: 'apiSettings.modelPools.llm', reranker: 'apiSettings.modelPools.reranker', }; return ( {formatMessage({ id: labelMap[pool.modelType] })} ); }; // Get strategy label const getStrategyLabel = () => { const strategyMap = { round_robin: 'apiSettings.modelPools.roundRobin', latency_aware: 'apiSettings.modelPools.latencyAware', weighted_random: 'apiSettings.modelPools.weightedRandom', }; return formatMessage({ id: strategyMap[pool.strategy] }); }; return (
{/* Left: Pool Info */}

{pool.name || pool.id}

{getModelTypeBadge()} {pool.enabled ? ( {formatMessage({ id: 'apiSettings.common.enabled' })} ) : ( {formatMessage({ id: 'apiSettings.common.disabled' })} )}
{getStrategyLabel()} {pool.autoDiscover && ( {formatMessage({ id: 'apiSettings.modelPools.autoDiscover' })} )}

{formatMessage({ id: 'apiSettings.modelPools.targetModel' })}: {pool.targetModel}

{pool.description && (

{pool.description}

)}
{formatMessage({ id: 'apiSettings.modelPools.defaultCooldown' })}: {pool.defaultCooldown}s {formatMessage({ id: 'apiSettings.modelPools.defaultConcurrent' })}: {pool.defaultMaxConcurrentPerKey}
{/* Right: Actions */}
{formatMessage({ id: 'apiSettings.modelPools.actions.edit' })} {formatMessage({ id: 'apiSettings.modelPools.actions.delete' })}
); } interface StatCardProps { label: string; value: number; icon: React.ReactNode; className?: string; } function StatCard({ label, value, icon, className }: StatCardProps) { return (
{icon}

{label}

{value}

); } // ========== Main Component ========== export function ModelPoolList({ onAddPool, onEditPool }: ModelPoolListProps) { const { formatMessage } = useIntl(); const { success, error } = useNotifications(); // Queries and mutations const { pools, totalCount, enabledCount, isLoading, refetch } = useModelPools(); const { deleteModelPool, isDeleting } = useDeleteModelPool(); const { updateModelPool } = useUpdateModelPool(); // UI state const [searchQuery, setSearchQuery] = useState(''); const [filterType, setFilterType] = useState('all'); const [deletePoolId, setDeletePoolId] = useState(null); const [togglingPoolId, setTogglingPoolId] = useState(null); // Filter pools const filteredPools = useMemo(() => { return pools.filter((pool) => { const matchesSearch = !searchQuery || pool.id.toLowerCase().includes(searchQuery.toLowerCase()) || pool.name?.toLowerCase().includes(searchQuery.toLowerCase()) || pool.targetModel.toLowerCase().includes(searchQuery.toLowerCase()); const matchesFilter = filterType === 'all' || pool.modelType === filterType; return matchesSearch && matchesFilter; }); }, [pools, searchQuery, filterType]); // Count pools by type const typeCounts = useMemo(() => { return { embedding: pools.filter((p) => p.modelType === 'embedding').length, llm: pools.filter((p) => p.modelType === 'llm').length, reranker: pools.filter((p) => p.modelType === 'reranker').length, }; }, [pools]); // Handle delete const handleDelete = async () => { if (!deletePoolId) return; try { await deleteModelPool(deletePoolId); success( formatMessage({ id: 'apiSettings.messages.settingsDeleted' }) ); setDeletePoolId(null); await refetch(); } catch (err) { error( formatMessage({ id: 'common.error' }), err instanceof Error ? err.message : formatMessage({ id: 'common.error' }) ); } }; // Handle toggle enabled const handleToggleEnabled = async (poolId: string, enabled: boolean) => { setTogglingPoolId(poolId); try { await updateModelPool(poolId, { enabled }); await refetch(); } catch (err) { error( formatMessage({ id: 'common.error' }), err instanceof Error ? err.message : formatMessage({ id: 'common.error' }) ); } finally { setTogglingPoolId(null); } }; return (
{/* Statistics Cards */}
} /> } /> } /> } />
{/* Search and Filter Bar */}
setSearchQuery(e.target.value)} className="pl-9" />
{/* Filter Buttons */}
{(Object.keys({ all: null, embedding: null, llm: null, reranker: null }) as FilterType[]).map((type) => ( ))}
{/* Pool List */} {isLoading ? (
{formatMessage({ id: 'common.loading' })}
) : filteredPools.length === 0 ? (

{formatMessage({ id: 'apiSettings.modelPools.emptyState.title' })}

{formatMessage({ id: 'apiSettings.modelPools.emptyState.message' })}

) : (
{filteredPools.map((pool) => ( onEditPool(pool.id)} onDelete={() => setDeletePoolId(pool.id)} onToggleEnabled={(enabled) => handleToggleEnabled(pool.id, enabled)} isDeleting={isDeleting && deletePoolId === pool.id} isToggling={togglingPoolId === pool.id} /> ))}
)} {/* Delete Confirmation Dialog */} setDeletePoolId(null)}> {formatMessage({ id: 'apiSettings.modelPools.actions.delete' })} {formatMessage( { id: 'apiSettings.modelPools.deleteConfirm' }, { name: pools.find((p) => p.id === deletePoolId)?.name || deletePoolId || '' } )} {formatMessage({ id: 'common.cancel' })} {isDeleting ? formatMessage({ id: 'common.loading' }) : formatMessage({ id: 'common.confirm' })}
); }