verifiers 0.2.1.dev27__py3-none-any.whl → 0.2.1.dev28__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,14 +5,15 @@ Behind a remote consumer each interception server needs a tunnel, and prime tunn
5
5
  is rate-capped per API token — so one-tunnel-per-rollout caps how wide a remote eval (or env
6
6
  server) can fan out. Each shared `InterceptionServer` multiplexes rollouts behind one
7
7
  tunnel; the harness is unchanged, authenticating with a per-rollout secret the server routes
8
- by. Two shapes: `ElasticInterceptionPool` grows servers on demand (`multiplex` rollouts
9
- each, always prime tunnels — the only kind the framework can mint) and fits both the bounded
10
- eval runner and the env server's unbounded request load; `StaticInterceptionPool` is a fixed
11
- set of servers (each with its own tunnel choice, e.g. bring-your-own endpoints), balanced
8
+ by. Two shapes: `ElasticInterceptionPool` warms one server, then grows on demand (`multiplex`
9
+ rollouts each, always prime tunnels — the only kind the framework can mint) and fits both the
10
+ bounded eval runner and the env server's unbounded request load; `StaticInterceptionPool` is a
11
+ fixed set of servers (each with its own tunnel choice, e.g. bring-your-own endpoints), balanced
12
12
  least-loaded.
13
13
  """
14
14
 
15
15
  import asyncio
16
+ import contextlib
16
17
  import logging
17
18
  from collections.abc import AsyncIterator
18
19
  from contextlib import asynccontextmanager
@@ -72,8 +73,8 @@ class StaticInterceptionPool(Interception):
72
73
 
73
74
 
74
75
  class ElasticInterceptionPoolConfig(BaseInterceptionConfig):
75
- """Interception servers grown on demand: `multiplex` rollouts share one server (and,
76
- behind a remote consumer, one prime tunnel). The default."""
76
+ """An eagerly warmed interception server, then more grown on demand: `multiplex`
77
+ rollouts share one server (and, behind a remote consumer, one prime tunnel). The default."""
77
78
 
78
79
  type: Literal["elastic"] = "elastic"
79
80
  multiplex: int = Field(32, ge=1)
@@ -83,9 +84,9 @@ class ElasticInterceptionPoolConfig(BaseInterceptionConfig):
83
84
 
84
85
 
85
86
  class ElasticInterceptionPool(Interception):
86
- """Interception servers grown on demand: `multiplex` rollouts share one server (one
87
- prime tunnel behind a remote consumer); `acquire` hands a rollout a slot on one,
88
- bringing up a new server when all are at capacity."""
87
+ """Warm the first interception server on start, then grow on demand: `multiplex`
88
+ rollouts share one server (one prime tunnel behind a remote consumer); `acquire` hands
89
+ a rollout a slot on one, bringing up a new server when all are at capacity."""
89
90
 
90
91
  def __init__(
91
92
  self,
@@ -97,14 +98,22 @@ class ElasticInterceptionPool(Interception):
97
98
  self.requires_tunnel = requires_tunnel
98
99
  self.servers: list[InterceptionServer] = []
99
100
  self._lock = asyncio.Lock()
101
+ self._warm_task: asyncio.Task[InterceptionServer] | None = None
100
102
 
101
103
  async def start(self) -> None:
102
- pass # servers are brought up lazily in `acquire`, on `stack`
104
+ self._warm_task = asyncio.create_task(self._server())
105
+
106
+ async def stop(self) -> None:
107
+ if self._warm_task is not None:
108
+ self._warm_task.cancel()
109
+ with contextlib.suppress(asyncio.CancelledError, Exception):
110
+ await self._warm_task
111
+ await super().stop()
103
112
 
104
113
  async def _server(self) -> InterceptionServer:
105
114
  """A server with spare capacity — reuse one under `multiplex`, else bring up a new
106
- one (its own tunnel, on `stack`, torn down with the pool). The caller holds
107
- `_lock`."""
115
+ one (its own tunnel, on `stack`, torn down with the pool). Acquires hold `_lock`;
116
+ the warm task runs before they reach this path."""
108
117
  for server in self.servers:
109
118
  if server.load < self.config.multiplex:
110
119
  return server
@@ -123,6 +132,10 @@ class ElasticInterceptionPool(Interception):
123
132
 
124
133
  @asynccontextmanager
125
134
  async def acquire(self, session: RolloutSession) -> AsyncIterator[Slot]:
135
+ if self._warm_task is not None:
136
+ with contextlib.suppress(Exception):
137
+ await asyncio.shield(self._warm_task)
138
+ self._warm_task = None
126
139
  # Register under the lock so concurrent acquires see each other's load.
127
140
  async with self._lock:
128
141
  server = await self._server()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: verifiers
3
- Version: 0.2.1.dev27
3
+ Version: 0.2.1.dev28
4
4
  Summary: Verifiers: Environments for LLM Reinforcement Learning
5
5
  Project-URL: Homepage, https://github.com/primeintellect-ai/verifiers
6
6
  Project-URL: Documentation, https://github.com/primeintellect-ai/verifiers
@@ -279,7 +279,7 @@ verifiers/v1/harnesses/terminus_2/harness.py,sha256=w3yGlJz6P9Pt2_xxHf7sHWt31e9Q
279
279
  verifiers/v1/harnesses/terminus_2/program.py,sha256=DbkjUXTC_8-N05G9DJfzhGaMZQAfdTSNmf_THuKE1Zw,2606
280
280
  verifiers/v1/interception/__init__.py,sha256=QkPPpcVkJYCeUXzfZS8IMcnCH5WkiaNbONxoNIMPTSg,3621
281
281
  verifiers/v1/interception/base.py,sha256=Riwf_qng-Gtfa4Phy2CH_HgHD4O23JaFkUpaMPc6sZw,2708
282
- verifiers/v1/interception/pool.py,sha256=AvdAU0y-FJ5ICoeioPJmvrcuW3iUX0yEesgglQtN1FQ,5569
282
+ verifiers/v1/interception/pool.py,sha256=A39woH2Zh01UzCrwxM7Be-GpvL8VmmMXHCmbA7yb_Rk,6196
283
283
  verifiers/v1/interception/server.py,sha256=A29Qweo9qlwmLRIbGxrxv4zCt4KL_mPe26s17fyeAr4,27509
284
284
  verifiers/v1/interception/tunnel/__init__.py,sha256=F9dv8v6EgMbZRcTfx-J6DmAM8Hucm0YIukpGiyi27Cc,1366
285
285
  verifiers/v1/interception/tunnel/base.py,sha256=KShv15XCdC_-eYX_onOOwFJUoLoOZqowDl2QnXXsGSU,2232
@@ -324,8 +324,8 @@ verifiers/v1/utils/interrupt.py,sha256=F-KKhc5ndPJJfhd3SuMqyqhXhA32FhCRy5KWFJpEo
324
324
  verifiers/v1/utils/logging.py,sha256=OcMHA6NsYux3oIzjPuI95rDWmFBHNcHDjZeNIhXTX-Y,2084
325
325
  verifiers/v1/utils/memory.py,sha256=ZkIvGk6uITAH5sKon65LifKPbvZr8mJ__PVc23FZpOQ,1835
326
326
  verifiers/v1/utils/sampling.py,sha256=JczGzBn6s3wsIrSY2Hy3hE8m6NreNgsNzhSjDikQqX0,1037
327
- verifiers-0.2.1.dev27.dist-info/METADATA,sha256=9AfTa1iQ6MEq01-m4QQa3kHde-_fdJ3vVJ2a1yaCg2c,5136
328
- verifiers-0.2.1.dev27.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
329
- verifiers-0.2.1.dev27.dist-info/entry_points.txt,sha256=dF82JUYEFslR1AOW8LZfuBWeZeQoyX4wCRrduklg8-I,551
330
- verifiers-0.2.1.dev27.dist-info/licenses/LICENSE,sha256=v0RrUsdV3IDoZhrRce297IXS3xMHNJ-_LdLpFAUWb9k,1072
331
- verifiers-0.2.1.dev27.dist-info/RECORD,,
327
+ verifiers-0.2.1.dev28.dist-info/METADATA,sha256=_HzWumtHEaV1pn6UhcJnfk30JENAYX5MAP3jpMrFhhE,5136
328
+ verifiers-0.2.1.dev28.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
329
+ verifiers-0.2.1.dev28.dist-info/entry_points.txt,sha256=dF82JUYEFslR1AOW8LZfuBWeZeQoyX4wCRrduklg8-I,551
330
+ verifiers-0.2.1.dev28.dist-info/licenses/LICENSE,sha256=v0RrUsdV3IDoZhrRce297IXS3xMHNJ-_LdLpFAUWb9k,1072
331
+ verifiers-0.2.1.dev28.dist-info/RECORD,,