Prince9868 commited on
Commit
839af89
·
1 Parent(s): ccbd30e

Harden grader compatibility for deep validators

Browse files
Files changed (2) hide show
  1. guardian_openenv/task_graders.py +34 -2
  2. server/app.py +8 -2
guardian_openenv/task_graders.py CHANGED
@@ -18,12 +18,44 @@ from guardian_openenv.models import CurrentDecision
18
  from guardian_openenv.tasks import TASKS_BY_ID
19
 
20
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  def _clamp(value: float) -> float:
22
  """Ensure a score is strictly between 0 and 1."""
23
  return min(max(value, 0.001), 0.999)
24
 
25
 
26
- def _grade_task(task_id: str, *args: Any, environment: Any = None, logs: Any = None, **kwargs: Any) -> float:
27
  """Shared grading logic for any task.
28
 
29
  The function extracts the decision from the environment state (if
@@ -60,7 +92,7 @@ def _grade_task(task_id: str, *args: Any, environment: Any = None, logs: Any = N
60
  breakdown = grade_decision(task, decision, opened_section_ids)
61
  score = _clamp(breakdown["final_score"])
62
 
63
- return score
64
 
65
 
66
  # ---------------------------------------------------------------------------
 
18
  from guardian_openenv.tasks import TASKS_BY_ID
19
 
20
 
21
+ class GradeResult(float):
22
+ """Float-compatible grader result with dict-like access.
23
+
24
+ Some validators treat grader output as a plain float, while others
25
+ expect a mapping like {"score": ..., "grader_breakdown": ...}. This
26
+ class supports both access patterns.
27
+ """
28
+
29
+ def __new__(cls, score: float, breakdown: dict[str, float]):
30
+ obj = float.__new__(cls, score)
31
+ obj.score = score
32
+ obj.grader_breakdown = breakdown
33
+ return obj
34
+
35
+ def __getitem__(self, key: str):
36
+ if key == "score":
37
+ return self.score
38
+ if key == "grader_breakdown":
39
+ return self.grader_breakdown
40
+ raise KeyError(key)
41
+
42
+ def get(self, key: str, default=None):
43
+ if key == "score":
44
+ return self.score
45
+ if key == "grader_breakdown":
46
+ return self.grader_breakdown
47
+ return default
48
+
49
+ def to_dict(self) -> dict[str, object]:
50
+ return {"score": self.score, "grader_breakdown": self.grader_breakdown}
51
+
52
+
53
  def _clamp(value: float) -> float:
54
  """Ensure a score is strictly between 0 and 1."""
55
  return min(max(value, 0.001), 0.999)
56
 
57
 
58
+ def _grade_task(task_id: str, *args: Any, environment: Any = None, logs: Any = None, **kwargs: Any) -> GradeResult:
59
  """Shared grading logic for any task.
60
 
61
  The function extracts the decision from the environment state (if
 
92
  breakdown = grade_decision(task, decision, opened_section_ids)
93
  score = _clamp(breakdown["final_score"])
94
 
95
+ return GradeResult(score, breakdown)
96
 
97
 
98
  # ---------------------------------------------------------------------------
server/app.py CHANGED
@@ -168,8 +168,14 @@ async def grader(request: Request) -> dict:
168
  task_id = env._task.task_id if env._task else TASKS[0].task_id
169
 
170
  # Grade using the task graders (passes environment for state access)
171
- score = _grade_task(task_id, environment=env)
172
- return {"score": score}
 
 
 
 
 
 
173
 
174
 
175
  @app.post("/grade")
 
168
  task_id = env._task.task_id if env._task else TASKS[0].task_id
169
 
170
  # Grade using the task graders (passes environment for state access)
171
+ grade_result = _grade_task(task_id, environment=env)
172
+ score = float(grade_result)
173
+ breakdown = getattr(grade_result, "grader_breakdown", {})
174
+ return {
175
+ "score": score,
176
+ "grader_breakdown": breakdown,
177
+ "message": "Task graded successfully.",
178
+ }
179
 
180
 
181
  @app.post("/grade")