Spaces:
Sleeping
Sleeping
I766446 commited on
Commit ·
791a54b
1
Parent(s): 71b519d
Rewrite graders: grade(episode)->float, no external deps, difficulty progression
Browse files- server.py +6 -6
- tasks/easy/grader.py +45 -37
- tasks/hard/grader.py +52 -31
- tasks/medium/grader.py +52 -30
server.py
CHANGED
|
@@ -546,13 +546,13 @@ async def grade_episode(req: GradeRequest):
|
|
| 546 |
raise HTTPException(status_code=400, detail=f"Unknown task_id '{task_id}'")
|
| 547 |
|
| 548 |
try:
|
| 549 |
-
|
| 550 |
-
|
| 551 |
-
|
| 552 |
return {
|
| 553 |
-
"task_id":
|
| 554 |
-
"score":
|
| 555 |
-
"passed":
|
| 556 |
"score_range": [0.0, 1.0],
|
| 557 |
"grader": f"tasks.{task_id}.grader.grade",
|
| 558 |
}
|
|
|
|
| 546 |
raise HTTPException(status_code=400, detail=f"Unknown task_id '{task_id}'")
|
| 547 |
|
| 548 |
try:
|
| 549 |
+
mod = importlib.import_module(f"tasks.{task_id}.grader")
|
| 550 |
+
score = mod.grade(req.episode or {}) # grade(episode) -> float
|
| 551 |
+
score = float(score)
|
| 552 |
return {
|
| 553 |
+
"task_id": task_id,
|
| 554 |
+
"score": round(max(0.005, min(0.995, score)), 4),
|
| 555 |
+
"passed": True,
|
| 556 |
"score_range": [0.0, 1.0],
|
| 557 |
"grader": f"tasks.{task_id}.grader.grade",
|
| 558 |
}
|
tasks/easy/grader.py
CHANGED
|
@@ -1,39 +1,47 @@
|
|
| 1 |
-
"""
|
| 2 |
-
tasks/easy/grader.py
|
| 3 |
-
====================
|
| 4 |
-
Grader for the easy content-moderation task.
|
| 5 |
-
|
| 6 |
-
Exposes a module-level ``grade(output, expected)`` function that the
|
| 7 |
-
OpenEnv submission platform calls to verify this task has a working grader.
|
| 8 |
-
|
| 9 |
-
Also delegates to EasyGrader for full episode scoring.
|
| 10 |
-
"""
|
| 11 |
-
|
| 12 |
from __future__ import annotations
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""tasks/easy/grader.py — OpenEnv-compliant grader for the easy task."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
from __future__ import annotations
|
| 3 |
|
| 4 |
+
_CREDIT = {
|
| 5 |
+
("allow", "allow"): 1.0,
|
| 6 |
+
("flag", "flag"): 1.0,
|
| 7 |
+
("remove", "remove"): 1.0,
|
| 8 |
+
("flag", "remove"): 0.5,
|
| 9 |
+
("remove", "flag"): 0.5,
|
| 10 |
+
("flag", "allow"): 0.5,
|
| 11 |
+
("allow", "flag"): 0.5,
|
| 12 |
+
("allow", "remove"): 0.0,
|
| 13 |
+
("remove", "allow"): 0.0,
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def grade(episode: dict) -> float:
|
| 18 |
+
"""
|
| 19 |
+
OpenEnv grader — easy difficulty.
|
| 20 |
+
Returns a float strictly in (0.0, 1.0).
|
| 21 |
+
|
| 22 |
+
Weights: decision 50% | evidence 25% | reasoning 15% | efficiency 10%
|
| 23 |
+
"""
|
| 24 |
+
decision = (episode.get("final_decision") or
|
| 25 |
+
episode.get("decision") or "none").lower().strip()
|
| 26 |
+
ground_truth = (episode.get("ground_truth") or
|
| 27 |
+
episode.get("correct_decision") or "none").lower().strip()
|
| 28 |
+
|
| 29 |
+
decision_score = _CREDIT.get((decision, ground_truth), 0.0)
|
| 30 |
+
|
| 31 |
+
all_src = episode.get("all_source_ids") or []
|
| 32 |
+
checked = episode.get("sources_checked") or []
|
| 33 |
+
evidence_score = min(1.0, len(set(checked)) / len(all_src)) if all_src else 1.0
|
| 34 |
+
|
| 35 |
+
all_clm = episode.get("all_claim_ids") or []
|
| 36 |
+
verified = episode.get("claims_verified") or []
|
| 37 |
+
reasoning_score = min(1.0, len(set(verified)) / len(all_clm)) if all_clm else 1.0
|
| 38 |
+
|
| 39 |
+
steps = int(episode.get("step_count") or 0)
|
| 40 |
+
efficiency_score = 1.0 if steps <= 8 else max(0.0, 1.0 - 0.1 * (steps - 8))
|
| 41 |
+
|
| 42 |
+
raw = (0.50 * decision_score +
|
| 43 |
+
0.25 * evidence_score +
|
| 44 |
+
0.15 * reasoning_score +
|
| 45 |
+
0.10 * efficiency_score)
|
| 46 |
+
|
| 47 |
+
return round(max(0.005, min(0.995, raw)), 4)
|
tasks/hard/grader.py
CHANGED
|
@@ -1,37 +1,58 @@
|
|
| 1 |
-
"""
|
| 2 |
-
tasks/hard/grader.py
|
| 3 |
-
====================
|
| 4 |
-
Grader for the hard content-moderation task.
|
| 5 |
-
|
| 6 |
-
Exposes a module-level ``grade(output, expected)`` function that the
|
| 7 |
-
OpenEnv submission platform calls to verify this task has a working grader.
|
| 8 |
-
"""
|
| 9 |
-
|
| 10 |
from __future__ import annotations
|
| 11 |
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
else:
|
| 29 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
|
| 31 |
-
|
|
|
|
|
|
|
| 32 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
|
| 34 |
-
|
| 35 |
-
"""Grade a full episode dict and return score + passed."""
|
| 36 |
-
score = _grader.grade(episode)
|
| 37 |
-
return {"score": score, "passed": True}
|
|
|
|
| 1 |
+
"""tasks/hard/grader.py — OpenEnv-compliant grader for the hard task."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
from __future__ import annotations
|
| 3 |
|
| 4 |
+
_CREDIT = {
|
| 5 |
+
("allow", "allow"): 1.0,
|
| 6 |
+
("flag", "flag"): 1.0,
|
| 7 |
+
("remove", "remove"): 1.0,
|
| 8 |
+
("flag", "remove"): 0.5,
|
| 9 |
+
("remove", "flag"): 0.5,
|
| 10 |
+
("flag", "allow"): 0.5,
|
| 11 |
+
("allow", "flag"): 0.5,
|
| 12 |
+
("allow", "remove"): 0.25, # hard: never 0 for non-trivially wrong
|
| 13 |
+
("remove", "allow"): 0.25,
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def grade(episode: dict) -> float:
|
| 18 |
+
"""
|
| 19 |
+
OpenEnv grader — hard difficulty.
|
| 20 |
+
Returns a float strictly in (0.0, 1.0).
|
| 21 |
+
|
| 22 |
+
Weights: decision 30% | evidence 30% | reasoning 30% | efficiency 10%
|
| 23 |
+
All claims must be verified for full reasoning score.
|
| 24 |
+
"""
|
| 25 |
+
decision = (episode.get("final_decision") or
|
| 26 |
+
episode.get("decision") or "none").lower().strip()
|
| 27 |
+
ground_truth = (episode.get("ground_truth") or
|
| 28 |
+
episode.get("correct_decision") or "none").lower().strip()
|
| 29 |
+
|
| 30 |
+
decision_score = _CREDIT.get((decision, ground_truth), 0.0)
|
| 31 |
+
|
| 32 |
+
all_src = episode.get("all_source_ids") or []
|
| 33 |
+
checked = episode.get("sources_checked") or []
|
| 34 |
+
frac_src = (len(set(checked)) / len(all_src)) if all_src else 1.0
|
| 35 |
+
# Hard requires 60% minimum for any credit
|
| 36 |
+
if frac_src >= 1.0:
|
| 37 |
+
evidence_score = 1.0
|
| 38 |
+
elif frac_src >= 0.60:
|
| 39 |
+
evidence_score = 0.5 + 0.5 * (frac_src - 0.60) / 0.40
|
| 40 |
else:
|
| 41 |
+
evidence_score = frac_src / 0.60 * 0.5
|
| 42 |
+
|
| 43 |
+
all_clm = episode.get("all_claim_ids") or []
|
| 44 |
+
verified = episode.get("claims_verified") or []
|
| 45 |
+
frac_clm = (len(set(verified)) / len(all_clm)) if all_clm else 1.0
|
| 46 |
+
# Hard requires ALL claims verified for full score
|
| 47 |
+
reasoning_score = min(1.0, 0.6 + 0.4 * frac_clm) if frac_clm >= 0.5 else frac_clm * 0.6 / 0.5
|
| 48 |
|
| 49 |
+
steps = int(episode.get("step_count") or 0)
|
| 50 |
+
# Hard: efficiency barely penalised — thorough exploration is rewarded
|
| 51 |
+
efficiency_score = 1.0 if steps <= 18 else max(0.0, 1.0 - 0.05 * (steps - 18))
|
| 52 |
|
| 53 |
+
raw = (0.30 * decision_score +
|
| 54 |
+
0.30 * evidence_score +
|
| 55 |
+
0.30 * reasoning_score +
|
| 56 |
+
0.10 * efficiency_score)
|
| 57 |
|
| 58 |
+
return round(max(0.005, min(0.995, raw)), 4)
|
|
|
|
|
|
|
|
|
tasks/medium/grader.py
CHANGED
|
@@ -1,36 +1,58 @@
|
|
| 1 |
-
"""
|
| 2 |
-
tasks/medium/grader.py
|
| 3 |
-
======================
|
| 4 |
-
Grader for the medium content-moderation task.
|
| 5 |
-
|
| 6 |
-
Exposes a module-level ``grade(output, expected)`` function that the
|
| 7 |
-
OpenEnv submission platform calls to verify this task has a working grader.
|
| 8 |
-
"""
|
| 9 |
-
|
| 10 |
from __future__ import annotations
|
| 11 |
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
|
|
|
| 20 |
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
elif (out, exp) in {
|
| 24 |
-
("flag", "remove"), ("remove", "flag"),
|
| 25 |
-
("flag", "allow"), ("allow", "flag")
|
| 26 |
-
}:
|
| 27 |
-
score = 0.5
|
| 28 |
-
else:
|
| 29 |
-
score = 0.0
|
| 30 |
|
| 31 |
-
|
|
|
|
|
|
|
|
|
|
| 32 |
|
| 33 |
-
|
| 34 |
-
"""Grade a full episode dict and return score + passed."""
|
| 35 |
-
score = _grader.grade(episode)
|
| 36 |
-
return {"score": score, "passed": True}
|
|
|
|
| 1 |
+
"""tasks/medium/grader.py — OpenEnv-compliant grader for the medium task."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
from __future__ import annotations
|
| 3 |
|
| 4 |
+
_CREDIT = {
|
| 5 |
+
("allow", "allow"): 1.0,
|
| 6 |
+
("flag", "flag"): 1.0,
|
| 7 |
+
("remove", "remove"): 1.0,
|
| 8 |
+
("flag", "remove"): 0.5,
|
| 9 |
+
("remove", "flag"): 0.5,
|
| 10 |
+
("flag", "allow"): 0.5,
|
| 11 |
+
("allow", "flag"): 0.5,
|
| 12 |
+
("allow", "remove"): 0.0,
|
| 13 |
+
("remove", "allow"): 0.0,
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def grade(episode: dict) -> float:
|
| 18 |
+
"""
|
| 19 |
+
OpenEnv grader — medium difficulty.
|
| 20 |
+
Returns a float strictly in (0.0, 1.0).
|
| 21 |
+
|
| 22 |
+
Weights: decision 35% | evidence 35% | reasoning 20% | efficiency 10%
|
| 23 |
+
Higher evidence and reasoning requirements than easy.
|
| 24 |
+
"""
|
| 25 |
+
decision = (episode.get("final_decision") or
|
| 26 |
+
episode.get("decision") or "none").lower().strip()
|
| 27 |
+
ground_truth = (episode.get("ground_truth") or
|
| 28 |
+
episode.get("correct_decision") or "none").lower().strip()
|
| 29 |
+
|
| 30 |
+
decision_score = _CREDIT.get((decision, ground_truth), 0.0)
|
| 31 |
+
|
| 32 |
+
all_src = episode.get("all_source_ids") or []
|
| 33 |
+
checked = episode.get("sources_checked") or []
|
| 34 |
+
frac_src = (len(set(checked)) / len(all_src)) if all_src else 1.0
|
| 35 |
+
# Medium requires 75% source coverage for full score
|
| 36 |
+
if frac_src >= 0.75:
|
| 37 |
+
evidence_score = 1.0
|
| 38 |
+
elif frac_src >= 0.50:
|
| 39 |
+
evidence_score = 0.6 + 0.4 * (frac_src - 0.50) / 0.25
|
| 40 |
+
elif frac_src >= 0.33:
|
| 41 |
+
evidence_score = 0.3 + 0.3 * (frac_src - 0.33) / 0.17
|
| 42 |
+
else:
|
| 43 |
+
evidence_score = frac_src / 0.33 * 0.3
|
| 44 |
|
| 45 |
+
all_clm = episode.get("all_claim_ids") or []
|
| 46 |
+
verified = episode.get("claims_verified") or []
|
| 47 |
+
frac_clm = (len(set(verified)) / len(all_clm)) if all_clm else 1.0
|
| 48 |
+
reasoning_score = min(1.0, frac_clm / 0.75) if frac_clm < 0.75 else 1.0
|
| 49 |
|
| 50 |
+
steps = int(episode.get("step_count") or 0)
|
| 51 |
+
efficiency_score = 1.0 if steps <= 12 else max(0.0, 1.0 - 0.1 * (steps - 12))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
|
| 53 |
+
raw = (0.35 * decision_score +
|
| 54 |
+
0.35 * evidence_score +
|
| 55 |
+
0.20 * reasoning_score +
|
| 56 |
+
0.10 * efficiency_score)
|
| 57 |
|
| 58 |
+
return round(max(0.005, min(0.995, raw)), 4)
|
|
|
|
|
|
|
|
|