Skip to content

Commit 094298f

Browse files
committed
fix: make eval_pipeline scripts pass pre-commit after rebase onto main
Rebase cl_agentic_grader onto upstream/main exposed skills/eval_pipeline files that failed black/isort/pylint. Apply formatting and resolve pylint warnings so the PR merge commit passes the CI grep-on-Failed check.
1 parent 1fedb03 commit 094298f

6 files changed

Lines changed: 168 additions & 80 deletions

File tree

skills/eval_pipeline/01-eval-design/scripts/coverage_check.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
2121
EXIT: 0 if coverage is adequate, 2 if thin cells found, 1 on usage error.
2222
"""
23+
# pylint: disable=missing-function-docstring
2324
from __future__ import annotations
2425

2526
import argparse
@@ -101,14 +102,16 @@ def _self_test() -> None:
101102
data += [{"metadata": {"dimension": "order_accuracy", "difficulty": "easy"}}] * 12
102103
data += [{"metadata": {"dimension": "order_accuracy", "difficulty": "boundary"}}] * 11
103104
data += [{"metadata": {"dimension": "order_accuracy", "difficulty": "adversarial"}}] * 10
104-
data += [{"metadata": {"dimension": "tone", "difficulty": "easy"}}] * 3 # thin dimension
105+
data += [{"metadata": {"dimension": "tone", "difficulty": "easy"}}] * 3 # thin dimension
105106
r = analyze(data)
106107
assert "tone" in r["thin_dimensions"], r["thin_dimensions"]
107108
assert r["verdict"] == "thin_coverage"
108109
# A well-covered single dimension.
109-
good = ([{"metadata": {"dimension": "d", "difficulty": "easy"}}] * 11
110-
+ [{"metadata": {"dimension": "d", "difficulty": "boundary"}}] * 10
111-
+ [{"metadata": {"dimension": "d", "difficulty": "adversarial"}}] * 10)
110+
good = (
111+
[{"metadata": {"dimension": "d", "difficulty": "easy"}}] * 11
112+
+ [{"metadata": {"dimension": "d", "difficulty": "boundary"}}] * 10
113+
+ [{"metadata": {"dimension": "d", "difficulty": "adversarial"}}] * 10
114+
)
112115
rg = analyze(good)
113116
assert rg["verdict"] == "adequate", rg
114117
print("self-test OK")

skills/eval_pipeline/03-align-human/scripts/calibration.py

Lines changed: 48 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
3030
EXIT CODE: 0 if calibrated, 2 if not_calibrated/insufficient_evidence, 1 on usage error.
3131
"""
32+
# pylint: disable=missing-function-docstring
3233
from __future__ import annotations
3334

3435
import argparse
@@ -86,12 +87,14 @@ def load_pairs(
8687
out: list[dict[str, str]] = []
8788
if pairs is not None:
8889
for row in _read_jsonl(pairs):
89-
out.append({
90-
"id": str(row.get("id", len(out))),
91-
"judge": _norm(row["judge"]),
92-
"human": _norm(row["human"]),
93-
"stratum": str(row.get(stratum_key, "")) or "all",
94-
})
90+
out.append(
91+
{
92+
"id": str(row.get("id", len(out))),
93+
"judge": _norm(row["judge"]),
94+
"human": _norm(row["human"]),
95+
"stratum": str(row.get(stratum_key, "")) or "all",
96+
}
97+
)
9598
return out
9699

97100
if verdicts is None or labels is None:
@@ -101,12 +104,14 @@ def load_pairs(
101104
rid = str(row["id"])
102105
if rid not in label_by_id:
103106
continue
104-
out.append({
105-
"id": rid,
106-
"judge": _norm(row.get("verdict", row.get("judge"))),
107-
"human": _norm(label_by_id[rid]),
108-
"stratum": str(row.get(stratum_key, "")) or "all",
109-
})
107+
out.append(
108+
{
109+
"id": rid,
110+
"judge": _norm(row.get("verdict", row.get("judge"))),
111+
"human": _norm(label_by_id[rid]),
112+
"stratum": str(row.get(stratum_key, "")) or "all",
113+
}
114+
)
110115
return out
111116

112117

@@ -133,7 +138,7 @@ def _safe_div(num: float, den: float) -> float:
133138
def core_metrics(pairs: list[dict[str, str]], positive: str = "fail") -> dict[str, float]:
134139
c = confusion(pairs, positive)
135140
tp, fp, tn, fn = c["tp"], c["fp"], c["tn"], c["fn"]
136-
tpr = _safe_div(tp, tp + fn) # recall on the positive (caught failures)
141+
tpr = _safe_div(tp, tp + fn) # recall on the positive (caught failures)
137142
tnr = _safe_div(tn, tn + fp)
138143
precision = _safe_div(tp, tp + fp)
139144
f1 = _safe_div(2 * precision * tpr, precision + tpr)
@@ -216,8 +221,9 @@ def per_stratum(pairs: list[dict[str, str]], positive: str = "fail") -> dict[str
216221
return out
217222

218223

219-
def calibration_gate(pairs: list[dict[str, str]], metrics: dict[str, float], kappa: float,
220-
positive: str = "fail") -> dict[str, Any]:
224+
def calibration_gate(
225+
pairs: list[dict[str, str]], metrics: dict[str, float], kappa: float, positive: str = "fail"
226+
) -> dict[str, Any]:
221227
"""Apply the SKILL.md hard gate. Returns verdict + blocking reasons."""
222228
n = len(pairs)
223229
n_pos = sum(1 for p in pairs if p["human"] == positive)
@@ -228,20 +234,29 @@ def calibration_gate(pairs: list[dict[str, str]], metrics: dict[str, float], kap
228234
if min(n_pos, n_neg) < N_PER_CLASS_MIN:
229235
reasons.append(f"min class count {min(n_pos, n_neg)} (need >= {N_PER_CLASS_MIN} per class)")
230236
if reasons:
231-
return {"verdict": "insufficient_evidence", "blocking": reasons,
232-
"next": f"collect more labels: aim for >= {N_TOTAL_MIN} total, >= {N_PER_CLASS_MIN} per class"}
237+
return {
238+
"verdict": "insufficient_evidence",
239+
"blocking": reasons,
240+
"next": f"collect more labels: aim for >= {N_TOTAL_MIN} total, >= {N_PER_CLASS_MIN} per class",
241+
}
233242
not_ready = []
234243
if metrics["tpr"] < TPR_MIN:
235244
not_ready.append(f"TPR {metrics['tpr']:.2f} < {TPR_MIN}")
236245
if metrics["tnr"] < TNR_MIN:
237246
not_ready.append(f"TNR {metrics['tnr']:.2f} < {TNR_MIN}")
238247
if not_ready:
239-
return {"verdict": "not_calibrated", "blocking": not_ready,
240-
"next": "refine the judge prompt (add borderline few-shot) then re-measure"}
248+
return {
249+
"verdict": "not_calibrated",
250+
"blocking": not_ready,
251+
"next": "refine the judge prompt (add borderline few-shot) then re-measure",
252+
}
241253
phase = 2 if kappa >= KAPPA_PHASE2_MIN else 1
242-
return {"verdict": "calibrated", "blocking": [],
243-
"human_reduction_phase": phase,
244-
"next": "phase 2 (assisted) ok" if phase == 2 else "calibrated but kappa<0.6: keep humans primary"}
254+
return {
255+
"verdict": "calibrated",
256+
"blocking": [],
257+
"human_reduction_phase": phase,
258+
"next": "phase 2 (assisted) ok" if phase == 2 else "calibrated but kappa<0.6: keep humans primary",
259+
}
245260

246261

247262
def analyze(pairs: list[dict[str, str]], positive: str = "fail", n_iter: int = 1000) -> dict[str, Any]:
@@ -279,8 +294,11 @@ def render(report: dict[str, Any]) -> str:
279294
f" kappa={report['kappa']:.2f} Gwet's AC1={report['gwet_ac1']:.2f}"
280295
+ (" [gap>0.15: class imbalance — trust AC1]" if report["kappa_ac1_gap"] > 0.15 else ""),
281296
f" bias={report['bias']:+.2f} "
282-
+ ("(stricter than humans)" if report["bias"] > 0.1 else
283-
"(more lenient than humans)" if report["bias"] < -0.1 else "(no significant bias)"),
297+
+ (
298+
"(stricter than humans)"
299+
if report["bias"] > 0.1
300+
else "(more lenient than humans)" if report["bias"] < -0.1 else "(no significant bias)"
301+
),
284302
]
285303
if report["per_stratum"]:
286304
lines.append(" per-stratum:")
@@ -327,8 +345,12 @@ def main(argv: list[str] | None = None) -> int:
327345
ap.add_argument("--pairs", type=Path, help="JSONL with id,judge,human[,stratum].")
328346
ap.add_argument("--verdicts", type=Path, help="JSONL with id,verdict[,stratum].")
329347
ap.add_argument("--labels", type=Path, help="JSONL with id,label.")
330-
ap.add_argument("--positive", default="fail", choices=["fail", "pass"],
331-
help="Positive class for TPR (default: fail = caught problems).")
348+
ap.add_argument(
349+
"--positive",
350+
default="fail",
351+
choices=["fail", "pass"],
352+
help="Positive class for TPR (default: fail = caught problems).",
353+
)
332354
ap.add_argument("--stratum-key", default="stratum", help="Field name for stratum (default: stratum).")
333355
ap.add_argument("--n-iter", type=int, default=1000, help="Bootstrap iterations.")
334356
ap.add_argument("--json", action="store_true", help="Emit JSON instead of a text report.")

skills/eval_pipeline/05-rag-eval/scripts/rag_diagnostic.py

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
2424
EXIT: 0 always (diagnostic), 1 on usage error.
2525
"""
26+
# pylint: disable=missing-function-docstring
2627
from __future__ import annotations
2728

2829
import argparse
@@ -59,21 +60,26 @@ def _retrieval_good(row: dict[str, Any], threshold: float) -> bool | None:
5960
return None
6061

6162

62-
def analyze(traces: list[dict[str, Any]], faithful_threshold: float = 4.0,
63-
recall_threshold: float = 0.5) -> dict[str, Any]:
63+
def analyze(
64+
traces: list[dict[str, Any]], faithful_threshold: float = 4.0, recall_threshold: float = 0.5
65+
) -> dict[str, Any]:
6466
n = len(traces)
6567
faith = [_faithful(t, faithful_threshold) for t in traces]
6668
retr = [_retrieval_good(t, recall_threshold) for t in traces]
6769
have_retrieval = all(r is not None for r in retr)
6870

69-
gen = {"faithful": round(sum(faith) / n, 3) if n else 0.0,
70-
"hallucinating": round(sum(1 for f in faith if not f) / n, 3) if n else 0.0}
71+
gen = {
72+
"faithful": round(sum(faith) / n, 3) if n else 0.0,
73+
"hallucinating": round(sum(1 for f in faith if not f) / n, 3) if n else 0.0,
74+
}
7175

7276
result: dict[str, Any] = {"n": n, "generation": gen, "has_retrieval_signal": have_retrieval}
7377
if not have_retrieval:
7478
result["primary_issue"] = (
75-
"generation: {:.0%} of answers hallucinate".format(gen["hallucinating"])
76-
if gen["hallucinating"] > 0.1 else "generation looks healthy; add a retrieval signal to localize further")
79+
f"generation: {gen['hallucinating']:.0%} of answers hallucinate"
80+
if gen["hallucinating"] > 0.1
81+
else "generation looks healthy; add a retrieval signal to localize further"
82+
)
7783
return result
7884

7985
# 2x2 matrix
@@ -84,21 +90,27 @@ def analyze(traces: list[dict[str, Any]], faithful_threshold: float = 4.0,
8490
matrix = {k: round(v / n, 3) for k, v in cells.items()} if n else cells
8591
# Primary issue heuristic
8692
if matrix["good_hallucinating"] >= matrix["poor_hallucinating"] and matrix["good_hallucinating"] > 0.1:
87-
primary = (f"generation: {matrix['good_hallucinating']:.0%} hallucinate DESPITE good retrieval "
88-
"-> fix the generation prompt/model, not retrieval")
93+
primary = (
94+
f"generation: {matrix['good_hallucinating']:.0%} hallucinate DESPITE good retrieval "
95+
"-> fix the generation prompt/model, not retrieval"
96+
)
8997
elif matrix["poor_hallucinating"] > 0.15:
90-
primary = (f"retrieval: {matrix['poor_hallucinating']:.0%} have poor retrieval AND hallucinate "
91-
"-> fix retrieval (chunking/embeddings) first")
98+
primary = (
99+
f"retrieval: {matrix['poor_hallucinating']:.0%} have poor retrieval AND hallucinate "
100+
"-> fix retrieval (chunking/embeddings) first"
101+
)
92102
else:
93103
primary = "system largely healthy"
94104
result.update({"matrix": matrix, "primary_issue": primary})
95105
return result
96106

97107

98108
def render(report: dict[str, Any]) -> str:
99-
lines = [f"RAG diagnostic (n={report['n']})",
100-
f" generation: faithful={report['generation']['faithful']:.0%} "
101-
f"hallucinating={report['generation']['hallucinating']:.0%}"]
109+
lines = [
110+
f"RAG diagnostic (n={report['n']})",
111+
f" generation: faithful={report['generation']['faithful']:.0%} "
112+
f"hallucinating={report['generation']['hallucinating']:.0%}",
113+
]
102114
if report["has_retrieval_signal"]:
103115
m = report["matrix"]
104116
lines += [

skills/eval_pipeline/06-prompt-regression/scripts/pairwise.py

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
1919
EXIT CODE: 0 if candidate is BETTER, 2 otherwise (worse/tied/inconclusive), 1 on usage error.
2020
"""
21+
# pylint: disable=missing-function-docstring
2122
from __future__ import annotations
2223

2324
import argparse
@@ -27,8 +28,8 @@
2728
from pathlib import Path
2829
from typing import Any
2930

30-
MIN_SAMPLES = 10 # below this, CI is too wide to conclude (see SKILL.md)
31-
TIE_CI_WIDTH = 0.30 # CI brackets 0.5 and is narrower than this => genuine tie
31+
MIN_SAMPLES = 10 # below this, CI is too wide to conclude (see SKILL.md)
32+
TIE_CI_WIDTH = 0.30 # CI brackets 0.5 and is narrower than this => genuine tie
3233

3334
try:
3435
import numpy as _np
@@ -94,8 +95,14 @@ def verdict_for(wins: list[str], candidate: str, baseline: str, n_iter: int = 10
9495
verdict = "TIED (CI brackets 0.5, narrow)"
9596
else:
9697
verdict = "INCONCLUSIVE (CI too wide — need more samples)"
97-
return {"n": n, "candidate_win_rate": round(cand, 3), "baseline_win_rate": round(base, 3),
98-
"tie_rate": round(tie, 3), "candidate_ci95": [round(ci[0], 3), round(ci[1], 3)], "verdict": verdict}
98+
return {
99+
"n": n,
100+
"candidate_win_rate": round(cand, 3),
101+
"baseline_win_rate": round(base, 3),
102+
"tie_rate": round(tie, 3),
103+
"candidate_ci95": [round(ci[0], 3), round(ci[1], 3)],
104+
"verdict": verdict,
105+
}
99106

100107

101108
def analyze(comparisons: list[dict[str, Any]], candidate: str, baseline: str, n_iter: int = 1000) -> dict[str, Any]:
@@ -108,22 +115,42 @@ def analyze(comparisons: list[dict[str, Any]], candidate: str, baseline: str, n_
108115

109116

110117
def render(report: dict[str, Any]) -> str:
111-
lines = [f"Pairwise: candidate='{report['candidate']}' vs baseline='{report['baseline']}'",
112-
f"{'Dimension':<16}{'Cand':>7}{'Base':>7}{'Tie':>7} {'95% CI':>14} Verdict",
113-
"-" * 78]
118+
lines = [
119+
f"Pairwise: candidate='{report['candidate']}' vs baseline='{report['baseline']}'",
120+
f"{'Dimension':<16}{'Cand':>7}{'Base':>7}{'Tie':>7} {'95% CI':>14} Verdict",
121+
"-" * 78,
122+
]
114123
for d, v in report["by_dimension"].items():
115124
ci = f"[{v['candidate_ci95'][0]:.2f},{v['candidate_ci95'][1]:.2f}]"
116-
lines.append(f"{d:<16}{v['candidate_win_rate']:>7.0%}{v['baseline_win_rate']:>7.0%}"
117-
f"{v['tie_rate']:>7.0%} {ci:>14} {v['verdict']}")
125+
lines.append(
126+
f"{d:<16}{v['candidate_win_rate']:>7.0%}{v['baseline_win_rate']:>7.0%}"
127+
f"{v['tie_rate']:>7.0%} {ci:>14} {v['verdict']}"
128+
)
118129
return "\n".join(lines)
119130

120131

121132
def _self_test() -> None:
122133
comps = []
123134
# Candidate clearly better on relevance: wins both orders for 15 queries (30 rows).
124135
for i in range(15):
125-
comps.append({"id": f"q{i}", "model_a": "baseline", "model_b": "candidate", "score": 0.0, "dimension": "relevance"})
126-
comps.append({"id": f"q{i}", "model_a": "candidate", "model_b": "baseline", "score": 1.0, "dimension": "relevance"})
136+
comps.append(
137+
{
138+
"id": f"q{i}",
139+
"model_a": "baseline",
140+
"model_b": "candidate",
141+
"score": 0.0,
142+
"dimension": "relevance",
143+
}
144+
)
145+
comps.append(
146+
{
147+
"id": f"q{i}",
148+
"model_a": "candidate",
149+
"model_b": "baseline",
150+
"score": 1.0,
151+
"dimension": "relevance",
152+
}
153+
)
127154
w = winners(comps)
128155
assert all(x == "candidate" for x in w), w[:5]
129156
r = analyze(comps, "candidate", "baseline", n_iter=200)

skills/eval_pipeline/07-redteam/scripts/asr_report.py

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
2222
EXIT: 0 always for reporting (this is diagnostic, not a gate); 1 on usage error.
2323
"""
24+
# pylint: disable=missing-function-docstring
2425
from __future__ import annotations
2526

2627
import argparse
@@ -42,7 +43,7 @@ def _read_jsonl(path: Path) -> list[dict[str, Any]]:
4243
return rows
4344

4445

45-
def _violated(row: dict[str, Any], score_scale: float, harm_threshold: float) -> bool:
46+
def _violated(row: dict[str, Any], _score_scale: float, harm_threshold: float) -> bool:
4647
"""Determine if an attack succeeded (produced a violation)."""
4748
if "violated" in row:
4849
return bool(row["violated"])
@@ -70,8 +71,10 @@ def asr(attacks: list[dict[str, Any]], score_scale: float = 1.0, harm_threshold:
7071
"asr": round(v / len(rows), 3) if rows else 0.0,
7172
"n": len(rows),
7273
"by_vector": {
73-
vec: {"asr": round(sum(1 for r in vr if _violated(r, score_scale, harm_threshold)) / len(vr), 3),
74-
"n": len(vr)}
74+
vec: {
75+
"asr": round(sum(1 for r in vr if _violated(r, score_scale, harm_threshold)) / len(vr), 3),
76+
"n": len(vr),
77+
}
7578
for vec, vr in sorted(vec_rows.items())
7679
},
7780
}
@@ -87,12 +90,19 @@ def over_refusal(rows: list[dict[str, Any]]) -> dict[str, Any]:
8790
for cat, crows in cat_rows.items():
8891
ref = sum(1 for r in crows if r.get("refused"))
8992
by_cat[cat] = {"over_refusal_rate": round(ref / len(crows), 3) if crows else 0.0, "n": len(crows)}
90-
return {"overall_over_refusal": round(total_r / len(rows), 3) if rows else 0.0,
91-
"n_benign": len(rows), "by_category": by_cat}
92-
93-
94-
def analyze(attacks: list[dict[str, Any]], over_refusals: list[dict[str, Any]] | None,
95-
score_scale: float = 1.0, harm_threshold: float = 1.0) -> dict[str, Any]:
93+
return {
94+
"overall_over_refusal": round(total_r / len(rows), 3) if rows else 0.0,
95+
"n_benign": len(rows),
96+
"by_category": by_cat,
97+
}
98+
99+
100+
def analyze(
101+
attacks: list[dict[str, Any]],
102+
over_refusals: list[dict[str, Any]] | None,
103+
score_scale: float = 1.0,
104+
harm_threshold: float = 1.0,
105+
) -> dict[str, Any]:
96106
out: dict[str, Any] = {"attack": asr(attacks, score_scale, harm_threshold)}
97107
out["over_refusal"] = over_refusal(over_refusals) if over_refusals else None
98108
return out
@@ -106,8 +116,10 @@ def render(report: dict[str, Any]) -> str:
106116
lines.append(f" {cat:<22} ASR={v['asr']:.0%} (n={v['n']}) [{vecs}]")
107117
o = report["over_refusal"]
108118
if o is None:
109-
lines.append("Over-Refusal: NOT MEASURED — ASR alone is misleading "
110-
"(a system that refuses everything shows ASR=0%). Add a benign over-refusal set.")
119+
lines.append(
120+
"Over-Refusal: NOT MEASURED — ASR alone is misleading "
121+
"(a system that refuses everything shows ASR=0%). Add a benign over-refusal set."
122+
)
111123
else:
112124
lines.append(f"Over-Refusal Rate (overall={o['overall_over_refusal']:.0%}, n={o['n_benign']})")
113125
for cat, v in sorted(o["by_category"].items()):

0 commit comments

Comments
 (0)