Spaces:
Running
Running
File size: 6,225 Bytes
731462e c5c4016 731462e c5c4016 731462e c5c4016 731462e c5c4016 731462e | 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 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 | "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=<composite_slug> — 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<BenchmarkEvalSummary | null>(null)
const [mergedRaw, setMergedRaw] = useState<MergedBenchmarkSummary | null>(null)
const [error, setError] = useState<string | null>(null)
const [activeSource, setActiveSource] = useState<string>(
() => opts.sourceParam?.trim() ?? "",
)
const [sourceSummary, setSourceSummary] = useState<BenchmarkEvalSummary | null>(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 (
<div className="mb-3 flex items-center gap-3">
<span
className="font-mono uppercase shrink-0"
style={{ fontSize: 10, letterSpacing: "0.14em", color: "var(--fg-subtle)" }}
>
Source
</span>
<select
className="ec-select"
value={value}
onChange={(e) => onChange(e.target.value)}
>
<option value="">Merged (all sources)</option>
{sources.map((s) => (
<option key={s.slug} value={s.slug}>
{s.label}
</option>
))}
</select>
</div>
)
}
|