Update inference.py
Browse files- inference.py +46 -177
inference.py
CHANGED
|
@@ -1,189 +1,58 @@
|
|
| 1 |
-
"""
|
| 2 |
-
inference.py — OpenEnv Baseline Inference Script
|
| 3 |
-
=================================================
|
| 4 |
-
Runs the rule-based classifier against all three tasks defined in
|
| 5 |
-
openenv.yaml and reports per-task scores in the 0.0 → 1.0 range.
|
| 6 |
-
|
| 7 |
-
This script proves reproducibility for the hackathon submission.
|
| 8 |
-
Run it with:
|
| 9 |
-
python inference.py
|
| 10 |
-
|
| 11 |
-
Expected output:
|
| 12 |
-
Task 1 [EASY] — Spam Detection : 1.000 ✅
|
| 13 |
-
Task 2 [MEDIUM] — Support Routing : 0.950 ✅
|
| 14 |
-
Task 3 [HARD] — Phishing / Security : 0.900 ✅
|
| 15 |
-
Overall Score : 0.950
|
| 16 |
-
"""
|
| 17 |
-
|
| 18 |
import numpy as np
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
EmailAction,
|
| 22 |
-
TASK_SPLITS,
|
| 23 |
-
URGENCY_LABELS,
|
| 24 |
-
ROUTING_LABELS,
|
| 25 |
-
RESOLUTION_LABELS,
|
| 26 |
-
)
|
| 27 |
-
|
| 28 |
-
# ── Rule-based classifier (your 95%-accuracy agent) ───────────────────────────
|
| 29 |
-
|
| 30 |
-
_LEGAL_SECURITY_KW = {"lawsuit", "attorney", "sue", "ransomware", "extortion"}
|
| 31 |
-
_BILLING_ESCALATE_KW = {"refund"}
|
| 32 |
|
| 33 |
-
|
| 34 |
-
def
|
| 35 |
-
""
|
| 36 |
-
Deterministic rule-based classifier.
|
| 37 |
-
Returns np.ndarray([urgency, routing, resolution]).
|
| 38 |
-
"""
|
| 39 |
-
kw = set(email.get("keywords", []))
|
| 40 |
context = email.get("context", "").lower()
|
| 41 |
-
|
| 42 |
-
|
|
|
|
| 43 |
return np.array([2, 2, 2], dtype=np.int64)
|
| 44 |
-
|
| 45 |
if context == "security":
|
| 46 |
-
if kw & _LEGAL_SECURITY_KW or ("hacked" in kw and "breach" in kw):
|
| 47 |
-
return np.array([2, 2, 2], dtype=np.int64)
|
| 48 |
return np.array([2, 1, 2], dtype=np.int64)
|
| 49 |
-
|
| 50 |
if context == "billing":
|
| 51 |
-
if kw & _BILLING_ESCALATE_KW:
|
| 52 |
-
return np.array([1, 2, 2], dtype=np.int64)
|
| 53 |
return np.array([1, 0, 1], dtype=np.int64)
|
| 54 |
-
|
| 55 |
-
if context == "tech" or kw & {"crash", "error", "bug", "slow"}:
|
| 56 |
-
return np.array([0, 1, 1], dtype=np.int64)
|
| 57 |
-
|
| 58 |
return np.array([0, 0, 0], dtype=np.int64)
|
| 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 |
-
pred_str = (f"{URGENCY_LABELS[action[0]]} | "
|
| 99 |
-
f"{ROUTING_LABELS[action[1]]} | "
|
| 100 |
-
f"{RESOLUTION_LABELS[action[2]]}")
|
| 101 |
-
corr_str = (f"{URGENCY_LABELS[ca[0]]} | "
|
| 102 |
-
f"{ROUTING_LABELS[ca[1]]} | "
|
| 103 |
-
f"{RESOLUTION_LABELS[ca[2]]}")
|
| 104 |
-
|
| 105 |
-
if raw >= 1.0:
|
| 106 |
-
verdict = "✅ EXACT"
|
| 107 |
-
elif raw > 0:
|
| 108 |
-
verdict = "🔶 PARTIAL"
|
| 109 |
-
elif raw < 0:
|
| 110 |
-
verdict = "🚨 SECURITY MISS"
|
| 111 |
-
else:
|
| 112 |
-
verdict = "❌ WRONG"
|
| 113 |
-
|
| 114 |
-
print(f" #{step+1:02d} [{current_email['difficulty'].upper():<6}] "
|
| 115 |
-
f"{current_email['description'][:35]:<35} "
|
| 116 |
-
f"reward={raw:+.1f} {verdict}")
|
| 117 |
-
if raw < 1.0:
|
| 118 |
-
print(f" Predicted : {pred_str}")
|
| 119 |
-
print(f" Correct : {corr_str}")
|
| 120 |
-
|
| 121 |
-
step += 1
|
| 122 |
-
|
| 123 |
-
# Clamp to [0.0, 1.0] — penalties can push below 0
|
| 124 |
-
final_score = max(0.0, min(1.0, cumulative_score))
|
| 125 |
-
|
| 126 |
-
env_state = env.state()
|
| 127 |
-
assert env_state.terminated, "Episode should be terminated after all steps"
|
| 128 |
-
|
| 129 |
-
return final_score
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
# ── Main ──────────────────────────────────────────────────────────────────────
|
| 133 |
-
|
| 134 |
-
def main():
|
| 135 |
-
print(f"\n{'═' * 62}")
|
| 136 |
-
print(" EMAIL GATEKEEPER — OpenEnv Baseline Inference")
|
| 137 |
-
print(" Meta x PyTorch Hackathon | Reproducibility Report")
|
| 138 |
-
print(f"{'═' * 62}")
|
| 139 |
-
|
| 140 |
-
tasks = [
|
| 141 |
-
("easy", "Task 1 [EASY] — Spam Detection "),
|
| 142 |
-
("medium", "Task 2 [MEDIUM] — Support Routing "),
|
| 143 |
-
("hard", "Task 3 [HARD] — Phishing / Security "),
|
| 144 |
-
]
|
| 145 |
-
|
| 146 |
-
scores = {}
|
| 147 |
-
all_correct = 0
|
| 148 |
-
all_total = 0
|
| 149 |
-
|
| 150 |
-
for task_id, label in tasks:
|
| 151 |
-
score = run_task(task_id, verbose=True)
|
| 152 |
-
scores[task_id] = score
|
| 153 |
-
|
| 154 |
-
n = len(TASK_SPLITS[task_id])
|
| 155 |
-
all_total += n
|
| 156 |
-
|
| 157 |
-
icon = "✅" if score >= 0.8 else ("⚠️ " if score >= 0.5 else "❌")
|
| 158 |
-
print(f"\n {label}: {score:.3f} {icon}")
|
| 159 |
-
|
| 160 |
-
# Overall score = weighted average by number of emails per task
|
| 161 |
-
weights = {t: len(TASK_SPLITS[t]) for t in scores}
|
| 162 |
-
total_weight = sum(weights.values())
|
| 163 |
-
overall = sum(scores[t] * weights[t] / total_weight for t in scores)
|
| 164 |
-
|
| 165 |
-
print(f"\n{'─' * 62}")
|
| 166 |
-
print(f" {'Overall Score (weighted avg)':<42}: {overall:.3f}")
|
| 167 |
-
print(f" {'Total Emails Evaluated':<42}: {total_weight}")
|
| 168 |
-
|
| 169 |
-
# Per-task summary table
|
| 170 |
-
print(f"\n {'Task':<10} {'Emails':>7} {'Score':>8} {'Status':>10}")
|
| 171 |
-
print(f" {'─'*10} {'─'*7} {'─'*8} {'─'*10}")
|
| 172 |
-
for task_id, label in tasks:
|
| 173 |
-
n = len(TASK_SPLITS[task_id])
|
| 174 |
-
s = scores[task_id]
|
| 175 |
-
status = "PASS ✅" if s >= 0.8 else ("WARN ⚠️ " if s >= 0.5 else "FAIL ❌")
|
| 176 |
-
print(f" {task_id:<10} {n:>7} {s:>8.3f} {status:>10}")
|
| 177 |
-
|
| 178 |
-
print(f"\n{'═' * 62}\n")
|
| 179 |
-
|
| 180 |
-
# Return scores dict for programmatic use (e.g. CI pipelines)
|
| 181 |
-
return {
|
| 182 |
-
"task_scores": scores,
|
| 183 |
-
"overall": round(overall, 4),
|
| 184 |
-
"total_emails": total_weight,
|
| 185 |
-
}
|
| 186 |
-
|
| 187 |
|
| 188 |
if __name__ == "__main__":
|
| 189 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import numpy as np
|
| 2 |
+
import sys
|
| 3 |
+
from env import EmailTriageEnv
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
|
| 5 |
+
# Classification logic (Wahi jo app.py mein hai)
|
| 6 |
+
def classify_email(email):
|
| 7 |
+
kw = set(email.get("keywords", []))
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
context = email.get("context", "").lower()
|
| 9 |
+
|
| 10 |
+
# Simple logic to match your agent
|
| 11 |
+
if context == "legal" or any(k in kw for k in ["lawsuit", "attorney", "sue"]):
|
| 12 |
return np.array([2, 2, 2], dtype=np.int64)
|
|
|
|
| 13 |
if context == "security":
|
|
|
|
|
|
|
| 14 |
return np.array([2, 1, 2], dtype=np.int64)
|
|
|
|
| 15 |
if context == "billing":
|
|
|
|
|
|
|
| 16 |
return np.array([1, 0, 1], dtype=np.int64)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
return np.array([0, 0, 0], dtype=np.int64)
|
| 18 |
|
| 19 |
+
def run_inference():
|
| 20 |
+
# Meta usually tests all tasks: easy, medium, hard
|
| 21 |
+
tasks = ["easy", "medium", "hard"]
|
| 22 |
+
|
| 23 |
+
for task_name in tasks:
|
| 24 |
+
env = EmailTriageEnv(task=task_name, shuffle=False)
|
| 25 |
+
obs, info = env.reset(seed=42)
|
| 26 |
+
|
| 27 |
+
# --- [START] TAG ---
|
| 28 |
+
print(f"[START] task={task_name}", flush=True)
|
| 29 |
+
|
| 30 |
+
terminated = False
|
| 31 |
+
step_count = 0
|
| 32 |
+
total_reward = 0.0
|
| 33 |
+
|
| 34 |
+
# Queue se emails uthao
|
| 35 |
+
emails = list(env._queue)
|
| 36 |
+
|
| 37 |
+
while not terminated and step_count < len(emails):
|
| 38 |
+
email = emails[step_count]
|
| 39 |
+
action = classify_email(email)
|
| 40 |
+
|
| 41 |
+
# Env step
|
| 42 |
+
obs, reward, terminated, truncated, info = env.step(action)
|
| 43 |
+
total_reward += reward
|
| 44 |
+
|
| 45 |
+
# --- [STEP] TAG ---
|
| 46 |
+
# Grader ko har step ka hisaab chahiye
|
| 47 |
+
print(f"[STEP] step={step_count+1} reward={reward:.2f}", flush=True)
|
| 48 |
+
|
| 49 |
+
step_count += 1
|
| 50 |
+
|
| 51 |
+
# Final Score (Normalized)
|
| 52 |
+
final_score = max(0.0, min(1.0, total_reward))
|
| 53 |
+
|
| 54 |
+
# --- [END] TAG ---
|
| 55 |
+
print(f"[END] task={task_name} score={final_score:.3f} steps={step_count}", flush=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 56 |
|
| 57 |
if __name__ == "__main__":
|
| 58 |
+
run_inference()
|