"use client" import type { ReactNode } from "react" import { ChevronDown, GitCompareArrows, Info, UsersRound } from "lucide-react" import { useAudienceMode } from "@/components/audience-mode-provider" import { Badge } from "@/components/ui/badge" import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible" import { SignalTooltip } from "@/components/signals/signal-tooltip" import type { BenchmarkComparability, ComparabilitySummary, DifferingSetupField } from "@/lib/backend-artifacts" import { formatFieldLabel, formatSignalNumber, formatSignalValue, } from "./signal-utils" export function ComparabilityPanel({ comparability, summary, }: { comparability?: BenchmarkComparability | null summary?: ComparabilitySummary }) { const { mode } = useAudienceMode() const isResearchView = mode === "research" const variantGroups = comparability?.variant_divergence_groups ?? [] const crossPartyGroups = comparability?.cross_party_divergence_groups ?? [] const showNoCrossPartyNote = summary?.groups_with_cross_party_check === 0 if (variantGroups.length === 0 && crossPartyGroups.length === 0 && !showNoCrossPartyNote) { return null } return (

{isResearchView ? "Comparability" : "Can these scores be compared directly?"}

{isResearchView ? "Groups where reported scores diverge across setups or reporting organizations." : "Flags cases where score differences may come from setup choices or different reporting sources."}

{summary && (
{summary.groups_with_variant_check} setup checks {summary.groups_with_cross_party_check} source checks
)}
{showNoCrossPartyNote && (
No third-party reports are available for cross-party comparison.
)} {(() => { const onlyOne = (variantGroups.length > 0 ? 1 : 0) + (crossPartyGroups.length > 0 ? 1 : 0) === 1 const sectionClass = onlyOne ? "" : "lg:grid lg:grid-cols-2 lg:gap-3" const itemsClass = onlyOne ? "grid gap-2 md:grid-cols-2" : "space-y-2" return (
{variantGroups.length > 0 && ( {variantGroups.slice(0, 8).map((group) => ( ))} )} {crossPartyGroups.length > 0 && ( {crossPartyGroups.slice(0, 8).map((group) => ( ))} )}
) })()}
) } function GroupList({ icon, title, count, children, itemsClassName = "space-y-2", }: { icon: "variant" | "cross-party" title: string count: number children: ReactNode itemsClassName?: string }) { const Icon = icon === "variant" ? GitCompareArrows : UsersRound return ( {children} ) } /** * Try to extract a human-readable label from a structured setup-field value. * Common shape from agentic evals: { additional_details: { agent_name, agent_framework } }. * Falls back to picking the first short string property, or null when the * value can't be summarized cleanly. */ function extractFriendlyLabel(value: unknown): string | null { if (value == null) return null if (typeof value === "string") return value.length > 60 ? null : value if (typeof value === "number" || typeof value === "boolean") return String(value) if (typeof value !== "object") return null const obj = value as Record const details = (obj.additional_details && typeof obj.additional_details === "object") ? (obj.additional_details as Record) : obj const agentName = typeof details.agent_name === "string" ? details.agent_name : null const agentFramework = typeof details.agent_framework === "string" ? details.agent_framework : null if (agentName) { return agentFramework && agentFramework !== agentName ? `${agentName} (${agentFramework})` : agentName } for (const key of ["name", "label", "id", "title"]) { const v = details[key] if (typeof v === "string" && v.length <= 60) return v } return null } function chipsForFieldValues(values: unknown[]): { label: string; raw: unknown }[] { const seen = new Set() const result: { label: string; raw: unknown }[] = [] for (const v of values) { const friendly = extractFriendlyLabel(v) const label = friendly ?? formatSignalValue(v) const truncated = label.length > 80 ? label.slice(0, 80) + "…" : label if (seen.has(truncated)) continue seen.add(truncated) result.push({ label: truncated, raw: v }) } return result } function DivergenceGroupItem({ modelRouteId, magnitude, threshold, fields, scoresByOrganization, }: { modelRouteId: string magnitude: number threshold: number fields: DifferingSetupField[] scoresByOrganization?: Record }) { const fieldLabels = fields.map((f) => formatFieldLabel(f.field)) const summarySentence = fieldLabels.length === 0 ? `Reported scores diverge by ${formatSignalNumber(magnitude)}, above the ${formatSignalNumber(threshold)} threshold. The setup difference is not labelled.` : `Reported scores diverge by ${formatSignalNumber(magnitude)} (threshold ${formatSignalNumber(threshold)}) because the runs differ on ${ fieldLabels.length === 1 ? fieldLabels[0] : fieldLabels.slice(0, -1).join(", ") + " and " + fieldLabels[fieldLabels.length - 1] }. The chips below show each variant.` return (
{modelRouteId}
Diverges by {formatSignalNumber(magnitude)} (threshold {formatSignalNumber(threshold)})
Jump to row
{fields.slice(0, 3).map((field) => { const chips = chipsForFieldValues(field.values).slice(0, 6) const overflow = field.values.length - chips.length return (
Differs by {formatFieldLabel(field.field)}
{chips.map((chip, idx) => { const friendly = extractFriendlyLabel(chip.raw) const tooltipBody = friendly ? formatSignalValue(chip.raw) : null const pill = ( {chip.label} ) return tooltipBody ? ( {tooltipBody} } > {pill} ) : ( {pill} ) })} {overflow > 0 && ( +{overflow} more )}
) })} {scoresByOrganization && Object.keys(scoresByOrganization).length > 0 && (
{Object.entries(scoresByOrganization).slice(0, 4).map(([org, score]) => ( {org}: {formatSignalNumber(score)} ))}
)}
) }