| from typing import List, Optional | |
| from codelens_env.models import GroundTruthIssue, Action | |
| def keyword_overlap(body: str, keywords: List[str]) -> float: | |
| """Returns 0.0–1.0 confidence score based on keyword coverage.""" | |
| if not body or not keywords: | |
| return 0.5 # Neutral credit for missing body or keywords | |
| body_lower = body.lower() | |
| hits = sum(1 for kw in keywords if kw.lower() in body_lower) | |
| # Roadmap logic: min(1.0, hits / max(4, len(keywords) * 0.6)) | |
| threshold = max(4, len(keywords) * 0.6) | |
| return min(1.0, hits / threshold) | |
| def find_best_match(action: Action, ground_truth: List[GroundTruthIssue], already_matched: set) -> Optional[GroundTruthIssue]: | |
| """Line-number match (exact-ish) OR category+file match.""" | |
| for gt in ground_truth: | |
| if gt.id in already_matched: | |
| continue | |
| # Line number match within 3 lines | |
| line_match = (action.line_number is not None and | |
| abs(action.line_number - gt.line_number) <= 3) | |
| # Category and filename match | |
| cat_match = (action.category == gt.category and | |
| action.filename == gt.filename) | |
| if line_match or cat_match: | |
| return gt | |
| return None | |