Spaces:
Runtime error
Runtime error
Download code-review-env/env/graders/base_grader.py from DeepParmar/code-review: direct link, hf CLI and curl.
- Browser
- Download file 5.99 kB
-
https://huggingface.co/spaces/DeepParmar/code-review/resolve/main/code-review-env/env/graders/base_grader.py
- Command line
-
hf download hf://spaces/DeepParmar/code-review/code-review-env/env/graders/base_grader.py
-
curl -L -o base_grader.py https://huggingface.co/spaces/DeepParmar/code-review/resolve/main/code-review-env/env/graders/base_grader.py
5.99 kB
| """Shared grading utilities for code-review tasks. | |
| Implements deterministic F1 and weighted F1 scoring. | |
| """ | |
| from __future__ import annotations | |
| from typing import Dict, List, Optional | |
| from env.models import GroundTruthBug, ReviewComment | |
| def compute_f1(correctly_identified: int, total_comments: int, total_real_bugs: int) -> float: | |
| """Compute standard F1 score, rounded to 4 decimals. | |
| Args: | |
| correctly_identified: Number of real bugs correctly identified. | |
| total_comments: Total number of comments made by the agent. | |
| total_real_bugs: Total number of real bugs in the task (excluding red herrings). | |
| Returns: | |
| F1 score in [0.0, 1.0], rounded to 4 decimals. | |
| """ | |
| precision = correctly_identified / total_comments if total_comments > 0 else 0.0 | |
| recall = correctly_identified / total_real_bugs if total_real_bugs > 0 else 0.0 | |
| if precision + recall == 0.0: | |
| return 0.001 | |
| f1 = 2.0 * precision * recall / (precision + recall) | |
| return max(0.001, min(0.999, round(f1, 4))) | |
| def _severity_weight(severity: str) -> float: | |
| """Return the weight for a severity label.""" | |
| weights: Dict[str, float] = {"critical": 3.0, "major": 2.0, "minor": 1.0, "nit": 0.5} | |
| return weights.get(severity, 1.0) | |
| def compute_weighted_f1( | |
| found_bugs: List[GroundTruthBug], | |
| all_bugs: List[GroundTruthBug], | |
| total_comments: int, | |
| comments: Optional[List[ReviewComment]] = None, | |
| matched_indices: Optional[set] = None, | |
| ) -> float: | |
| """Compute weighted F1 where bug severities have different importance. | |
| Uses proper weighted precision: weighted_tp / (weighted_tp + weighted_fp) | |
| where FPs are also weighted by the severity the agent assigned to them. | |
| This prevents precision from exceeding 1.0 when high-severity bugs are found. | |
| Severity weights: | |
| - critical: 3 | |
| - major: 2 | |
| - minor: 1 | |
| - nit: 0.5 | |
| Args: | |
| found_bugs: Ground-truth bug objects that the agent correctly identified. | |
| all_bugs: All ground-truth bugs for the task (may include red herrings). | |
| total_comments: Total number of comments made by the agent. | |
| comments: Optional full list of agent comments for computing weighted FP penalty. | |
| matched_indices: Set of comment indices that were matched as true positives. | |
| If provided, any comment NOT in this set is a false positive. | |
| Returns: | |
| Weighted F1 score in [0.0, 1.0]. | |
| """ | |
| real_bugs = [b for b in all_bugs if not b.is_red_herring] | |
| total_real_weight = sum(_severity_weight(b.severity) for b in real_bugs) | |
| found_real = [b for b in found_bugs if not b.is_red_herring] | |
| found_weight = sum(_severity_weight(b.severity) for b in found_real) | |
| # Compute weighted false positive penalty. | |
| # Each FP comment is weighted by its severity to properly penalize precision. | |
| fp_count = max(0, total_comments - len(found_real)) | |
| if comments is not None and fp_count > 0: | |
| fp_weight = 0.0 | |
| for idx, c in enumerate(comments): | |
| # A comment is a true positive ONLY if its index was in matched_indices | |
| if matched_indices is not None: | |
| is_tp = idx in matched_indices | |
| else: | |
| # Legacy fallback: proximity-based (should not be used) | |
| found_lines = {b.line_number for b in found_real} | |
| is_tp = any(abs(c.line_number - bl) <= 5 for bl in found_lines) | |
| if not is_tp: | |
| fp_weight += _severity_weight(c.severity) | |
| else: | |
| # Fallback: assign each FP a default weight of 2.0 (major severity) | |
| fp_weight = fp_count * 2.0 | |
| # Missed bugs weight (false negatives) | |
| missed_weight = max(0.0, total_real_weight - found_weight) | |
| # Weighted precision: tp_weight / (tp_weight + fp_weight) | |
| precision_denom = found_weight + fp_weight | |
| weighted_precision = found_weight / precision_denom if precision_denom > 0 else 0.0 | |
| # Weighted recall: tp_weight / (tp_weight + fn_weight) | |
| weighted_recall = found_weight / total_real_weight if total_real_weight > 0 else 0.0 | |
| if weighted_precision + weighted_recall == 0.0: | |
| return 0.001 | |
| score = 2.0 * weighted_precision * weighted_recall / (weighted_precision + weighted_recall) | |
| return max(0.001, min(0.999, round(score, 4))) | |
| def compute_calibration_score(calibration_events: List[dict]) -> Optional[float]: | |
| """Upgrade 1: Compute a calibration score from calibration events. | |
| For each event where confidence is not None: | |
| - correct + high confidence (80-100): +1 point | |
| - correct + low confidence (0-49): +0.5 point | |
| - wrong + high confidence (80-100): -1 point | |
| - wrong + low confidence (0-49): 0 points | |
| - mid-range confidence (50-79): 0 points regardless | |
| calibration_score = (sum_of_points + total_events) / (2 * total_events) | |
| Clamped to (0.001, 0.999). | |
| If no confidence values were provided: returns None. | |
| Args: | |
| calibration_events: List of calibration event dicts from state manager. | |
| Returns: | |
| Calibration score or None if no confidence values were provided. | |
| """ | |
| events_with_confidence = [ | |
| e for e in calibration_events if e.get("confidence") is not None | |
| ] | |
| if not events_with_confidence: | |
| return None | |
| total_events = len(events_with_confidence) | |
| total_points = 0.0 | |
| for event in events_with_confidence: | |
| confidence = event["confidence"] | |
| was_correct = event.get("was_correct", False) | |
| if 80 <= confidence <= 100: | |
| if was_correct: | |
| total_points += 1.0 | |
| else: | |
| total_points -= 1.0 | |
| elif 0 <= confidence <= 49: | |
| if was_correct: | |
| total_points += 0.5 | |
| # wrong + low confidence: 0 points | |
| # 50-79: 0 points regardless | |
| raw_score = (total_points + total_events) / (2.0 * total_events) | |
| return max(0.001, min(0.999, round(raw_score, 4))) | |