verifiers 0.2.1.dev26__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()
@@ -125,13 +125,15 @@ async def _install_in_sandbox(server: ServerBase, runtime: Runtime) -> str:
125
125
  # build, silently running the server against a released (older) API. Pretend the
126
126
  # local version so the floor is satisfied by the build we uploaded.
127
127
  vf_version = importlib.metadata.version("verifiers")
128
+ extras = ",".join(type(server).EXTRAS)
128
129
  setup = (
129
130
  f"{_ENSURE_UV}; set -e; "
130
131
  f'for t in {root}/*.tar.gz; do tar -xzf "$t" -C {root}; done && '
131
132
  f"uv venv {venv} && "
132
133
  f"SETUPTOOLS_SCM_PRETEND_VERSION={shlex.quote(vf_version)} "
133
134
  f"uv pip install --python {venv} {root}/{shlex.quote(vf.name)} && "
134
- f"uv pip install --python {venv} {root}/{shlex.quote(env.name)}"
135
+ f"uv pip install --python {venv} "
136
+ f"{shlex.quote(f'{root}/{env.name}' + (f'[{extras}]' if extras else ''))}"
135
137
  )
136
138
  result = await runtime.run(["sh", "-c", setup], {})
137
139
  if result.exit_code != 0:
@@ -116,6 +116,9 @@ class ServerBase(Generic[ConfigT, StateT]):
116
116
  # The empty value falls back to the snake-cased class name.
117
117
  TOOL_PREFIX: ClassVar[str] = ""
118
118
 
119
+ EXTRAS: ClassVar[tuple[str, ...]] = ()
120
+ """Package extras the server's module needs, applied at sandbox install."""
121
+
119
122
  def __init__(self, config: ConfigT) -> None:
120
123
  self.config = config
121
124
  self._state_cls = state_cls(type(self))
@@ -39,6 +39,8 @@ class TextArenaState(vf.State):
39
39
  class TextArenaUser(vf.User[vf.UserConfig, TextArenaState]):
40
40
  """Keep a seeded game alive across user turns in the harness process."""
41
41
 
42
+ EXTRAS = ("ta",)
43
+
42
44
  async def setup(self) -> None:
43
45
  if not self.config.colocated:
44
46
  raise ValueError(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: verifiers
3
- Version: 0.2.1.dev26
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
@@ -291,8 +291,8 @@ verifiers/v1/judges/reference.txt,sha256=Ej35kGXiT2uJ0LcHIeez49rCS_9uHmCQKoVdn93
291
291
  verifiers/v1/judges/rubric.py,sha256=HEWFLhTKYPO6c1uuY4FY2iKDyQnan-6DiLB5NeQpiPI,11856
292
292
  verifiers/v1/judges/rubric.txt,sha256=3KA2ZeYYdcDweJhUGopxu9EFgARhu2aWft5S5cj8Bc8,323
293
293
  verifiers/v1/mcp/__init__.py,sha256=Bn_eIjJxPXOoRqAroC_W92jDmEesUVkq-0QTUwxoV3Y,634
294
- verifiers/v1/mcp/launch.py,sha256=wt-pCIoQKsdQkYWn0v_ACkageDbp4_YzjmeVT7o60A0,21092
295
- verifiers/v1/mcp/server.py,sha256=MXEW6w72TkLa3iEPqrWgxQSlOBR-ostjImt5LNMtVo0,10692
294
+ verifiers/v1/mcp/launch.py,sha256=RaePuHsugHObSqIoYBrDgd4yy1dzv8rn-jcltKNvz-0,21188
295
+ verifiers/v1/mcp/server.py,sha256=rWmvGU1SEA9ioJZR1PrLftcJC1UbRN3eUJsM-egQWpc,10816
296
296
  verifiers/v1/mcp/toolset.py,sha256=tSPXgTNREZyaeCnoFVnDfXxFAj_pStZMPUIgyPrY_Dk,996
297
297
  verifiers/v1/mcp/user.py,sha256=u_2KMRtNfg6WkwOCRLpR7j4ziFCUzGb1t6SomTbbu4Q,1591
298
298
  verifiers/v1/runtimes/__init__.py,sha256=L8BCIcL9InpQi3ip2-WoZKyO6FHXLnKBoYFWexEpI8Y,2138
@@ -314,7 +314,7 @@ verifiers/v1/tasksets/lean/__init__.py,sha256=-13Mjoj6oGpiV0hxAF9fsw9-4xWpr5MPJl
314
314
  verifiers/v1/tasksets/lean/scoring.py,sha256=sfzsT6MUK0zdMgAQ57_P_0CBaPHy3M7go71QMr5hDrs,10599
315
315
  verifiers/v1/tasksets/lean/taskset.py,sha256=gIgKjv6foV7RywOVRpOr9Lv9Qe4NjEZalWvlchxHnWk,9196
316
316
  verifiers/v1/tasksets/textarena/__init__.py,sha256=_R_E3PFP3X0lQGoedu_J9lKlB1a8tURvu6aVjthGkjA,375
317
- verifiers/v1/tasksets/textarena/taskset.py,sha256=VChXZQwzevEI4wpDnl3Cn_xY8Xhe-UUEl_gSzAop4g8,5236
317
+ verifiers/v1/tasksets/textarena/taskset.py,sha256=8lpfK4XXmpuGQVBcin6mi2EuDgwttpHvkr6Bv7GPtW4,5258
318
318
  verifiers/v1/utils/__init__.py,sha256=1RDUX9pjAuQHKYEDevehifHd3IB03y9LAUsln3exPQo,27
319
319
  verifiers/v1/utils/aio.py,sha256=ZKTHeURNbWhTpHMyYuqMLsdPWVHyn5anfG1IJs5y-Zg,1481
320
320
  verifiers/v1/utils/format.py,sha256=NfQo9M5KMzWCZpQaet5YtsJx56vCKAZPfbjZZJLJhhM,2187
@@ -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.dev26.dist-info/METADATA,sha256=69g7DZC5s7QDH--eqWNvjqAyV-7pGPmOP5CrT0UoFO8,5136
328
- verifiers-0.2.1.dev26.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
329
- verifiers-0.2.1.dev26.dist-info/entry_points.txt,sha256=dF82JUYEFslR1AOW8LZfuBWeZeQoyX4wCRrduklg8-I,551
330
- verifiers-0.2.1.dev26.dist-info/licenses/LICENSE,sha256=v0RrUsdV3IDoZhrRce297IXS3xMHNJ-_LdLpFAUWb9k,1072
331
- verifiers-0.2.1.dev26.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,,