44) -> tuple[Path, Path]:
46 json_path = cwd / _JSON_NAME
47 png_path = cwd / _PNG_NAME
51 "saved_at": datetime.now(timezone.utc).isoformat(),
55 json_path.write_text(json.dumps(payload, indent=2, default=str), encoding=
"utf-8")
57 return json_path, png_path
64 import matplotlib.pyplot
as plt
66 if result.get(
"error"):
67 fig, ax = plt.subplots(figsize=(6, 2), facecolor=
"#0f1117")
69 ax.text(0.5, 0.5, f
"Error: {result['error']}", ha=
"center", va=
"center", wrap=
True, color=
"#e5e7eb")
70 fig.savefig(png_path, dpi=140, bbox_inches=
"tight", facecolor=
"#0f1117")
74 name = str(result.get(
"name")
or "eval")
75 model = str(result.get(
"model_id")
or "")
76 threshold = float(result.get(
"threshold")
or 0.5)
77 mean_score = float(result.get(
"mean_score")
or 0)
78 pass_rate = float(result.get(
"pass_rate")
or 0)
79 rows = result.get(
"prompts")
or []
81 title = f
"Custom eval — {name}"
83 title += f
" · {model}"
84 subtitle = f
"mean {mean_score:.0%} · pass rate {pass_rate:.0%} · threshold {threshold:.0%}"
86 fig, axes = plt.subplots(1, 2, figsize=(11, max(4.5, min(12, len(rows) * 0.38 + 2))), facecolor=
"#0f1117")
87 fig.suptitle(title, color=
"#e5e7eb", fontsize=11, y=1.02)
88 fig.text(0.5, 0.96, subtitle, ha=
"center", fontsize=8, color=
"#9ca3af")
94 ax.set_facecolor(
"#0f1117")
95 for spine
in ax.spines.values():
96 spine.set_color(
"#374151")
98 fig.tight_layout(rect=(0, 0, 1, 0.93))
99 fig.savefig(png_path, dpi=140, bbox_inches=
"tight", facecolor=
"#0f1117")
104 ax.set_title(
"Per-prompt score", color=
"#e5e7eb", fontsize=10)
108 ax.text(0.5, 0.5,
"No prompts", ha=
"center", va=
"center", color=
"#9ca3af")
111 labels = [
_trunc(str(r.get(
"prompt")
or ""), 28)
for r
in rows]
112 vals = [float(r.get(
"score")
or 0)
for r
in rows]
113 colors = [
"#34d399" if r.get(
"passed")
else "#f87171" for r
in rows]
115 y_pos = list(range(len(rows)))
116 ax.barh(y_pos, vals, color=colors, height=0.65, alpha=0.9)
117 ax.axvline(threshold, color=
"#fbbf24", linestyle=
"--", linewidth=0.8, alpha=0.75)
119 ax.set_yticklabels(labels, fontsize=7, color=
"#d1d5db")
122 ax.set_xlabel(
"Score (0–1)", color=
"#9ca3af", fontsize=8)
123 ax.tick_params(axis=
"x", colors=
"#6b7280", labelsize=8)
124 ax.grid(axis=
"x", alpha=0.2, color=
"#4b5563")
125 for i, v
in enumerate(vals):
126 ax.text(min(v + 0.02, 0.98), i, f
"{v:.2f}", va=
"center", fontsize=7, color=
"#9ca3af")
134 rows: list[dict[str, Any]],
136 ax.set_title(
"Summary", color=
"#e5e7eb", fontsize=10)
138 metrics = [(
"mean score", mean_score), (
"pass rate", pass_rate), (
"threshold", threshold)]
139 labels = [m[0]
for m
in metrics]
140 vals = [m[1]
for m
in metrics]
143 y_pos = list(range(len(metrics)))
144 ax.barh(y_pos, vals, color=colors, height=0.55, alpha=0.9)
146 ax.set_yticklabels(labels, fontsize=9, color=
"#d1d5db")
149 ax.set_xlabel(
"Value (0–1)", color=
"#9ca3af", fontsize=8)
150 ax.tick_params(axis=
"x", colors=
"#6b7280", labelsize=8)
151 ax.grid(axis=
"x", alpha=0.2, color=
"#4b5563")
152 for i, v
in enumerate(vals):
153 ax.text(min(v + 0.02, 0.98), i, f
"{v:.0%}", va=
"center", fontsize=8, color=
"#9ca3af")
155 n_pass = sum(1
for r
in rows
if r.get(
"passed"))
158 f
"{n_pass}/{len(rows)} passed",
159 transform=ax.transAxes,