"use client" import { useState, useMemo, useEffect } from "react" import { useAudienceMode } from "@/components/audience-mode-provider" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { ArrowUpDown, ArrowRightLeft, Search, X } from "lucide-react" import { BenchmarkEvaluationCard, type BenchmarkEvaluationCardData } from "@/components/benchmark-evaluation-card" import { DeveloperCard } from "@/components/developer-card" import { ListPagination } from "@/components/list-pagination" import { ModelCompareDialog } from "@/components/model-compare-dialog" import { Navigation } from "@/components/navigation" import { PageHeader } from "@/components/page-header" import { Badge } from "@/components/ui/badge" import { fetchDevelopers, fetchModelCards, fetchBenchmarkMetadata, type DeveloperListItem } from "@/lib/dashboard-data-client" import type { BenchmarkCard } from "@/lib/benchmark-schema" import { getCategoryColor, type CategoryType } from "@/lib/benchmark-schema" import { cn } from "@/lib/utils" const PAGE_SIZE = 40 const MAX_COMPARE_MODELS = 4 const PARAM_RANGE_VALUES = [1, 2, 3, 4, 6, 8, 10, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 500] as const const PARAM_RANGE_MARKERS = [ { label: "< 1B", step: 0 }, { label: "6B", step: PARAM_RANGE_VALUES.indexOf(6) }, { label: "12B", step: PARAM_RANGE_VALUES.indexOf(12) }, { label: "32B", step: PARAM_RANGE_VALUES.indexOf(32) }, { label: "128B", step: PARAM_RANGE_VALUES.indexOf(128) }, { label: "> 500B", step: PARAM_RANGE_VALUES.length - 1 }, ] as const function formatParamBoundLabel(step: number, bound: "min" | "max") { const maxStepIndex = PARAM_RANGE_VALUES.length - 1 if (bound === "min" && step <= 0) { return "< 1B" } if (bound === "max" && step >= maxStepIndex) { return "> 500B" } const value = PARAM_RANGE_VALUES[step] return value != null ? `${value}B` : "Not reported" } function getReproducibilitySortValue(status: BenchmarkEvaluationCardData["reproducibility_status"]) { switch (status) { case "complete": return 2 case "partial": return 1 default: return 0 } } export default function ModelsPage() { const { mode } = useAudienceMode() const [evaluations, setEvaluations] = useState([]) const [developers, setDevelopers] = useState([]) const [benchmarkCards, setBenchmarkCards] = useState>({}) const [loadingModels, setLoadingModels] = useState(true) const [loadingDevelopers, setLoadingDevelopers] = useState(true) const [groupByDeveloper, setGroupByDeveloper] = useState(false) const [modelSortBy, setModelSortBy] = useState<"date" | "name" | "benchmarks" | "reporting" | "reproducibility" | "size">("benchmarks") const [developerSortBy, setDeveloperSortBy] = useState<"coverage" | "evaluated" | "models" | "name">("coverage") const [searchQuery, setSearchQuery] = useState("") const [minParamStep, setMinParamStep] = useState(0) const [maxParamStep, setMaxParamStep] = useState(PARAM_RANGE_VALUES.length - 1) const [selectedModelIds, setSelectedModelIds] = useState([]) const [selectedCategories, setSelectedCategories] = useState([]) const [compareOpen, setCompareOpen] = useState(false) const [page, setPage] = useState(1) useEffect(() => { Promise.all([fetchModelCards(), fetchBenchmarkMetadata()]) .then(([cards, metadata]) => { setEvaluations(cards) setBenchmarkCards(metadata) }) .catch((error) => { console.error("Failed to load evaluations:", error) }) .finally(() => setLoadingModels(false)) fetchDevelopers() .then(setDevelopers) .catch((error) => { console.error("Failed to load developers:", error) }) .finally(() => setLoadingDevelopers(false)) }, []) useEffect(() => { if (typeof window === "undefined") { return } const params = new URLSearchParams(window.location.search) setGroupByDeveloper(params.get("group") === "developer") }, []) useEffect(() => { setSelectedModelIds((current) => current.filter((id) => evaluations.some((evaluation) => evaluation.id === id)) ) }, [evaluations]) const numericMinParams = useMemo(() => { if (minParamStep <= 0) { return null } return PARAM_RANGE_VALUES[minParamStep] ?? null }, [minParamStep]) const numericMaxParams = useMemo(() => { if (maxParamStep >= PARAM_RANGE_VALUES.length - 1) { return null } return PARAM_RANGE_VALUES[maxParamStep] ?? null }, [maxParamStep]) const allCategories = useMemo(() => { const catSet = new Set() for (const evaluation of evaluations) { for (const cat of evaluation.categories ?? []) { catSet.add(cat) } } return Array.from(catSet).sort((a, b) => a.localeCompare(b)) }, [evaluations]) const filteredEvaluations = useMemo(() => { const query = searchQuery.trim().toLowerCase() return evaluations.filter((evaluation) => { if (numericMinParams != null) { if (evaluation.params_billions == null || evaluation.params_billions < numericMinParams) { return false } } if (numericMaxParams != null) { if (evaluation.params_billions == null || evaluation.params_billions > numericMaxParams) { return false } } if (selectedCategories.length > 0) { if (!evaluation.categories.some((c) => selectedCategories.includes(c))) { return false } } if (!query) { return true } const haystacks = [ evaluation.model_name, evaluation.canonical_model_name, evaluation.developer, evaluation.architecture, evaluation.latest_source_name, evaluation.reproducibility_status, ...evaluation.evaluator_names, ...evaluation.top_scores.map((score) => score.benchmark), ] return haystacks.some((value) => value?.toLowerCase().includes(query)) }) }, [evaluations, numericMaxParams, numericMinParams, searchQuery, selectedCategories]) const sortedEvaluations = useMemo(() => { const sorted = [...filteredEvaluations] switch (modelSortBy) { case "date": sorted.sort((a, b) => new Date(b.latest_timestamp).getTime() - new Date(a.latest_timestamp).getTime() ) break case "name": sorted.sort((a, b) => a.model_name.localeCompare(b.model_name)) break case "benchmarks": sorted.sort((a, b) => b.benchmarks_count - a.benchmarks_count) break case "reporting": sorted.sort((a, b) => { if (b.evaluator_count !== a.evaluator_count) { return b.evaluator_count - a.evaluator_count } if (b.independent_verification_ratio !== a.independent_verification_ratio) { return b.independent_verification_ratio - a.independent_verification_ratio } return b.benchmarks_count - a.benchmarks_count }) break case "reproducibility": sorted.sort((a, b) => { const reproducibilityDiff = getReproducibilitySortValue(b.reproducibility_status) - getReproducibilitySortValue(a.reproducibility_status) if (reproducibilityDiff !== 0) { return reproducibilityDiff } if (b.independent_verification_ratio !== a.independent_verification_ratio) { return b.independent_verification_ratio - a.independent_verification_ratio } return b.benchmarks_count - a.benchmarks_count }) break case "size": sorted.sort((a, b) => (b.params_billions ?? -1) - (a.params_billions ?? -1)) break } return sorted }, [filteredEvaluations, modelSortBy]) const filteredDevelopers = useMemo(() => { const query = searchQuery.trim().toLowerCase() const filtered = query ? developers.filter((developer) => { const haystacks = [ developer.developer, ...developer.popular_evals.map((evaluation) => evaluation.benchmark), ] return haystacks.some((value) => value?.toLowerCase().includes(query)) }) : [...developers] filtered.sort((a, b) => { switch (developerSortBy) { case "coverage": if (b.benchmark_count !== a.benchmark_count) { return b.benchmark_count - a.benchmark_count } if (b.evaluation_count !== a.evaluation_count) { return b.evaluation_count - a.evaluation_count } if (b.model_count !== a.model_count) { return b.model_count - a.model_count } break case "evaluated": if (b.evaluation_count !== a.evaluation_count) { return b.evaluation_count - a.evaluation_count } break case "models": if (b.model_count !== a.model_count) { return b.model_count - a.model_count } break case "name": break } return a.developer.localeCompare(b.developer) }) return filtered }, [developerSortBy, developers, searchQuery]) useEffect(() => { setPage(1) }, [developerSortBy, groupByDeveloper, maxParamStep, minParamStep, modelSortBy, searchQuery, selectedCategories]) const pagedEvaluations = useMemo( () => sortedEvaluations.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE), [sortedEvaluations, page] ) const pagedDevelopers = useMemo( () => filteredDevelopers.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE), [filteredDevelopers, page] ) const handleDelete = (id: string) => { setEvaluations((prev) => prev.filter((e) => e.id !== id)) setSelectedModelIds((prev) => prev.filter((selectedId) => selectedId !== id)) } const selectedModels = useMemo( () => selectedModelIds .map((id) => evaluations.find((evaluation) => evaluation.id === id)) .filter((evaluation): evaluation is BenchmarkEvaluationCardData => Boolean(evaluation)), [evaluations, selectedModelIds] ) useEffect(() => { if (compareOpen && selectedModels.length < 2) { setCompareOpen(false) } }, [compareOpen, selectedModels.length]) const toggleModelSelection = (id: string) => { setSelectedModelIds((current) => { if (current.includes(id)) { return current.filter((selectedId) => selectedId !== id) } if (current.length >= MAX_COMPARE_MODELS) { return current } return [...current, id] }) } const loading = loadingModels || loadingDevelopers const maxParamStepIndex = PARAM_RANGE_VALUES.length - 1 const minHandlePercent = (minParamStep / maxParamStepIndex) * 100 const maxHandlePercent = (maxParamStep / maxParamStepIndex) * 100 if (loading) { return (
Loading models...
) } return (
sum + developer.model_count, 0).toString(), } : { label: "Reported results", value: sortedEvaluations.reduce((sum, e) => sum + e.evaluations_count, 0).toString() }, groupByDeveloper ? { label: "Benchmarks", value: filteredDevelopers.reduce((sum, developer) => sum + developer.benchmark_count, 0).toString(), } : { label: "Reporting orgs", value: new Set(sortedEvaluations.flatMap((e) => e.evaluator_names)).size.toString() }, !groupByDeveloper ? { label: "Compare tray", value: selectedModels.length.toString() } : { label: "View", value: "Developer" }, ...(selectedCategories.length > 0 ? [{ label: "Category filter", value: selectedCategories.join(", ") }] : []), ]} /> {!groupByDeveloper ? (
Compare Workflow
Show the most useful information first: narrow to a similar parameter range, scan key benchmarks, then select up to {MAX_COMPARE_MODELS} models for a table comparison.
Parameter range filter Table comparison Trust signals first
) : null}
setSearchQuery(event.target.value)} placeholder={ groupByDeveloper ? "Search developers or popular evals" : "Search models, developers, or benchmarks" } className="pl-9" />
{!groupByDeveloper ? (
Parameters
{PARAM_RANGE_MARKERS.map((marker) => ( {marker.label} ))}
{PARAM_RANGE_VALUES.map((_, stepIndex) => (
{ const nextMin = Number(event.target.value) setMinParamStep(Math.min(nextMin, maxParamStep)) }} className="param-range-input" aria-label="Minimum parameter filter" /> { const nextMax = Number(event.target.value) setMaxParamStep(Math.max(nextMax, minParamStep)) }} className="param-range-input" aria-label="Maximum parameter filter" />
{formatParamBoundLabel(minParamStep, "min")} to {formatParamBoundLabel(maxParamStep, "max")}
) : null}
{/* Category filter chips — only shown for model view */} {!groupByDeveloper && allCategories.length > 0 && (
Category {allCategories.map((cat) => { const isActive = selectedCategories.includes(cat) return ( ) })} {selectedCategories.length > 0 && ( )}
)}
{(groupByDeveloper ? filteredDevelopers.length === 0 : sortedEvaluations.length === 0) ? (

{groupByDeveloper ? "No developers found matching your filters" : "No evaluations found matching your filters"}

) : (
{groupByDeveloper ? pagedDevelopers.map((developer, index) => ( )) : pagedEvaluations.map((evaluation, index) => ( ))}
)} {!groupByDeveloper && selectedModels.length > 0 ? (
Compare Tray
{selectedModels.map((model) => ( {model.model_name} ))}
Select up to {MAX_COMPARE_MODELS} models. The compare view is most useful when you keep the parameter range tight.
) : null}
) }