Spaces:
Build error
Build error
| import { useState, useEffect } from 'react' | |
| import { FiDownload, FiTrash2, FiInfo, FiSearch, FiFilter, FiCpu, FiDatabase, FiCloud, FiCheckCircle, FiXCircle } from 'react-icons/fi' | |
| import axios from 'axios' | |
| export default function ModelSelector() { | |
| const [models, setModels] = useState([]) | |
| const [selectedModel, setSelectedModel] = useState(null) | |
| const [isLoading, setIsLoading] = useState(false) | |
| const [downloadedModels, setDownloadedModels] = useState([]) | |
| const [searchTerm, setSearchTerm] = useState('') | |
| const [filterType, setFilterType] = useState('all') | |
| const [showDetails, setShowDetails] = useState(false) | |
| useEffect(() => { | |
| fetchModels() | |
| const saved = localStorage.getItem('downloadedModels') | |
| if (saved) { | |
| setDownloadedModels(JSON.parse(saved)) | |
| } | |
| }, []) | |
| const fetchModels = async () => { | |
| try { | |
| setIsLoading(true) | |
| const response = await axios.get('/api/models') | |
| setModels(response.data) | |
| } catch (error) { | |
| console.error('Error fetching models:', error) | |
| } finally { | |
| setIsLoading(false) | |
| } | |
| } | |
| const handleDownload = async (model) => { | |
| setIsLoading(true) | |
| setSelectedModel(model) | |
| try { | |
| await new Promise(resolve => setTimeout(resolve, 1500)) | |
| const updatedModels = [...downloadedModels, model] | |
| setDownloadedModels(updatedModels) | |
| localStorage.setItem('downloadedModels', JSON.stringify(updatedModels)) | |
| } catch (error) { | |
| console.error('Download failed:', error) | |
| } finally { | |
| setIsLoading(false) | |
| } | |
| } | |
| const handleRemove = (modelId) => { | |
| const updatedModels = downloadedModels.filter(m => m.id !== modelId) | |
| setDownloadedModels(updatedModels) | |
| localStorage.setItem('downloadedModels', JSON.stringify(updatedModels)) | |
| } | |
| const filteredModels = models.filter(model => { | |
| const matchesSearch = model.name.toLowerCase().includes(searchTerm.toLowerCase()) || | |
| model.description.toLowerCase().includes(searchTerm.toLowerCase()) | |
| const matchesFilter = filterType === 'all' || model.type === filterType | |
| return matchesSearch && matchesFilter | |
| }) | |
| const getModelStatus = (modelId) => { | |
| return downloadedModels.some(m => m.id === modelId) ? 'downloaded' : 'available' | |
| } | |
| return ( | |
| <div className="space-y-6"> | |
| <div className="flex flex-wrap justify-between items-center gap-4"> | |
| <h2 className="text-2xl font-bold text-gray-800">AI Model Hub</h2> | |
| <div className="flex items-center space-x-4"> | |
| <div className="relative"> | |
| <FiSearch className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400" /> | |
| <input | |
| type="text" | |
| placeholder="Search models..." | |
| value={searchTerm} | |
| onChange={(e) => setSearchTerm(e.target.value)} | |
| className="pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-transparent" | |
| /> | |
| </div> | |
| <select | |
| value={filterType} | |
| onChange={(e) => setFilterType(e.target.value)} | |
| className="px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-transparent" | |
| > | |
| <option value="all">All Types</option> | |
| <option value="offline">Offline</option> | |
| <option value="online">Online</option> | |
| </select> | |
| </div> | |
| </div> | |
| {isLoading && ( | |
| <div className="flex justify-center items-center py-8"> | |
| <div className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-primary"></div> | |
| </div> | |
| )} | |
| <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> | |
| {filteredModels.map((model) => ( | |
| <div key={model.id} className="bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow"> | |
| <div className="p-4"> | |
| <div className="flex justify-between items-start mb-3"> | |
| <h3 className="font-bold text-lg">{model.name}</h3> | |
| <span className={`px-2 py-1 rounded-full text-xs ${model.type === 'offline' ? 'bg-green-100 text-green-800' : 'bg-blue-100 text-blue-800'}`}> | |
| {model.type} | |
| </span> | |
| </div> | |
| <p className="text-gray-600 text-sm mb-4">{model.description}</p> | |
| <div className="flex justify-between items-center text-sm text-gray-500 mb-4"> | |
| <span>Size: {model.size}</span> | |
| <span>Version: {model.version || '1.0'}</span> | |
| </div> | |
| <div className="space-y-2"> | |
| {getModelStatus(model.id) === 'downloaded' ? ( | |
| <> | |
| <button | |
| onClick={() => setSelectedModel(model)} | |
| className="w-full py-2 bg-success hover:bg-green-600 text-white rounded-lg flex items-center justify-center space-x-2 transition-colors" | |
| > | |
| <FiInfo /> | |
| <span>View Details</span> | |
| </button> | |
| <button | |
| onClick={() => handleRemove(model.id)} | |
| className="w-full py-2 bg-danger hover:bg-red-600 text-white rounded-lg flex items-center justify-center space-x-2 transition-colors" | |
| > | |
| <FiTrash2 /> | |
| <span>Remove</span> | |
| </button> | |
| </> | |
| ) : ( | |
| <button | |
| onClick={() => handleDownload(model)} | |
| disabled={isLoading} | |
| className={`w-full py-2 rounded-lg flex items-center justify-center space-x-2 transition-colors ${isLoading ? 'bg-gray-400' : 'bg-primary hover:bg-blue-600 text-white'}`} | |
| > | |
| {isLoading && selectedModel?.id === model.id ? ( | |
| <> | |
| <span className="animate-spin rounded-full h-4 w-4 border-t-2 border-b-2 border-white"></span> | |
| <span>Downloading...</span> | |
| </> | |
| ) : ( | |
| <> | |
| <FiDownload /> | |
| <span>Download</span> | |
| </> | |
| )} | |
| </button> | |
| )} | |
| </div> | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| {selectedModel && ( | |
| <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50"> | |
| <div className="bg-white rounded-lg p-6 max-w-2xl w-full max-h-[80vh] overflow-y-auto"> | |
| <div className="flex justify-between items-center mb-4"> | |
| <h3 className="text-xl font-bold">{selectedModel.name}</h3> | |
| <button | |
| onClick={() => setSelectedModel(null)} | |
| className="text-gray-400 hover:text-gray-600 text-2xl" | |
| > | |
| × | |
| </button> | |
| </div> | |
| <div className="space-y-4"> | |
| <div> | |
| <h4 className="font-medium text-gray-700 mb-1">Description</h4> | |
| <p className="text-gray-600">{selectedModel.description}</p> | |
| </div> | |
| <div className="grid grid-cols-2 gap-4"> | |
| <div> | |
| <h4 className="font-medium text-gray-700 mb-1">Type</h4> | |
| <p className="text-gray-600">{selectedModel.type}</p> | |
| </div> | |
| <div> | |
| <h4 className="font-medium text-gray-700 mb-1">Size</h4> | |
| <p className="text-gray-600">{selectedModel.size}</p> | |
| </div> | |
| <div> | |
| <h4 className="font-medium text-gray-700 mb-1">Version</h4> | |
| <p className="text-gray-600">{selectedModel.version || '1.0'}</p> | |
| </div> | |
| <div> | |
| <h4 className="font-medium text-gray-700 mb-1">Last Updated</h4> | |
| <p className="text-gray-600">{selectedModel.updatedAt || 'N/A'}</p> | |
| </div> | |
| </div> | |
| <div> | |
| <h4 className="font-medium text-gray-700 mb-2">Features</h4> | |
| <ul className="list-disc list-inside text-gray-600 space-y-1"> | |
| <li>Code completion and suggestions</li> | |
| <li>Multi-language support</li> | |
| <li>Offline functionality</li> | |
| <li>Customizable parameters</li> | |
| <li>Real-time analysis</li> | |
| </ul> | |
| </div> | |
| <div> | |
| <h4 className="font-medium text-gray-700 mb-2">Usage Example</h4> | |
| <div className="bg-gray-100 p-3 rounded-lg font-mono text-sm"> | |
| <pre> | |
| {`// Import the model | |
| const model = require('${selectedModel.name.toLowerCase().replace(/\s+/g, '-')}') | |
| // Initialize with your parameters | |
| const instance = new model({ | |
| temperature: 0.7, | |
| maxTokens: 100 | |
| }) | |
| // Use the model | |
| const result = instance.generate('Write a function to calculate factorial')`} | |
| </pre> | |
| </div> | |
| </div> | |
| <div className="flex space-x-2"> | |
| <button | |
| onClick={() => setSelectedModel(null)} | |
| className="flex-1 py-2 bg-gray-200 hover:bg-gray-300 text-gray-800 rounded-lg transition-colors" | |
| > | |
| Close | |
| </button> | |
| <button className="flex-1 py-2 bg-primary hover:bg-blue-600 text-white rounded-lg transition-colors"> | |
| Documentation | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| )} | |
| {downloadedModels.length > 0 && ( | |
| <div className="mt-8 p-4 bg-gray-50 rounded-lg"> | |
| <h3 className="font-bold text-gray-800 mb-4">Your Downloaded Models ({downloadedModels.length})</h3> | |
| <div className="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
| {downloadedModels.map((model) => ( | |
| <div key={model.id} className="bg-white rounded-lg p-4 shadow-sm"> | |
| <div className="flex justify-between items-start"> | |
| <div> | |
| <h4 className="font-medium text-gray-800">{model.name}</h4> | |
| <p className="text-sm text-gray-500">{model.size}</p> | |
| </div> | |
| <button | |
| onClick={() => handleRemove(model.id)} | |
| className="text-red-500 hover:text-red-700" | |
| title="Remove model" | |
| > | |
| <FiTrash2 /> | |
| </button> | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| )} | |
| </div> | |
| ) | |
| } |