verifiers 0.3.2.dev44__py3-none-any.whl → 0.3.2.dev46__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.
verifiers/v1/agent.py CHANGED
@@ -11,7 +11,7 @@ import asyncio
11
11
  import logging
12
12
  from collections.abc import AsyncIterator, Callable, Mapping
13
13
  from contextlib import asynccontextmanager, nullcontext
14
- from dataclasses import dataclass
14
+ from dataclasses import dataclass, replace
15
15
  from typing import Self
16
16
 
17
17
  from verifiers.v1.clients import (
@@ -54,6 +54,27 @@ __all__ = ["Agent", "AgentConfig", "Agents", "TimeoutConfig", "make_agent"]
54
54
  logger = logging.getLogger(__name__)
55
55
 
56
56
 
57
+ def resolve_rollout_timeouts(timeout: TimeoutConfig, task: Task) -> RolloutTimeouts:
58
+ """Apply an agent's stage-timeout precedence to one task."""
59
+ agent_timeout = (
60
+ timeout.rollout if timeout.rollout is not None else task.data.timeout.agent
61
+ )
62
+ return RolloutTimeouts(
63
+ setup=timeout.setup if timeout.setup is not None else task.data.timeout.setup,
64
+ agent=agent_timeout,
65
+ finalize=(
66
+ timeout.finalize
67
+ if timeout.finalize is not None
68
+ else task.data.timeout.finalize
69
+ ),
70
+ scoring=(
71
+ timeout.scoring
72
+ if timeout.scoring is not None
73
+ else task.data.timeout.scoring
74
+ ),
75
+ )
76
+
77
+
57
78
  def _check_borrowed_placement(
58
79
  task: Task, runtime: Runtime, base_config: RuntimeConfig
59
80
  ) -> None:
@@ -526,34 +547,15 @@ class Agent:
526
547
  runtime_config,
527
548
  tools=[*task.toolsets(task.config), *shared_tools.values()],
528
549
  )
529
- # Timeout precedence: agent-level wins, else the task's, else no limit.
530
- agent_timeout = (
531
- self.timeout.rollout
532
- if self.timeout.rollout is not None
533
- else task.data.timeout.agent
534
- )
550
+ timeouts = resolve_rollout_timeouts(self.timeout, task)
535
551
  return {
536
552
  "agent_config": self.config,
537
553
  "harness": self.harness,
538
554
  "ctx": self.ctx,
539
555
  "runtime_config": runtime_config,
540
- "timeouts": RolloutTimeouts(
541
- setup=(
542
- self.timeout.setup
543
- if self.timeout.setup is not None
544
- else task.data.timeout.setup
545
- ),
546
- agent=cap_remote_agent_timeout(agent_timeout, runtime_config, task),
547
- finalize=(
548
- self.timeout.finalize
549
- if self.timeout.finalize is not None
550
- else task.data.timeout.finalize
551
- ),
552
- scoring=(
553
- self.timeout.scoring
554
- if self.timeout.scoring is not None
555
- else task.data.timeout.scoring
556
- ),
556
+ "timeouts": replace(
557
+ timeouts,
558
+ agent=cap_remote_agent_timeout(timeouts.agent, runtime_config, task),
557
559
  ),
558
560
  "limits": self.limits,
559
561
  "shared_tools": shared_tools,
@@ -0,0 +1,7 @@
1
+ from verifiers.v1.envs.isolated_verifier.env import (
2
+ IsolatedVerifierEnv,
3
+ IsolatedVerifierEnvConfig,
4
+ VerifierConfig,
5
+ )
6
+
7
+ __all__ = ["IsolatedVerifierEnv", "IsolatedVerifierEnvConfig", "VerifierConfig"]
@@ -0,0 +1,182 @@
1
+ """Deterministic task verification in a fresh runtime.
2
+
3
+ One solver agent runs the task. Its task scoring is deferred, declared artifacts
4
+ are collected after normal task finalization, and the solver runtime is destroyed.
5
+ The task is then set up with a fresh controller in a fresh runtime, its artifacts
6
+ are restored, and its ordinary metrics and rewards run there onto the solver's trace.
7
+ """
8
+
9
+ import asyncio
10
+ import copy
11
+ import logging
12
+ from contextlib import AsyncExitStack
13
+ from pathlib import PurePosixPath
14
+ from typing import Any
15
+
16
+ from pydantic import Field, SerializationInfo, field_serializer
17
+
18
+ import verifiers.v1 as vf
19
+ from verifiers.v1.agent import resolve_rollout_timeouts
20
+ from verifiers.v1.errors import TaskError, boundary
21
+ from verifiers.v1.runtimes import Runtime, RuntimeConfig, provision_runtime
22
+ from verifiers.v1.utils.compile import resolve_runtime_config
23
+ from verifiers.v1.utils.decorators import invoke
24
+ from verifiers.v1.utils.retries import backoff
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+
29
+ class VerifierConfig(vf.BaseConfig):
30
+ runtime: RuntimeConfig | None = None
31
+ """Independent verifier placement and policy. None provisions a fresh runtime
32
+ equivalent to the solver's resolved task runtime."""
33
+ env: dict[str, str] | None = None
34
+ """Process environment for verifier setup and scoring. None uses the task's
35
+ normal runtime environment."""
36
+ retries: int = Field(2, ge=0)
37
+ """Extra fresh-runtime attempts after setup, restoration, staging, or scoring
38
+ failures."""
39
+
40
+ @field_serializer("runtime")
41
+ def serialize_runtime(
42
+ self, runtime: RuntimeConfig | None, info: SerializationInfo
43
+ ) -> dict | None:
44
+ """Keep an omitted image omitted across a resolved-config round trip."""
45
+ if runtime is None:
46
+ return None
47
+ values = runtime.model_dump(mode=info.mode)
48
+ if "image" not in runtime.model_fields_set:
49
+ values.pop("image", None)
50
+ return values
51
+
52
+
53
+ class IsolatedVerifierEnvConfig(vf.EnvConfig):
54
+ agent: vf.AgentConfig = vf.AgentConfig()
55
+ """The one solver seat."""
56
+ verifier: VerifierConfig = VerifierConfig()
57
+ """The fresh verifier's runtime, process environment, and retry policy."""
58
+
59
+
60
+ class IsolatedVerifierEnv(vf.Env[IsolatedVerifierEnvConfig]):
61
+ """Run one solver, then its deterministic task scoring in a fresh runtime."""
62
+
63
+ async def run(self, task: vf.Task, agents: vf.Agents) -> None:
64
+ if task.config.judges:
65
+ raise ValueError(
66
+ "isolated-verifier runs deterministic task metrics and rewards; "
67
+ "model-backed task judges are not supported"
68
+ )
69
+ self.verifier_config(task) # Refuse an impossible verifier before solving.
70
+ await agents.agent.run(task.defer_scoring(), collect_artifacts=True)
71
+
72
+ def verifier_config(self, task: vf.Task) -> RuntimeConfig:
73
+ base = self.config.verifier.runtime or self.config.agent.runtime
74
+ config = resolve_runtime_config(base, task)
75
+ image_spec = type(base).model_fields.get("image")
76
+ if (
77
+ self.config.verifier.runtime is not None
78
+ and image_spec is not None
79
+ and "image" in base.model_fields_set
80
+ ):
81
+ config = config.model_copy(update={"image": base.image})
82
+ if isinstance(config, vf.SubprocessConfig):
83
+ raise TypeError(
84
+ "isolated-verifier requires a container runtime so artifacts can be "
85
+ "restored safely; configure the agent or verifier runtime as docker, "
86
+ "prime, or modal"
87
+ )
88
+ relative = [
89
+ artifact.source
90
+ for artifact in task.data.artifacts
91
+ if not PurePosixPath(artifact.source).is_absolute()
92
+ ]
93
+ solver = resolve_runtime_config(self.config.agent.runtime, task)
94
+ solver_workdir = PurePosixPath(getattr(solver, "workdir", "") or "/")
95
+ verifier_workdir = PurePosixPath(config.workdir)
96
+ if relative and solver_workdir != verifier_workdir:
97
+ raise ValueError(
98
+ "isolated-verifier cannot transfer relative artifacts "
99
+ f"{relative!r} between solver workdir {str(solver_workdir)!r} and "
100
+ f"verifier workdir {str(verifier_workdir)!r}; use matching workdirs "
101
+ "or absolute artifact paths"
102
+ )
103
+ return config
104
+
105
+ async def finalize(self, task: vf.Task, episode: vf.Episode) -> None:
106
+ solution = episode.traces[0]
107
+ if solution.ok:
108
+ graded = await self.grade(self.verifier_config(task), task, solution)
109
+ episode.traces[0] = graded[1]
110
+
111
+ async def stage_verifier(
112
+ self, task: vf.Task, solution: vf.Trace, runtime: Runtime
113
+ ) -> None:
114
+ artifacts = dict(solution.state.artifacts)
115
+ async with boundary(TaskError, "verifier task setup"):
116
+ await invoke(task.setup, {"trace": solution, "runtime": runtime})
117
+ await vf.restore(runtime, artifacts)
118
+ async with boundary(TaskError, "verifier staging"):
119
+ await invoke(task.stage_verifier, {"trace": solution, "runtime": runtime})
120
+
121
+ async def verify(self, task: vf.Task, solution: vf.Trace, runtime: Runtime) -> Any:
122
+ await task.score(solution, runtime)
123
+
124
+ async def grade(
125
+ self,
126
+ config: RuntimeConfig,
127
+ task: vf.Task,
128
+ solution: vf.Trace,
129
+ *,
130
+ scoring_timeout_covers_attempt: bool = False,
131
+ ) -> tuple[Any, vf.Trace]:
132
+ timeouts = resolve_rollout_timeouts(self.config.agent.timeout, task)
133
+ last: Exception | None = None
134
+ for attempt in range(self.config.verifier.retries + 1):
135
+ if attempt:
136
+ delay = backoff(attempt - 1)
137
+ logger.warning(
138
+ "isolated verifier attempt %d/%d failed (%s); retrying in %.1fs",
139
+ attempt,
140
+ self.config.verifier.retries + 1,
141
+ last,
142
+ delay,
143
+ )
144
+ await asyncio.sleep(delay)
145
+ try:
146
+ # Teardown is outside the stage deadlines: a completed score must
147
+ # survive a slow cleanup of the verifier runtime.
148
+ async with (
149
+ AsyncExitStack() as boxes,
150
+ asyncio.timeout(
151
+ timeouts.scoring if scoring_timeout_covers_attempt else None
152
+ ),
153
+ ):
154
+ async with asyncio.timeout(timeouts.setup):
155
+ # Failed setup or scoring must not alter the next attempt.
156
+ # Only the successful controller and trace leave this scope.
157
+ verifier_task = copy.deepcopy(task)
158
+ verifier_solution = copy.deepcopy(solution)
159
+ runtime = await boxes.enter_async_context(
160
+ provision_runtime(
161
+ config,
162
+ env=(
163
+ verifier_task.runtime_env()
164
+ if self.config.verifier.env is None
165
+ else self.config.verifier.env
166
+ ),
167
+ )
168
+ )
169
+ await runtime.prepare_setup()
170
+ await self.stage_verifier(
171
+ verifier_task, verifier_solution, runtime
172
+ )
173
+ await runtime.prepare_execution([])
174
+ async with asyncio.timeout(timeouts.scoring):
175
+ result = await self.verify(
176
+ verifier_task, verifier_solution, runtime
177
+ )
178
+ return result, verifier_solution
179
+ except Exception as error: # noqa: BLE001 - retry the whole fresh box
180
+ last = error
181
+ assert last is not None
182
+ raise last
verifiers/v1/rollout.py CHANGED
@@ -467,6 +467,7 @@ class Rollout:
467
467
  if trace.timing.agent.start and not trace.timing.agent.end:
468
468
  trace.timing.agent.end = time.time()
469
469
  if not self._failed and self._opened:
470
+ assert runtime is not None
470
471
  trace.timing.finalize.start = time.time()
471
472
  async with boundary(TaskError, "task finalize"):
472
473
  async with asyncio.timeout(self._timeouts.finalize):
verifiers/v1/task.py CHANGED
@@ -173,15 +173,36 @@ class Task(Generic[DataT, StateT, ConfigT]):
173
173
  async def finalize(self, trace: Trace, runtime: Runtime) -> None:
174
174
  return None
175
175
 
176
+ async def stage_verifier(self, trace: Trace, runtime: Runtime) -> None:
177
+ """Prepare trusted verifier-only inputs after artifacts are restored."""
178
+ return
179
+
176
180
  async def validate(self, runtime: Runtime) -> bool | None:
177
181
  """Check the ground truth, or return None when no model-free check exists."""
178
182
  return None
179
183
 
184
+ def defer_scoring(self) -> Self:
185
+ """An independent copy whose task signals are deferred.
186
+
187
+ Lifecycle hooks still run normally: in particular, ``finalize`` can prepare
188
+ state before declared artifacts are collected and the solver runtime is
189
+ destroyed. Only task metrics, rewards, and judges are skipped; harness
190
+ metrics remain attached to the solver trace.
191
+ """
192
+ clone = copy.deepcopy(self)
193
+ clone.scoring_deferred = True
194
+ return clone
195
+
196
+ scoring_deferred: bool = False
197
+
180
198
  async def score(
181
199
  self,
182
200
  trace: Trace,
183
201
  runtime: Runtime | None = None,
184
202
  ) -> None:
203
+ if self.scoring_deferred:
204
+ return
205
+
185
206
  def requires_runtime(fn) -> bool:
186
207
  param = inspect.signature(fn).parameters.get("runtime")
187
208
  # A defaulted runtime parameter can still be called offline with None.
@@ -4,47 +4,30 @@ grading for tasks that declare ``[verifier].environment_mode = "separate"``.
4
4
  The default env for harbor runs (the taskset package exports it). A shared-verifier
5
5
  task runs exactly as under the single-agent env: one `agent` trace, graded in the
6
6
  box it worked in. A separate-verifier task is graded by `finalize` instead: the
7
- solver's declared artifacts travel (collected by its task `finalize` while its box
8
- is alive), a fresh box is provisioned from the task's verifier declaration,
7
+ solver's declared artifacts travel (collected after its task `finalize` while its
8
+ box is alive), a fresh box is provisioned from the task's verifier declaration,
9
9
  `tests/` is staged there, and the verifier's rewards land on the solver's trace.
10
10
  No second agent is involved — the verifier is the task's own `tests/test.sh`.
11
11
  """
12
12
 
13
- import asyncio
14
- import logging
15
- from contextlib import AsyncExitStack
16
-
17
- from pydantic import Field
18
-
19
13
  import verifiers.v1 as vf
20
- from verifiers.v1.runtimes import RuntimeConfig, provision_runtime
14
+ from verifiers.v1.envs.isolated_verifier import (
15
+ IsolatedVerifierEnv,
16
+ IsolatedVerifierEnvConfig,
17
+ )
18
+ from verifiers.v1.runtimes import Runtime, RuntimeConfig
21
19
  from verifiers.v1.tasksets.harbor.taskset import (
22
20
  HarborTask,
23
21
  verifier_box_data,
24
22
  )
25
- from verifiers.v1.utils.artifacts import restore
26
23
  from verifiers.v1.utils.compile import resolve_runtime_config
27
- from verifiers.v1.utils.retries import backoff
28
24
 
29
- logger = logging.getLogger(__name__)
30
25
 
26
+ class HarborEnvConfig(IsolatedVerifierEnvConfig):
27
+ """The Harbor solver plus its optional independent verifier runtime."""
31
28
 
32
- class HarborEnvConfig(vf.EnvConfig):
33
- agent: vf.AgentConfig = vf.AgentConfig()
34
- """The one seat — the policy under evaluation/training; pin
35
- `--env.agent.harness.*` to choose its program or runtime."""
36
- verifier_runtime: RuntimeConfig | None = None
37
- """Where a separate-verifier task grades. None derives the grading box from
38
- the solver's runtime policy; set it (e.g. `--env.verifier-runtime.type prime
39
- --env.verifier-runtime.vm true`) when the verifier needs different placement
40
- than the agent."""
41
- verifier_retries: int = Field(2, ge=0)
42
- """Extra attempts at provisioning-and-grading the separate box before the
43
- episode fails. Grading is deterministic; what these retry is the
44
- infrastructure around it (image pulls, provisioning)."""
45
29
 
46
-
47
- class HarborEnv(vf.Env[HarborEnvConfig]):
30
+ class HarborEnv(IsolatedVerifierEnv, vf.Env[HarborEnvConfig]):
48
31
  async def run(self, task: vf.Task, agents: vf.Agents) -> None:
49
32
  if not isinstance(task, HarborTask):
50
33
  raise TypeError(
@@ -56,13 +39,13 @@ class HarborEnv(vf.Env[HarborEnvConfig]):
56
39
  # Resolve the verifier's box before the solve, so an impossible pairing
57
40
  # (e.g. a restricted Prime verifier without vm=true) costs nothing
58
41
  # rather than a full agent run.
59
- self._verifier_config(task)
60
- await agents.agent.run(task.graded_elsewhere())
42
+ self.verifier_config(task)
43
+ await agents.agent.run(task.defer_scoring(), collect_artifacts=True)
61
44
 
62
- def _verifier_config(self, task: HarborTask) -> RuntimeConfig:
45
+ def verifier_config(self, task: HarborTask) -> RuntimeConfig:
63
46
  base = (
64
- self.config.verifier_runtime
65
- if self.config.verifier_runtime is not None
47
+ self.config.verifier.runtime
48
+ if self.config.verifier.runtime is not None
66
49
  else self.config.agent.runtime
67
50
  )
68
51
  return resolve_runtime_config(base, HarborTask(verifier_box_data(task.data)))
@@ -73,55 +56,27 @@ class HarborEnv(vf.Env[HarborEnvConfig]):
73
56
  Provision a fresh box from the task's verifier declaration, restore the
74
57
  solver's collected artifacts, stage `tests/`, run the verifier, and record
75
58
  its rewards (and any extra reward.json keys as metrics) on the solver's
76
- trace. Infrastructure failures retry per `verifier_retries`; the last one
77
- fails the episode — a grading box that can't be reached must never read
78
- as reward 0."""
59
+ trace. Setup, restoration, staging, and scoring failures retry per
60
+ `verifier.retries`; the last one fails the episode."""
79
61
  if not isinstance(task, HarborTask) or task.data.verifier is None:
80
62
  return
81
63
  solution = episode.traces[0]
82
64
  if not solution.ok:
83
65
  return
84
66
  grader = HarborTask(verifier_box_data(task.data))
85
- scores = await self._grade(self._verifier_config(task), grader, solution)
67
+ scores, solution = await self.grade(
68
+ self.verifier_config(task),
69
+ grader,
70
+ solution,
71
+ scoring_timeout_covers_attempt=True,
72
+ )
86
73
  items = scores.items() if isinstance(scores, dict) else [("solved", scores)]
87
74
  for name, value in items:
88
75
  solution.record_reward(name, value)
76
+ episode.traces[0] = solution
89
77
 
90
- async def _grade(
91
- self, config: RuntimeConfig, grader: HarborTask, solution: vf.Trace
78
+ async def verify(
79
+ self, task: vf.Task, solution: vf.Trace, runtime: Runtime
92
80
  ) -> float | dict[str, float]:
93
- last: Exception | None = None
94
- for attempt in range(self.config.verifier_retries + 1):
95
- if attempt:
96
- delay = backoff(attempt - 1)
97
- logger.warning(
98
- "harbor verifier attempt %d/%d failed (%s); retrying in %.1fs",
99
- attempt,
100
- self.config.verifier_retries + 1,
101
- last,
102
- delay,
103
- )
104
- await asyncio.sleep(delay)
105
- try:
106
- # The scoring deadline covers provisioning and grading, but not the
107
- # box's teardown: a score already in hand must not be discarded
108
- # because the teardown ran out the clock.
109
- async with AsyncExitStack() as boxes:
110
- async with asyncio.timeout(grader.data.timeout.scoring):
111
- box = await boxes.enter_async_context(
112
- provision_runtime(config, env=grader.runtime_env())
113
- )
114
- await box.prepare_setup()
115
- await grader.setup(box)
116
- # Artifacts first, tests second: an artifact entry pointing
117
- # into /tests must not survive staging, which wipes and
118
- # rebuilds that directory.
119
- await restore(box, solution.state.artifacts)
120
- await grader._stage_tests(box, wipe=True)
121
- await box.prepare_execution([])
122
- scores = await grader._graded(box, solution)
123
- return scores
124
- except Exception as e: # noqa: BLE001 - each attempt's failure is retried
125
- last = e
126
- assert last is not None
127
- raise last
81
+ assert isinstance(task, HarborTask)
82
+ return await task.run_verifier(runtime, solution)
@@ -16,7 +16,6 @@ otherwise build the verifier image from ``tests/Dockerfile``.
16
16
  """
17
17
 
18
18
  import asyncio
19
- import copy
20
19
  import hashlib
21
20
  import io
22
21
  import logging
@@ -163,6 +162,8 @@ class HarborData(TaskData):
163
162
  class HarborTask(Task[HarborData]):
164
163
  """Stage and run Harbor's verifier inside the task's live runtime."""
165
164
 
165
+ verifier_staged: bool = False
166
+
166
167
  def runtime_env(self) -> dict[str, str]:
167
168
  return resolve_env(self.data.env)
168
169
 
@@ -186,9 +187,9 @@ class HarborTask(Task[HarborData]):
186
187
  f"{(result.stderr or result.stdout).strip()[-500:]}"
187
188
  )
188
189
  if self.data.healthcheck is not None:
189
- await self._wait_for_health(runtime, self.data.healthcheck)
190
+ await self.wait_for_health(runtime, self.data.healthcheck)
190
191
 
191
- async def _wait_for_health(self, runtime: Runtime, healthcheck: dict) -> None:
192
+ async def wait_for_health(self, runtime: Runtime, healthcheck: dict) -> None:
192
193
  from harbor.environments.base import HealthcheckError
193
194
  from harbor.models.task.config import HealthcheckConfig
194
195
 
@@ -246,18 +247,15 @@ class HarborTask(Task[HarborData]):
246
247
  f"collect hook failed (exit {result.exit_code}): "
247
248
  f"{hook.command}\n{detail}"
248
249
  )
249
- trace.state.artifacts = await collect(runtime, self.data.artifacts)
250
-
251
- def graded_elsewhere(self) -> "HarborTask":
252
- """A copy whose `solved` records nothing here: the harbor env grades this
253
- task's finished work in a separate box of the task's choosing."""
254
- clone = copy.copy(self)
255
- clone._graded_elsewhere = True
256
- return clone
250
+ if not self.scoring_deferred:
251
+ trace.state.artifacts = await collect(runtime, self.data.artifacts)
257
252
 
258
- _graded_elsewhere: bool = False
253
+ async def stage_verifier(self, runtime: Runtime) -> None:
254
+ # An artifact under /tests must not replace the task package's verifier.
255
+ await self.stage_tests(runtime, wipe=True)
256
+ self.verifier_staged = True
259
257
 
260
- async def _stage_tests(self, runtime: Runtime, wipe: bool = False) -> None:
258
+ async def stage_tests(self, runtime: Runtime, wipe: bool = False) -> None:
261
259
  """Put the task package's `tests/` in `/tests`, where `test.sh` expects it.
262
260
 
263
261
  Raises rather than scoring stale state: a leftover reward file — planted by
@@ -286,24 +284,26 @@ class HarborTask(Task[HarborData]):
286
284
  @reward(weight=1.0)
287
285
  async def solved(self, runtime: Runtime, trace: Trace) -> float | dict[str, float]:
288
286
  if self.data.verifier is not None:
289
- if not self._graded_elsewhere:
287
+ if not self.verifier_staged:
290
288
  raise TaskError(
291
289
  f"task {self.data.name!r} declares a separate verifier "
292
290
  '([verifier].environment_mode = "separate"); grade it through '
293
291
  "the harbor env (this taskset's default), or force shared "
294
292
  "grading with --taskset.ignore-separate-verifier"
295
293
  )
296
- return {}
297
- await self._stage_tests(runtime)
298
- return await self._graded(runtime, trace)
294
+ else:
295
+ await self.stage_tests(runtime)
296
+ return await self.run_verifier(runtime, trace)
299
297
 
300
- async def _graded(self, runtime: Runtime, trace: Trace) -> float | dict[str, float]:
298
+ async def run_verifier(
299
+ self, runtime: Runtime, trace: Trace
300
+ ) -> float | dict[str, float]:
301
301
  # By absolute path, in the runtime's configured workdir: Harbor execs the
302
302
  # script the same way, and scripts do grade the agent's work at `$PWD`.
303
303
  await runtime.run(
304
304
  ["bash", "/tests/test.sh"], resolve_env(self.data.verifier_env)
305
305
  )
306
- scores = await self._reward_json(runtime)
306
+ scores = await self.read_reward_json(runtime)
307
307
  if scores is not None:
308
308
  if isinstance(scores, dict) and "reward" in scores:
309
309
  trace.record_metrics(
@@ -325,7 +325,9 @@ class HarborTask(Task[HarborData]):
325
325
  except (SandboxError, OSError, ValueError):
326
326
  return 0.0
327
327
 
328
- async def _reward_json(self, runtime: Runtime) -> float | dict[str, float] | None:
328
+ async def read_reward_json(
329
+ self, runtime: Runtime
330
+ ) -> float | dict[str, float] | None:
329
331
  """Read Harbor's scalar or keyed JSON reward, if it is valid.
330
332
 
331
333
  Bounded: this is a grading input, and nothing guarantees its size.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: verifiers
3
- Version: 0.3.2.dev44
3
+ Version: 0.3.2.dev46
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
@@ -24,6 +24,8 @@ Requires-Python: <3.14,>=3.11
24
24
  Requires-Dist: aiohttp>=3.14.1
25
25
  Requires-Dist: anthropic>=1.0.0
26
26
  Requires-Dist: gepa>=0.0.6
27
+ Requires-Dist: h11>=0.16.0
28
+ Requires-Dist: httpx2>=2.12.0
27
29
  Requires-Dist: httpx>=0.27.0
28
30
  Requires-Dist: loguru>=0.7.0
29
31
  Requires-Dist: math-verify>=0.8.0
@@ -41,6 +43,7 @@ Requires-Dist: rich>=11.0.0
41
43
  Requires-Dist: tenacity>=8.5.0
42
44
  Requires-Dist: tomli-w>=1.0.0
43
45
  Requires-Dist: typing-extensions>=4.12.2
46
+ Requires-Dist: uvicorn>=0.52.0
44
47
  Provides-Extra: harbor
45
48
  Requires-Dist: harbor==0.21.0; (python_full_version >= '3.12') and extra == 'harbor'
46
49
  Provides-Extra: lean
@@ -1,17 +1,17 @@
1
1
  verifiers/__init__.py,sha256=yDYsckVQbUkwoh9mU_hBeCLGvS_aWgAxuoNm5LxQpDI,517
2
2
  verifiers/v1/__init__.py,sha256=qJKOsiUADKX4C1tMKsAPQXiJMo9-80FNeyPpAyRVshY,8352
3
- verifiers/v1/agent.py,sha256=doqKNn77Ldm1EIe-V69IwgBbMiN79FoVnvMcyZA0pNM,32223
3
+ verifiers/v1/agent.py,sha256=h4zI924A-tLgezoGQaus9msogj1fSoC1Ugty01RFqU4,32206
4
4
  verifiers/v1/env.py,sha256=CEhzjojhg5jqbOYruUnc5U7TtgDHDSx8NiiFA0qfbt8,18234
5
5
  verifiers/v1/episode.py,sha256=Fft0R2W6INAVeJhoE9b8h7L_KQSz7XuxP4FT_XhcsUY,5741
6
6
  verifiers/v1/errors.py,sha256=uQySmCUrjDYFkBIii1vBFPiEcPFPQZ9EGpas_6HvcQI,5279
7
7
  verifiers/v1/graph.py,sha256=K6wKkoT_8TpNqPQISu2Xf0eHtAYW_KUGw0CteN5zr3c,36529
8
8
  verifiers/v1/harness.py,sha256=Yx-XCIs1HAmqv1F-nhCTDBUwAx51xQWNWgFcWL1WRTM,13777
9
9
  verifiers/v1/judge.py,sha256=uqn-djkK2_7Q3kOjkDo6ipOvV_HOvuLa9y6AoDjRux8,9618
10
- verifiers/v1/rollout.py,sha256=hoKGn1CbShbyjEM9PgYq8pdKgoC91VebTkBSgUHLtiE,23978
10
+ verifiers/v1/rollout.py,sha256=FT6ZYfSqkKOFa5tm52XDTLijKxqpeLr3x2MBdLIk4xk,24021
11
11
  verifiers/v1/semantic.py,sha256=wssvBSEpY_tgzpFUADG9-N4HkDXgCZvNQ1pdf6qAsS0,4680
12
12
  verifiers/v1/session.py,sha256=xM7JJJVJACk0JfH9RAN1Ts8gJJKr2tGGRiIgQTrdieY,18692
13
13
  verifiers/v1/state.py,sha256=EckF2bWp-vV4b1jYJ9sLI5xrfGuI5spIgYYwW926toI,595
14
- verifiers/v1/task.py,sha256=vaVkBu00riiNU1Ke4NeeY2NQTXxOguz9d6NLe55hO_Q,11468
14
+ verifiers/v1/task.py,sha256=9YhllEDgoHSz72-1fcYwBADl-rOzASnrSVimLheWmwI,12237
15
15
  verifiers/v1/taskset.py,sha256=fp2E0IEhL_Ybj9cegZwljfTmW27p_30zTWHFKw_kXE4,4374
16
16
  verifiers/v1/trace.py,sha256=8KCGMF2iaUNH_PypKNx--QwbSsl5dUCGeXp9Dmkm-24,29623
17
17
  verifiers/v1/types.py,sha256=PH1SxRWXOjPCgQD9aZgM18_bECvFSNQ25gkCbulmTKU,9857
@@ -68,6 +68,8 @@ verifiers/v1/envs/agentic_judge/__init__.py,sha256=LfEcRHLG_siKzhzWmODDXWSLpLgfu
68
68
  verifiers/v1/envs/agentic_judge/env.py,sha256=IZ8G8OgMI4P5JKJhXM0rrsBloWKgqUSzYiKHpqhEPDY,14639
69
69
  verifiers/v1/envs/best_of_n/__init__.py,sha256=mubASiwXMNIhnQFDccNAc7yZ0OFsOVK8hH8WIx9b2A4,119
70
70
  verifiers/v1/envs/best_of_n/env.py,sha256=KEUJBCVqblnXP_lEwm3mjGdaikBMkecz8UjOFPBhxc0,1987
71
+ verifiers/v1/envs/isolated_verifier/__init__.py,sha256=HZxc65f87pSIPRslwTZzs0I17wGa-UMM-c-CHliwPss,214
72
+ verifiers/v1/envs/isolated_verifier/env.py,sha256=zy0IhzCDyYpqo6pPvIFIdsJy-N_aSM_vG7qSJNFz0Wo,7983
71
73
  verifiers/v1/envs/shared_agentic_judge/__init__.py,sha256=9T4vyYAuqgZSQjqTIceghxnpauvqeW6XGhZSQDCttMA,168
72
74
  verifiers/v1/envs/single_agent/__init__.py,sha256=r0edAc_6gtHBhPzeBGVcdHZZqSdCPZnLMje6eVgyz5U,138
73
75
  verifiers/v1/envs/single_agent/env.py,sha256=lPs_a6VA7KoH1xqA3T8S32Jzb_d4zrx0_54LrqT5z1Q,1187
@@ -152,8 +154,8 @@ verifiers/v1/serve/server.py,sha256=h1B53RZBuYgRt4xmh9L6jBJ0KvCdq7wmCAa3nwkJMok,
152
154
  verifiers/v1/serve/types.py,sha256=CjZ6PWQlzSRkBG77gXfzKHnw_S_qKUm9dZSSxVgkv1w,1552
153
155
  verifiers/v1/tasksets/__init__.py,sha256=5hTKSPLXSZTgKBbeKQI7FEp06c0ZsaQfuwvrvVFLgb8,712
154
156
  verifiers/v1/tasksets/harbor/__init__.py,sha256=JxThzHFYEkuSfQbVf3pEnjordgdfe4MU-QOsSSbyovU,326
155
- verifiers/v1/tasksets/harbor/env.py,sha256=J3ggeLVD9Uyv0jRACsNb4jSMobpUcuT3EAqdIAd4VNI,5975
156
- verifiers/v1/tasksets/harbor/taskset.py,sha256=idR4Yo1UJHLwBdAloAZ00BI-SaJxr6-to1wxKvQgqvQ,32277
157
+ verifiers/v1/tasksets/harbor/env.py,sha256=rZEh-1IaHpfsrwSOn7qTq7uV0K7cZZ28bHB7BEWQdnc,3622
158
+ verifiers/v1/tasksets/harbor/taskset.py,sha256=4WPpsc7xIP5Fux8xM4VgEceRKxRc6lCGixRWjAwojW4,32275
157
159
  verifiers/v1/tasksets/lean/__init__.py,sha256=oyv-GbJQBvCyGwo7Ysxm2NjZW-aX4j1uDoHU0f-E7xQ,767
158
160
  verifiers/v1/tasksets/lean/scoring.py,sha256=sfzsT6MUK0zdMgAQ57_P_0CBaPHy3M7go71QMr5hDrs,10599
159
161
  verifiers/v1/tasksets/lean/taskset.py,sha256=Jax76i22G9dOAswQHiWujVbFIM_PUz5yGJI_ShR4n8Y,9238
@@ -185,8 +187,8 @@ verifiers/v1/utils/prime.py,sha256=UTYRjp9cbjNb6CVmBHda-1wWZAIIfxNOmTyNuT7_wL4,9
185
187
  verifiers/v1/utils/retries.py,sha256=Y2ZgrAjn-qNkRKZP_RVNL_7EK0iaRcZeCa404lpGYi4,5417
186
188
  verifiers/v1/utils/score.py,sha256=493yJVMw8teCu9JxapxMFPFzI0hNUqdo0Y2nGW4kckk,6200
187
189
  verifiers/v1/utils/version.py,sha256=-obEo_-l9-D8FLef4hYxncOe-uJpxrM1g2Hig_37Sgs,1607
188
- verifiers-0.3.2.dev44.dist-info/METADATA,sha256=B0BGMenQq8aKxh-kAWwQ1xfbmu0Q1cXac2ZgyEefDkk,4065
189
- verifiers-0.3.2.dev44.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
190
- verifiers-0.3.2.dev44.dist-info/entry_points.txt,sha256=uqQje0TMsr7k6nXlWxlPs13Si0evb3pVCEDF6c-YL80,241
191
- verifiers-0.3.2.dev44.dist-info/licenses/LICENSE,sha256=v0RrUsdV3IDoZhrRce297IXS3xMHNJ-_LdLpFAUWb9k,1072
192
- verifiers-0.3.2.dev44.dist-info/RECORD,,
190
+ verifiers-0.3.2.dev46.dist-info/METADATA,sha256=ywadnUcDH-_ti14xITC_YxDyDv2IvcS5wkyYlcJlqDs,4153
191
+ verifiers-0.3.2.dev46.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
192
+ verifiers-0.3.2.dev46.dist-info/entry_points.txt,sha256=uqQje0TMsr7k6nXlWxlPs13Si0evb3pVCEDF6c-YL80,241
193
+ verifiers-0.3.2.dev46.dist-info/licenses/LICENSE,sha256=v0RrUsdV3IDoZhrRce297IXS3xMHNJ-_LdLpFAUWb9k,1072
194
+ verifiers-0.3.2.dev46.dist-info/RECORD,,