""" Task generation for AETHER-TaskFlow. Generates realistic workflow tasks across three difficulty levels: easy – stable, predictable, moderate resources medium – dynamic priorities, tighter deadlines hard – scarce resources, high uncertainty, adversarial failures """ from __future__ import annotations import random from typing import List, Optional, Tuple from models import TaskInfo # --------------------------------------------------------------------------- # Task templates – drawn from realistic enterprise/ops domains # --------------------------------------------------------------------------- _TASK_TEMPLATES: List[Tuple[str, str]] = [ # (name_template, category) ("Email triage: {volume} messages", "communication"), ("Code review: PR #{pr_id}", "engineering"), ("Data pipeline: {dataset} ETL", "data"), ("Customer support ticket #{tid}", "support"), ("Security audit: {module} module", "security"), ("Performance optimization: {service}", "engineering"), ("Database backup: {db_name}", "infrastructure"), ("Report generation: {report_type}", "analytics"), ("Incident response: {severity} alert", "operations"), ("Content moderation: batch #{bid}", "moderation"), ("API rate-limit review: {api_name}", "infrastructure"), ("ML model retraining: {model_name}", "ml"), ("Budget reconciliation: {quarter}", "finance"), ("Compliance check: {regulation}", "legal"), ("System health scan: {region}", "operations"), ] _FILL_VALUES: dict = { "volume": ["50", "120", "300", "500"], "pr_id": ["1042", "2381", "9001", "4417"], "dataset": ["sales_Q3", "user_events", "inventory", "logs_prod"], "tid": ["55123", "10984", "30021", "77654"], "module": ["auth", "payments", "admin", "reporting"], "service": ["checkout", "search", "recommendations", "notifications"], "db_name": ["prod_main", "analytics_dw", "user_db", "logs_archive"], "report_type": ["weekly_KPI", "SLA_breach", "revenue_forecast", "churn"], "severity": ["P1", "P2", "P3"], "bid": ["4401", "8812", "1123"], "api_name": ["stripe", "twilio", "sendgrid", "maps"], "model_name": ["churn_v3", "fraud_detector", "recommender_v2"], "quarter": ["Q3-2025", "Q4-2025", "Q1-2026"], "regulation": ["GDPR", "SOC2", "HIPAA", "PCI-DSS"], "region": ["us-east-1", "eu-west-2", "ap-southeast-1"], } def _fill_template(template: str, rng: random.Random) -> str: result = template for key, choices in _FILL_VALUES.items(): placeholder = "{" + key + "}" if placeholder in result: result = result.replace(placeholder, rng.choice(choices)) return result # --------------------------------------------------------------------------- # Difficulty profiles # --------------------------------------------------------------------------- _PROFILES: dict = { "easy": { "n_tasks": 5, "priority_range": (0.4, 0.9), "deadline_range": (4, 8), "uncertainty_range": (0.05, 0.35), "value_range": (8.0, 20.0), "energy_cost_range": (0.5, 1.5), "budget_cost_range": (1.0, 5.0), "initial_time": 10, "initial_energy": 12.0, "initial_budget": 60.0, "max_steps": 10, }, "medium": { "n_tasks": 8, "priority_range": (0.3, 1.0), "deadline_range": (2, 6), "uncertainty_range": (0.15, 0.65), "value_range": (5.0, 25.0), "energy_cost_range": (0.8, 2.5), "budget_cost_range": (2.0, 10.0), "initial_time": 10, "initial_energy": 10.0, "initial_budget": 50.0, "max_steps": 10, }, "hard": { "n_tasks": 12, "priority_range": (0.2, 1.0), "deadline_range": (1, 4), "uncertainty_range": (0.35, 0.95), "value_range": (3.0, 30.0), "energy_cost_range": (1.2, 4.0), "budget_cost_range": (5.0, 20.0), "initial_time": 10, "initial_energy": 8.0, "initial_budget": 40.0, "max_steps": 10, }, } def get_profile(difficulty: str) -> dict: return _PROFILES[difficulty] def generate_tasks(difficulty: str, seed: Optional[int] = None) -> List[TaskInfo]: """Generate a task queue for the given difficulty level.""" rng = random.Random(seed) profile = _PROFILES[difficulty] tasks: List[TaskInfo] = [] used_templates = rng.choices(range(len(_TASK_TEMPLATES)), k=profile["n_tasks"]) for i, t_idx in enumerate(used_templates): name_template, category = _TASK_TEMPLATES[t_idx] name = _fill_template(name_template, rng) priority = rng.uniform(*profile["priority_range"]) deadline = rng.randint(*profile["deadline_range"]) uncertainty = rng.uniform(*profile["uncertainty_range"]) value = rng.uniform(*profile["value_range"]) energy_cost = rng.uniform(*profile["energy_cost_range"]) budget_cost = rng.uniform(*profile["budget_cost_range"]) tasks.append( TaskInfo( task_id=i, name=name, priority=priority, deadline=deadline, uncertainty=uncertainty, value=value, required_energy=energy_cost, required_budget=budget_cost, category=category, ) ) # Sort by priority descending so agent sees most urgent first tasks.sort(key=lambda t: t.priority, reverse=True) # Re-index after sort for idx, t in enumerate(tasks): t.task_id = idx return tasks def apply_dynamic_updates( tasks: List[TaskInfo], step: int, difficulty: str, rng: random.Random, ) -> List[TaskInfo]: """ Apply stochastic dynamic updates to the task queue (medium/hard only). - Priority drift - Deadline tightening - Uncertainty spikes """ if difficulty == "easy": return tasks for task in tasks: if task.status != "pending": continue # NOTE: deadline countdown is handled centrally in aether_env.py # _step_impl() to avoid double-decrement on hard mode. # Priority drift ±0.1 drift = rng.uniform(-0.08, 0.12) task.priority = min(1.0, max(0.1, task.priority + drift)) # Uncertainty spike (hard mode) if difficulty == "hard" and rng.random() < 0.15: task.uncertainty = min(0.95, task.uncertainty + rng.uniform(0.1, 0.25)) return tasks