|
import { |
|
Bot, |
|
Heart, |
|
Download, |
|
Cpu, |
|
DatabaseIcon, |
|
CheckCircle, |
|
XCircle, |
|
ExternalLink |
|
} from 'lucide-react' |
|
import { getModelSize } from '../lib/huggingface' |
|
import { useModel } from '../contexts/ModelContext' |
|
import ModelLoader from './ModelLoader' |
|
import Tooltip from './Tooltip' |
|
|
|
const ModelInfo = () => { |
|
const formatNumber = (num: number) => { |
|
if (num >= 1000000000) { |
|
return (num / 1000000000).toFixed(1) + 'B' |
|
} else if (num >= 1000000) { |
|
return (num / 1000000).toFixed(1) + 'M' |
|
} else if (num >= 1000) { |
|
return (num / 1000).toFixed(1) + 'K' |
|
} |
|
return num.toString() |
|
} |
|
|
|
const { |
|
models, |
|
status, |
|
modelInfo, |
|
selectedQuantization, |
|
isFetching, |
|
errorText |
|
} = useModel() |
|
|
|
const ModelInfoSkeleton = () => ( |
|
<div className="bg-gradient-to-r from-secondary to-accent px-3 py-3 rounded-lg border border-border space-y-3 animate-pulse w-4/5"> |
|
<div className="flex items-center space-x-2"> |
|
<Bot className="w-4 h-4 text-green-500" /> |
|
<div className="h-4 bg-muted rounded-sm flex-1"></div> |
|
<div className="w-4 h-4 bg-muted rounded-full"></div> |
|
</div> |
|
|
|
<div className="flex items-center space-x-2 ml-6"> |
|
<div className="h-3 bg-muted/80 rounded-sm w-32"></div> |
|
</div> |
|
|
|
<div className="grid grid-cols-2 gap-2 text-xs"> |
|
<div className="flex items-center space-x-1"> |
|
<Heart className="w-3 h-3 text-destructive/60" /> |
|
<div className="h-3 bg-muted/80 rounded-sm w-8"></div> |
|
</div> |
|
<div className="flex items-center space-x-1"> |
|
<Download className="w-3 h-3 text-purple-500" /> |
|
<div className="h-3 bg-muted/80 rounded-sm w-8"></div> |
|
</div> |
|
<div className="flex items-center space-x-1"> |
|
<Cpu className="w-3 h-3 text-chart-4/60" /> |
|
<div className="h-3 bg-muted/80 rounded-sm w-8"></div> |
|
</div> |
|
<div className="flex items-center space-x-1"> |
|
<DatabaseIcon className="w-3 h-3 text-purple-500" /> |
|
<div className="h-3 bg-muted/80 rounded-sm w-12"></div> |
|
</div> |
|
</div> |
|
<hr className="border-border" /> |
|
<div className="h-8 bg-muted/80 rounded-sm w-full"></div> |
|
</div> |
|
) |
|
|
|
if (!modelInfo || isFetching || models.length === 0) { |
|
return <ModelInfoSkeleton /> |
|
} |
|
|
|
return ( |
|
<div className="relative bg-gradient-to-r from-secondary to-accent px-3 py-3 rounded-lg border border-border space-y-3 h-full w-4/5"> |
|
{/* Model Name Row */} |
|
<div className="flex justify-center items-center space-x-2"> |
|
{/* Compatibility Status */} |
|
{typeof modelInfo.isCompatible === 'boolean' && ( |
|
<div className="shrink-0 "> |
|
{modelInfo.isCompatible && status !== 'error' ? ( |
|
<CheckCircle className="w-4 h-4 text-green-500" /> |
|
) : ( |
|
<XCircle className="w-4 h-4 text-destructive" /> |
|
)} |
|
</div> |
|
)} |
|
<div className="flex-1 min-w-0"> |
|
<a |
|
href={`https://huggingface.co/${modelInfo.name}`} |
|
target="_blank" |
|
rel="noopener noreferrer" |
|
className="text-sm font-medium text-foreground/80 hover:underline block truncate" |
|
title={modelInfo.name} |
|
> |
|
<ExternalLink className="w-3 h-3 inline-block mr-1" /> |
|
{modelInfo.name} |
|
</a> |
|
{/* Base Model Link */} |
|
{modelInfo.baseId && modelInfo.baseId !== modelInfo.id && ( |
|
<a |
|
href={`https://huggingface.co/${modelInfo.baseId}`} |
|
target="_blank" |
|
rel="noopener noreferrer" |
|
className="text-xs text-muted-foreground hover:underline block truncate mt-1" |
|
title={`Base model: ${modelInfo.baseId}`} |
|
> |
|
<ExternalLink className="w-3 h-3 inline-block mr-1" />( |
|
{modelInfo.baseId}) |
|
</a> |
|
)} |
|
</div> |
|
</div> |
|
|
|
{/* Stats Grid */} |
|
<div className="grid grid-cols-2 gap-2 text-xs text-muted-foreground"> |
|
{modelInfo.likes > 0 && ( |
|
<div className="flex items-center space-x-1"> |
|
<Heart className="w-3 h-3 text-destructive" /> |
|
<span>{formatNumber(modelInfo.likes)}</span> |
|
</div> |
|
)} |
|
|
|
{modelInfo.downloads > 0 && ( |
|
<div className="flex items-center space-x-1"> |
|
<Download className="w-3 h-3 text-green-500" /> |
|
<span>{formatNumber(modelInfo.downloads)}</span> |
|
</div> |
|
)} |
|
|
|
<Tooltip content="Model parameters according to Hugging Face API"> |
|
<div className="flex items-center space-x-1 cursor-default"> |
|
<Cpu className="w-3 h-3 text-purple-500" /> |
|
{modelInfo.parameters ? ( |
|
<span>{formatNumber(modelInfo.parameters)}</span> |
|
) : ( |
|
<span>?</span> |
|
)} |
|
</div> |
|
</Tooltip> |
|
|
|
<Tooltip |
|
content={`Estimated size with ${selectedQuantization} quantization`} |
|
> |
|
<div className="flex items-center space-x-1 cursor-default"> |
|
<DatabaseIcon className="w-3 h-3 text-purple-500" /> |
|
{modelInfo.parameters ? ( |
|
<span> |
|
{`~${getModelSize( |
|
modelInfo.parameters, |
|
selectedQuantization |
|
).toFixed(1)}MB`} |
|
</span> |
|
) : ( |
|
<span>?</span> |
|
)} |
|
</div> |
|
</Tooltip> |
|
</div> |
|
|
|
<ModelLoader /> |
|
|
|
{/* Incompatibility Message */} |
|
{((modelInfo.isCompatible === false && modelInfo.incompatibilityReason) || |
|
errorText) && ( |
|
<div className="bg-destructive/10 border border-destructive/20 rounded-md px-2 py-2"> |
|
<p className="text-xs text-destructive whitespace-break-spaces"> |
|
{errorText ? errorText : modelInfo.incompatibilityReason} |
|
</p> |
|
</div> |
|
)} |
|
</div> |
|
) |
|
} |
|
|
|
export default ModelInfo |
|
|