Spaces:
Sleeping
Sleeping
Download env/rewards.py from prathvi9607/SupportOps: direct link, hf CLI and curl.
- Browser
- Download file 6.18 kB
-
https://huggingface.co/spaces/prathvi9607/SupportOps/resolve/main/env/rewards.py
- Command line
-
hf download hf://spaces/prathvi9607/SupportOps/env/rewards.py
-
curl -L -o rewards.py https://huggingface.co/spaces/prathvi9607/SupportOps/resolve/main/env/rewards.py
6.18 kB
| """ | |
| Reward calculation system for SupportOps Arena. | |
| Implements dense reward shaping with detailed breakdown tracking. | |
| """ | |
| from typing import Any | |
| from env.state import EnvState, TaskLevel | |
| from env.actions import ActionType | |
| # βββ Reward Events ββββββββββββββββββββββββββββββββββββββββββββ | |
| REWARD_EVENTS = { | |
| # Positive rewards | |
| "DIAGNOSTIC_ACTION": +0.10, | |
| "CORRECT_TARGETED_DIAGNOSTIC": +0.20, | |
| "EVIDENCE_BEFORE_RISKY": +0.05, | |
| "CORRECT_REMEDIATION": +0.40, | |
| "OPTIMAL_RESOLUTION": +0.20, # Only if steps_used <= min_required_steps | |
| "JUSTIFIED_ESCALATION": +0.30, | |
| "TERMINAL_CORRECT": +1.00, | |
| # Negative rewards | |
| "REDUNDANT_ACTION": -0.05, | |
| "RISKY_WITHOUT_DIAGNOSTIC": -0.15, | |
| "INCORRECT_REMEDIATION": -0.25, | |
| "HARMFUL_ACTION": -0.35, | |
| "PREMATURE_CLOSURE": -0.40, | |
| "BUDGET_EXCEEDED_PER_STEP": -0.02, | |
| "PREMATURE_ESCALATION": -0.20, # Escalate before unlock | |
| "UNJUSTIFIED_ESCALATION": -0.20, # Escalate without enough evidence | |
| } | |
| # βββ Configuration ββββββββββββββββββββββββββββββββββββββββββββ | |
| MIN_STEPS_FOR_OPTIMAL = { | |
| TaskLevel.EASY: 4, | |
| TaskLevel.MEDIUM: 7, | |
| TaskLevel.HARD: 12, | |
| } | |
| MAX_POSSIBLE_REWARD = { | |
| TaskLevel.EASY: 2.05, | |
| TaskLevel.MEDIUM: 2.35, | |
| TaskLevel.HARD: 2.75, | |
| } | |
| class RewardCalculator: | |
| """ | |
| Calculates rewards for agent actions with dense reward shaping. | |
| Tracks individual reward events for interpretability. | |
| """ | |
| def calculate( | |
| self, | |
| events: list[str], | |
| state: EnvState, | |
| action: ActionType | |
| ) -> tuple[float, dict[str, Any]]: | |
| """ | |
| Calculate total reward from a list of reward event keys. | |
| Args: | |
| events: List of reward event keys (e.g., ["DIAGNOSTIC_ACTION", "CORRECT_TARGETED_DIAGNOSTIC"]) | |
| state: Current environment state | |
| action: Action that was taken | |
| Returns: | |
| Tuple of (total_reward, breakdown_dict) | |
| - total_reward: Sum of all event rewards | |
| - breakdown_dict: Dictionary mapping event keys to their reward values | |
| """ | |
| breakdown = {} | |
| total_reward = 0.0 | |
| for event_key in events: | |
| if event_key not in REWARD_EVENTS: | |
| raise ValueError(f"Unknown reward event: {event_key}") | |
| reward_value = REWARD_EVENTS[event_key] | |
| breakdown[event_key] = reward_value | |
| total_reward += reward_value | |
| return total_reward, breakdown | |
| def calculate_episode_score( | |
| self, | |
| cumulative_reward: float, | |
| task_level: TaskLevel | |
| ) -> float: | |
| """ | |
| Normalize cumulative reward to episode score in [0, 1]. | |
| Args: | |
| cumulative_reward: Total reward accumulated during episode | |
| task_level: Difficulty level of the task | |
| Returns: | |
| Normalized score clamped to [0, 1] | |
| """ | |
| max_reward = MAX_POSSIBLE_REWARD[task_level] | |
| score = cumulative_reward / max_reward | |
| return max(0.0, min(1.0, score)) | |
| def is_optimal_resolution( | |
| self, | |
| step_count: int, | |
| task_level: TaskLevel | |
| ) -> bool: | |
| """ | |
| Check if resolution was achieved within optimal step count. | |
| Args: | |
| step_count: Number of steps taken | |
| task_level: Difficulty level | |
| Returns: | |
| True if step_count <= minimum optimal steps for this level | |
| """ | |
| return step_count <= MIN_STEPS_FOR_OPTIMAL[task_level] | |
| def get_max_possible_reward(self, task_level: TaskLevel) -> float: | |
| """Get maximum possible reward for a task level.""" | |
| return MAX_POSSIBLE_REWARD[task_level] | |
| def validate_events(self, events: list[str]) -> bool: | |
| """ | |
| Validate that all event keys are recognized. | |
| Args: | |
| events: List of event keys to validate | |
| Returns: | |
| True if all events are valid | |
| Raises: | |
| ValueError: If any event key is not recognized | |
| """ | |
| for event in events: | |
| if event not in REWARD_EVENTS: | |
| raise ValueError(f"Unknown reward event: {event}") | |
| return True | |
| def get_reward_event_description(event_key: str) -> str: | |
| """Get human-readable description of a reward event.""" | |
| descriptions = { | |
| "DIAGNOSTIC_ACTION": "Performed a diagnostic action to gather information", | |
| "CORRECT_TARGETED_DIAGNOSTIC": "Performed diagnostic action that targets the actual root cause", | |
| "EVIDENCE_BEFORE_RISKY": "Gathered evidence before taking risky action", | |
| "CORRECT_REMEDIATION": "Applied the correct fix for the root cause", | |
| "OPTIMAL_RESOLUTION": "Resolved issue within optimal step count", | |
| "JUSTIFIED_ESCALATION": "Escalated with sufficient evidence", | |
| "TERMINAL_CORRECT": "Episode terminated with correct resolution", | |
| "REDUNDANT_ACTION": "Repeated an action already performed", | |
| "RISKY_WITHOUT_DIAGNOSTIC": "Took risky action without prior diagnostics", | |
| "INCORRECT_REMEDIATION": "Applied wrong fix for the root cause", | |
| "HARMFUL_ACTION": "Action caused harm or disruption", | |
| "PREMATURE_CLOSURE": "Closed ticket without proper resolution", | |
| "BUDGET_EXCEEDED_PER_STEP": "Exceeded step budget (per step penalty)", | |
| "PREMATURE_ESCALATION": "Escalated before gathering minimum evidence", | |
| "UNJUSTIFIED_ESCALATION": "Escalated without sufficient diagnostic evidence", | |
| } | |
| return descriptions.get(event_key, f"Unknown event: {event_key}") | |
| def get_all_positive_events() -> list[str]: | |
| """Get list of all positive reward event keys.""" | |
| return [k for k, v in REWARD_EVENTS.items() if v > 0] | |
| def get_all_negative_events() -> list[str]: | |
| """Get list of all negative reward event keys.""" | |
| return [k for k, v in REWARD_EVENTS.items() if v < 0] | |