from typing import List from .models import Action def grade_easy(task, actions: List[Action]) -> float: expected = task["expected"]["category"].lower() for a in actions: if a.action_type == "classify": return 0.999 if (a.category or "").lower() == expected else 0.001 return 0.001 def grade_medium(task, actions: List[Action]) -> float: score = 0.0 expected_cat = task["expected"]["category"].lower() keywords = [k.lower() for k in task["expected"]["keywords"]] for a in actions: if a.action_type == "classify": if (a.category or "").lower() == expected_cat: score += 0.4 break for a in actions: if a.action_type == "reply" and a.content: hits = sum(1 for k in keywords if k in a.content.lower()) score += min(0.6, hits * 0.15) break return round(max(0.001, min(0.999, score)), 4) def grade_hard(task, actions: List[Action]) -> float: score = 0.0 expected_cat = task["expected"]["category"].lower() keywords = [k.lower() for k in task["expected"]["keywords"]] needs_escalation = task["expected"]["requires_escalation"] for a in actions: if a.action_type == "classify": if (a.category or "").lower() == expected_cat: score += 0.2 break for a in actions: if a.action_type == "reply" and a.content: hits = sum(1 for k in keywords if k in a.content.lower()) score += min(0.3, hits * 0.075) break escalated = any(a.action_type == "escalate" for a in actions) if needs_escalation and escalated: score += 0.2 elif not needs_escalation and escalated: score -= 0.1 if any(a.action_type == "close" for a in actions): score += 0.3 return round(max(0.001, min(0.999, score)), 4) GRADERS = { "easy": grade_easy, "medium": grade_medium, "hard": grade_hard, } def grade_task(task, actions: List[Action]) -> float: grader = GRADERS.get(task.get("id", "easy"), grade_easy) raw_score = grader(task, actions) epsilon = 1e-3 return float(max(epsilon, min(raw_score, 1 - epsilon)))