| 313 | return self.config.explorer.name |
| 314 | |
| 315 | async def explore_step(self) -> bool: |
| 316 | if self.explore_start_time is None: |
| 317 | self.explore_start_time = time.time() |
| 318 | try: |
| 319 | tasks = await self.taskset.read() |
| 320 | except StopAsyncIteration: |
| 321 | self.logger.warning("No more tasks to explore. Stop exploring.") |
| 322 | await self.finish_current_steps() |
| 323 | await self.save_checkpoint() |
| 324 | await self.synchronizer.set_explorer_status.remote( |
| 325 | RunningStatus.STOPPED, |
| 326 | old_status=RunningStatus.RUNNING, |
| 327 | ) |
| 328 | await self.shutdown() |
| 329 | return False |
| 330 | self.explore_step_num += 1 |
| 331 | if self.rollout_coordinator is None: |
| 332 | return False |
| 333 | # FULLY_ASYNC: when the in-flight window is full, wait for the oldest batch to |
| 334 | # finish before submitting a new one. This provides rolling backpressure without |
| 335 | # waiting for a sync-interval boundary. |
| 336 | if ( |
| 337 | self.sync_style == SyncStyle.FULLY_ASYNC |
| 338 | and len(self._inflight_train_steps) >= self.config.explorer.max_inflight_batches |
| 339 | ): |
| 340 | oldest_step = self._inflight_train_steps.popleft() |
| 341 | self.logger.debug( |
| 342 | f"FULLY_ASYNC: at capacity, draining oldest batch (step {oldest_step})." |
| 343 | ) |
| 344 | await self._finish_explore_step(step=oldest_step) |
| 345 | self.last_monitored_step = oldest_step |
| 346 | await self.rollout_coordinator.submit_batch.remote( |
| 347 | batch_id=self.explore_step_num, |
| 348 | tasks=tasks, |
| 349 | batch_type="train", |
| 350 | min_wait_num=self.min_wait_num, |
| 351 | ) |
| 352 | if self.sync_style == SyncStyle.FULLY_ASYNC: |
| 353 | self._inflight_train_steps.append(self.explore_step_num) |
| 354 | return True |
| 355 | |
| 356 | async def finish_current_steps(self) -> None: |
| 357 | if self.rollout_coordinator is not None: |