import { useEffect, useState } from 'react'; import { Cpu, ChevronDown, ChevronRight } from 'lucide-react'; import { cn } from '@/lib/utils'; import { Popover, PopoverButton, PopoverPanel, Transition, } from '@headlessui/react'; import { Fragment } from 'react'; interface ModelOption { provider: string; model: string; displayName: string; } interface ProviderModelMap { [provider: string]: { displayName: string; models: ModelOption[]; }; } const ModelSelector = ({ selectedModel, setSelectedModel, truncateModelName = true, }: { selectedModel: { provider: string; model: string } | null; setSelectedModel: (model: { provider: string; model: string }) => void; truncateModelName?: boolean; }) => { const [providerModels, setProviderModels] = useState({}); const [providersList, setProvidersList] = useState([]); const [loading, setLoading] = useState(true); const [selectedModelDisplay, setSelectedModelDisplay] = useState(''); const [selectedProviderDisplay, setSelectedProviderDisplay] = useState(''); const [expandedProviders, setExpandedProviders] = useState< Record >({}); useEffect(() => { const fetchModels = async () => { try { const response = await fetch('/api/models', { headers: { 'Content-Type': 'application/json', }, }); if (!response.ok) { throw new Error(`Failed to fetch models: ${response.status}`); } const data = await response.json(); const providersData: ProviderModelMap = {}; // Organize models by provider Object.entries(data.chatModelProviders).forEach( ([provider, models]: [string, any]) => { const providerDisplayName = provider.charAt(0).toUpperCase() + provider.slice(1); providersData[provider] = { displayName: providerDisplayName, models: [], }; Object.entries(models).forEach( ([modelKey, modelData]: [string, any]) => { providersData[provider].models.push({ provider, model: modelKey, displayName: modelData.displayName || modelKey, }); }, ); }, ); // Filter out providers with no models Object.keys(providersData).forEach((provider) => { if (providersData[provider].models.length === 0) { delete providersData[provider]; } }); // Sort providers by name (only those that have models) const sortedProviders = Object.keys(providersData).sort(); setProvidersList(sortedProviders); // Initialize expanded state for all providers const initialExpandedState: Record = {}; sortedProviders.forEach((provider) => { initialExpandedState[provider] = selectedModel?.provider === provider; }); // Expand the first provider if none is selected if (sortedProviders.length > 0 && !selectedModel) { initialExpandedState[sortedProviders[0]] = true; } setExpandedProviders(initialExpandedState); setProviderModels(providersData); // Find the current model in our options to display its name if (selectedModel) { const provider = providersData[selectedModel.provider]; if (provider) { const currentModel = provider.models.find( (option) => option.model === selectedModel.model, ); if (currentModel) { setSelectedModelDisplay(currentModel.displayName); setSelectedProviderDisplay(provider.displayName); } } else { setSelectedModelDisplay(''); setSelectedProviderDisplay(''); } } else { setSelectedModelDisplay(''); setSelectedProviderDisplay(''); } setLoading(false); } catch (error) { console.error('Error fetching models:', error); setLoading(false); } }; fetchModels(); }, [selectedModel]); const toggleProviderExpanded = (provider: string) => { setExpandedProviders((prev) => ({ ...prev, [provider]: !prev[provider], })); }; const handleSelectModel = (option: ModelOption) => { setSelectedModel({ provider: option.provider, model: option.model, }); setSelectedModelDisplay(option.displayName); setSelectedProviderDisplay( providerModels[option.provider]?.displayName || option.provider, ); }; const getDisplayText = () => { if (loading) return 'Loading...'; if (!selectedModelDisplay) return 'Select model'; return `${selectedModelDisplay} (${selectedProviderDisplay})`; }; return ( {({ open }) => ( <>

Select Model

Choose a provider and model for your conversation

{loading ? (
Loading available models...
) : providersList.length === 0 ? (
No models available
) : (
{providersList.map((providerKey) => { const provider = providerModels[providerKey]; const isExpanded = expandedProviders[providerKey]; return (
{/* Provider header */} {/* Models list */} {isExpanded && (
{provider.models.map((modelOption) => ( handleSelectModel(modelOption) } >
{modelOption.displayName}
{/* Active indicator */} {selectedModel?.provider === modelOption.provider && selectedModel?.model === modelOption.model && (
Active
)}
))}
)}
); })}
)}
)}
); }; export default ModelSelector;