Spaces:
Paused
Paused
File size: 4,231 Bytes
8c1b9fe | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | "use client";
import type { ModelFitScore } from "@/lib/modelfit";
function ScoreBar({ label, value }: { label: string; value: number }) {
const color =
value >= 85
? "bg-emerald-500"
: value >= 70
? "bg-sky-500"
: value >= 50
? "bg-amber-500"
: "bg-red-500";
return (
<div className="space-y-1">
<div className="flex justify-between text-xs text-zinc-400">
<span>{label}</span>
<span className="font-mono">{value}</span>
</div>
<div className="h-1.5 rounded bg-zinc-800">
<div className={`h-full rounded ${color}`} style={{ width: `${value}%` }} />
</div>
</div>
);
}
const LABEL_COLOR: Record<string, string> = {
"Excellent fit": "text-emerald-400",
"Recommended": "text-sky-400",
"Usable with limits": "text-amber-400",
"Not recommended": "text-orange-400",
"Does not fit": "text-red-400",
};
const FIT_LEVEL_COLOR: Record<string, string> = {
comfortable: "text-emerald-400",
tight: "text-amber-400",
not_recommended: "text-orange-400",
impossible: "text-red-400",
};
export function ModelFitScoreCard({
score,
showDetails = false,
}: {
score: ModelFitScore;
showDetails?: boolean;
}) {
const re = score.resource_estimate;
return (
<div className="rounded-xl border border-zinc-700 bg-zinc-900 p-5 space-y-4">
{/* Header */}
<div className="flex items-start justify-between gap-3">
<div className="space-y-0.5">
<div className="text-sm font-semibold text-zinc-100 truncate max-w-[200px]" title={score.model_id}>
{score.model_id.replace(/^(ollama:|hf:|local:)/, "")}
</div>
<div className={`text-xs font-mono ${LABEL_COLOR[score.label] ?? "text-zinc-400"}`}>
{score.label}
</div>
</div>
<div className="text-3xl font-bold font-mono text-zinc-100 shrink-0">
{Math.round(score.overall_score)}
</div>
</div>
{/* Sub-scores */}
<div className="space-y-2">
<ScoreBar label="Hardware fit" value={score.hardware_fit} />
<ScoreBar label="Speed fit" value={score.speed_fit} />
<ScoreBar label="RAG fit" value={score.rag_fit} />
<ScoreBar label="Task fit" value={score.task_fit} />
<ScoreBar label="Deployment" value={score.deployment_fit} />
</div>
{/* Quick facts */}
<div className="text-xs text-zinc-400 space-y-1">
<div>
Quantization:{" "}
<span className="text-zinc-200 font-mono">{score.best_quantization}</span>
</div>
{re && (
<>
<div>
Est. VRAM:{" "}
<span className="font-mono text-zinc-200">{re.estimated_vram_gb} GB</span>
{" "}
<span className={`font-mono ${FIT_LEVEL_COLOR[re.fit_level] ?? ""}`}>
({re.fit_level.replace("_", " ")})
</span>
</div>
<div>
Rec. context:{" "}
<span className="font-mono text-zinc-200">
{re.recommended_context.toLocaleString()} tokens
</span>
</div>
</>
)}
{score.benchmark?.avg_tok_per_sec != null ? (
<div>
Tok/s:{" "}
<span className="font-mono text-emerald-400">
{score.benchmark.avg_tok_per_sec} (measured)
</span>
</div>
) : (
<div className="text-zinc-500 italic">tok/s: not measured yet</div>
)}
{score.estimate_used && (
<div className="text-amber-500 italic text-[10px]">
Speed score uses estimates — run benchmark for measured data.
</div>
)}
</div>
{/* Reason */}
{showDetails && (
<p className="text-xs text-zinc-400 border-t border-zinc-800 pt-3">{score.reason}</p>
)}
{/* Warnings */}
{showDetails && score.warnings.length > 0 && (
<ul className="space-y-1">
{score.warnings.map((w, i) => (
<li key={i} className="text-[10px] text-amber-400">
⚠ {w}
</li>
))}
</ul>
)}
</div>
);
}
|