diff --git a/evalharness/model/pool.py b/evalharness/model/pool.py index 4b1a586..8fcf5db 100644 --- a/evalharness/model/pool.py +++ b/evalharness/model/pool.py @@ -203,6 +203,18 @@ class AdaptiveGate: except Exception: pass + def push_inflight(self) -> None: + """Tell the bar how many requests the gate has ACTUALLY admitted + (bar shows 'admitted/held' -- a bare held count with gate 2 read + as 'the gate is broken').""" + rep = (self.adapter.extra or {}).get('progress_reporter') + fn = getattr(rep, 'set_admitted', None) + if fn is not None: + try: + fn(self._inflight) + except Exception: + pass + # ---- gate semantics ---- async def acquire(self) -> None: loop = asyncio.get_running_loop() @@ -230,9 +242,11 @@ class AdaptiveGate: finally: self._cond.release() self._inflight += 1 + self.push_inflight() def release(self, ok: bool) -> None: self._inflight = max(0, self._inflight - 1) + self.push_inflight() if ok: self._interval_ok += 1 self._level_ok += 1 diff --git a/evalharness/progress/rich_terminal.py b/evalharness/progress/rich_terminal.py index 199db6e..cba9b22 100644 --- a/evalharness/progress/rich_terminal.py +++ b/evalharness/progress/rich_terminal.py @@ -60,6 +60,7 @@ class RichTerminalProgress: self.started = 0.0 self.current_started = 0.0 self.inflight = 0 + self.admitted = None # requests past the pool gate (None = no gate) self.restored = 0 # checkpoint head start (drives the '+N new' marker) self.heartbeat_task = None @@ -195,7 +196,7 @@ class RichTerminalProgress: return self.inflight += 1 self.current_started = time.monotonic() - self.progress.update(self.task_id, inflight=self.inflight, cur='0s') + self.progress.update(self.task_id, inflight=self._inflight_txt(), cur='0s') def set_retries(self, n: int): """Show the retry count on the bar (from the adapter's attempt).""" @@ -207,6 +208,21 @@ class RichTerminalProgress: if self.task_id is not None: self.progress.update(self.task_id, gate=f'gate {n}') + def _inflight_txt(self) -> str: + # 'held' counts workers past the global semaphore; when a pool gate + # is active most of them are QUEUED on it -- 'admitted' is what + # actually hits the server. Showing a bare 96 with gate 2 read as + # 'the gate is not working' + if self.admitted is None: + return str(self.inflight) + return f'{self.admitted}/{self.inflight}' + + def set_admitted(self, n: int): + """Pooled runs: requests actually admitted by the gate.""" + self.admitted = n + if self.task_id is not None: + self.progress.update(self.task_id, inflight=self._inflight_txt()) + def rollback(self): if self.disabled: return @@ -214,7 +230,7 @@ class RichTerminalProgress: just decrement the in-flight count, no success/fail bookkeeping.""" self.inflight = max(0, self.inflight - 1) if self.task_id is not None: - self.progress.update(self.task_id, inflight=self.inflight, cur='0s') + self.progress.update(self.task_id, inflight=self._inflight_txt(), cur='0s') def advance(self, success: bool = True): if self.disabled: @@ -233,7 +249,7 @@ class RichTerminalProgress: advance=1, new=self._new_txt(completed), rate=f"{fresh / elapsed:.2f}", - inflight=self.inflight, cur='0s', + inflight=self._inflight_txt(), cur='0s', elapsed=_fmt(elapsed), eta=_fmt((task.total - completed) * elapsed / fresh) if fresh and task.total and task.total > completed else '-',