AQIT
0.1.0
Toggle main menu visibility
Loading...
Searching...
No Matches
suppression_eval_check.py
Go to the documentation of this file.
1
# Copyright (c) 2025-present Aquin Labs Private Limited. All Rights Reserved.
2
"""`aquin suppression-eval --check` — save JSON + diagram to the current working directory."""
3
4
from
__future__
import
annotations
5
6
import
json
7
from
datetime
import
datetime, timezone
8
from
pathlib
import
Path
9
from
typing
import
Any
10
11
_JSON_NAME =
"suppression-eval-check.json"
12
_PNG_NAME =
"suppression-eval-check.png"
13
14
_STATUS_COLORS = {
15
"suppressed"
:
"#f87171"
,
16
"softened"
:
"#fbbf24"
,
17
"unfiltered"
:
"#34d399"
,
18
}
19
20
21
def
_normalize_suppression_eval_result
(result: dict[str, Any]) -> dict[str, Any]:
22
if
not
isinstance(result, dict):
23
return
{
"error"
:
"Invalid suppression-eval result"
}
24
25
if
result.get(
"error"
):
26
return
dict(result)
27
28
data = result
29
if
isinstance(result.get(
"content"
), dict):
30
data = result[
"content"
]
31
if
isinstance(data.get(
"suppression"
), dict):
32
data = data[
"suppression"
]
33
card = result.get(
"card"
)
34
if
isinstance(card, dict)
and
isinstance(card.get(
"data"
), dict):
35
nested = card[
"data"
].get(
"suppression"
)
36
if
isinstance(nested, dict):
37
data = nested
38
39
return
dict(data)
40
41
42
def
has_suppression_eval_payload
(result: dict[str, Any]) -> bool:
43
data =
_normalize_suppression_eval_result
(result)
44
if
data.get(
"error"
):
45
return
False
46
return
bool(data.get(
"topics"
))
or
bool(data.get(
"baseline"
))
47
48
49
def
write_suppression_eval_check
(
50
result: dict[str, Any],
51
*,
52
tool_name: str |
None
,
53
cwd: str | Path,
54
) -> tuple[Path, Path]:
55
cwd = Path(cwd)
56
json_path = cwd / _JSON_NAME
57
png_path = cwd / _PNG_NAME
58
59
flat =
_normalize_suppression_eval_result
(result)
60
payload = {
61
"saved_at"
: datetime.now(timezone.utc).isoformat(),
62
"tool"
: tool_name,
63
**flat,
64
}
65
json_path.write_text(json.dumps(payload, indent=2, default=str), encoding=
"utf-8"
)
66
_plot_suppression_eval_check
(flat, png_path)
67
return
json_path, png_path
68
69
70
def
_plot_suppression_eval_check
(result: dict[str, Any], png_path: Path) ->
None
:
71
import
matplotlib
72
73
matplotlib.use(
"Agg"
)
74
import
matplotlib.pyplot
as
plt
75
76
if
result.get(
"error"
):
77
fig, ax = plt.subplots(figsize=(6, 2), facecolor=
"#0f1117"
)
78
ax.axis(
"off"
)
79
ax.text(0.5, 0.5, f
"Error: {result['error']}"
, ha=
"center"
, va=
"center"
, wrap=
True
, color=
"#e5e7eb"
)
80
fig.savefig(png_path, dpi=140, bbox_inches=
"tight"
, facecolor=
"#0f1117"
)
81
plt.close(fig)
82
return
83
84
baseline = result.get(
"baseline"
)
or
{}
85
topics = result.get(
"topics"
)
or
[]
86
n_topics = len(topics)
87
suppressed = sum(1
for
t
in
topics
if
t.get(
"status"
) ==
"suppressed"
)
88
softened = sum(1
for
t
in
topics
if
t.get(
"status"
) ==
"softened"
)
89
90
title = f
"Suppression eval — {n_topics} topics"
91
subtitle = f
"baseline len {baseline.get('mean_length', '—')} · hedge {baseline.get('mean_hedge', '—')} · {suppressed} suppressed · {softened} softened"
92
93
fig, axes = plt.subplots(1, 2, figsize=(11, max(4.5, min(10, n_topics * 0.35 + 2))), facecolor=
"#0f1117"
)
94
fig.suptitle(title, color=
"#e5e7eb"
, fontsize=11, y=1.02)
95
fig.text(0.5, 0.96, subtitle, ha=
"center"
, fontsize=8, color=
"#9ca3af"
)
96
97
_plot_suppression_scores
(axes[0], topics)
98
_plot_ratio_panel
(axes[1], topics)
99
100
for
ax
in
axes:
101
ax.set_facecolor(
"#0f1117"
)
102
for
spine
in
ax.spines.values():
103
spine.set_color(
"#374151"
)
104
105
fig.tight_layout(rect=(0, 0, 1, 0.93))
106
fig.savefig(png_path, dpi=140, bbox_inches=
"tight"
, facecolor=
"#0f1117"
)
107
plt.close(fig)
108
109
110
def
_plot_suppression_scores
(ax: Any, topics: list[dict[str, Any]]) ->
None
:
111
ax.set_title(
"Suppression score by topic"
, color=
"#e5e7eb"
, fontsize=10)
112
113
if
not
topics:
114
ax.axis(
"off"
)
115
ax.text(0.5, 0.5,
"No topics"
, ha=
"center"
, va=
"center"
, color=
"#9ca3af"
)
116
return
117
118
rows = sorted(topics, key=
lambda
t: float(t.get(
"suppression_score"
)
or
0), reverse=
True
)
119
labels = [str(t.get(
"topic"
)
or
"?"
)
for
t
in
rows]
120
vals = [float(t.get(
"suppression_score"
)
or
0)
for
t
in
rows]
121
colors = [_STATUS_COLORS.get(str(t.get(
"status"
)
or
""
),
"#78716c"
)
for
t
in
rows]
122
123
y_pos = list(range(len(rows)))
124
ax.barh(y_pos, vals, color=colors, height=0.65, alpha=0.9)
125
ax.set_yticks(y_pos)
126
ax.set_yticklabels(labels, fontsize=8, color=
"#d1d5db"
)
127
ax.invert_yaxis()
128
ax.set_xlim(0, 1.05)
129
ax.set_xlabel(
"Suppression score"
, color=
"#9ca3af"
, fontsize=8)
130
ax.tick_params(axis=
"x"
, colors=
"#6b7280"
, labelsize=8)
131
ax.grid(axis=
"x"
, alpha=0.2, color=
"#4b5563"
)
132
for
i, v
in
enumerate(vals):
133
ax.text(min(v + 0.02, 0.98), i, f
"{v:.2f}"
, va=
"center"
, fontsize=7, color=
"#9ca3af"
)
134
135
136
def
_plot_ratio_panel
(ax: Any, topics: list[dict[str, Any]]) ->
None
:
137
ax.set_title(
"Length × hedge vs baseline"
, color=
"#e5e7eb"
, fontsize=10)
138
139
if
not
topics:
140
ax.axis(
"off"
)
141
ax.text(0.5, 0.5,
"No topics"
, ha=
"center"
, va=
"center"
, color=
"#9ca3af"
)
142
return
143
144
labels = [str(t.get(
"topic"
)
or
"?"
)[:12]
for
t
in
topics]
145
x = list(range(len(topics)))
146
width = 0.38
147
len_ratios = [float(t.get(
"length_ratio"
)
or
0)
for
t
in
topics]
148
hedge_ratios = [float(t.get(
"hedge_ratio"
)
or
0)
for
t
in
topics]
149
150
ax.bar([i - width / 2
for
i
in
x], len_ratios, width=width, color=
"#6366f1"
, alpha=0.9, label=
"length×"
)
151
ax.bar([i + width / 2
for
i
in
x], hedge_ratios, width=width, color=
"#fbbf24"
, alpha=0.9, label=
"hedge×"
)
152
ax.axhline(1.0, color=
"#9ca3af"
, linestyle=
"--"
, linewidth=0.8, alpha=0.5)
153
ax.set_xticks(x)
154
ax.set_xticklabels(labels, fontsize=7, color=
"#d1d5db"
, rotation=30, ha=
"right"
)
155
ax.set_ylabel(
"Ratio vs baseline"
, color=
"#9ca3af"
, fontsize=8)
156
ax.legend(fontsize=7, facecolor=
"#0f1117"
, edgecolor=
"#374151"
, labelcolor=
"#d1d5db"
)
157
ax.tick_params(colors=
"#6b7280"
, labelsize=7)
158
ax.grid(axis=
"y"
, alpha=0.2, color=
"#4b5563"
)
aquin.suppression_eval_check.write_suppression_eval_check
tuple[Path, Path] write_suppression_eval_check(dict[str, Any] result, *, str|None tool_name, str|Path cwd)
Definition
suppression_eval_check.py:58
aquin.suppression_eval_check._normalize_suppression_eval_result
dict[str, Any] _normalize_suppression_eval_result(dict[str, Any] result)
Definition
suppression_eval_check.py:25
aquin.suppression_eval_check._plot_suppression_eval_check
None _plot_suppression_eval_check(dict[str, Any] result, Path png_path)
Definition
suppression_eval_check.py:74
aquin.suppression_eval_check.has_suppression_eval_payload
bool has_suppression_eval_payload(dict[str, Any] result)
Definition
suppression_eval_check.py:46
aquin.suppression_eval_check._plot_suppression_scores
None _plot_suppression_scores(Any ax, list[dict[str, Any]] topics)
Definition
suppression_eval_check.py:114
aquin.suppression_eval_check._plot_ratio_panel
None _plot_ratio_panel(Any ax, list[dict[str, Any]] topics)
Definition
suppression_eval_check.py:140
aquin
suppression_eval_check.py
AQIT · Aquin Labs Private Limited · Apache 2.0 · Generated by
1.18.0