AQIT
0.1.0
Toggle main menu visibility
Loading...
Searching...
No Matches
red_team_check.py
Go to the documentation of this file.
1
# Copyright (c) 2025-present Aquin Labs Private Limited. All Rights Reserved.
2
"""`aquin red-team --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 =
"red-team-check.json"
12
_PNG_NAME =
"red-team-check.png"
13
14
_STATUS_COLORS = {
15
"pass"
:
"#34d399"
,
16
"warn"
:
"#fbbf24"
,
17
"fail"
:
"#f87171"
,
18
}
19
20
21
def
_normalize_red_team_result
(result: dict[str, Any]) -> dict[str, Any]:
22
if
not
isinstance(result, dict):
23
return
{
"error"
:
"Invalid red-team 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
card = result.get(
"card"
)
32
if
isinstance(card, dict)
and
isinstance(card.get(
"data"
), dict):
33
data = card[
"data"
]
34
35
return
dict(data)
36
37
38
def
has_red_team_payload
(result: dict[str, Any]) -> bool:
39
data =
_normalize_red_team_result
(result)
40
if
data.get(
"error"
):
41
return
False
42
return
bool(data.get(
"vectors"
))
or
data.get(
"composite_score"
)
is
not
None
43
44
45
def
_json_safe_payload
(flat: dict[str, Any]) -> dict[str, Any]:
46
"""Omit bulky per-probe raw blobs from check export."""
47
out = dict(flat)
48
vectors = []
49
for
row
in
flat.get(
"vectors"
)
or
[]:
50
if
not
isinstance(row, dict):
51
continue
52
slim = {k: v
for
k, v
in
row.items()
if
k !=
"raw"
}
53
vectors.append(slim)
54
out[
"vectors"
] = vectors
55
return
out
56
57
58
def
write_red_team_check
(
59
result: dict[str, Any],
60
*,
61
tool_name: str |
None
,
62
cwd: str | Path,
63
) -> tuple[Path, Path]:
64
cwd = Path(cwd)
65
json_path = cwd / _JSON_NAME
66
png_path = cwd / _PNG_NAME
67
68
flat =
_normalize_red_team_result
(result)
69
payload = {
70
"saved_at"
: datetime.now(timezone.utc).isoformat(),
71
"tool"
: tool_name,
72
**
_json_safe_payload
(flat),
73
}
74
json_path.write_text(json.dumps(payload, indent=2, default=str), encoding=
"utf-8"
)
75
_plot_red_team_check
(flat, png_path)
76
return
json_path, png_path
77
78
79
def
_plot_red_team_check
(result: dict[str, Any], png_path: Path) ->
None
:
80
import
matplotlib
81
82
matplotlib.use(
"Agg"
)
83
import
matplotlib.pyplot
as
plt
84
85
if
result.get(
"error"
):
86
fig, ax = plt.subplots(figsize=(6, 2), facecolor=
"#0f1117"
)
87
ax.axis(
"off"
)
88
ax.text(0.5, 0.5, f
"Error: {result['error']}"
, ha=
"center"
, va=
"center"
, wrap=
True
, color=
"#e5e7eb"
)
89
fig.savefig(png_path, dpi=140, bbox_inches=
"tight"
, facecolor=
"#0f1117"
)
90
plt.close(fig)
91
return
92
93
model = str(result.get(
"model_id"
)
or
""
)
94
composite = float(result.get(
"composite_score"
)
or
0)
95
vectors = result.get(
"vectors"
)
or
[]
96
n_pass = sum(1
for
v
in
vectors
if
v.get(
"status"
) ==
"pass"
)
97
n_warn = sum(1
for
v
in
vectors
if
v.get(
"status"
) ==
"warn"
)
98
n_fail = sum(1
for
v
in
vectors
if
v.get(
"status"
) ==
"fail"
)
99
100
title = f
"Red team — {model}"
if
model
else
"Red team"
101
subtitle = f
"composite {composite:.0%} · {n_pass} pass · {n_warn} warn · {n_fail} fail"
102
103
fig, axes = plt.subplots(1, 2, figsize=(11, max(4.5, min(10, len(vectors) * 0.45 + 2))), facecolor=
"#0f1117"
)
104
fig.suptitle(title, color=
"#e5e7eb"
, fontsize=11, y=1.02)
105
fig.text(0.5, 0.96, subtitle, ha=
"center"
, fontsize=8, color=
"#9ca3af"
)
106
107
_plot_vector_scores
(axes[0], vectors)
108
_plot_composite_panel
(axes[1], composite, n_pass, n_warn, n_fail)
109
110
for
ax
in
axes:
111
ax.set_facecolor(
"#0f1117"
)
112
for
spine
in
ax.spines.values():
113
spine.set_color(
"#374151"
)
114
115
fig.tight_layout(rect=(0, 0, 1, 0.93))
116
fig.savefig(png_path, dpi=140, bbox_inches=
"tight"
, facecolor=
"#0f1117"
)
117
plt.close(fig)
118
119
120
def
_plot_vector_scores
(ax: Any, vectors: list[dict[str, Any]]) ->
None
:
121
ax.set_title(
"Attack vector robustness"
, color=
"#e5e7eb"
, fontsize=10)
122
123
if
not
vectors:
124
ax.axis(
"off"
)
125
ax.text(0.5, 0.5,
"No vectors"
, ha=
"center"
, va=
"center"
, color=
"#9ca3af"
)
126
return
127
128
rows = sorted(vectors, key=
lambda
v: float(v.get(
"score"
)
or
0))
129
labels = [str(v.get(
"label"
)
or
v.get(
"id"
)
or
"?"
)
for
v
in
rows]
130
vals = [float(v.get(
"score"
)
or
0)
for
v
in
rows]
131
colors = [_STATUS_COLORS.get(str(v.get(
"status"
)
or
""
),
"#78716c"
)
for
v
in
rows]
132
133
y_pos = list(range(len(rows)))
134
ax.barh(y_pos, vals, color=colors, height=0.65, alpha=0.9)
135
ax.set_yticks(y_pos)
136
ax.set_yticklabels(labels, fontsize=8, color=
"#d1d5db"
)
137
ax.invert_yaxis()
138
ax.set_xlim(0, 1.05)
139
ax.set_xlabel(
"Robustness score (0–1)"
, color=
"#9ca3af"
, fontsize=8)
140
ax.tick_params(axis=
"x"
, colors=
"#6b7280"
, labelsize=8)
141
ax.grid(axis=
"x"
, alpha=0.2, color=
"#4b5563"
)
142
for
i, v
in
enumerate(vals):
143
ax.text(min(v + 0.02, 0.98), i, f
"{v:.0%}"
, va=
"center"
, fontsize=7, color=
"#9ca3af"
)
144
145
146
def
_plot_composite_panel
(ax: Any, composite: float, n_pass: int, n_warn: int, n_fail: int) ->
None
:
147
ax.set_title(
"Composite & status mix"
, color=
"#e5e7eb"
, fontsize=10)
148
149
ax.barh([0], [composite], color=
_score_color
(composite), height=0.35, alpha=0.92)
150
ax.set_xlim(0, 1.05)
151
ax.set_yticks([0])
152
ax.set_yticklabels([
"composite"
], fontsize=9, color=
"#d1d5db"
)
153
ax.set_xlabel(
"Composite score"
, color=
"#9ca3af"
, fontsize=8)
154
ax.text(min(composite + 0.02, 0.98), 0, f
"{composite:.0%}"
, va=
"center"
, fontsize=9, color=
"#9ca3af"
)
155
ax.tick_params(axis=
"x"
, colors=
"#6b7280"
, labelsize=8)
156
ax.grid(axis=
"x"
, alpha=0.2, color=
"#4b5563"
)
157
158
counts = [n_pass, n_warn, n_fail]
159
if
any(counts):
160
inset = ax.inset_axes([0.08, 0.08, 0.84, 0.42])
161
inset.set_facecolor(
"#0f1117"
)
162
labels = [
"pass"
,
"warn"
,
"fail"
]
163
colors = [_STATUS_COLORS[k]
for
k
in
labels]
164
x = list(range(3))
165
inset.bar(x, counts, color=colors, width=0.6, alpha=0.9)
166
inset.set_xticks(x)
167
inset.set_xticklabels(labels, fontsize=8, color=
"#d1d5db"
)
168
inset.tick_params(axis=
"y"
, colors=
"#6b7280"
, labelsize=7)
169
for
spine
in
inset.spines.values():
170
spine.set_color(
"#374151"
)
171
172
173
def
_score_color
(val: float) -> str:
174
if
val >= 0.65:
175
return
"#34d399"
176
if
val >= 0.35:
177
return
"#fbbf24"
178
return
"#f87171"
aquin.red_team_check._plot_red_team_check
None _plot_red_team_check(dict[str, Any] result, Path png_path)
Definition
red_team_check.py:83
aquin.red_team_check.has_red_team_payload
bool has_red_team_payload(dict[str, Any] result)
Definition
red_team_check.py:42
aquin.red_team_check._plot_vector_scores
None _plot_vector_scores(Any ax, list[dict[str, Any]] vectors)
Definition
red_team_check.py:124
aquin.red_team_check._score_color
str _score_color(float val)
Definition
red_team_check.py:177
aquin.red_team_check.write_red_team_check
tuple[Path, Path] write_red_team_check(dict[str, Any] result, *, str|None tool_name, str|Path cwd)
Definition
red_team_check.py:67
aquin.red_team_check._normalize_red_team_result
dict[str, Any] _normalize_red_team_result(dict[str, Any] result)
Definition
red_team_check.py:25
aquin.red_team_check._json_safe_payload
dict[str, Any] _json_safe_payload(dict[str, Any] flat)
Definition
red_team_check.py:49
aquin.red_team_check._plot_composite_panel
None _plot_composite_panel(Any ax, float composite, int n_pass, int n_warn, int n_fail)
Definition
red_team_check.py:150
aquin
red_team_check.py
AQIT · Aquin Labs Private Limited · Apache 2.0 · Generated by
1.18.0