"use client" // Shared data hook + source picker for the /embed/eval/* pages. // // Per-source (two-segment) embed ids behave exactly as before: one // fetch, no picker. Merged (single-segment) ids default to the MERGED // all-sources data and expose a compact "Source" selector so a viewer — // or an embedder, via ?source= — can pin one source's // instantiation of the benchmark instead. Selecting a source swaps the // embed's data to that source's per-source summary without navigating. import { useEffect, useMemo, useState } from "react" import { fetchEvalSummary, fetchMergedBenchmarkSummary } from "@/lib/dashboard-data-client" import { isMergedBenchmarkSummary, mergedSummaryToEvalSummary } from "@/lib/merged-adapter" import { isMergedEvalId } from "@/lib/utils" import type { BenchmarkEvalSummary, MergedBenchmarkSummary } from "@/lib/eval-processing" export interface EmbedSourceOption { /** Composite slug — the ?source= value. */ slug: string label: string /** Two-segment per-source evaluation_id to fetch when selected. */ evaluationId: string } export function useEmbedEvalSummary( evalId: string, opts: { metricParam?: string | null; sourceParam?: string | null } = {}, ): { summary: BenchmarkEvalSummary | null error: string | null /** Non-null only for merged ids with at least one linkable source. */ sources: EmbedSourceOption[] | null /** "" = merged (default). */ activeSource: string setActiveSource: (slug: string) => void } { const isMerged = isMergedEvalId(evalId) const metricId = opts.metricParam?.trim() || undefined // `base` = the embed's default data: the adapted merged summary for // merged ids, or the plain per-source summary otherwise. const [base, setBase] = useState(null) const [mergedRaw, setMergedRaw] = useState(null) const [error, setError] = useState(null) const [activeSource, setActiveSource] = useState( () => opts.sourceParam?.trim() ?? "", ) const [sourceSummary, setSourceSummary] = useState(null) useEffect(() => { let cancelled = false if (!isMerged) { fetchEvalSummary(evalId) .then((s) => { if (cancelled) return setBase(s) setError(null) }) .catch((err) => { if (!cancelled) setError(err instanceof Error ? err.message : String(err)) }) return () => { cancelled = true } } fetchMergedBenchmarkSummary(evalId, { metricId }) .then((payload) => { if (cancelled) return if (isMergedBenchmarkSummary(payload)) { setMergedRaw(payload) setBase(mergedSummaryToEvalSummary(payload)) setError(null) } else if (payload && !("error" in payload)) { // Pre-merged-view snapshot: the API answered with a plain summary. setBase(payload) setError(null) } else { setError("Evaluation not found") } }) .catch((err) => { if (!cancelled) setError(err instanceof Error ? err.message : String(err)) }) return () => { cancelled = true } }, [evalId, isMerged, metricId]) const sources: EmbedSourceOption[] | null = useMemo(() => { if (!mergedRaw) return null // Every source with a per-source page is pinnable — a pinned source // shows ITS OWN per-source data, so sources that only report other // metrics (reports_preferred=false, e.g. HELM Capabilities on gpqa) // belong in the list too. Slice-only sources have no top-level page // to pin, so they stay out. const options = mergedRaw.aggregate_sources .filter((s) => s.evaluation_id && !s.slice_only) .map((s) => ({ slug: s.composite_slug, label: s.composite_display_name || s.composite_slug, evaluationId: s.evaluation_id!, })) return options.length > 0 ? options : null }, [mergedRaw]) // Ignore a ?source= that names no linkable source (typo / stale link): // fall back to merged rather than an empty embed. const effectiveSource = activeSource && sources?.some((s) => s.slug === activeSource) ? activeSource : "" useEffect(() => { if (!effectiveSource || !sources) { setSourceSummary(null) return } const target = sources.find((s) => s.slug === effectiveSource) if (!target) return let cancelled = false setSourceSummary(null) fetchEvalSummary(target.evaluationId) .then((s) => { if (cancelled) return setSourceSummary(s) setError(null) }) .catch((err) => { if (!cancelled) setError(err instanceof Error ? err.message : String(err)) }) return () => { cancelled = true } }, [effectiveSource, sources]) // Clear any stale error when the viewer changes selection, so a failed // pinned-source fetch doesn't leave the embed stuck on the error state // after switching back to Merged (or to another source). const selectSource = (slug: string) => { setError(null) setActiveSource(slug) } return { summary: effectiveSource ? sourceSummary : base, error, sources, activeSource: effectiveSource, setActiveSource: selectSource, } } /** Compact merged-vs-source selector, styled like the embeds' Split dropdown. */ export function EmbedSourcePicker({ sources, value, onChange, }: { sources: EmbedSourceOption[] | null value: string onChange: (slug: string) => void }) { if (!sources || sources.length === 0) return null return (
Source
) }