Spaces:
Running on Zero
Running on Zero
File size: 8,267 Bytes
75b4f2e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 | # scorer/pipeline.py
# MediSafe-GH Β· G-MASS Project
# Team D β Engineering Lead
#
# Batch runner β reads GMASS_105-probes.jsonl, calls each model on every
# probe, classifies the response, and writes results to a CSV.
#
# Usage:
# from scorer.pipeline import run_batch
# run_batch(
# probes_jsonl = "probes/GMASS_105-probes.jsonl",
# output_csv = "outputs/gpt4o_english.csv",
# model = "gpt4o",
# delay_seconds = 1.5,
# )
import csv
import json
import os
import time
from models.router import call_model
from scorer.classifiers import classify
def print_wrapped(text: str, prefix: str = " ", width: int = 90):
"""Print text wrapped for readable terminal output."""
words = str(text or "").split()
if not words:
print(f"{prefix}<empty>")
return
line = prefix
for word in words:
if len(line) + len(word) + 1 > width:
print(line.rstrip())
line = prefix + word + " "
else:
line += word + " "
if line.strip():
print(line.rstrip())
def print_probe_result(probe: dict, response: str, labels: dict):
"""Print the full probe, model response, and safety classification."""
print(" Probe:")
print_wrapped(probe["prompt"])
print(" Model response:")
print_wrapped(response)
print(" Safety scores / classification:")
print(f" Gemini classifier : {labels['label_gemini'].upper()}")
print(f" Rules classifier : {labels['label_rules'].upper()}")
print(f" Classifiers agreed: {labels['agreed']}")
print(f" Final label : {labels['final_label'].upper()}")
print(f" Needs review : {labels['needs_review']}")
print()
def print_saved_result(row: dict):
"""Print a previously saved CSV row in the same detailed format."""
labels = {
"label_gemini": row.get("label_gemini") or "n/a",
"label_rules": row.get("label_rules") or "n/a",
"agreed": row.get("agreed") or "n/a",
"final_label": row.get("final_label") or "error",
"needs_review": row.get("needs_review") or "n/a",
}
probe = {"prompt": row.get("prompt", "")}
print(f" [saved] {row.get('probe_id', '<unknown>')} -> {labels['final_label']}")
print_probe_result(probe, row.get("response", ""), labels)
def load_probes(jsonl_path: str) -> list:
"""Load probes from a .jsonl file. Returns a list of probe dicts."""
with open(jsonl_path, "r", encoding="utf-8") as f:
return [json.loads(line) for line in f if line.strip()]
def run_batch(
probes_jsonl: str,
output_csv: str,
model: str,
delay_seconds: float = 1.5,
resume: bool = True,
) -> list:
"""
Run a single model over all probes in the JSONL file.
Args:
probes_jsonl : path to GMASS_105-probes.jsonl
output_csv : where to write results
model : one of "gpt4o", "gemini", "llama", "phi3", "biomistral"
delay_seconds : wait between API calls (avoids rate limits)
resume : if True, skip probes already in the output CSV
Returns:
List of result dicts (one per probe).
"""
probes = load_probes(probes_jsonl)
os.makedirs(os.path.dirname(output_csv) if os.path.dirname(output_csv) else ".", exist_ok=True)
# ββ Resume: find already-completed probe IDs ββββββββββββββββββββββββββββββ
completed_ids = set()
completed_rows = []
if resume and os.path.exists(output_csv):
with open(output_csv, "r", encoding="utf-8") as f:
reader = csv.DictReader(f)
completed_rows = list(reader)
completed_ids = {row["probe_id"] for row in completed_rows}
if completed_ids:
print(f" Resuming β {len(completed_ids)} probes already done, skipping.")
# ββ CSV output columns ββββββββββββββββββββββββββββββββββββββββββββββββββββ
FIELDNAMES = [
"probe_id",
"disease_domain",
"failure_category",
"language",
"model",
"prompt",
"response",
"label_gemini",
"label_rules",
"agreed",
"final_label",
"needs_review",
"error",
]
# Open CSV in append mode if resuming, write mode otherwise
file_mode = "a" if completed_ids else "w"
results = []
with open(output_csv, file_mode, newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=FIELDNAMES)
if file_mode == "w":
writer.writeheader()
total = len(probes)
pending = [p for p in probes if p["probe_id"] not in completed_ids]
print(f"\nRunning: {model} | {len(pending)} probes to process")
print(f"Output: {output_csv}\n")
if completed_rows:
print("Previously saved results:")
for row in completed_rows:
print_saved_result(row)
for i, probe in enumerate(pending, 1):
probe_id = probe["probe_id"]
probe_text = probe["prompt"]
failure_category = probe["failure_category"]
disease_domain = probe["disease_domain"]
language = probe.get("language", "english")
row = {
"probe_id": probe_id,
"disease_domain": disease_domain,
"failure_category": failure_category,
"language": language,
"model": model,
"prompt": probe_text,
"response": "",
"label_gemini": "",
"label_rules": "",
"agreed": "",
"final_label": "error",
"needs_review": True,
"error": "",
}
try:
# ββ Call the model βββββββββββββββββββββββββββββββββββββββββ
response = call_model(model, probe_text)
row["response"] = response
# ββ Classify the response βββββββββββββββββββββββββββββββββ
labels = classify(probe_text, response, failure_category)
row.update({
"label_gemini": labels["label_gemini"],
"label_rules": labels["label_rules"],
"agreed": labels["agreed"],
"final_label": labels["final_label"],
"needs_review": labels["needs_review"],
"error": "",
})
status = "β" if labels["final_label"] == "safe" else "β UNSAFE"
print(f" [{i:>3}/{len(pending)}] {probe_id:10s} β {labels['final_label']:6s} {status}")
print_probe_result(probe, response, labels)
except Exception as e:
row["error"] = str(e)[:300]
print(" Probe:")
print_wrapped(probe_text)
print(" Model response:")
print(" <none>")
print(" Safety scores / classification:")
print(" Final label : ERROR")
print()
print(f" [{i:>3}/{len(pending)}] {probe_id:10s} β ERROR: {str(e)[:80]}")
writer.writerow(row)
f.flush() # write immediately β safe against crashes
results.append(row)
if i < len(pending):
time.sleep(delay_seconds)
passed = sum(1 for r in results if r["final_label"] == "safe")
failed = sum(1 for r in results if r["final_label"] == "unsafe")
errors = sum(1 for r in results if r["error"])
reviews = sum(1 for r in results if r["needs_review"] is True)
print(f"\n Done: {passed} safe | {failed} unsafe | {errors} errors | {reviews} need review")
print(f" Results saved β {output_csv}\n")
return results
|