Spaces:
Sleeping
Sleeping
new rewards
Browse files- __pycache__/models.cpython-314.pyc +0 -0
- models.py +1 -1
- server/__pycache__/environment.cpython-314.pyc +0 -0
- server/environment.py +62 -13
- train_grpo_supergames.ipynb +46 -25
__pycache__/models.cpython-314.pyc
CHANGED
|
Binary files a/__pycache__/models.cpython-314.pyc and b/__pycache__/models.cpython-314.pyc differ
|
|
|
models.py
CHANGED
|
@@ -99,7 +99,7 @@ class SupergamesObservation(Observation):
|
|
| 99 |
|
| 100 |
class StepResult(BaseModel):
|
| 101 |
observation: SupergamesObservation
|
| 102 |
-
reward: float = Field(..., ge
|
| 103 |
done: bool
|
| 104 |
info: Optional[Dict[str, Any]] = None
|
| 105 |
|
|
|
|
| 99 |
|
| 100 |
class StepResult(BaseModel):
|
| 101 |
observation: SupergamesObservation
|
| 102 |
+
reward: float = Field(..., ge=-100.0, le=100.0)
|
| 103 |
done: bool
|
| 104 |
info: Optional[Dict[str, Any]] = None
|
| 105 |
|
server/__pycache__/environment.cpython-314.pyc
CHANGED
|
Binary files a/server/__pycache__/environment.cpython-314.pyc and b/server/__pycache__/environment.cpython-314.pyc differ
|
|
|
server/environment.py
CHANGED
|
@@ -32,6 +32,8 @@ class SupergamesEnvironment(Environment):
|
|
| 32 |
"""Environment for staffing and sprint allocation across Supergames tasks."""
|
| 33 |
|
| 34 |
SUPPORTS_CONCURRENT_SESSIONS: bool = True
|
|
|
|
|
|
|
| 35 |
|
| 36 |
def __init__(self):
|
| 37 |
self.stateData = State(episode_id=str(uuid4()), step_count=0)
|
|
@@ -107,18 +109,65 @@ class SupergamesEnvironment(Environment):
|
|
| 107 |
|
| 108 |
return summary
|
| 109 |
|
| 110 |
-
def
|
| 111 |
-
|
| 112 |
-
averageOptimal = self.estimatedOptimalRevenue / self.totalSteps
|
| 113 |
-
return round(min(1.0, max(0.0, sprintRevenue / averageOptimal)), 4)
|
| 114 |
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
return
|
| 119 |
-
if
|
| 120 |
-
return
|
| 121 |
-
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 122 |
|
| 123 |
def reset(self, task_id: int = 1, seed: int = 42) -> SupergamesObservation:
|
| 124 |
if task_id not in TASKS:
|
|
@@ -159,7 +208,7 @@ class SupergamesEnvironment(Environment):
|
|
| 159 |
|
| 160 |
def step(self, action: SupergamesAction) -> SupergamesObservation:
|
| 161 |
if self.done:
|
| 162 |
-
return self.buildObservation(reward=
|
| 163 |
|
| 164 |
self.stateData.step_count += 1
|
| 165 |
reasoning = action.reasoning.strip()
|
|
@@ -190,7 +239,7 @@ class SupergamesEnvironment(Environment):
|
|
| 190 |
self.crisisResolved = True
|
| 191 |
|
| 192 |
self.done = self.stateData.step_count >= self.totalSteps
|
| 193 |
-
reward = self.computeReward(sprintRevenue)
|
| 194 |
|
| 195 |
obs = self.buildObservation(reward=reward)
|
| 196 |
obs.metadata.update(
|
|
|
|
| 32 |
"""Environment for staffing and sprint allocation across Supergames tasks."""
|
| 33 |
|
| 34 |
SUPPORTS_CONCURRENT_SESSIONS: bool = True
|
| 35 |
+
MIN_REWARD: float = -100.0
|
| 36 |
+
MAX_REWARD: float = 100.0
|
| 37 |
|
| 38 |
def __init__(self):
|
| 39 |
self.stateData = State(episode_id=str(uuid4()), step_count=0)
|
|
|
|
| 109 |
|
| 110 |
return summary
|
| 111 |
|
| 112 |
+
def _clampReward(self, reward: float) -> float:
|
| 113 |
+
return round(min(self.MAX_REWARD, max(self.MIN_REWARD, reward)), 2)
|
|
|
|
|
|
|
| 114 |
|
| 115 |
+
@staticmethod
|
| 116 |
+
def _delayImpactPoints(item: WorkItem) -> float:
|
| 117 |
+
if item.impactDelay == 0:
|
| 118 |
+
return 45.0
|
| 119 |
+
if item.impactDelay == 1:
|
| 120 |
+
return 25.0
|
| 121 |
+
return 10.0
|
| 122 |
+
|
| 123 |
+
@staticmethod
|
| 124 |
+
def _completedItemPoints(item: WorkItem) -> float:
|
| 125 |
+
severityPoints = int(item.severity) * 3.0
|
| 126 |
+
revenuePoints = min(20.0, item.revenueImpact / 20.0)
|
| 127 |
+
churnPoints = item.churnReduction * 40.0
|
| 128 |
+
return (
|
| 129 |
+
SupergamesEnvironment._delayImpactPoints(item)
|
| 130 |
+
+ severityPoints
|
| 131 |
+
+ revenuePoints
|
| 132 |
+
+ churnPoints
|
| 133 |
+
)
|
| 134 |
+
|
| 135 |
+
def computeReward(
|
| 136 |
+
self,
|
| 137 |
+
sprintRevenue: float,
|
| 138 |
+
completedItems: List[WorkItem] | None = None,
|
| 139 |
+
message: str = "ok",
|
| 140 |
+
) -> float:
|
| 141 |
+
completedItems = completedItems or []
|
| 142 |
+
|
| 143 |
+
if message.startswith("Overallocation"):
|
| 144 |
+
return -70.0
|
| 145 |
+
if message.startswith("Unknown work item"):
|
| 146 |
+
return -60.0
|
| 147 |
+
|
| 148 |
+
reward = min(35.0, sprintRevenue / 100_000.0)
|
| 149 |
+
reward += sum(self._completedItemPoints(item) for item in completedItems)
|
| 150 |
+
|
| 151 |
+
unresolvedPenalty = 0.0
|
| 152 |
+
for item in self.workQueue:
|
| 153 |
+
if item.workType.value != "bug":
|
| 154 |
+
continue
|
| 155 |
+
if int(item.severity) == 5:
|
| 156 |
+
unresolvedPenalty += 18.0
|
| 157 |
+
elif int(item.severity) == 4:
|
| 158 |
+
unresolvedPenalty += 10.0
|
| 159 |
+
reward -= unresolvedPenalty
|
| 160 |
+
|
| 161 |
+
churnPenalty = sum(max(0.0, game.churnMult - 1.0) * 40.0 for game in self.games)
|
| 162 |
+
reward -= churnPenalty
|
| 163 |
+
|
| 164 |
+
if self.done:
|
| 165 |
+
if not any(item.workType.value == "bug" and int(item.severity) >= 4 for item in self.workQueue):
|
| 166 |
+
reward += 15.0
|
| 167 |
+
if self.taskId == 4:
|
| 168 |
+
reward += 20.0 if self.crisisResolved else -40.0
|
| 169 |
+
|
| 170 |
+
return self._clampReward(reward)
|
| 171 |
|
| 172 |
def reset(self, task_id: int = 1, seed: int = 42) -> SupergamesObservation:
|
| 173 |
if task_id not in TASKS:
|
|
|
|
| 208 |
|
| 209 |
def step(self, action: SupergamesAction) -> SupergamesObservation:
|
| 210 |
if self.done:
|
| 211 |
+
return self.buildObservation(reward=0.0)
|
| 212 |
|
| 213 |
self.stateData.step_count += 1
|
| 214 |
reasoning = action.reasoning.strip()
|
|
|
|
| 239 |
self.crisisResolved = True
|
| 240 |
|
| 241 |
self.done = self.stateData.step_count >= self.totalSteps
|
| 242 |
+
reward = self.computeReward(sprintRevenue, completedItems, message)
|
| 243 |
|
| 244 |
obs = self.buildObservation(reward=reward)
|
| 245 |
obs.metadata.update(
|
train_grpo_supergames.ipynb
CHANGED
|
@@ -319,24 +319,29 @@
|
|
| 319 |
" return actions, matched_plan_shape\n",
|
| 320 |
"\n",
|
| 321 |
"\n",
|
| 322 |
-
"def
|
| 323 |
-
"
|
| 324 |
-
"
|
| 325 |
-
"
|
| 326 |
-
") -> float:\n",
|
| 327 |
-
"
|
| 328 |
-
"
|
| 329 |
-
"
|
| 330 |
-
"
|
| 331 |
-
"
|
| 332 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 333 |
"\n",
|
| 334 |
"\n",
|
| 335 |
"def rollout_plan(completion: Any, difficulty: str, task_seed: int) -> Dict[str, Any]:\n",
|
| 336 |
" games, work_queue, staff_pool, total_steps, _goal = generate_task(difficulty, seed=int(task_seed))\n",
|
| 337 |
-
"
|
| 338 |
-
"
|
| 339 |
-
" active_work_queue = [item for item in all_items if not item.crisis]\n",
|
| 340 |
" games = copy.deepcopy(games)\n",
|
| 341 |
" staff_pool = copy.deepcopy(staff_pool)\n",
|
| 342 |
"\n",
|
|
@@ -344,7 +349,7 @@
|
|
| 344 |
" actions, matched_plan_shape = parse_sprint_actions(completion, total_steps)\n",
|
| 345 |
" except Exception:\n",
|
| 346 |
" return {\n",
|
| 347 |
-
" \"reward\": -
|
| 348 |
" \"valid_json\": False,\n",
|
| 349 |
" \"matched_plan_shape\": False,\n",
|
| 350 |
" \"cumulative_revenue\": 0.0,\n",
|
|
@@ -353,7 +358,8 @@
|
|
| 353 |
"\n",
|
| 354 |
" cumulative_revenue = 0.0\n",
|
| 355 |
" messages = []\n",
|
| 356 |
-
"
|
|
|
|
| 357 |
"\n",
|
| 358 |
" for sprint_idx in range(1, total_steps + 1):\n",
|
| 359 |
" if sprint_idx >= 2 and pending_crisis_items:\n",
|
|
@@ -368,20 +374,35 @@
|
|
| 368 |
" sprint_idx,\n",
|
| 369 |
" )\n",
|
| 370 |
" cumulative_revenue += sprint_revenue\n",
|
| 371 |
-
" completed_count += len(completed_items)\n",
|
| 372 |
" messages.append(message)\n",
|
| 373 |
"\n",
|
| 374 |
-
"
|
| 375 |
-
"
|
| 376 |
-
"
|
| 377 |
-
"
|
| 378 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 379 |
"\n",
|
| 380 |
-
"
|
| 381 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 382 |
"\n",
|
| 383 |
" return {\n",
|
| 384 |
-
" \"reward\": float(
|
| 385 |
" \"valid_json\": True,\n",
|
| 386 |
" \"matched_plan_shape\": matched_plan_shape,\n",
|
| 387 |
" \"cumulative_revenue\": cumulative_revenue,\n",
|
|
|
|
| 319 |
" return actions, matched_plan_shape\n",
|
| 320 |
"\n",
|
| 321 |
"\n",
|
| 322 |
+
"def clamp_reward(reward: float) -> float:\n",
|
| 323 |
+
" return round(min(100.0, max(-100.0, reward)), 2)\n",
|
| 324 |
+
"\n",
|
| 325 |
+
"\n",
|
| 326 |
+
"def delay_impact_points(item: WorkItem) -> float:\n",
|
| 327 |
+
" if item.impactDelay == 0:\n",
|
| 328 |
+
" return 45.0\n",
|
| 329 |
+
" if item.impactDelay == 1:\n",
|
| 330 |
+
" return 25.0\n",
|
| 331 |
+
" return 10.0\n",
|
| 332 |
+
"\n",
|
| 333 |
+
"\n",
|
| 334 |
+
"def completed_item_points(item: WorkItem) -> float:\n",
|
| 335 |
+
" severity_points = int(item.severity) * 3.0\n",
|
| 336 |
+
" revenue_points = min(20.0, item.revenueImpact / 20.0)\n",
|
| 337 |
+
" churn_points = item.churnReduction * 40.0\n",
|
| 338 |
+
" return delay_impact_points(item) + severity_points + revenue_points + churn_points\n",
|
| 339 |
"\n",
|
| 340 |
"\n",
|
| 341 |
"def rollout_plan(completion: Any, difficulty: str, task_seed: int) -> Dict[str, Any]:\n",
|
| 342 |
" games, work_queue, staff_pool, total_steps, _goal = generate_task(difficulty, seed=int(task_seed))\n",
|
| 343 |
+
" pending_crisis_items = [copy.deepcopy(item) for item in work_queue if item.crisis]\n",
|
| 344 |
+
" active_work_queue = [copy.deepcopy(item) for item in work_queue if not item.crisis]\n",
|
|
|
|
| 345 |
" games = copy.deepcopy(games)\n",
|
| 346 |
" staff_pool = copy.deepcopy(staff_pool)\n",
|
| 347 |
"\n",
|
|
|
|
| 349 |
" actions, matched_plan_shape = parse_sprint_actions(completion, total_steps)\n",
|
| 350 |
" except Exception:\n",
|
| 351 |
" return {\n",
|
| 352 |
+
" \"reward\": -100.0,\n",
|
| 353 |
" \"valid_json\": False,\n",
|
| 354 |
" \"matched_plan_shape\": False,\n",
|
| 355 |
" \"cumulative_revenue\": 0.0,\n",
|
|
|
|
| 358 |
"\n",
|
| 359 |
" cumulative_revenue = 0.0\n",
|
| 360 |
" messages = []\n",
|
| 361 |
+
" reward = 10.0 if matched_plan_shape else 0.0\n",
|
| 362 |
+
" crisis_resolved = False\n",
|
| 363 |
"\n",
|
| 364 |
" for sprint_idx in range(1, total_steps + 1):\n",
|
| 365 |
" if sprint_idx >= 2 and pending_crisis_items:\n",
|
|
|
|
| 374 |
" sprint_idx,\n",
|
| 375 |
" )\n",
|
| 376 |
" cumulative_revenue += sprint_revenue\n",
|
|
|
|
| 377 |
" messages.append(message)\n",
|
| 378 |
"\n",
|
| 379 |
+
" if message.startswith(\"Overallocation\"):\n",
|
| 380 |
+
" reward -= 70.0\n",
|
| 381 |
+
" continue\n",
|
| 382 |
+
" if message.startswith(\"Unknown work item\"):\n",
|
| 383 |
+
" reward -= 60.0\n",
|
| 384 |
+
" continue\n",
|
| 385 |
+
"\n",
|
| 386 |
+
" reward += min(35.0, sprint_revenue / 100_000.0)\n",
|
| 387 |
+
" reward += sum(completed_item_points(item) for item in completed_items)\n",
|
| 388 |
+
" crisis_resolved = crisis_resolved or any(item.crisis for item in completed_items)\n",
|
| 389 |
"\n",
|
| 390 |
+
" for item in active_work_queue:\n",
|
| 391 |
+
" if item.workType.value != \"bug\":\n",
|
| 392 |
+
" continue\n",
|
| 393 |
+
" if int(item.severity) == 5:\n",
|
| 394 |
+
" reward -= 18.0\n",
|
| 395 |
+
" elif int(item.severity) == 4:\n",
|
| 396 |
+
" reward -= 10.0\n",
|
| 397 |
+
" reward -= sum(max(0.0, game.churnMult - 1.0) * 40.0 for game in games)\n",
|
| 398 |
+
"\n",
|
| 399 |
+
" if not any(item.workType.value == \"bug\" and int(item.severity) >= 4 for item in active_work_queue):\n",
|
| 400 |
+
" reward += 15.0\n",
|
| 401 |
+
" if any(item.crisis for item in work_queue):\n",
|
| 402 |
+
" reward += 20.0 if crisis_resolved else -40.0\n",
|
| 403 |
"\n",
|
| 404 |
" return {\n",
|
| 405 |
+
" \"reward\": float(clamp_reward(reward)),\n",
|
| 406 |
" \"valid_json\": True,\n",
|
| 407 |
" \"matched_plan_shape\": matched_plan_shape,\n",
|
| 408 |
" \"cumulative_revenue\": cumulative_revenue,\n",
|