mirror of
https://github.com/wassname/moral-maps.git
synced 2026-09-22 13:20:36 +08:00
Publish score-all-options map refresh and HLE data
Co-Authored-By: PI[openai-codex] <288921227+claudypoo@users.noreply.github.com>
This commit is contained in:
co-authored by
PI[openai-codex]
parent
b444e1fcf7
commit
bdf593e0de
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Frontier LLMs on the World Values Survey</title>
|
||||
<title>Moral Maps: Where Do Frontier Models' Cultural Values Lie?</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -16,11 +16,11 @@ function Tooltip({ active, geometry, id = 'model-tooltip', panel }) {
|
||||
const release = active.provenance.release_created && <span>release {active.provenance.release_created}</span>;
|
||||
return <aside id={id} className="tooltip" style={{ left, top }} role="status">
|
||||
<strong>{active.name}</strong>
|
||||
{active.tooltipIndex != null && <span>Intelligence Index: {active.tooltipIndex.toFixed(2)}</span>}
|
||||
{active.tooltipScore != null && <span>HLE score: {active.tooltipScore.toFixed(3)}</span>}
|
||||
{panel === 'secular' && <span>Secular-Rational: {active.y.toFixed(3)}</span>}
|
||||
{panel === 'self-expression' && <span>Self-expression: {active.tooltipValue.toFixed(3)}</span>}
|
||||
{!panel && <><span>Self-expression/Survival: {active.x.toFixed(3)}</span><span>Traditional/Secular-Rational: {active.y.toFixed(3)}</span></>}
|
||||
{active.tooltipIndex == null && release}
|
||||
{active.tooltipScore == null && release}
|
||||
</aside>;
|
||||
}
|
||||
|
||||
@@ -57,12 +57,12 @@ function ordinaryLeastSquares(models, xValue, yValue) {
|
||||
function ReleaseScatter({ data, hidden, field, title, axisMode }) {
|
||||
const dated = useMemo(() => data.models.filter(model => Number.isFinite(Date.parse(model.provenance.release_created ?? '')))
|
||||
.toSorted((a, b) => a.provenance.release_created.localeCompare(b.provenance.release_created) || a.name.localeCompare(b.name)), [data]);
|
||||
const matched = useMemo(() => data.models.filter(model => Number.isFinite(model.provenance.intelligence_index))
|
||||
.toSorted((a, b) => a.provenance.intelligence_index - b.provenance.intelligence_index || a.name.localeCompare(b.name)), [data]);
|
||||
const matched = useMemo(() => data.models.filter(model => Number.isFinite(model.provenance.hle_score))
|
||||
.toSorted((a, b) => a.provenance.hle_score - b.provenance.hle_score || a.name.localeCompare(b.name)), [data]);
|
||||
const plotted = axisMode === 'release-date' ? dated : matched;
|
||||
const visible = plotted.filter(model => !hidden.has(model.family));
|
||||
const coordinate = model => field === 'x' ? -model.x : model.y;
|
||||
const xValue = model => axisMode === 'release-date' ? Date.parse(model.provenance.release_created) : model.provenance.intelligence_index;
|
||||
const xValue = model => axisMode === 'release-date' ? Date.parse(model.provenance.release_created) : model.provenance.hle_score;
|
||||
const fit = ordinaryLeastSquares(visible, xValue, coordinate);
|
||||
const [active, setActive] = useState(null);
|
||||
const width = 1200, height = 320, left = 96, right = 35, top = 42, bottom = 48;
|
||||
@@ -80,9 +80,9 @@ function ReleaseScatter({ data, hidden, field, title, axisMode }) {
|
||||
const frontier = useMemo(() => {
|
||||
if (axisMode !== 'release-date') return [];
|
||||
let high = -Infinity;
|
||||
return visible.filter(model => Number.isFinite(model.provenance.intelligence_index)).filter(model => {
|
||||
if (model.provenance.intelligence_index <= high) return false;
|
||||
high = model.provenance.intelligence_index;
|
||||
return visible.filter(model => Number.isFinite(model.provenance.hle_score)).filter(model => {
|
||||
if (model.provenance.hle_score <= high) return false;
|
||||
high = model.provenance.hle_score;
|
||||
return true;
|
||||
});
|
||||
}, [axisMode, visible]);
|
||||
@@ -109,7 +109,7 @@ function ReleaseScatter({ data, hidden, field, title, axisMode }) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found) throw new Error(`no frontier label location for ${model.name}`);
|
||||
if (!found) found = box(anchor.x, Math.max(bounds.top + size.height / 2, anchor.y - size.height), size.width, size.height);
|
||||
placement[model.name] = { ...found, anchor };
|
||||
taken.push(found);
|
||||
}
|
||||
@@ -118,14 +118,14 @@ function ReleaseScatter({ data, hidden, field, title, axisMode }) {
|
||||
const activate = (model, event) => {
|
||||
const box = event.currentTarget.closest('.scatter-shell').getBoundingClientRect();
|
||||
const point = event.currentTarget.getBoundingClientRect();
|
||||
setActive({ ...model, tooltipValue: coordinate(model), tooltipIndex: axisMode === 'capability' ? model.provenance.intelligence_index : null, tooltipLeft: `${Math.min(82, (point.left - box.left) / box.width * 100)}%`, tooltipTop: `${Math.min(78, (point.top - box.top) / box.height * 100)}%` });
|
||||
setActive({ ...model, tooltipValue: coordinate(model), tooltipScore: axisMode === 'capability' ? model.provenance.hle_score : null, tooltipLeft: `${Math.min(82, (point.left - box.left) / box.width * 100)}%`, tooltipTop: `${Math.min(78, (point.top - box.top) / box.height * 100)}%` });
|
||||
};
|
||||
return <section className="release-panel" data-coordinate={field} aria-labelledby={`${panelId}-heading`}>
|
||||
<h2 id={`${panelId}-heading`}>{title}</h2>
|
||||
<div className="scatter-shell">
|
||||
<svg id={`${panelId}-svg`} viewBox={`0 0 ${width} ${height}`} role="img" aria-labelledby={`${panelId}-svg-title ${panelId}-svg-desc`} data-axis-mode={axisMode} data-panel-model-count={visible.length} data-omitted-model-count={data.models.length - plotted.length} data-fit-n={fit?.n ?? 0} data-fit-r2={fit?.r2 ?? ''} data-fit-slope={fit?.slope ?? ''}>
|
||||
<title id={`${panelId}-svg-title`}>{title}</title>
|
||||
<desc id={`${panelId}-svg-desc`}>Scatter plot with {axisMode === 'release-date' ? 'release date' : 'Artificial Analysis Intelligence Index'} on the horizontal axis and {yDirection.join(' to ')} increasing upward on the vertical axis. {visible.length} of {plotted.length} matched models are visible from {visibleFamilyNames(Object.groupBy(data.models, model => model.family), hidden).join(', ') || 'no families'}; {data.models.length - plotted.length} plotted models lack this x value and are omitted only here. Each white-ring logo mark is a model. {fit ? `The thin line is an ordinary least squares descriptive fit to the ${fit.n} currently visible matched models${fit.r2 === null ? '; R squared is unavailable because the y values are constant' : `; R squared is ${fit.r2.toFixed(2)}`}.` : 'The fit is hidden because fewer than two distinct x values are visible.'} Hover or keyboard focus a mark for model-specific details.</desc>
|
||||
<desc id={`${panelId}-svg-desc`}>Scatter plot with {axisMode === 'release-date' ? 'release date' : 'HLE score'} on the horizontal axis and {yDirection.join(' to ')} increasing upward on the vertical axis. {visible.length} of {plotted.length} matched models are visible from {visibleFamilyNames(Object.groupBy(data.models, model => model.family), hidden).join(', ') || 'no families'}; {data.models.length - plotted.length} plotted models lack this x value and are omitted only here. Each white-ring logo mark is a model. {fit ? `The thin line is an ordinary least squares descriptive fit to the ${fit.n} currently visible matched models${fit.r2 === null ? '; R squared is unavailable because the y values are constant' : `; R squared is ${fit.r2.toFixed(2)}`}.` : 'The fit is hidden because fewer than two distinct x values are visible.'} Hover or keyboard focus a mark for model-specific details.</desc>
|
||||
<defs><marker id={`${panelId}-arrow`} markerWidth="8" markerHeight="8" refX="6" refY="3" orient="auto"><path d="M0,0 L0,6 L6,3 z" className="scatter-arrow" /></marker><clipPath id={`${panelId}-fit-clip`}><rect x={left} y={top} width={width - left - right} height={height - top - bottom} /></clipPath></defs>
|
||||
<rect className="canvas" width={width} height={height} />
|
||||
<g className="scatter-grid">{Array.from({ length: 5 }, (_, index) => <line key={index} x1={left} x2={width - right} y1={top + index * (height - top - bottom) / 4} y2={top + index * (height - top - bottom) / 4} />)}</g>
|
||||
@@ -138,13 +138,13 @@ function ReleaseScatter({ data, hidden, field, title, axisMode }) {
|
||||
<text className="scatter-tick" x={left - 8} y={height - bottom} textAnchor="end">{minValue.toFixed(2)}</text>
|
||||
{fitEnd && <g className="release-fit" data-fit-n={fit.n} data-fit-r2={fit.r2 ?? ''}><line clipPath={`url(#${panelId}-fit-clip)`} x1={fitEnd[0][0]} y1={fitEnd[0][1]} x2={fitEnd[1][0]} y2={fitEnd[1][1]} /> <text x={left + 6} y={top + 13}>OLS, n={fit.n}, R² {fit.r2 === null ? 'unavailable' : fit.r2.toFixed(2)}</text></g>}
|
||||
{!fit && <text className="release-fit-unavailable" x={left + 6} y={top + 13}>Fit unavailable: fewer than two release dates</text>}
|
||||
{plotted.map(model => <g key={model.name} className="release-mark" data-family={model.family} data-release-model={model.name} data-release-date={model.provenance.release_created} data-capability-score={model.provenance.intelligence_index} data-coordinate-value={coordinate(model)} display={hidden.has(model.family) ? 'none' : 'inline'}
|
||||
tabIndex="0" role="button" aria-label={`${model.name}, ${axisMode === 'release-date' ? model.provenance.release_created : `Intelligence Index ${model.provenance.intelligence_index}`}`} aria-describedby={tooltipId}
|
||||
{plotted.map(model => <g key={model.name} className="release-mark" data-family={model.family} data-release-model={model.name} data-release-date={model.provenance.release_created} data-hle-score={model.provenance.hle_score} data-coordinate-value={coordinate(model)} display={hidden.has(model.family) ? 'none' : 'inline'}
|
||||
tabIndex="0" role="button" aria-label={`${model.name}, ${axisMode === 'release-date' ? model.provenance.release_created : `HLE score ${model.provenance.hle_score}`}`} aria-describedby={tooltipId}
|
||||
onPointerEnter={event => activate(model, event)} onPointerLeave={() => setActive(null)} onFocus={event => activate(model, event)} onBlur={() => setActive(null)}>
|
||||
<circle className="model-ring" cx={plotX(xValue(model))} cy={valueY(coordinate(model))} r="8" stroke={model.color} />
|
||||
<image href={`${LOGO_ROOT}${data.logos[model.family]}`} x={plotX(xValue(model)) - 5} y={valueY(coordinate(model)) - 5} width="10" height="10" preserveAspectRatio="xMidYMid meet" aria-hidden="true" focusable="false" />
|
||||
</g>)}
|
||||
{axisMode === 'release-date' && frontier.map(model => <g key={`frontier-${model.name}`} className="frontier-label" data-frontier-model={model.name}><line x1={frontierPlacement[model.name].anchor.x} y1={frontierPlacement[model.name].anchor.y} x2={frontierPlacement[model.name].cx} y2={frontierPlacement[model.name].cy} /><text x={frontierPlacement[model.name].cx} y={frontierPlacement[model.name].cy + 4} textAnchor="middle">{model.name}</text></g>)}
|
||||
{axisMode === 'release-date' && frontier.map(model => <g key={`frontier-${model.name}`} className="frontier-label" data-frontier-model={model.name}><text x={frontierPlacement[model.name].cx} y={frontierPlacement[model.name].cy + 4} textAnchor="middle">{model.name}</text></g>)}
|
||||
</svg>
|
||||
<Tooltip active={active} geometry={null} id={tooltipId} panel={field === 'y' ? 'secular' : 'self-expression'} />
|
||||
</div>
|
||||
@@ -159,7 +159,7 @@ function ReleaseScatters({ data, hidden, axisMode, setAxisMode }) {
|
||||
<option value="release-date">Release date</option>
|
||||
<option value="capability">{data.capability_x.label}</option>
|
||||
</select></label>
|
||||
<span><a href={data.capability_x.source_url}>{data.capability_x.label}</a>, saved {data.capability_x.fetched_utc.slice(0, 10)}. {axisMode === 'capability' ? `${data.capability_x.matched_models} matched, ${data.models.length - data.capability_x.matched_models} omitted.` : 'Release-date labels mark running Intelligence Index highs among shown mapped models.'}</span>
|
||||
<span><a href={data.capability_x.source_url}>Artificial Analysis HLE score</a>, saved {data.capability_x.fetched_utc.slice(0, 10)}. {axisMode === 'capability' ? `${data.capability_x.matched_models} matched, ${data.models.length - data.capability_x.matched_models} omitted.` : 'Release-date labels mark running HLE score highs among shown mapped models.'}</span>
|
||||
</div>
|
||||
<ReleaseScatter data={data} hidden={hidden} field="y" axisMode={axisMode} title={`${xLabel} vs Secular-Rational`} />
|
||||
<ReleaseScatter data={data} hidden={hidden} field="x" axisMode={axisMode} title={`${xLabel} vs Self-expression`} />
|
||||
@@ -201,7 +201,7 @@ function Map({ data }) {
|
||||
})}</section>
|
||||
<div className="chart-shell">
|
||||
<svg viewBox={`0 0 ${VIEW.width} ${VIEW.height}`} role="img" aria-labelledby="map-svg-title map-svg-desc" data-median-x={data.median.x} data-median-y={data.median.y} data-visible-model-count={visibleModelCount}>
|
||||
<title id="map-svg-title">Frontier LLMs on the World Values Survey</title>
|
||||
<title id="map-svg-title">Moral Maps: Where Do Frontier Models' Cultural Values Lie?</title>
|
||||
<desc id="map-svg-desc">World Values Survey cultural map. Horizontal direction runs from Self-expression on the left to Survival on the right. Vertical direction runs from Traditional below to Secular-Rational above. Coloured dots are selected WVS countries, outlines are cultural regions, and white-ring logo marks are models. {visibleModelCount} model marks are visible from {visibleFamilies.join(', ') || 'no families'}. Use the family controls to hide marks and labels. Hover or keyboard focus a model for model-specific details.</desc>
|
||||
<defs><marker id="arrow" markerWidth="8" markerHeight="8" refX="6" refY="3" orient="auto"><path d="M0,0 L0,6 L6,3 z" className="arrow" /></marker></defs>
|
||||
<rect className="canvas" width={VIEW.width} height={VIEW.height} />
|
||||
@@ -213,8 +213,8 @@ function Map({ data }) {
|
||||
{data.zone_hulls.map(zone => <text key={zone.name} className="zone-label" x={labels[`zone:${zone.name}`].cx} y={labels[`zone:${zone.name}`].cy + 5} textAnchor="middle" fill={zone.color}>{zone.name}</text>)}
|
||||
{Object.entries(groups).map(([family, models]) => <g key={family} data-family={family} display={hidden.has(family) ? 'none' : 'inline'}>{models.map(model => <ModelMarker key={model.name} model={model} placement={labels} geometry={geometry} setActive={setActive} clearActive={clearActive} markerRef={model.name === focusName ? focusRef : null} logo={data.logos[model.family]} />)}</g>)}
|
||||
<g className="poles"><line x1={xMedian} y1="62" x2={xMedian} y2={geometry.bounds.top} markerEnd="url(#arrow)" /><line x1={xMedian} y1={geometry.bounds.bottom} x2={xMedian} y2="838" markerEnd="url(#arrow)" /><line x1="64" y1={yMedian} x2={geometry.bounds.left} y2={yMedian} markerEnd="url(#arrow)" /><line x1={geometry.bounds.right} y1={yMedian} x2="1184" y2={yMedian} markerEnd="url(#arrow)" /><text x={xMedian} y="40" textAnchor="middle">{data.axis.y[1]}</text><text x={xMedian} y="870" textAnchor="middle">{data.axis.y[0]}</text><text x="25" y={yMedian + 7}>{data.axis.x[0]}</text><text x="1136" y={yMedian + 7} textAnchor="end">{data.axis.x[1]}</text></g>
|
||||
<text className="map-title" x={geometry.bounds.left + 8} y={geometry.bounds.bottom - 34}>{data.title.split('\n').map((line, index) => <tspan key={line} x={geometry.bounds.left + 8} dy={index ? 17 : 0}>{line}</tspan>)}</text>
|
||||
<text className="map-note" x={geometry.bounds.right - 8} y={geometry.bounds.bottom - 20} textAnchor="end">{data.note.split('\n').map((line, index) => <tspan key={line} x={geometry.bounds.right - 8} dy={index ? 11 : 0}>{line}</tspan>)}</text>
|
||||
<text className="map-title" x={geometry.bounds.left + 8} y={geometry.bounds.bottom - 54}>{data.title.split('\n').map((line, index) => <tspan key={line} x={geometry.bounds.left + 8} dy={index ? 17 : 0}>{line}</tspan>)}</text>
|
||||
<text className="map-note" x={geometry.bounds.left + 8} y={geometry.bounds.bottom - 34} textAnchor="start">{data.note.split('\n').map((line, index) => <tspan key={line} x={geometry.bounds.left + 8} dy={index ? 11 : 0}>{line}</tspan>)}</text>
|
||||
</svg>
|
||||
<Tooltip active={active} geometry={geometry} />
|
||||
</div>
|
||||
@@ -228,7 +228,7 @@ function App() {
|
||||
const [data, setData] = useState(null);
|
||||
useEffect(() => { fetch('wvs/wvs_map_data.json').then(response => response.json()).then(setData); }, []);
|
||||
return <main>
|
||||
<h1>How do AI models score on human values surveys? Which culture are they most similar to? Is it changing over time?</h1>
|
||||
<h1>Moral Maps: Where Do Frontier Models' Cultural Values Lie?</h1>
|
||||
<p className="lede">We start with the <a href="https://www.worldvaluessurvey.org/">World Values Survey</a>, a map of human values across about ninety countries.</p>
|
||||
{data && <Map data={data} />}
|
||||
</main>;
|
||||
|
||||
+47
-13
@@ -51,10 +51,12 @@ def main() -> None:
|
||||
if chrome is None:
|
||||
raise RuntimeError("google-chrome is required for the Playwright UAT")
|
||||
data = json.loads((ROOT / "docs/wvs/wvs_map_data.json").read_text())
|
||||
assert data["schema"] == 2
|
||||
assert data["capability_x"]["status"] == "blocked"
|
||||
assert data["capability_x"]["source_url"] == "https://artificialanalysis.ai/models"
|
||||
assert len(data["models"]) == 64
|
||||
assert data["schema"] == 3
|
||||
assert data["capability_x"]["status"] == "available"
|
||||
assert data["capability_x"]["source_url"] == "https://artificialanalysis.ai/evaluations/humanitys-last-exam"
|
||||
assert data["capability_x"]["fetched_utc"] == "2026-09-17T10:13:20Z"
|
||||
capability_matched = [model for model in data["models"] if model["provenance"]["hle_score"] is not None]
|
||||
assert len(capability_matched) == data["capability_x"]["matched_models"] == 46
|
||||
assert len(data["countries"]) == 90
|
||||
assert len(data["zone_hulls"]) == 4
|
||||
assert len(data["latest_by_family"]) == 13
|
||||
@@ -79,20 +81,23 @@ def main() -> None:
|
||||
|
||||
screenshot(page, f"{BASE}/", "wvs_react_root_playwright_default.png")
|
||||
page.wait_for_selector("svg .model-mark")
|
||||
assert page.title() == "Frontier LLMs on the World Values Survey"
|
||||
assert page.title() == "Moral Maps: Where Do Frontier Models' Cultural Values Lie?"
|
||||
assert page.locator("html").get_attribute("lang") == "en"
|
||||
assert page.locator('meta[name="viewport"]').count() == 1
|
||||
assert page.locator("h1").inner_text().startswith("How do AI models score on human values surveys?")
|
||||
assert page.locator("h1").inner_text() == "Moral Maps: Where Do Frontier Models' Cultural Values Lie?"
|
||||
assert page.locator(".lede").inner_text() == "We start with the World Values Survey, a map of human values across about ninety countries."
|
||||
visible_copy = page.locator("main").inner_text()
|
||||
for absent in ("React/SVG rendering", "Historical coordinates", "Dated releases only"):
|
||||
assert absent not in visible_copy
|
||||
assert "Artificial Analysis Intelligence Index, unavailable pending redistribution permission" in visible_copy
|
||||
assert data["capability_x"]["blocker"] in visible_copy
|
||||
assert "Artificial Analysis HLE score" in visible_copy
|
||||
assert "redistribution permission" not in visible_copy
|
||||
capability_option = page.get_by_label("Release-panel x axis").locator('option[value="capability"]')
|
||||
assert capability_option.get_attribute("disabled") is not None
|
||||
assert capability_option.get_attribute("disabled") is None
|
||||
assert page.locator('.release-axis-selector a[href="https://artificialanalysis.ai/evaluations/humanitys-last-exam"]').count() == 1
|
||||
assert "saved 2026-09-17" in page.locator(".release-axis-selector").inner_text()
|
||||
layout_order = page.locator("main > *").evaluate_all("nodes => nodes.map(node => node.className.baseVal || node.className || node.tagName)")
|
||||
assert layout_order.index("chart-shell") < layout_order.index("map-explanation") < layout_order.index("release-panels") < layout_order.index("caption")
|
||||
assert page.locator(".release-panels .release-axis-selector").count() == 1
|
||||
assert "weak descriptive correlations" in page.locator(".caption").inner_text()
|
||||
assert "code and records" in page.locator(".caption").inner_text()
|
||||
|
||||
@@ -100,8 +105,9 @@ def main() -> None:
|
||||
svg_description(page, "svg[data-median-x]", "map-svg-title", "map-svg-desc")
|
||||
assert page.locator("path.zone").count() == 4
|
||||
assert page.locator("polygon.zone").count() == 0
|
||||
assert page.locator(".map-note").get_attribute("text-anchor") == "end"
|
||||
assert float(page.locator(".map-note").get_attribute("x")) > float(page.locator(".map-title").get_attribute("x"))
|
||||
assert page.locator(".map-note").get_attribute("text-anchor") == "start"
|
||||
assert float(page.locator(".map-note").get_attribute("x")) == float(page.locator(".map-title").get_attribute("x"))
|
||||
assert float(page.locator(".map-note").get_attribute("y")) > float(page.locator(".map-title").get_attribute("y"))
|
||||
assert float(map_svg.get_attribute("data-median-x")) == data["median"]["x"]
|
||||
assert float(map_svg.get_attribute("data-median-y")) == data["median"]["y"]
|
||||
models_dom = page.locator(".model-mark").evaluate_all(
|
||||
@@ -123,6 +129,10 @@ def main() -> None:
|
||||
svg_description(page, '.release-panel[data-coordinate="y"] svg', "release-y-svg-title", "release-y-svg-desc")
|
||||
svg_description(page, '.release-panel[data-coordinate="x"] svg', "release-x-svg-title", "release-x-svg-desc")
|
||||
assert page.locator(".release-mark").count() == len(dated) * 2
|
||||
assert page.locator(".frontier-label").count() > 0
|
||||
frontier_boxes = page.locator('.frontier-label text').evaluate_all("nodes => nodes.map(node => { const b = node.getBBox(); return [b.x, b.y, b.width, b.height]; })")
|
||||
assert all(x >= 96 and y >= 42 and x + width <= 1165 and y + height <= 272 for x, y, width, height in frontier_boxes)
|
||||
assert all(a[0] + a[2] <= b[0] or b[0] + b[2] <= a[0] or a[1] + a[3] <= b[1] or b[1] + b[3] <= a[1] for index, a in enumerate(frontier_boxes) for b in frontier_boxes[index + 1:])
|
||||
for grok_name in grok_dates:
|
||||
assert page.locator(f'[data-release-model="{grok_name}"]').count() == 2
|
||||
self_expression_panel = page.locator('.release-panel[data-coordinate="x"]')
|
||||
@@ -144,6 +154,27 @@ def main() -> None:
|
||||
assert panel.locator(".release-fit line").count() == 1
|
||||
page.screenshot(path=OUT / "wvs_react_playwright_release_fit_all_families.png", full_page=True)
|
||||
|
||||
page.get_by_label("Release-panel x axis").select_option("capability")
|
||||
assert page.locator('.release-panel[data-coordinate="y"] svg').get_attribute("data-axis-mode") == "capability"
|
||||
assert page.locator('.release-mark').count() == len(capability_matched) * 2
|
||||
assert all(int(panel.locator("svg").get_attribute("data-fit-n")) == len(capability_matched) for panel in page.locator(".release-panel").all())
|
||||
assert all(int(panel.locator("svg").get_attribute("data-omitted-model-count")) == len(data["models"]) - len(capability_matched) for panel in page.locator(".release-panel").all())
|
||||
page.screenshot(path=OUT / "wvs_react_playwright_capability_all_families.png", full_page=True)
|
||||
page.get_by_role("button", name="qwen", exact=True).click()
|
||||
assert all(int(panel.locator("svg").get_attribute("data-fit-n")) == sum(model["family"] != "qwen" for model in capability_matched) for panel in page.locator(".release-panel").all())
|
||||
page.screenshot(path=OUT / "wvs_react_playwright_capability_qwen_hidden.png", full_page=True)
|
||||
page.get_by_role("button", name="qwen", exact=True).click()
|
||||
for family in sorted(families - {"gpt"}):
|
||||
page.get_by_role("button", name=family, exact=True).click()
|
||||
page.wait_for_timeout(100)
|
||||
gpt_capability = sum(model["family"] == "gpt" for model in capability_matched)
|
||||
gpt_fit_n = [int(panel.locator("svg").get_attribute("data-fit-n")) for panel in page.locator(".release-panel").all()]
|
||||
assert gpt_fit_n == [gpt_capability, gpt_capability], (gpt_fit_n, gpt_capability)
|
||||
page.screenshot(path=OUT / "wvs_react_playwright_capability_gpt_only.png", full_page=True)
|
||||
for family in sorted(families - {"gpt"}):
|
||||
page.get_by_role("button", name=family, exact=True).click()
|
||||
page.get_by_label("Release-panel x axis").select_option("release-date")
|
||||
|
||||
page.get_by_role("button", name="qwen", exact=True).click()
|
||||
page.wait_for_timeout(100)
|
||||
assert map_svg.locator(':scope > g[data-family="qwen"]').get_attribute("display") == "none"
|
||||
@@ -243,8 +274,11 @@ def main() -> None:
|
||||
"qwen_toggle": "clicked, map and dated-panel family groups hidden, country coordinates invariant",
|
||||
"tooltip": "map and release-panel pointer hover and focus show panel-specific model name, coordinate, and release date only",
|
||||
"release_panels": {"panels": 2, "dated_models": len(dated), "date_order": "DOM order verified; both catalog-dated Grok models rendered in each panel",
|
||||
"ols": "line, n, and R squared recompute from currently visible dated models; the Self-expression panel renders negative stored x, so upward means Self-expression; fewer than two distinct dates hide the fit"},
|
||||
"copy": "one short linked WVS sentence above the map; history and axes below it; weak descriptive-fit method and code link below the release panels; capability selector remains visible but scores are absent pending redistribution permission",
|
||||
"ols": "line, n, and R squared recompute from currently visible matched models; the Self-expression panel renders negative stored x, so upward means Self-expression; fewer than two distinct x values hide the fit"},
|
||||
"capability_panels": {"matched_models": len(capability_matched), "omitted_models": len(data["models"]) - len(capability_matched),
|
||||
"source": data["capability_x"]["source_url"], "fetched_utc": data["capability_x"]["fetched_utc"],
|
||||
"selector": "enabled; all-family and Qwen-hidden fits checked"},
|
||||
"copy": "one short linked WVS sentence above the map; history and axes below it; source credit beside the enabled lower-panel selector; weak descriptive-fit method and code link below the release panels",
|
||||
"hashes": hashes,
|
||||
}, indent=2))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user