PranavKK1201 commited on
Commit Β·
3a871a0
1
Parent(s): 5144b7e
fixed task 1, 2, 3
Browse files- grader.py +27 -10
- models.py +5 -0
- server/AntiAtropos_environment.py +1 -0
- simulator.py +12 -0
grader.py
CHANGED
|
@@ -60,23 +60,36 @@ class Grade:
|
|
| 60 |
|
| 61 |
Weights deliberately penalise cost heavily so that brute-force
|
| 62 |
SCALE_UP spam cannot achieve a high composite even with perfect uptime.
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
|
|
|
| 66 |
"""
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 72 |
|
| 73 |
def summary(self) -> str:
|
| 74 |
s = self.scores
|
| 75 |
-
|
| 76 |
f"[{self.task_id}] composite={self.composite:.3f} | "
|
| 77 |
f"uptime={s['uptime']:.3f} | cost={s['cost']:.3f} | "
|
| 78 |
f"stability={s['stability']:.3f} | SLA violations={int(s['violations'])}/101"
|
| 79 |
)
|
|
|
|
|
|
|
|
|
|
| 80 |
|
| 81 |
|
| 82 |
class EpisodeGrader:
|
|
@@ -135,11 +148,15 @@ class EpisodeGrader:
|
|
| 135 |
ratio = avg_energy / TARGET_ENERGY
|
| 136 |
stability_score = 1.0 / (1.0 + (ratio ** STABILITY_CURVE_POWER))
|
| 137 |
|
|
|
|
|
|
|
|
|
|
| 138 |
return Grade(self.task_id, {
|
| 139 |
"uptime": uptime_score,
|
| 140 |
"cost": cost_score,
|
| 141 |
"stability": stability_score,
|
| 142 |
-
"violations": total_violations
|
|
|
|
| 143 |
})
|
| 144 |
|
| 145 |
|
|
|
|
| 60 |
|
| 61 |
Weights deliberately penalise cost heavily so that brute-force
|
| 62 |
SCALE_UP spam cannot achieve a high composite even with perfect uptime.
|
| 63 |
+
|
| 64 |
+
Hardening:
|
| 65 |
+
- Task 3 coupling: Cost only rewards if Uptime is >= 50%. Stops 'Cheap-but-Dead'.
|
| 66 |
+
- Invalid Action Penalty: -0.05 per forbidden command (SHED_LOAD on critical).
|
| 67 |
"""
|
| 68 |
+
uptime = self.scores["uptime"]
|
| 69 |
+
stability = self.scores["stability"]
|
| 70 |
+
cost = self.scores["cost"]
|
| 71 |
+
invalid_penalty = self.scores.get("invalid_actions", 0) * 0.05
|
| 72 |
+
|
| 73 |
+
if self.task_id == "task-3":
|
| 74 |
+
# Coupling: If uptime < 0.5, the cost benefit is zeroed out.
|
| 75 |
+
# Mirroring real-world priority: Budget doesn't matter if the site is down.
|
| 76 |
+
cost_weight = 1.0 if uptime >= 0.5 else 0.0
|
| 77 |
+
score = (0.4 * uptime + 0.2 * stability + 0.4 * (cost * cost_weight))
|
| 78 |
+
else:
|
| 79 |
+
score = (0.4 * uptime + 0.2 * stability + 0.4 * cost)
|
| 80 |
+
|
| 81 |
+
return max(0.0, score - invalid_penalty)
|
| 82 |
|
| 83 |
def summary(self) -> str:
|
| 84 |
s = self.scores
|
| 85 |
+
summary = (
|
| 86 |
f"[{self.task_id}] composite={self.composite:.3f} | "
|
| 87 |
f"uptime={s['uptime']:.3f} | cost={s['cost']:.3f} | "
|
| 88 |
f"stability={s['stability']:.3f} | SLA violations={int(s['violations'])}/101"
|
| 89 |
)
|
| 90 |
+
if s.get("invalid_actions", 0) > 0:
|
| 91 |
+
summary += f" | INVALID ACTIONS={int(s['invalid_actions'])}"
|
| 92 |
+
return summary
|
| 93 |
|
| 94 |
|
| 95 |
class EpisodeGrader:
|
|
|
|
| 148 |
ratio = avg_energy / TARGET_ENERGY
|
| 149 |
stability_score = 1.0 / (1.0 + (ratio ** STABILITY_CURVE_POWER))
|
| 150 |
|
| 151 |
+
# ββ 4. Invalid Action tracking ββββββββββββββββββββββββββββββββββββββ
|
| 152 |
+
total_invalid = self._records[-1].get("invalid_action_count", 0)
|
| 153 |
+
|
| 154 |
return Grade(self.task_id, {
|
| 155 |
"uptime": uptime_score,
|
| 156 |
"cost": cost_score,
|
| 157 |
"stability": stability_score,
|
| 158 |
+
"violations": total_violations,
|
| 159 |
+
"invalid_actions": total_invalid
|
| 160 |
})
|
| 161 |
|
| 162 |
|
models.py
CHANGED
|
@@ -124,6 +124,11 @@ class ClusterObservation(BaseModel):
|
|
| 124 |
description="Cumulative count of SLA violations this episode.",
|
| 125 |
)
|
| 126 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 127 |
nodes: list[NodeObservation]
|
| 128 |
|
| 129 |
# Episode interaction fields (handled by framework)
|
|
|
|
| 124 |
description="Cumulative count of SLA violations this episode.",
|
| 125 |
)
|
| 126 |
|
| 127 |
+
invalid_action_count: int = Field(
|
| 128 |
+
default=0,
|
| 129 |
+
description="Number of forbidden actions (e.g. SHED_LOAD on critical nodes).",
|
| 130 |
+
)
|
| 131 |
+
|
| 132 |
nodes: list[NodeObservation]
|
| 133 |
|
| 134 |
# Episode interaction fields (handled by framework)
|
server/AntiAtropos_environment.py
CHANGED
|
@@ -206,6 +206,7 @@ class AntiAtroposEnvironment(Environment):
|
|
| 206 |
step=self._state.step_count,
|
| 207 |
max_steps=MAX_STEPS,
|
| 208 |
sla_violations=self._sla_violations,
|
|
|
|
| 209 |
done=False,
|
| 210 |
reward=0.0,
|
| 211 |
)
|
|
|
|
| 206 |
step=self._state.step_count,
|
| 207 |
max_steps=MAX_STEPS,
|
| 208 |
sla_violations=self._sla_violations,
|
| 209 |
+
invalid_action_count=self._sim.invalid_action_count,
|
| 210 |
done=False,
|
| 211 |
reward=0.0,
|
| 212 |
)
|
simulator.py
CHANGED
|
@@ -55,6 +55,10 @@ T3_SURGE_BASE_END: int = 40 # Nominal end of surge within cycle
|
|
| 55 |
T3_SURGE_JITTER: int = 10 # Β±jitter applied to start/end each episode
|
| 56 |
T3_SURGE_MAGNITUDE: float = 70.0 # Extra req/tick added to node-1 and node-2
|
| 57 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
|
| 59 |
class NodeStatus(str, Enum):
|
| 60 |
HEALTHY = "HEALTHY"
|
|
@@ -132,6 +136,7 @@ class ClusterSimulator:
|
|
| 132 |
# Per-node reroute weights for REROUTE_TRAFFIC (node_id β fraction)
|
| 133 |
self._reroute_weights: dict[str, float] = {}
|
| 134 |
self._nodes: list[NodeState] = []
|
|
|
|
| 135 |
self._randomize_domain()
|
| 136 |
self._reset_nodes()
|
| 137 |
|
|
@@ -166,6 +171,7 @@ class ClusterSimulator:
|
|
| 166 |
self._tick_count = 0
|
| 167 |
self._failed_node_id = None
|
| 168 |
self._reroute_weights = {}
|
|
|
|
| 169 |
self._randomize_domain()
|
| 170 |
self._reset_nodes()
|
| 171 |
|
|
@@ -217,6 +223,12 @@ class ClusterSimulator:
|
|
| 217 |
self._reroute_weights[node_id] = frac
|
| 218 |
|
| 219 |
elif at == "SHED_LOAD":
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 220 |
frac = min(1.0, param)
|
| 221 |
target.shed_fraction = frac
|
| 222 |
# Note: physically applied in _update_queues() to incoming traffic
|
|
|
|
| 55 |
T3_SURGE_JITTER: int = 10 # Β±jitter applied to start/end each episode
|
| 56 |
T3_SURGE_MAGNITUDE: float = 70.0 # Extra req/tick added to node-1 and node-2
|
| 57 |
|
| 58 |
+
# Hardening: Critical infrastructure that CANNOT be shed
|
| 59 |
+
# In Task 3, these receive the surge. Forcing the agent to SCALE.
|
| 60 |
+
CRITICAL_NODES: list[str] = ["node-0", "node-1", "node-2"]
|
| 61 |
+
|
| 62 |
|
| 63 |
class NodeStatus(str, Enum):
|
| 64 |
HEALTHY = "HEALTHY"
|
|
|
|
| 136 |
# Per-node reroute weights for REROUTE_TRAFFIC (node_id β fraction)
|
| 137 |
self._reroute_weights: dict[str, float] = {}
|
| 138 |
self._nodes: list[NodeState] = []
|
| 139 |
+
self.invalid_action_count: int = 0
|
| 140 |
self._randomize_domain()
|
| 141 |
self._reset_nodes()
|
| 142 |
|
|
|
|
| 171 |
self._tick_count = 0
|
| 172 |
self._failed_node_id = None
|
| 173 |
self._reroute_weights = {}
|
| 174 |
+
self.invalid_action_count = 0
|
| 175 |
self._randomize_domain()
|
| 176 |
self._reset_nodes()
|
| 177 |
|
|
|
|
| 223 |
self._reroute_weights[node_id] = frac
|
| 224 |
|
| 225 |
elif at == "SHED_LOAD":
|
| 226 |
+
# Rule: Cannot shed critical nodes (database/control plane).
|
| 227 |
+
# This forces the agent to handle Task-3 surge via Scaling/Rerouting.
|
| 228 |
+
if node_id in CRITICAL_NODES:
|
| 229 |
+
self.invalid_action_count += 1
|
| 230 |
+
return
|
| 231 |
+
|
| 232 |
frac = min(1.0, param)
|
| 233 |
target.shed_fraction = frac
|
| 234 |
# Note: physically applied in _update_queues() to incoming traffic
|