"use client"; import { useState, useEffect } from "react"; import { Header, AppLayout } from "@/components/layout"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { apiClient, type ModelInfo, type EnhancedModelsResponse, } from "@/lib/api"; import { Loader2, Download, Upload, CheckCircle, XCircle, AlertCircle, Copy, RefreshCw, Package, Hash } from "lucide-react"; export default function ModelsPage() { const [models, setModels] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [success, setSuccess] = useState(null); const [activeTab, setActiveTab] = useState("all"); const [modelTypeFilter, setModelTypeFilter] = useState("all"); const [searchQuery, setSearchQuery] = useState(""); const [loadingModels, setLoadingModels] = useState>(new Set()); const [computingHashes, setComputingHashes] = useState>(new Set()); // Load models on component mount useEffect(() => { loadModels(); }, []); const loadModels = async () => { setLoading(true); setError(null); try { const response = await apiClient.getModels(); setModels(response.models); } catch (err) { setError(err instanceof Error ? err.message : "Failed to load models"); } finally { setLoading(false); } }; const refreshModels = async () => { setLoading(true); setError(null); try { await apiClient.scanModels(); // After scanning, reload the models list const response = await apiClient.getModels(); setModels(response.models); setSuccess("Models refreshed successfully"); } catch (err) { setError(err instanceof Error ? err.message : "Failed to refresh models"); } finally { setLoading(false); } }; const loadModel = async (modelId: string, modelName: string) => { setLoadingModels(prev => new Set(prev).add(modelId)); setError(null); setSuccess(null); try { await apiClient.loadModel(modelId); setSuccess(`Model "${modelName}" loaded successfully`); // Update the model's loaded status setModels(prev => prev.map(model => model.id === modelId || model.sha256 === modelId || model.sha256_short === modelId ? { ...model, loaded: true } : model )); } catch (err) { const errorMessage = err instanceof Error ? err.message : "Failed to load model"; // Check if it's a hash validation error if (errorMessage.includes("INVALID_MODEL_IDENTIFIER") || errorMessage.includes("MODEL_NOT_FOUND")) { setError(`Failed to load model: ${errorMessage}. Please ensure you're using the model hash instead of the name.`); } else { setError(`Failed to load model "${modelName}": ${errorMessage}`); } } finally { setLoadingModels(prev => { const newSet = new Set(prev); newSet.delete(modelId); return newSet; }); } }; const unloadModel = async (modelId: string, modelName: string) => { setLoadingModels(prev => new Set(prev).add(modelId)); setError(null); setSuccess(null); try { await apiClient.unloadModel(modelId); setSuccess(`Model "${modelName}" unloaded successfully`); // Update the model's loaded status setModels(prev => prev.map(model => model.id === modelId || model.sha256 === modelId || model.sha256_short === modelId ? { ...model, loaded: false } : model )); } catch (err) { setError(err instanceof Error ? err.message : "Failed to unload model"); } finally { setLoadingModels(prev => { const newSet = new Set(prev); newSet.delete(modelId); return newSet; }); } }; const computeModelHash = async (modelId: string, modelName: string) => { setComputingHashes(prev => new Set(prev).add(modelId)); setError(null); setSuccess(null); try { const result = await apiClient.computeModelHash(modelId); setSuccess(`Hash computation started for "${modelName}". Request ID: ${result.request_id}`); // Refresh models after a delay to get updated hash information setTimeout(() => { loadModels(); }, 2000); } catch (err) { setError(err instanceof Error ? err.message : "Failed to compute model hash"); } finally { setComputingHashes(prev => { const newSet = new Set(prev); newSet.delete(modelId); return newSet; }); } }; const copyToClipboard = (text: string) => { navigator.clipboard.writeText(text).then(() => { setSuccess("Copied to clipboard"); setTimeout(() => setSuccess(null), 2000); }); }; // Filter models based on active tab and search query const filteredModels = models.filter(model => { const matchesTab = activeTab === "all" || (activeTab === "loaded" && model.loaded) || (activeTab === "unloaded" && !model.loaded); const matchesType = modelTypeFilter === "all" || model.type === modelTypeFilter; const matchesSearch = searchQuery === "" || model.name.toLowerCase().includes(searchQuery.toLowerCase()) || (model.sha256_short && model.sha256_short.toLowerCase().includes(searchQuery.toLowerCase())); return matchesTab && matchesType && matchesSearch; }); // Get unique model types for filter dropdown const modelTypes = Array.from(new Set(models.map(model => model.type))).sort(); const getModelIdentifier = (model: ModelInfo) => { // Prefer hash over name for model identification return model.sha256_short || model.sha256 || model.id || model.name; }; return (
{error && ( {error} )} {success && ( {success} )}
Models Manage your AI models - use hashes for reliable identification
setSearchQuery(e.target.value)} />
All Models ({models.length}) Loaded ({models.filter(m => m.loaded).length}) Unloaded ({models.filter(m => !m.loaded).length}) {loading ? (
) : filteredModels.length === 0 ? (
No models found matching your criteria
) : (
{filteredModels.map((model) => (

{model.name}

{model.loaded ? "Loaded" : "Unloaded"} {model.type}
{model.sha256_short || "No hash"} {model.sha256_short && ( )}
{model.file_size_mb && (
Size: {model.file_size_mb.toFixed(2)} MB
)} {model.architecture && (
Architecture: {model.architecture}
)}
{!model.sha256_short && ( This model doesn't have a computed hash. Compute the hash to use it with the hash-only loading system. )}
{!model.sha256_short && ( )} {model.loaded ? ( ) : ( )}
))}
)}
); }