Spaces:
Sleeping
Sleeping
| from __future__ import annotations | |
| from collections import deque | |
| class CurriculumEngine: | |
| def __init__(self): | |
| self.tasks = ["easy", "medium", "hard", "bonus", "security", "database", "failover"] | |
| self.scores: dict[str, deque] = { | |
| task_id: deque(maxlen=5) for task_id in self.tasks | |
| } | |
| self.mastery: dict[str, int] = {task_id: 0 for task_id in self.tasks} | |
| self._hints: dict[str, str] = { | |
| "easy": "Focus on the service with highest memory_percent. Read its logs before acting.", | |
| "medium": "Follow the dependency map backwards from the erroring service to find the root cause.", | |
| "hard": "All services look green. Check WARN-level logs and business metrics, not error rates.", | |
| "bonus": "There are two independent failures. Fix each one separately — do not conflate them.", | |
| "security": "Look for repeated login failures from the same IP range in the access logs.", | |
| "database": "Check for sequential scans in slow query logs. The fix is structural, not a restart.", | |
| "failover": "Read the failover runbook first. Not all services are safe — check compliance constraints.", | |
| } | |
| self._rotation_index = 0 | |
| self._total_episodes_recorded = 0 | |
| def _ensure_task(self, task_id: str) -> None: | |
| if task_id not in self.scores: | |
| raise ValueError(f"Unknown task_id: {task_id}") | |
| def record_episode(self, task_id: str, score: float) -> None: | |
| self._ensure_task(task_id) | |
| self.scores[task_id].append(float(score)) | |
| self._total_episodes_recorded += 1 | |
| self._update_mastery(task_id) | |
| def _update_mastery(self, task_id: str) -> None: | |
| self._ensure_task(task_id) | |
| rolling_avg = self.get_rolling_avg(task_id) | |
| if rolling_avg > 0.75 and self.mastery[task_id] < 3: | |
| self.mastery[task_id] += 1 | |
| elif rolling_avg < 0.30 and self.mastery[task_id] > 0: | |
| self.mastery[task_id] -= 1 | |
| def get_mastery(self, task_id: str) -> int: | |
| self._ensure_task(task_id) | |
| return self.mastery[task_id] | |
| def get_rolling_avg(self, task_id: str) -> float: | |
| self._ensure_task(task_id) | |
| recent_scores = self.scores[task_id] | |
| if not recent_scores: | |
| return 0.0 | |
| return sum(recent_scores) / len(recent_scores) | |
| def should_scaffold(self, task_id: str) -> bool: | |
| self._ensure_task(task_id) | |
| return len(self.scores[task_id]) >= 3 and self.get_rolling_avg(task_id) < 0.30 | |
| def get_hint(self, task_id: str) -> str: | |
| self._ensure_task(task_id) | |
| return self._hints[task_id] | |
| def _get_non_mastered_tasks(self) -> list[str]: | |
| return [task_id for task_id in self.tasks if self.mastery[task_id] < 3] | |
| def _sorted_candidates(self) -> list[str]: | |
| candidates = self._get_non_mastered_tasks() | |
| return sorted( | |
| candidates, | |
| key=lambda task_id: (self.get_rolling_avg(task_id), self.tasks.index(task_id)), | |
| ) | |
| def get_recommended_task(self) -> str: | |
| candidates = self._sorted_candidates() | |
| if not candidates: | |
| return "bonus" | |
| return candidates[0] | |
| def get_next_curriculum_task(self) -> str: | |
| candidates = self._sorted_candidates() | |
| if not candidates: | |
| return "bonus" | |
| task_id = candidates[self._rotation_index % len(candidates)] | |
| self._rotation_index = (self._rotation_index + 1) % len(candidates) | |
| return task_id | |
| def get_status(self) -> dict: | |
| mastery_labels = { | |
| 0: "novice", | |
| 1: "intermediate", | |
| 2: "advanced", | |
| 3: "mastered", | |
| } | |
| tasks = {} | |
| for task_id in self.tasks: | |
| scaffold_needed = self.should_scaffold(task_id) | |
| tasks[task_id] = { | |
| "mastery_level": self.mastery[task_id], | |
| "mastery_label": mastery_labels[self.mastery[task_id]], | |
| "rolling_avg": self.get_rolling_avg(task_id), | |
| "recent_scores": list(self.scores[task_id]), | |
| "scaffold_needed": scaffold_needed, | |
| "hint": self.get_hint(task_id) if scaffold_needed else None, | |
| } | |
| return { | |
| "tasks": tasks, | |
| "recommended_task": self.get_recommended_task(), | |
| "total_episodes_recorded": self._total_episodes_recorded, | |
| } | |