Skip to content

Commit 633e96d

Browse files
authored
docs: refresh top-models telemetry figure and add uv generator (#734)
1 parent 4fe479c commit 633e96d

6 files changed

Lines changed: 299 additions & 3 deletions

File tree

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
[![CI](https://github.com/NVIDIA-NeMo/DataDesigner/actions/workflows/ci.yml/badge.svg)](https://github.com/NVIDIA-NeMo/DataDesigner/actions/workflows/ci.yml)
44
[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
5-
[![Python 3.10 - 3.14](https://img.shields.io/badge/🐍_Python-3.10_|_3.11_|_3.12_|_3.13_|_3.14-blue.svg)](https://www.python.org/downloads/) [![NeMo Microservices](https://img.shields.io/badge/NeMo-Microservices-76b900)](https://docs.nvidia.com/nemo/microservices/latest/index.html) [![Code](https://img.shields.io/badge/Code-Documentation-8A2BE2.svg)](https://nvidia-nemo.github.io/DataDesigner/) ![Tokens](https://img.shields.io/badge/400+_Billion-Tokens_Generated-76b900.svg?logo=nvidia&logoColor=white)
5+
[![Python 3.10 - 3.14](https://img.shields.io/badge/🐍_Python-3.10_|_3.11_|_3.12_|_3.13_|_3.14-blue.svg)](https://www.python.org/downloads/) [![NeMo Microservices](https://img.shields.io/badge/NeMo-Microservices-76b900)](https://docs.nvidia.com/nemo/microservices/latest/index.html) [![Code](https://img.shields.io/badge/Code-Documentation-8A2BE2.svg)](https://nvidia-nemo.github.io/DataDesigner/) ![Tokens](https://img.shields.io/badge/2.6T+-Tokens_Processed-76b900.svg?logo=nvidia&logoColor=white)
66

77
**Generate high-quality synthetic datasets from scratch or using your own seed data.**
88

@@ -153,11 +153,11 @@ Disable with `NEMO_TELEMETRY_ENABLED=false`. **[More details →](#telemetry-and
153153

154154
### Top models (YTD)
155155

156-
Aggregate model usage across synthetic data generation jobs, year-to-date 1/1/2026–5/1/2026:
156+
Aggregate model usage across synthetic data generation jobs, year-to-date 1/1/2026–6/1/2026:
157157

158158
![Top models used for synthetic data generation](docs/images/top-models.png)
159159

160-
_Last updated on May 1, 2026_
160+
_Last updated on June 1, 2026_
161161

162162
---
163163

docs/images/top-models.png

141 KB
Loading
Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
# /// script
4+
# requires-python = ">=3.10"
5+
# dependencies = [
6+
# "matplotlib==3.9.4",
7+
# ]
8+
# ///
9+
"""Regenerate the "Top Model Usage" telemetry figure.
10+
11+
Renders the ranked input-vs-output token breakdown shown in the README's
12+
"Top models (YTD)" section, styled to match the Data Designer devnote charts
13+
(near-black canvas, NVIDIA-green duotone). The same PNG is written to both
14+
tracked copies so the README and Fern docs site stay in sync:
15+
16+
docs/images/top-models.png (rendered by the README)
17+
fern/images/top-models.png (Fern's /images/* mirror)
18+
19+
The source telemetry export lives at docs/scripts/top-model-usage.csv with
20+
columns: model name, input (context) tokens, output (generated) tokens, plus a
21+
trailing "Other" aggregate row. Drop in a fresh export to refresh the figure.
22+
23+
Run:
24+
# Regenerate from the committed CSV (zero args)
25+
uv run docs/scripts/generate_top_models_figure.py
26+
27+
# Refresh from a new telemetry export
28+
uv run docs/scripts/generate_top_models_figure.py --csv ~/Downloads/new-export.csv
29+
30+
# Options
31+
uv run docs/scripts/generate_top_models_figure.py --help
32+
"""
33+
34+
from __future__ import annotations
35+
36+
import argparse
37+
import csv
38+
import shutil
39+
from pathlib import Path
40+
41+
import matplotlib.pyplot as plt
42+
from matplotlib import rcParams
43+
from matplotlib.ticker import FuncFormatter, MaxNLocator
44+
45+
# Repo root is two levels up from docs/scripts/.
46+
REPO_ROOT = Path(__file__).resolve().parents[2]
47+
DEFAULT_CSV = REPO_ROOT / "docs" / "scripts" / "top-model-usage.csv"
48+
# Tracked copies of the figure; first entry is the canonical render target.
49+
# docs/images/ is what the README renders; fern/images/ is Fern's mirror for
50+
# /images/* references.
51+
TARGETS = (
52+
REPO_ROOT / "docs" / "images" / "top-models.png",
53+
REPO_ROOT / "fern" / "images" / "top-models.png",
54+
)
55+
56+
# ---------------------------------------------------------------- palette ----
57+
BG = "#0E0E0E" # near-black canvas (matches DD devnote charts)
58+
GREEN = "#76B900" # NVIDIA green -> input (context) tokens
59+
LIME = "#C5E86C" # light NVIDIA-tint green -> output (generated) tokens
60+
WHITE = "#FFFFFF"
61+
SUBTLE = "#9A9A9A"
62+
AXIS = "#B8B8B8"
63+
MODELNAME = "#ECECEC"
64+
GRID = "#FFFFFF"
65+
SPINE = "#4A4A4A"
66+
INK = "#0E0E0E" # dark ink for labels sitting on bright bars
67+
68+
B = 1e9 # render token counts in billions
69+
70+
71+
def load_rows(csv_path: Path) -> list[tuple[str, float, float]]:
72+
"""Parse the telemetry CSV into (name, input_tokens, output_tokens) rows."""
73+
rows: list[tuple[str, float, float]] = []
74+
with csv_path.open(newline="", encoding="utf-8-sig") as fh:
75+
reader = csv.reader(fh)
76+
next(reader) # header
77+
for name, inp, out in reader:
78+
rows.append((name, float(inp.replace(",", "")), float(out.replace(",", ""))))
79+
return rows
80+
81+
82+
def configure_matplotlib() -> None:
83+
"""Pin rendering to deterministic settings so the asset is reproducible.
84+
85+
Forces the Agg backend and matplotlib's bundled DejaVu Sans face rather than
86+
opportunistically selecting a system Helvetica/Arial. Combined with the
87+
pinned matplotlib version in the script metadata, this keeps the checked-in
88+
PNG byte-reproducible across machines and CI.
89+
"""
90+
plt.switch_backend("Agg")
91+
rcParams["font.family"] = "DejaVu Sans"
92+
rcParams["font.size"] = 13
93+
94+
95+
def fmt(v: float) -> str:
96+
"""Compact billions/trillions label."""
97+
if v >= 1e12:
98+
return f"{v / 1e12:.2f}T"
99+
return f"{v / 1e9:.0f}B"
100+
101+
102+
def render(rows: list[tuple[str, float, float]], out_path: Path) -> None:
103+
"""Render the ranked stacked-bar figure to out_path."""
104+
# Split the "Other" aggregate out; sort named models by total descending.
105+
other = next((r for r in rows if r[0].lower() == "other"), None)
106+
models = [r for r in rows if r[0].lower() != "other"]
107+
models.sort(key=lambda r: r[1] + r[2], reverse=True)
108+
109+
n = len(models)
110+
ypos = list(range(n, 0, -1)) # n, n-1, ... 1 (top -> down)
111+
labels = [m[0] for m in models]
112+
inputs = [m[1] for m in models]
113+
outputs = [m[2] for m in models]
114+
115+
if other is not None:
116+
ypos.append(-0.6) # gap below the named models
117+
labels.append("Other models")
118+
inputs.append(other[1])
119+
outputs.append(other[2])
120+
121+
fig, ax = plt.subplots(figsize=(14.5, 9.2), dpi=200)
122+
fig.patch.set_facecolor(BG)
123+
ax.set_facecolor(BG)
124+
125+
xmax = max(i + o for i, o in zip(inputs, outputs)) / B
126+
bar_h = 0.62
127+
128+
for idx, (y, inp, out) in enumerate(zip(ypos, inputs, outputs)):
129+
is_other = other is not None and idx == len(ypos) - 1
130+
a = 0.45 if is_other else 1.0
131+
132+
ax.barh(y, inp / B, height=bar_h, color=GREEN, alpha=a, zorder=3, edgecolor=BG, linewidth=1.2)
133+
ax.barh(y, out / B, height=bar_h, left=inp / B, color=LIME, alpha=a, zorder=3, edgecolor=BG, linewidth=1.2)
134+
135+
total = (inp + out) / B
136+
ax.text(
137+
total + xmax * 0.008,
138+
y,
139+
fmt(inp + out),
140+
va="center",
141+
ha="left",
142+
color=SUBTLE if is_other else WHITE,
143+
fontweight="bold",
144+
fontsize=13.5,
145+
zorder=5,
146+
)
147+
148+
# In-segment value labels only where the segment is wide enough.
149+
if inp / B > xmax * 0.085:
150+
ax.text(
151+
(inp / B) / 2,
152+
y,
153+
fmt(inp),
154+
va="center",
155+
ha="center",
156+
color=INK,
157+
fontweight="bold",
158+
fontsize=11.5,
159+
alpha=a,
160+
zorder=5,
161+
)
162+
if out / B > xmax * 0.085:
163+
ax.text(
164+
inp / B + (out / B) / 2,
165+
y,
166+
fmt(out),
167+
va="center",
168+
ha="center",
169+
color=INK,
170+
fontweight="bold",
171+
fontsize=11.5,
172+
alpha=a,
173+
zorder=5,
174+
)
175+
176+
# ------------------------------------------------------------- axes -----
177+
ax.set_yticks(ypos)
178+
ax.set_yticklabels(labels, fontsize=12.5)
179+
is_other_flags = [False] * n + ([True] if other else [])
180+
for tick, is_other in zip(ax.get_yticklabels(), is_other_flags):
181+
tick.set_color(SUBTLE if is_other else MODELNAME)
182+
if is_other:
183+
tick.set_fontstyle("italic")
184+
185+
ax.set_xlim(0, xmax * 1.13)
186+
ax.set_ylim(-1.3, n + 0.8)
187+
188+
# Derive ticks from the data so the axis stays sane as totals grow; fmt()
189+
# promotes B -> T automatically, so the labels never need hand-editing.
190+
ax.xaxis.set_major_locator(MaxNLocator(nbins=8, steps=[1, 2, 2.5, 5, 10]))
191+
ax.xaxis.set_major_formatter(FuncFormatter(lambda v, _pos: "0" if v <= 0 else fmt(v * B)))
192+
ax.tick_params(axis="y", length=0, pad=10)
193+
ax.tick_params(axis="x", colors=AXIS, length=0, pad=8, labelsize=11)
194+
ax.set_xlabel("Tokens processed", color=AXIS, fontsize=12.5, labelpad=12)
195+
196+
ax.xaxis.grid(True, color=GRID, alpha=0.07, linewidth=1, zorder=0)
197+
ax.set_axisbelow(True)
198+
for s in ("top", "right"):
199+
ax.spines[s].set_visible(False)
200+
for s in ("bottom", "left"):
201+
ax.spines[s].set_color(SPINE)
202+
ax.spines[s].set_linewidth(1.0)
203+
204+
# ---------------------------------------------------------- titling -----
205+
fig.subplots_adjust(left=0.235, right=0.965, top=0.83, bottom=0.085)
206+
# Signature DD green left-accent rule (mirrors the .devnote-dek element).
207+
ax.add_patch(
208+
plt.Rectangle(
209+
(-0.018, 1.045),
210+
0.006,
211+
0.135,
212+
transform=ax.transAxes,
213+
facecolor=GREEN,
214+
edgecolor="none",
215+
clip_on=False,
216+
zorder=6,
217+
)
218+
)
219+
ax.text(
220+
0.012,
221+
1.145,
222+
"Top Model Usage",
223+
transform=ax.transAxes,
224+
color=WHITE,
225+
fontweight="bold",
226+
fontsize=26,
227+
ha="left",
228+
va="bottom",
229+
)
230+
ax.text(
231+
0.012,
232+
1.07,
233+
"Context vs. generated tokens across the most-used models",
234+
transform=ax.transAxes,
235+
color=SUBTLE,
236+
fontsize=13.5,
237+
ha="left",
238+
va="bottom",
239+
)
240+
241+
# Manual legend, top-right of the plotting area.
242+
leg_x, leg_y = 0.99, 1.115
243+
legend = [(GREEN, "Input · context tokens"), (LIME, "Output · generated tokens")]
244+
for i, (c, lbl) in enumerate(legend):
245+
yy = leg_y - i * 0.052
246+
ax.add_patch(
247+
plt.Rectangle(
248+
(leg_x - 0.205, yy - 0.012),
249+
0.022,
250+
0.026,
251+
transform=ax.transAxes,
252+
facecolor=c,
253+
edgecolor="none",
254+
clip_on=False,
255+
zorder=6,
256+
)
257+
)
258+
ax.text(leg_x - 0.172, yy, lbl, transform=ax.transAxes, color=MODELNAME, fontsize=12, ha="left", va="center")
259+
260+
out_path.parent.mkdir(parents=True, exist_ok=True)
261+
fig.savefig(out_path, facecolor=BG, dpi=200, bbox_inches="tight", pad_inches=0.25)
262+
plt.close(fig)
263+
264+
265+
def main() -> None:
266+
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
267+
parser.add_argument("--csv", type=Path, default=DEFAULT_CSV, help=f"Telemetry export CSV (default: {DEFAULT_CSV})")
268+
args = parser.parse_args()
269+
270+
configure_matplotlib()
271+
rows = load_rows(args.csv)
272+
273+
primary, *mirrors = TARGETS
274+
render(rows, primary)
275+
for mirror in mirrors:
276+
mirror.parent.mkdir(parents=True, exist_ok=True)
277+
shutil.copyfile(primary, mirror)
278+
279+
for target in TARGETS:
280+
print(f"wrote {target.relative_to(REPO_ROOT)}")
281+
282+
283+
if __name__ == "__main__":
284+
main()

docs/scripts/top-model-usage.csv

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
"Top 10 Model Usage","Input Tokens (Context)","Output Tokens (Generated)"
2+
"openai/gpt-oss-120b","581,991,035,603","69,823,305,523"
3+
"google/gemma-4-31B-it","305,097,721,372","139,909,403,045"
4+
"Qwen/Qwen3-VL-235B-A22B-Instruct","252,299,362,661","2,506,282,983"
5+
"Qwen/Qwen3.5-397B-A17B-FP8","185,392,972,434","72,214,577,833"
6+
"google/gemma-4-26B-A4B-it","112,014,037,550","16,872,099,656"
7+
"Qwen/Qwen3.5-122B-A10B","87,216,522,178","41,888,115,144"
8+
"gcp/google/gemini-3.1-flash-lite-preview","61,793,069,244","7,206,950,344"
9+
"Qwen/Qwen3-VL-235B-A22B-Thinking-FP8","52,889,942,762","9,031,174,934"
10+
"Qwen/Qwen3.6-35B-A3B","46,115,903,437","4,269,353,359"
11+
"Qwen/Qwen3-VL-30B-A3B-Thinking","42,718,861,428","7,201,483,397"
12+
Other,"394,226,701,751","189,813,318,234"

fern/assets/images/top-models.png

-202 KB
Binary file not shown.

fern/images/top-models.png

20.9 KB
Loading

0 commit comments

Comments
 (0)