| import ast |
| from typing import Any |
|
|
|
|
| def _extract_text(payload: Any) -> str: |
| if payload is None: |
| return "" |
| if isinstance(payload, str): |
| return payload |
| if isinstance(payload, dict): |
| for key in ("candidate", "code", "action", "text", "output"): |
| value = payload.get(key) |
| if isinstance(value, str): |
| return value |
| return str(payload) |
| return str(payload) |
|
|
|
|
| class EasyGrader: |
| """Easy: syntax/lint style signal.""" |
|
|
| def grade(self, payload: Any) -> float: |
| text = _extract_text(payload) |
| if not text.strip(): |
| return 0.10 |
| try: |
| ast.parse(text) |
| return 0.90 |
| except SyntaxError: |
| return 0.20 |
|
|
|
|
| class MediumGrader: |
| """Medium: safety patch signal.""" |
|
|
| _unsafe = ("eval(", "exec(", "compile(", "__import__(") |
|
|
| def grade(self, payload: Any) -> float: |
| text = _extract_text(payload) |
| if not text.strip(): |
| return 0.20 |
| lower = text.lower() |
| score = 0.20 |
| if all(tok not in lower for tok in self._unsafe): |
| score += 0.45 |
| if "def " in lower: |
| score += 0.25 |
| return max(0.01, min(0.99, round(score, 2))) |
|
|
|
|
| class HardGrader: |
| """Hard: structure + typing signal.""" |
|
|
| def grade(self, payload: Any) -> float: |
| text = _extract_text(payload) |
| if not text.strip(): |
| return 0.30 |
| try: |
| tree = ast.parse(text) |
| except SyntaxError: |
| return 0.25 |
|
|
| score = 0.25 |
| has_def_or_class = any( |
| isinstance(node, (ast.FunctionDef, ast.ClassDef)) for node in ast.walk(tree) |
| ) |
| if has_def_or_class: |
| score += 0.30 |
|
|
| has_annotations = False |
| for node in ast.walk(tree): |
| if isinstance(node, ast.AnnAssign): |
| has_annotations = True |
| break |
| if isinstance(node, ast.FunctionDef): |
| if node.returns is not None: |
| has_annotations = True |
| break |
| if any(arg.annotation is not None for arg in node.args.args): |
| has_annotations = True |
| break |
| if has_annotations: |
| score += 0.35 |
|
|
| return max(0.01, min(0.99, round(score, 2))) |
|
|