52) -> tuple[Path, Path]:
54 json_path = cwd / _JSON_NAME
55 png_path = cwd / _PNG_NAME
59 "saved_at": datetime.now(timezone.utc).isoformat(),
63 json_path.write_text(json.dumps(payload, indent=2, default=str), encoding=
"utf-8")
65 return json_path, png_path
72 import matplotlib.pyplot
as plt
74 if result.get(
"error"):
75 fig, ax = plt.subplots(figsize=(6, 2), facecolor=
"#0f1117")
77 ax.text(0.5, 0.5, f
"Error: {result['error']}", ha=
"center", va=
"center", wrap=
True, color=
"#e5e7eb")
78 fig.savefig(png_path, dpi=140, bbox_inches=
"tight", facecolor=
"#0f1117")
82 model = str(result.get(
"model_id")
or "")
83 mode = str(result.get(
"mode")
or "")
84 n_probes = int(result.get(
"n_probes")
or 0)
85 threshold = float(result.get(
"threshold")
or 0.4)
86 mean_conf = float(result.get(
"mean_confidence")
or 0)
87 low_count = int(result.get(
"low_confidence_count")
or 0)
88 join_sae = bool(result.get(
"join_sae"))
89 sae_layer = result.get(
"sae_layer")
91 title = f
"Confidence analysis — {model} · {mode} · {n_probes} probes"
92 subtitle = f
"mean conf {mean_conf:.3f} · threshold {threshold:.2f} · low-conf {low_count}"
93 if join_sae
and sae_layer
is not None:
94 subtitle += f
" · SAE L{sae_layer}"
96 fig = plt.figure(figsize=(12, 7.5), facecolor=
"#0f1117")
97 gs = fig.add_gridspec(2, 2, height_ratios=[1, 1.2], hspace=0.38, wspace=0.28)
98 ax_probes = fig.add_subplot(gs[0, 0])
99 ax_stress = fig.add_subplot(gs[0, 1])
100 ax_heat = fig.add_subplot(gs[1, :])
102 fig.suptitle(title, color=
"#e5e7eb", fontsize=11, y=0.98)
103 fig.text(0.5, 0.93, subtitle, ha=
"center", fontsize=8, color=
"#9ca3af")
109 for ax
in (ax_probes, ax_stress, ax_heat):
110 ax.set_facecolor(
"#0f1117")
111 for spine
in ax.spines.values():
112 spine.set_color(
"#374151")
114 fig.savefig(png_path, dpi=140, bbox_inches=
"tight", facecolor=
"#0f1117")
119 ax.set_title(
"Per-probe confidence", color=
"#e5e7eb", fontsize=10)
123 ax.text(0.5, 0.5,
"No probe data", ha=
"center", va=
"center", color=
"#9ca3af")
126 rows = sorted(probes, key=
lambda p: float(p.get(
"mean_confidence")
or 0))[:16]
128 vals = [float(p.get(
"mean_confidence")
or 0)
for p
in rows]
129 colors = [
"#f87171" if v < threshold
else "#34d399" if v >= 0.7
else "#6366f1" for v
in vals]
131 y_pos = list(range(len(rows)))
132 ax.barh(y_pos, vals, color=colors, height=0.65, alpha=0.9)
133 ax.axvline(threshold, color=
"#fbbf24", linestyle=
"--", linewidth=0.8, alpha=0.7)
135 ax.set_yticklabels(labels, fontsize=7, color=
"#d1d5db")
138 ax.set_xlabel(
"Mean confidence", color=
"#9ca3af", fontsize=8)
139 ax.tick_params(axis=
"x", colors=
"#6b7280", labelsize=8)
140 ax.grid(axis=
"x", alpha=0.2, color=
"#4b5563")
144 ax.set_title(
"By stressor", color=
"#e5e7eb", fontsize=10)
148 ax.text(0.5, 0.5,
"No stressor summary", ha=
"center", va=
"center", color=
"#9ca3af")
151 labels = [str(s.get(
"stressor")
or "?")
for s
in summary]
152 vals = [float(s.get(
"mean_confidence")
or 0)
for s
in summary]
153 y_pos = list(range(len(summary)))
154 ax.barh(y_pos, vals, color=
"#38bdf8", height=0.65, alpha=0.9)
156 ax.set_yticklabels(labels, fontsize=8, color=
"#d1d5db")
159 ax.set_xlabel(
"Mean confidence", color=
"#9ca3af", fontsize=8)
160 ax.tick_params(axis=
"x", colors=
"#6b7280", labelsize=8)
161 ax.grid(axis=
"x", alpha=0.2, color=
"#4b5563")
162 for i, s
in enumerate(summary):
163 low = int(s.get(
"low_confidence_count")
or 0)
164 ax.text(min(vals[i] + 0.02, 0.92), i, f
"{vals[i]:.3f} ({low} low)", va=
"center", fontsize=7, color=
"#9ca3af")
168 rows = heatmap.get(
"rows")
or []
169 cols = heatmap.get(
"cols")
or []
170 values = heatmap.get(
"values")
or []
172 ax.set_title(
"Stressor × metrics", color=
"#e5e7eb", fontsize=10, pad=10)
174 if not rows
or not cols
or not values:
176 ax.text(0.5, 0.5,
"No heatmap data", ha=
"center", va=
"center", color=
"#9ca3af")
181 data = np.array(values, dtype=float)
184 ax.text(0.5, 0.5,
"Invalid heatmap shape", ha=
"center", va=
"center", color=
"#9ca3af")
187 im = ax.imshow(data, aspect=
"auto", cmap=
"plasma", origin=
"upper")
188 ax.set_xticks(range(len(cols)))
189 ax.set_xticklabels(cols, fontsize=8, color=
"#d1d5db", rotation=30, ha=
"right")
190 ax.set_yticks(range(len(rows)))
191 ax.set_yticklabels(rows, fontsize=8, color=
"#d1d5db")
192 ax.tick_params(axis=
"x", colors=
"#6b7280")
193 ax.tick_params(axis=
"y", colors=
"#6b7280")
194 cbar = ax.figure.colorbar(im, ax=ax, fraction=0.025, pad=0.02)
195 cbar.ax.tick_params(colors=
"#9ca3af", labelsize=7)
199 pid = str(probe.get(
"id")
or "")
200 stressor = probe.get(
"stressor")