Spaces:
Running
Running
File size: 5,590 Bytes
f073e7a 06313c1 d249d5b 06313c1 bc08b3b f073e7a bc08b3b 06313c1 f073e7a 06313c1 d249d5b 06313c1 d249d5b 06313c1 f073e7a 06313c1 bc08b3b 06313c1 bc08b3b d249d5b 06313c1 bc08b3b f073e7a d249d5b f073e7a 06313c1 f073e7a 06313c1 f073e7a d249d5b f073e7a d249d5b f073e7a d249d5b f073e7a bc08b3b f073e7a 06313c1 bc08b3b f073e7a | 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 | import type {
EvalHierarchy,
HierarchyComposite,
HierarchyFamily,
} from "@/lib/backend-artifacts"
export interface HierarchyEvalLocation {
familyKey: string
familyDisplayName: string
compositeKey?: string
compositeDisplayName?: string
/** Resolved leaf benchmark — set when `eval_summary_id` lives inside a
* hierarchy benchmark's `constituent_evaluation_ids`. Lets the model-detail
* plotbox builder bucket every eval row that resolves to the same
* benchmark together (so a standalone like Fibble Arena with N
* per-split eval rows renders as one plotbox, not N). */
benchmarkKey?: string
benchmarkDisplayName?: string
/** Curated category tags (data/benchmarks/categories.json vocabulary)
* for the leaf benchmark this eval belongs to, falling back to its
* composite/family. Decorated by `decorateHierarchyDerivedTags` at
* hydration time. */
tags?: string[]
}
interface FamilyAppearance {
family: HierarchyFamily
composite?: HierarchyComposite
benchmarkTags?: string[]
benchmarkKey?: string
benchmarkDisplayName?: string
}
function findComposite(
family: HierarchyFamily,
evalSummaryId: string,
): HierarchyComposite | undefined {
const composites = family.composites
if (!composites?.length) {
return undefined
}
const sourcePrefix = evalSummaryId.split("%2F")[0]
const byPrefix = composites.find((composite) => composite.key === sourcePrefix)
if (byPrefix) return byPrefix
// Fallback: scan benchmarks' `constituent_evaluation_ids`. The clean-hierarchy
// post-processor synthesises composites for split families (Fibble
// Arena's per-N-lies splits, CapArena-Auto, AgentHarm) whose
// children carry mixed source prefixes (`fibble1-arena%2F…`,
// `fibble2-arena%2F…`, …) that wouldn't match the synthetic
// composite's key by prefix alone.
return composites.find((composite) =>
composite.benchmarks?.some((bench) =>
bench.constituent_evaluation_ids?.includes(evalSummaryId),
),
)
}
function findBenchmark(
family: HierarchyFamily,
composite: HierarchyComposite | undefined,
evalSummaryId: string,
):
| { key: string; displayName: string; tags?: string[] }
| undefined {
const benchmarks = [
...(composite?.benchmarks ?? []),
...(family.standalone_benchmarks ?? []),
...(family.benchmarks ?? []),
]
for (const benchmark of benchmarks) {
if (benchmark.constituent_evaluation_ids?.includes(evalSummaryId)) {
return {
key: benchmark.key,
displayName: benchmark.display_name,
tags: benchmark.derivedTags,
}
}
}
return undefined
}
function buildAppearancesIndex(
hierarchy: EvalHierarchy | null | undefined,
): Map<string, FamilyAppearance[]> {
const index = new Map<string, FamilyAppearance[]>()
if (!hierarchy?.families) {
return index
}
for (const family of hierarchy.families) {
for (const evalSummaryId of family.constituent_evaluation_ids ?? []) {
const composite = findComposite(family, evalSummaryId)
const bench = findBenchmark(family, composite, evalSummaryId)
const list = index.get(evalSummaryId) ?? []
list.push({
family,
composite,
benchmarkTags: bench?.tags,
benchmarkKey: bench?.key,
benchmarkDisplayName: bench?.displayName,
})
index.set(evalSummaryId, list)
}
}
return index
}
/**
* Build a lookup that maps each `eval_summary_id` to the family / composite
* that contains it in `hierarchy.json`. The model-detail benchmark grouping
* needs this because the eval row's own `family_id` is null for some evals
* (e.g. CySE2 composites) and points at the leaf instead of the parent for
* singleton families. The hierarchy is the only source that captures
* curated family→composite→benchmark grouping.
*
* Some constituent_evaluation_ids appear in multiple families. The optional
* `preferFamilyKey(evalSummaryId)` callback lets the caller pick the canonical
* family — typically by passing in the eval row's own `family_id`. When no
* preference is given, the first family encountered wins.
*/
export function buildHierarchyEvalIndex(
hierarchy: EvalHierarchy | null | undefined,
preferFamilyKey?: (evalSummaryId: string) => string | null | undefined,
): Map<string, HierarchyEvalLocation> {
const appearances = buildAppearancesIndex(hierarchy)
const index = new Map<string, HierarchyEvalLocation>()
for (const [evalSummaryId, candidates] of appearances) {
let chosen = candidates[0]
if (candidates.length > 1) {
const preferredKey = preferFamilyKey?.(evalSummaryId)?.toString().trim()
if (preferredKey) {
const match = candidates.find((c) => c.family.key === preferredKey)
if (match) {
chosen = match
}
}
}
// Tag preference order for the leaf: benchmark > composite > family.
// We want the most specific tags available so the model-view bucketing
// groups by leaf semantics, not by the family-level union.
const tags =
chosen.benchmarkTags && chosen.benchmarkTags.length > 0
? chosen.benchmarkTags
: chosen.composite?.derivedTags ?? chosen.family.derivedTags ?? []
index.set(evalSummaryId, {
familyKey: chosen.family.key,
familyDisplayName: chosen.family.display_name,
compositeKey: chosen.composite?.key,
compositeDisplayName: chosen.composite?.display_name,
benchmarkKey: chosen.benchmarkKey,
benchmarkDisplayName: chosen.benchmarkDisplayName,
tags,
})
}
return index
}
|