verifiers 0.3.2.dev30__py3-none-any.whl → 0.3.2.dev32__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 +23 -5
- verifiers/v1/envs/agentic_judge/env.py +1 -1
- verifiers/v1/rollout.py +10 -5
- verifiers/v1/utils/artifacts.py +53 -14
- {verifiers-0.3.2.dev30.dist-info → verifiers-0.3.2.dev32.dist-info}/METADATA +1 -1
- {verifiers-0.3.2.dev30.dist-info → verifiers-0.3.2.dev32.dist-info}/RECORD +9 -9
- {verifiers-0.3.2.dev30.dist-info → verifiers-0.3.2.dev32.dist-info}/WHEEL +0 -0
- {verifiers-0.3.2.dev30.dist-info → verifiers-0.3.2.dev32.dist-info}/entry_points.txt +0 -0
- {verifiers-0.3.2.dev30.dist-info → verifiers-0.3.2.dev32.dist-info}/licenses/LICENSE +0 -0
verifiers/v1/agent.py
CHANGED
|
@@ -362,21 +362,26 @@ class Agent:
|
|
|
362
362
|
runtime: Runtime | None = None,
|
|
363
363
|
tools: Mapping[str, SharedToolServer] | None = None,
|
|
364
364
|
on_trace: Callable[[Trace], None] | None = None,
|
|
365
|
+
collect_artifacts: bool = False,
|
|
365
366
|
) -> Trace:
|
|
366
367
|
"""Run this agent on `task` once and return the trace: one segment — the
|
|
367
368
|
program runs on the task's prompt until it exits (a multi-turn exchange
|
|
368
369
|
is `interaction()`). `runtime` places it into a live borrowed box instead of
|
|
369
370
|
provisioning one; `tools` are live servers borrowed from their
|
|
370
371
|
owner, counted in the pairing check; `on_trace` observes the trace the
|
|
371
|
-
moment it's minted, before any I/O.
|
|
372
|
-
|
|
373
|
-
the
|
|
372
|
+
moment it's minted, before any I/O. `collect_artifacts` captures the task's
|
|
373
|
+
declared artifacts after its finalizer while its container runtime is still
|
|
374
|
+
alive. Retries whole while the trace ends with a retryable error
|
|
375
|
+
(`config.retries`) — never into a borrowed box; the final trace keeps earlier
|
|
376
|
+
attempts' errors."""
|
|
374
377
|
if self._closed:
|
|
375
378
|
raise RuntimeError("Agent is closed; create a new agent")
|
|
376
379
|
retry = self.config.retries
|
|
377
380
|
history: list = []
|
|
378
381
|
for attempt in range(retry.max_retries + 1):
|
|
379
|
-
trace = await self._run_once(
|
|
382
|
+
trace = await self._run_once(
|
|
383
|
+
task, runtime, tools, on_trace, collect_artifacts
|
|
384
|
+
)
|
|
380
385
|
if attempt == retry.max_retries or not trace_should_retry(trace, retry):
|
|
381
386
|
break
|
|
382
387
|
if runtime is not None:
|
|
@@ -407,9 +412,20 @@ class Agent:
|
|
|
407
412
|
runtime: Runtime | None,
|
|
408
413
|
shared_tools: Mapping[str, SharedToolServer] | None,
|
|
409
414
|
on_trace: Callable[[Trace], None] | None,
|
|
415
|
+
collect_artifacts: bool,
|
|
410
416
|
) -> Trace:
|
|
411
417
|
params = self._rollout_params(task, runtime, dict(shared_tools or {}))
|
|
412
|
-
|
|
418
|
+
if collect_artifacts and isinstance(params["runtime_config"], SubprocessConfig):
|
|
419
|
+
raise TypeError(
|
|
420
|
+
"artifact collection requires a container runtime; subprocess "
|
|
421
|
+
"artifacts live in a host-only temporary working directory"
|
|
422
|
+
)
|
|
423
|
+
run = Rollout(
|
|
424
|
+
task=task,
|
|
425
|
+
on_trace=on_trace,
|
|
426
|
+
collect_artifacts=collect_artifacts,
|
|
427
|
+
**params,
|
|
428
|
+
)
|
|
413
429
|
try:
|
|
414
430
|
if await run.open():
|
|
415
431
|
await run.step()
|
|
@@ -626,6 +642,7 @@ class _EpisodeAgent(Agent):
|
|
|
626
642
|
runtime: Runtime | None = None,
|
|
627
643
|
tools: Mapping[str, SharedToolServer] | None = None,
|
|
628
644
|
on_trace: Callable[[Trace], None] | None = None,
|
|
645
|
+
collect_artifacts: bool = False,
|
|
629
646
|
) -> Trace:
|
|
630
647
|
async with self._gate or nullcontext():
|
|
631
648
|
trace = await super().run(
|
|
@@ -633,6 +650,7 @@ class _EpisodeAgent(Agent):
|
|
|
633
650
|
runtime=runtime,
|
|
634
651
|
tools=tools if tools is not None else self._shared_for(task),
|
|
635
652
|
on_trace=self._watch(on_trace),
|
|
653
|
+
collect_artifacts=collect_artifacts,
|
|
636
654
|
)
|
|
637
655
|
self._completed.append(trace)
|
|
638
656
|
return trace
|
|
@@ -342,7 +342,7 @@ class IsolatedAgenticJudgeEnv(AgenticJudgeEnv):
|
|
|
342
342
|
"""Judge only collected artifacts in a fresh box with the solver's policy."""
|
|
343
343
|
|
|
344
344
|
async def run(self, task: vf.Task, agents: vf.Agents) -> None:
|
|
345
|
-
solution = await agents.solver.run(task)
|
|
345
|
+
solution = await agents.solver.run(task, collect_artifacts=True)
|
|
346
346
|
if not solution.ok:
|
|
347
347
|
raise RuntimeError("the solver's rollout failed, so the judge never ran")
|
|
348
348
|
await agents.judge.run(
|
verifiers/v1/rollout.py
CHANGED
|
@@ -32,6 +32,7 @@ from verifiers.v1.state import state_cls
|
|
|
32
32
|
from verifiers.v1.task import Task
|
|
33
33
|
from verifiers.v1.trace import AgentInfo, Trace, TraceTask
|
|
34
34
|
from verifiers.v1.types import Messages, Request, Response, SystemMessage, UserMessage
|
|
35
|
+
from verifiers.v1.utils.artifacts import collect
|
|
35
36
|
from verifiers.v1.utils.decorators import discover_decorated, invoke
|
|
36
37
|
|
|
37
38
|
logger = logging.getLogger(__name__)
|
|
@@ -69,6 +70,7 @@ class Rollout:
|
|
|
69
70
|
interception: Interception | None = None,
|
|
70
71
|
runtime: Runtime | None = None,
|
|
71
72
|
on_trace: Callable[[Trace], None] | None = None,
|
|
73
|
+
collect_artifacts: bool = False,
|
|
72
74
|
) -> None:
|
|
73
75
|
self.task = task
|
|
74
76
|
self.harness = harness
|
|
@@ -81,6 +83,7 @@ class Rollout:
|
|
|
81
83
|
self._interception = interception
|
|
82
84
|
self.runtime = runtime
|
|
83
85
|
self._borrowed_runtime = runtime
|
|
86
|
+
self._collect_artifacts = collect_artifacts
|
|
84
87
|
self.trace: Trace = Trace(
|
|
85
88
|
task=TraceTask(
|
|
86
89
|
type=type(task).__name__,
|
|
@@ -466,12 +469,14 @@ class Rollout:
|
|
|
466
469
|
if not self._failed and self._opened:
|
|
467
470
|
trace.timing.finalize.start = time.time()
|
|
468
471
|
async with boundary(TaskError, "task finalize"):
|
|
469
|
-
|
|
470
|
-
invoke(
|
|
472
|
+
async with asyncio.timeout(self._timeouts.finalize):
|
|
473
|
+
await invoke(
|
|
471
474
|
self.task.finalize, {"trace": trace, "runtime": runtime}
|
|
472
|
-
)
|
|
473
|
-
self.
|
|
474
|
-
|
|
475
|
+
)
|
|
476
|
+
if self._collect_artifacts and not trace.state.artifacts:
|
|
477
|
+
trace.state.artifacts = await collect(
|
|
478
|
+
runtime, self.task.data.artifacts
|
|
479
|
+
)
|
|
475
480
|
now = time.time()
|
|
476
481
|
trace.timing.finalize.end = now
|
|
477
482
|
trace.timing.scoring.start = now
|
verifiers/v1/utils/artifacts.py
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Bounded, validated artifact collection and restoration across runtimes."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import io
|
|
5
6
|
import logging
|
|
6
7
|
import shlex
|
|
8
|
+
import tarfile
|
|
7
9
|
import uuid
|
|
8
10
|
from pathlib import PurePosixPath
|
|
9
11
|
from typing import TYPE_CHECKING
|
|
@@ -110,7 +112,12 @@ async def collect(
|
|
|
110
112
|
|
|
111
113
|
|
|
112
114
|
async def restore(runtime: Runtime, collected: dict[str, bytes | None]) -> None:
|
|
113
|
-
"""Extract `collected` in `runtime` at the original absolute paths.
|
|
115
|
+
"""Extract `collected` in `runtime` at the original absolute paths.
|
|
116
|
+
|
|
117
|
+
Archive bytes are untrusted: the agent controls both their source files and the
|
|
118
|
+
runtime tooling that creates them. Every archive is therefore validated on the host
|
|
119
|
+
before the grading runtime is changed. Only regular files and directories travel.
|
|
120
|
+
"""
|
|
114
121
|
if not collected:
|
|
115
122
|
return
|
|
116
123
|
# Restoring into the subprocess runtime would extract absolute paths onto the
|
|
@@ -120,6 +127,8 @@ async def restore(runtime: Runtime, collected: dict[str, bytes | None]) -> None:
|
|
|
120
127
|
"refusing to restore artifacts into the subprocess runtime: extraction "
|
|
121
128
|
"writes to absolute paths on the host. Grade in a container."
|
|
122
129
|
)
|
|
130
|
+
for root, archive in collected.items():
|
|
131
|
+
_validate_restore(root, archive)
|
|
123
132
|
# Clear every root up front, not per entry: a later nested root would otherwise
|
|
124
133
|
# delete content an earlier one just restored. Clearing also drops any file or
|
|
125
134
|
# symlink the image left at the target.
|
|
@@ -141,23 +150,16 @@ async def _tar_out(runtime: Runtime, artifact: Artifact, budget: int) -> bytes:
|
|
|
141
150
|
path = f"/tmp/vf-artifact-{uuid.uuid4().hex}.tar"
|
|
142
151
|
excludes = " ".join(f"--exclude={shlex.quote(p)}" for p in artifact.exclude)
|
|
143
152
|
try:
|
|
153
|
+
# macOS tar otherwise adds AppleDouble sidecars next to a directory root.
|
|
144
154
|
await _run(
|
|
145
155
|
runtime,
|
|
146
|
-
f"tar -cf {shlex.quote(path)} -C / {excludes} -- "
|
|
156
|
+
f"COPYFILE_DISABLE=1 tar -cf {shlex.quote(path)} -C / {excludes} -- "
|
|
147
157
|
f"{shlex.quote(artifact.source.lstrip('/'))}",
|
|
148
158
|
f"collect artifact {artifact.source!r}",
|
|
149
159
|
)
|
|
150
|
-
#
|
|
151
|
-
#
|
|
152
|
-
|
|
153
|
-
if (raw := sized.stdout.strip()).isdigit() and int(raw) > budget:
|
|
154
|
-
raise RuntimeError(
|
|
155
|
-
f"artifact {artifact.source!r} takes the collection over the "
|
|
156
|
-
f"{MAX_ARTIFACT_BYTES} byte limit. The grading box boots from the "
|
|
157
|
-
"agent's image, so only the delta needs to travel — narrow the source "
|
|
158
|
-
"or add `exclude` patterns."
|
|
159
|
-
)
|
|
160
|
-
return await runtime.read(path)
|
|
160
|
+
# The runtime enforces the remaining collection budget while transferring the
|
|
161
|
+
# bytes, so replacing or growing the archive cannot race a separate size probe.
|
|
162
|
+
return await runtime.read(path, max_bytes=budget)
|
|
161
163
|
finally:
|
|
162
164
|
# Best-effort: the box is about to be destroyed and the name is unique per call.
|
|
163
165
|
try:
|
|
@@ -166,6 +168,43 @@ async def _tar_out(runtime: Runtime, artifact: Artifact, budget: int) -> bytes:
|
|
|
166
168
|
logger.debug("failed to remove %s", path, exc_info=True)
|
|
167
169
|
|
|
168
170
|
|
|
171
|
+
def _validate_restore(root: str, archive: bytes | None) -> None:
|
|
172
|
+
"""Reject archive content that could restore outside its declared root."""
|
|
173
|
+
root_path = PurePosixPath(root)
|
|
174
|
+
if (
|
|
175
|
+
root_path.anchor != "/"
|
|
176
|
+
or ".." in root_path.parts
|
|
177
|
+
or root_path == PurePosixPath("/")
|
|
178
|
+
):
|
|
179
|
+
raise RuntimeError(
|
|
180
|
+
f"artifact root {root!r} must be an absolute path below '/' with no '..'"
|
|
181
|
+
)
|
|
182
|
+
if archive is None:
|
|
183
|
+
return
|
|
184
|
+
|
|
185
|
+
try:
|
|
186
|
+
with tarfile.open(fileobj=io.BytesIO(archive), mode="r:") as tar:
|
|
187
|
+
for member in tar:
|
|
188
|
+
member_path = PurePosixPath(member.name)
|
|
189
|
+
destination = PurePosixPath("/") / member_path
|
|
190
|
+
if (
|
|
191
|
+
not member.name
|
|
192
|
+
or member_path.is_absolute()
|
|
193
|
+
or ".." in member_path.parts
|
|
194
|
+
or not destination.is_relative_to(root_path)
|
|
195
|
+
):
|
|
196
|
+
raise RuntimeError(
|
|
197
|
+
f"artifact member {member.name!r} is outside declared root "
|
|
198
|
+
f"{root!r}"
|
|
199
|
+
)
|
|
200
|
+
if not (member.isfile() or member.isdir()):
|
|
201
|
+
raise RuntimeError(
|
|
202
|
+
f"artifact member {member.name!r} is a link or special file"
|
|
203
|
+
)
|
|
204
|
+
except tarfile.TarError as exc:
|
|
205
|
+
raise RuntimeError(f"unreadable artifact archive for {root!r}: {exc}") from exc
|
|
206
|
+
|
|
207
|
+
|
|
169
208
|
async def _run(runtime: Runtime, command: str, action: str) -> None:
|
|
170
209
|
result = await runtime.run(["sh", "-c", command], {})
|
|
171
210
|
if result.exit_code:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: verifiers
|
|
3
|
-
Version: 0.3.2.
|
|
3
|
+
Version: 0.3.2.dev32
|
|
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
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
verifiers/__init__.py,sha256=yDYsckVQbUkwoh9mU_hBeCLGvS_aWgAxuoNm5LxQpDI,517
|
|
2
2
|
verifiers/v1/__init__.py,sha256=AYjjpOBfpSlWZRN51V1KaxMYMBWSZhBEIOzvNk23pxg,8348
|
|
3
|
-
verifiers/v1/agent.py,sha256=
|
|
3
|
+
verifiers/v1/agent.py,sha256=QvP7jJMR1NQEzAdcqSxcPRgYYTZYt6gxst3vxmXymOE,32384
|
|
4
4
|
verifiers/v1/env.py,sha256=CEhzjojhg5jqbOYruUnc5U7TtgDHDSx8NiiFA0qfbt8,18234
|
|
5
5
|
verifiers/v1/episode.py,sha256=uStvI4WqgJT50hmHIB55m5B-ASqqLz-fZ-ynBz9uzEY,5474
|
|
6
6
|
verifiers/v1/errors.py,sha256=uQySmCUrjDYFkBIii1vBFPiEcPFPQZ9EGpas_6HvcQI,5279
|
|
7
7
|
verifiers/v1/graph.py,sha256=09uysIuX7kHCt9XRndpACkQtWocgVbtiLewcEmnzmD4,35787
|
|
8
8
|
verifiers/v1/harness.py,sha256=ziECqEBWV34UWbLlxrkhY6MwkbVYkoXBOoaY7mZxwTs,14753
|
|
9
9
|
verifiers/v1/judge.py,sha256=Pvr0C41ah1qkNSZ0WXdkDvBMfv5YSfY1REHncwlJ8cg,9346
|
|
10
|
-
verifiers/v1/rollout.py,sha256=
|
|
10
|
+
verifiers/v1/rollout.py,sha256=hoKGn1CbShbyjEM9PgYq8pdKgoC91VebTkBSgUHLtiE,23978
|
|
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
|
|
@@ -65,7 +65,7 @@ verifiers/v1/dialects/chat.py,sha256=_pMsNpbffiXiK0_0tboAKtuj7-smUnkS9H0LPK4j1mU
|
|
|
65
65
|
verifiers/v1/dialects/responses.py,sha256=KRuS9TKw2uoNz4NsG7E-H6eyhm_EJQwVtZ5_XW9iB3c,29090
|
|
66
66
|
verifiers/v1/envs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
67
67
|
verifiers/v1/envs/agentic_judge/__init__.py,sha256=LfEcRHLG_siKzhzWmODDXWSLpLgfum3UFdQDK9XG0gk,325
|
|
68
|
-
verifiers/v1/envs/agentic_judge/env.py,sha256=
|
|
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
71
|
verifiers/v1/envs/shared_agentic_judge/__init__.py,sha256=9T4vyYAuqgZSQjqTIceghxnpauvqeW6XGhZSQDCttMA,168
|
|
@@ -168,7 +168,7 @@ verifiers/v1/tasksets/textarena/__init__.py,sha256=Os2OlBY_pSH0B1DP32RitumgSLpq2
|
|
|
168
168
|
verifiers/v1/tasksets/textarena/taskset.py,sha256=9fa_unlFiFZyuN4I02rW3x4qyRnFWXqwTUZTloQefu0,4433
|
|
169
169
|
verifiers/v1/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
170
170
|
verifiers/v1/utils/aio.py,sha256=0yNFOqB2oO6Qktc6NI_ZSAGuo1aHsIK_wuT1BtmoZ3I,1532
|
|
171
|
-
verifiers/v1/utils/artifacts.py,sha256=
|
|
171
|
+
verifiers/v1/utils/artifacts.py,sha256=GowZubXGk6NhVgaAUKCVXYoBh_3L1Aw0LAHe6tNwl8g,8447
|
|
172
172
|
verifiers/v1/utils/compile.py,sha256=5pxy31wx2Q-3pQjOO5B9SH6OT-wwb8r4F1LAl6HMeQA,5835
|
|
173
173
|
verifiers/v1/utils/decorators.py,sha256=vhoN6YoKb15-Fkqmc9RaNlUIbt9WRvNK4zAn2bAxfS4,5367
|
|
174
174
|
verifiers/v1/utils/format.py,sha256=NfQo9M5KMzWCZpQaet5YtsJx56vCKAZPfbjZZJLJhhM,2187
|
|
@@ -185,8 +185,8 @@ verifiers/v1/utils/prime.py,sha256=UTYRjp9cbjNb6CVmBHda-1wWZAIIfxNOmTyNuT7_wL4,9
|
|
|
185
185
|
verifiers/v1/utils/retries.py,sha256=Y2ZgrAjn-qNkRKZP_RVNL_7EK0iaRcZeCa404lpGYi4,5417
|
|
186
186
|
verifiers/v1/utils/score.py,sha256=493yJVMw8teCu9JxapxMFPFzI0hNUqdo0Y2nGW4kckk,6200
|
|
187
187
|
verifiers/v1/utils/version.py,sha256=-obEo_-l9-D8FLef4hYxncOe-uJpxrM1g2Hig_37Sgs,1607
|
|
188
|
-
verifiers-0.3.2.
|
|
189
|
-
verifiers-0.3.2.
|
|
190
|
-
verifiers-0.3.2.
|
|
191
|
-
verifiers-0.3.2.
|
|
192
|
-
verifiers-0.3.2.
|
|
188
|
+
verifiers-0.3.2.dev32.dist-info/METADATA,sha256=d9DcoI2d4e5C0TSyk5lbWqDmU37zuU8GZp5dh0IEJIM,4144
|
|
189
|
+
verifiers-0.3.2.dev32.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
|
|
190
|
+
verifiers-0.3.2.dev32.dist-info/entry_points.txt,sha256=uqQje0TMsr7k6nXlWxlPs13Si0evb3pVCEDF6c-YL80,241
|
|
191
|
+
verifiers-0.3.2.dev32.dist-info/licenses/LICENSE,sha256=v0RrUsdV3IDoZhrRce297IXS3xMHNJ-_LdLpFAUWb9k,1072
|
|
192
|
+
verifiers-0.3.2.dev32.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|