"use client" import { useCallback, useDeferredValue, useEffect, useMemo, useState } from "react" import { ChevronDown, ChevronUp, Search, Tag } from "lucide-react" import { FamilyTable } from "@/components/family-table" import { InfiniteScrollSentinel } from "@/components/infinite-scroll" import { Navigation } from "@/components/navigation" import type { EvalHierarchy, HierarchyFamily } from "@/lib/backend-artifacts" import { fetchBenchmarkMetadata, fetchEvalHierarchy, fetchEvalList } from "@/lib/dashboard-data-client" import type { BenchmarkEvalListItem } from "@/lib/eval-processing" import type { BenchmarkCard } from "@/lib/benchmark-schema" const PAGE_SIZE = 60 type FamilySort = "results" | "benchmarks" | "name" | "category" function familyEvalsCount(fam: HierarchyFamily): number { if (fam.evals_count != null) return fam.evals_count const composites = fam.composites ?? [] const standalone = fam.standalone_benchmarks ?? [] const benchmarks = fam.benchmarks ?? [] const leaves = fam.leaves ?? [] const allBenchmarks = [ ...standalone, ...benchmarks, ...composites.flatMap((c) => c.benchmarks ?? []), ] return ( (fam.metrics?.length ?? 0) + allBenchmarks.reduce( (sum, b) => sum + ((b as { metrics?: unknown[] }).metrics?.length ?? 0), 0, ) + leaves.reduce((sum, l) => sum + (l.evals_count ?? 0), 0) ) } function familyBenchmarkCount(fam: HierarchyFamily): number { const composites = fam.composites ?? [] const standalone = fam.standalone_benchmarks ?? [] const benchmarks = fam.benchmarks ?? [] const leaves = fam.leaves ?? [] const all = [ ...standalone, ...benchmarks, ...composites.flatMap((c) => c.benchmarks ?? []), ] return all.length > 0 ? all.length : leaves.length } export default function EvalsPage() { const [hierarchy, setHierarchy] = useState(null) const [totalModels, setTotalModels] = useState(0) const [evalItems, setEvalItems] = useState>(new Map()) const [benchmarkCards, setBenchmarkCards] = useState>({}) const [loading, setLoading] = useState(true) const [searchQuery, setSearchQuery] = useState("") const [sortBy, setSortBy] = useState("results") const [visibleCount, setVisibleCount] = useState(PAGE_SIZE) const [domainPanelOpen, setDomainPanelOpen] = useState(false) const [domainFilter, setDomainFilter] = useState>(new Set()) const [selectedCategories, setSelectedCategories] = useState([]) const deferredSearchQuery = useDeferredValue(searchQuery) useEffect(() => { Promise.all([fetchEvalHierarchy(), fetchEvalList(), fetchBenchmarkMetadata()]) .then(([h, list, metadata]) => { setHierarchy(h) setTotalModels(list.totalModels) const map = new Map() for (const item of list.evals) map.set(item.evaluation_id, item) setEvalItems(map) setBenchmarkCards(metadata) }) .catch(console.error) .finally(() => setLoading(false)) }, []) const families = hierarchy?.families ?? [] // Build a domain → family-count map. The lite eval list doesn't carry // benchmark cards, so we read domains from `benchmark-metadata.json` // (keyed by benchmark / leaf / family key). For each family we union // the domains across the family key itself and every leaf key, then // count one bump per family per distinct domain. const familyDomains = useMemo(() => { const out = new Map>() const lookupDomains = (key: string | null | undefined): string[] => { if (!key) return [] const card = benchmarkCards[key] const domains = card?.benchmark_details?.domains return Array.isArray(domains) ? domains : [] } for (const fam of families) { const seen = new Set() for (const d of lookupDomains(fam.key)) seen.add(d.trim().toLowerCase()) for (const leaf of fam.leaves ?? []) { for (const d of leaf.tags?.domains ?? []) seen.add(d.trim().toLowerCase()) for (const d of lookupDomains(leaf.key)) seen.add(d.trim().toLowerCase()) } for (const id of fam.eval_summary_ids ?? []) { for (const d of lookupDomains(id)) seen.add(d.trim().toLowerCase()) } seen.delete("") out.set(fam.key, seen) } return out }, [families, benchmarkCards]) // Domain → display label (from the first non-empty card occurrence) + // count of families touching that domain. Sorted descending by count. const domainCounts = useMemo(() => { const counts = new Map() const labels = new Map() const recordLabel = (raw: string) => { const key = raw.trim().toLowerCase() if (!key || labels.has(key)) return labels.set(key, raw.trim()) } for (const card of Object.values(benchmarkCards)) { for (const d of card?.benchmark_details?.domains ?? []) recordLabel(d) } for (const fam of families) { for (const leaf of fam.leaves ?? []) { for (const d of leaf.tags?.domains ?? []) recordLabel(d) } } for (const set of familyDomains.values()) { for (const key of set) counts.set(key, (counts.get(key) ?? 0) + 1) } return Array.from(counts.entries()) .map(([key, count]) => ({ domain: labels.get(key) ?? key, count, key })) .sort((a, b) => b.count - a.count || a.domain.localeCompare(b.domain)) }, [familyDomains, families, benchmarkCards]) const toggleDomain = useCallback((domain: string) => { setDomainFilter((current) => { const next = new Set(current) if (next.has(domain)) next.delete(domain) else next.add(domain) return next }) }, []) const clearDomainFilter = useCallback(() => setDomainFilter(new Set()), []) // Categories present on the family list — drives the pill selector // below the toolbar. Sort them by descending family count so the most // common ones surface first. const availableCategories = useMemo(() => { const counts = new Map() for (const fam of families) { const cat = fam.category ?? "General" counts.set(cat, (counts.get(cat) ?? 0) + 1) } return Array.from(counts.entries()) .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])) .map(([category]) => category) }, [families]) const filteredFamilies = useMemo(() => { const query = deferredSearchQuery.trim().toLowerCase() let list = families if (query) { list = list.filter( (fam) => fam.display_name.toLowerCase().includes(query) || fam.key.toLowerCase().includes(query) || fam.category?.toLowerCase().includes(query), ) } if (selectedCategories.length > 0) { const set = new Set(selectedCategories) list = list.filter((fam) => set.has(fam.category ?? "General")) } if (domainFilter.size > 0) { list = list.filter((fam) => { const set = familyDomains.get(fam.key) if (!set) return false for (const key of set) if (domainFilter.has(key)) return true return false }) } return list.slice().sort((a, b) => { switch (sortBy) { case "name": return a.display_name.localeCompare(b.display_name) case "category": return (a.category ?? "").localeCompare(b.category ?? "") case "benchmarks": return familyBenchmarkCount(b) - familyBenchmarkCount(a) case "results": default: return familyEvalsCount(b) - familyEvalsCount(a) } }) }, [families, deferredSearchQuery, sortBy, domainFilter, selectedCategories, familyDomains]) useEffect(() => { setVisibleCount(PAGE_SIZE) }, [deferredSearchQuery, sortBy, domainFilter, selectedCategories]) const visibleFamilies = useMemo( () => filteredFamilies.slice(0, visibleCount), [filteredFamilies, visibleCount], ) const hasMore = visibleCount < filteredFamilies.length const handleLoadMore = useCallback(() => { setVisibleCount((current) => Math.min(current + PAGE_SIZE, filteredFamilies.length)) }, [filteredFamilies.length]) const stats = hierarchy?.stats return (
{/* HEADER --------------------------------------------------- */}
Index

Evaluations

Evaluations are grouped into families. A family holds one or more benchmarks; each benchmark has one or more slices; each slice reports one or more metrics.

{/* META ROW ------------------------------------------------- */} {stats && (
Families {stats.family_count.toLocaleString()}
Suites {stats.composite_count.toLocaleString()}
Single benchmarks {(stats.single_benchmark_count + stats.standalone_benchmark_count).toLocaleString()}
Slices {stats.slice_count.toLocaleString()}
Metrics {stats.metric_count.toLocaleString()}
)} {/* FILTER BAR ----------------------------------------------- */}
setSearchQuery(event.target.value)} placeholder="Search family, category…" />
{domainCounts.length > 0 && ( )}
{/* DOMAIN FILTER PANEL — collapsed by default, opens when the user wants to slice the family list by topical domain. Picks unfurl every aggregator family in the table below so matching benchmarks are immediately visible. */} {domainPanelOpen && domainCounts.length > 0 && (
{domainFilter.size === 0 ? `Pick one or more domains · ${domainCounts.length} available` : `${domainFilter.size} selected · ${domainCounts.length - domainFilter.size} more`}
{domainFilter.size > 0 && ( )}
{domainCounts.map(({ domain, count }) => { const key = domain.trim().toLowerCase() const selected = domainFilter.has(key) return ( ) })}
)} {/* CATEGORY PILLS — quick toggle filter by category. Mirrors the same pattern used on benchmark-detail's matrix browser. */} {availableCategories.length > 0 && (
Category {availableCategories.map((category) => { const isSelected = selectedCategories.includes(category) return ( ) })}
)} {/* TABLE ---------------------------------------------------- */} {loading ? (
Loading…
) : filteredFamilies.length === 0 ? (

No families found matching your filters.

) : ( )}
) }