verifiers 0.2.2.dev58__py3-none-any.whl → 0.2.2.dev60__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
@@ -25,7 +25,7 @@ from verifiers.v1.harness import Harness
25
25
  from verifiers.v1.interception import Interception, InterceptionServer
26
26
  from verifiers.v1.mcp import SharedToolServer
27
27
  from verifiers.v1.retries import backoff, trace_should_retry
28
- from verifiers.v1.rollout import RolloutRun, _as_messages
28
+ from verifiers.v1.rollout import RolloutRun, RolloutTimeouts, _as_messages
29
29
  from verifiers.v1.runtimes import (
30
30
  NetworkPolicyConfig,
31
31
  Runtime,
@@ -533,23 +533,23 @@ class Agent:
533
533
  "harness": self.harness,
534
534
  "ctx": self.ctx,
535
535
  "runtime_config": runtime_config,
536
- "setup_timeout": (
537
- self.timeout.setup
538
- if self.timeout.setup is not None
539
- else task.data.timeout.setup
540
- ),
541
- "agent_timeout": cap_remote_agent_timeout(
542
- agent_timeout, runtime_config, task
543
- ),
544
- "finalize_timeout": (
545
- self.timeout.finalize
546
- if self.timeout.finalize is not None
547
- else task.data.timeout.finalize
548
- ),
549
- "scoring_timeout": (
550
- self.timeout.scoring
551
- if self.timeout.scoring is not None
552
- else task.data.timeout.scoring
536
+ "timeouts": RolloutTimeouts(
537
+ setup=(
538
+ self.timeout.setup
539
+ if self.timeout.setup is not None
540
+ else task.data.timeout.setup
541
+ ),
542
+ agent=cap_remote_agent_timeout(agent_timeout, runtime_config, task),
543
+ finalize=(
544
+ self.timeout.finalize
545
+ if self.timeout.finalize is not None
546
+ else task.data.timeout.finalize
547
+ ),
548
+ scoring=(
549
+ self.timeout.scoring
550
+ if self.timeout.scoring is not None
551
+ else task.data.timeout.scoring
552
+ ),
553
553
  ),
554
554
  "limits": self.limits,
555
555
  "shared_tools": shared_tools,
@@ -8,6 +8,10 @@ from verifiers.v1.harnesses.claude_code import (
8
8
  ClaudeCodeHarnessConfig,
9
9
  )
10
10
  from verifiers.v1.harnesses.codex import CodexHarness, CodexHarnessConfig
11
+ from verifiers.v1.harnesses.hermes_agent import (
12
+ HermesAgentHarness,
13
+ HermesAgentHarnessConfig,
14
+ )
11
15
  from verifiers.v1.harnesses.kimi_code import KimiCodeHarness, KimiCodeHarnessConfig
12
16
  from verifiers.v1.harnesses.mini_swe_agent import (
13
17
  MiniSWEAgentHarness,
@@ -28,6 +32,8 @@ __all__ = [
28
32
  "ClaudeCodeHarnessConfig",
29
33
  "CodexHarness",
30
34
  "CodexHarnessConfig",
35
+ "HermesAgentHarness",
36
+ "HermesAgentHarnessConfig",
31
37
  "KimiCodeHarness",
32
38
  "KimiCodeHarnessConfig",
33
39
  "MiniSWEAgentHarness",
@@ -0,0 +1,6 @@
1
+ from verifiers.v1.harnesses.hermes_agent.harness import (
2
+ HermesAgentHarness,
3
+ HermesAgentHarnessConfig,
4
+ )
5
+
6
+ __all__ = ["HermesAgentHarness", "HermesAgentHarnessConfig"]
@@ -0,0 +1,140 @@
1
+ """Run Hermes Agent against interception through its native ACP server."""
2
+
3
+ import json
4
+ import logging
5
+ from pathlib import Path
6
+
7
+ from pydantic import Field
8
+
9
+ from verifiers.v1.acp import ACP
10
+ from verifiers.v1.clients import ModelContext
11
+ from verifiers.v1.configs.harness import HarnessConfig
12
+ from verifiers.v1.harness import Harness
13
+ from verifiers.v1.runtimes import ProgramResult, Runtime
14
+ from verifiers.v1.task import TaskData
15
+ from verifiers.v1.trace import Trace
16
+
17
+ PROGRAM_SOURCE = (Path(__file__).resolve().parent / "program.py").read_text()
18
+ SKILLS_DIR = ".vf-hermes-skills"
19
+ HERMES_ACP = ACP()
20
+ PROVIDER = "intercept"
21
+ logger = logging.getLogger(__name__)
22
+
23
+
24
+ class HermesAgentHarnessConfig(HarnessConfig):
25
+ version: str = Field(default="0.19.0", pattern=r"^[A-Za-z0-9._+-]+$")
26
+ """Hermes Agent release to install, pinned for reproducibility."""
27
+ use_bundled_skill: bool = False
28
+ """Enable Hermes Agent's bundled skill catalog in addition to uploaded skills."""
29
+
30
+
31
+ class HermesAgentHarness(Harness[HermesAgentHarnessConfig]):
32
+ APPENDS_SYSTEM_PROMPT = True
33
+ SUPPORTS_MCP = True
34
+ SUPPORTS_RESUME = True
35
+ SUPPORTS_SKILLS = True
36
+
37
+ async def setup(self, runtime: Runtime) -> None:
38
+ await self.install_skills(runtime, SKILLS_DIR)
39
+ logger.info(
40
+ "hermes-agent: ensuring Hermes Agent %s is installed", self.config.version
41
+ )
42
+ source = PROGRAM_SOURCE.replace("{version}", self.config.version)
43
+ await runtime.prepare_uv_script(source, self.config.resolved_env)
44
+ await HERMES_ACP.setup(self, runtime)
45
+
46
+ async def launch(
47
+ self,
48
+ ctx: ModelContext,
49
+ trace: Trace,
50
+ runtime: Runtime,
51
+ endpoint: str,
52
+ secret: str,
53
+ mcp_urls: dict[str, str],
54
+ data: TaskData,
55
+ ) -> ProgramResult:
56
+ if self.config.disabled_tools:
57
+ raise ValueError("Hermes Agent ACP does not support disabling tools")
58
+
59
+ system_prompt, prompt = self.resolve_prompt(data)
60
+ home = self._home(trace)
61
+ model: dict[str, object] = {
62
+ "provider": PROVIDER,
63
+ "default": ctx.model,
64
+ "base_url": endpoint,
65
+ "api_mode": "chat_completions",
66
+ }
67
+ if ctx.sampling.max_tokens is not None:
68
+ model["max_tokens"] = ctx.sampling.max_tokens
69
+ await runtime.write(
70
+ f"{home}/config.yaml",
71
+ json.dumps(
72
+ {
73
+ "model": model,
74
+ # The ACP client already approves tool requests. Avoid routing Hermes'
75
+ # redundant smart-approval model calls through interception as turns.
76
+ "approvals": {"mode": "off"},
77
+ "providers": {
78
+ PROVIDER: {
79
+ "api": endpoint,
80
+ "api_key": secret,
81
+ "transport": "chat_completions",
82
+ "discover_models": False,
83
+ "models": [ctx.model],
84
+ }
85
+ },
86
+ }
87
+ ).encode(),
88
+ )
89
+ if not self.config.use_bundled_skill:
90
+ await runtime.write(
91
+ f"{home}/.no-bundled-skills",
92
+ b"Verifiers supplies evaluation skills explicitly.\n",
93
+ )
94
+
95
+ if self.config.skills:
96
+ skill_home = f"{home}/skills"
97
+ for command in (
98
+ ["rm", "-rf", skill_home],
99
+ ["cp", "-R", SKILLS_DIR, skill_home],
100
+ ):
101
+ copied = await runtime.run(command, {})
102
+ if copied.exit_code != 0:
103
+ raise RuntimeError(
104
+ f"failed to stage Hermes skills: {copied.stderr.strip()[-500:]}"
105
+ )
106
+
107
+ env = {
108
+ **self.config.resolved_env,
109
+ "HERMES_HOME": home,
110
+ "HERMES_ACP_SKIP_CONFIGURED_MCP": "1",
111
+ }
112
+ source = PROGRAM_SOURCE.replace("{version}", self.config.version)
113
+ command = await runtime.prepare_uv_script(source, env)
114
+ calls_before = len(trace.calls)
115
+ result = await HERMES_ACP.run(
116
+ runtime,
117
+ env,
118
+ command,
119
+ prompt,
120
+ mcp_urls=mcp_urls,
121
+ system_prompt=system_prompt,
122
+ session_path=f"{home}/acp-session",
123
+ )
124
+ if not any(call.node is not None for call in trace.calls[calls_before:]):
125
+ detail = (result.stderr or result.stdout).strip()[-500:] or "<no output>"
126
+ raise RuntimeError(
127
+ "Hermes Agent completed without committing a model turn: " + detail
128
+ )
129
+ return result
130
+
131
+ async def cleanup(self, trace: Trace, runtime: Runtime) -> None:
132
+ result = await runtime.run(["rm", "-rf", self._home(trace)], {})
133
+ if result.exit_code != 0:
134
+ raise RuntimeError(
135
+ f"failed to clean up Hermes home: {result.stderr.strip()[-500:]}"
136
+ )
137
+
138
+ @staticmethod
139
+ def _home(trace: Trace) -> str:
140
+ return f"/tmp/vf-hermes/{trace.id}"
@@ -0,0 +1,10 @@
1
+ # /// script
2
+ # requires-python = ">=3.11,<3.14"
3
+ # dependencies = ["hermes-agent[acp,mcp]=={version}"]
4
+ # ///
5
+ """Start Hermes Agent's native ACP server."""
6
+
7
+ from acp_adapter.entry import main
8
+
9
+ if __name__ == "__main__":
10
+ main()
verifiers/v1/rollout.py CHANGED
@@ -23,6 +23,7 @@ import logging
23
23
  import time
24
24
  from collections.abc import AsyncIterator, Callable
25
25
  from contextlib import AsyncExitStack, asynccontextmanager
26
+ from dataclasses import dataclass
26
27
 
27
28
  from verifiers.v1.clients import ModelContext
28
29
  from verifiers.v1.configs.agent import AgentConfig
@@ -57,6 +58,19 @@ from verifiers.v1.types import Messages
57
58
  logger = logging.getLogger(__name__)
58
59
 
59
60
 
61
+ @dataclass(frozen=True)
62
+ class RolloutTimeouts:
63
+ """Per-stage rollout timeouts in seconds (None = unlimited), each bounding one
64
+ lifecycle stage: `setup` covers task + harness setup in `open()`, `agent` is the
65
+ cumulative budget the run's segments draw down across `step()`s, `finalize` and
66
+ `scoring` bound their `close()` stages."""
67
+
68
+ setup: float | None = None
69
+ agent: float | None = None
70
+ finalize: float | None = None
71
+ scoring: float | None = None
72
+
73
+
60
74
  def _as_messages(raw: Messages) -> Messages:
61
75
  """A turn's messages may arrive typed or as wire dicts (env code naturally
62
76
  writes `{"role": "user", ...}`); the trace speaks typed, so normalize here."""
@@ -115,10 +129,7 @@ class RolloutRun:
115
129
  runtime_config: RuntimeConfig,
116
130
  wire_data: TaskData | None = None,
117
131
  has_user: bool = False,
118
- setup_timeout: float | None = None,
119
- agent_timeout: float | None = None,
120
- finalize_timeout: float | None = None,
121
- scoring_timeout: float | None = None,
132
+ timeouts: RolloutTimeouts | None = None,
122
133
  limits: RolloutLimits | None = None,
123
134
  shared_tools: dict[str, SharedToolServer] | None = None,
124
135
  interception: Interception | None = None,
@@ -130,11 +141,8 @@ class RolloutRun:
130
141
  self.ctx = ctx
131
142
  self.runtime_config = runtime_config
132
143
  self._has_user = has_user
133
- self._setup_timeout = setup_timeout
134
- self._agent_timeout = agent_timeout
135
- self._agent_time_remaining = agent_timeout
136
- self._finalize_timeout = finalize_timeout
137
- self._scoring_timeout = scoring_timeout
144
+ self._timeouts = timeouts or RolloutTimeouts()
145
+ self._agent_time_remaining = self._timeouts.agent
138
146
  self._shared_tools = shared_tools or {}
139
147
  self._interception = interception
140
148
  self.runtime = runtime
@@ -164,7 +172,7 @@ class RolloutRun:
164
172
  self.deadline_at: float | None = None
165
173
  """The active harness segment's absolute deadline (event-loop clock), or
166
174
  None between segments / when unbounded. An interaction spends one cumulative
167
- `agent_timeout` budget only while its own segments run, so time awaiting
175
+ `timeouts.agent` budget only while its own segments run, so time awaiting
168
176
  the caller (including another interleaved agent) cannot starve it."""
169
177
 
170
178
  @property
@@ -244,8 +252,8 @@ class RolloutRun:
244
252
  # Task setup and harness provisioning share one setup-stage deadline.
245
253
  setup_deadline = (
246
254
  None
247
- if self._setup_timeout is None
248
- else asyncio.get_running_loop().time() + self._setup_timeout
255
+ if self._timeouts.setup is None
256
+ else asyncio.get_running_loop().time() + self._timeouts.setup
249
257
  )
250
258
  async with (
251
259
  boundary(TaskError, "task setup"),
@@ -340,7 +348,7 @@ class RolloutRun:
340
348
  self.fail(
341
349
  HarnessError(
342
350
  f"agent timeout: rollout exceeded its "
343
- f"{self._agent_timeout:g}s budget"
351
+ f"{self._timeouts.agent:g}s budget"
344
352
  )
345
353
  )
346
354
  else:
@@ -406,7 +414,7 @@ class RolloutRun:
406
414
  invoke(
407
415
  self.task.finalize, {"trace": trace, "runtime": runtime}
408
416
  ),
409
- self._finalize_timeout,
417
+ self._timeouts.finalize,
410
418
  )
411
419
  now = time.time()
412
420
  trace.timing.finalize.end = now
@@ -418,7 +426,7 @@ class RolloutRun:
418
426
  self.task.score(trace, runtime),
419
427
  self.harness.score(trace, runtime),
420
428
  ),
421
- self._scoring_timeout,
429
+ self._timeouts.scoring,
422
430
  )
423
431
  trace.timing.scoring.end = time.time()
424
432
  except Exception as e: # noqa: BLE001 - finalize boundary records every rollout failure
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: verifiers
3
- Version: 0.2.2.dev58
3
+ Version: 0.2.2.dev60
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
@@ -168,7 +168,7 @@ verifiers/utils/tool_utils.py,sha256=gInWZODWQUZN4TyEMuuITyOUm9qlHJxtcvr1zZl_zJs
168
168
  verifiers/utils/usage_utils.py,sha256=Mw6WC78r6EsLyRFdP9ICveqptkjizbSkAC86ZH0geVU,3918
169
169
  verifiers/utils/version_utils.py,sha256=39gV8-mNF9GspAgKloLo8oy7_MVluMrwGPrntQv2hx4,2656
170
170
  verifiers/v1/__init__.py,sha256=Im5qTZy9mH6PRTUi-1V6W7EWKZzJyTWLGKMYALbWz_w,7467
171
- verifiers/v1/agent.py,sha256=t5Z_iI6Zyt0ieMDI7pfHUVYIKViuSXF-CI_rIApx3f4,32071
171
+ verifiers/v1/agent.py,sha256=ASbe6IHAYcwivzQkHunX3cGu-o10Gez8tlVhO-pLYyE,32134
172
172
  verifiers/v1/artifacts.py,sha256=5eKOjDSUWwj7GrhxILMHdz2ngEONWMdcTGlBcuGgrLU,5834
173
173
  verifiers/v1/decorators.py,sha256=9CcCwgZlRjVGPqT5G1CFJUHP4OIMuBGkUJZkcI6dnbk,3865
174
174
  verifiers/v1/env.py,sha256=TGCq_rPp_VdyGAKpHuscSDyIxIGnim2N-dpKkG-J5p0,18175
@@ -181,7 +181,7 @@ verifiers/v1/legacy.py,sha256=ISk3Bix9Hy5EjZtyV9QO0LNyDNDklBHVjFokOrw_3U8,22628
181
181
  verifiers/v1/loaders.py,sha256=NSIBdNU3JVeyddL-dM4dJt6khCHyjCgzJuFtLrgiwD0,9989
182
182
  verifiers/v1/push.py,sha256=MWZmvyj0iyzNIO0xRhlkCXtFF1plq6EJi8rWlvrFtvI,11363
183
183
  verifiers/v1/retries.py,sha256=quUGVruUkZx-9WQcFiocMfL_M056voU8gwDqsuLR3f4,5412
184
- verifiers/v1/rollout.py,sha256=ehPoN6EnrIg_lvF1-pdf9pS8j47K3z6jDHc66MixzX0,20470
184
+ verifiers/v1/rollout.py,sha256=s8ji1W19FHZOn-Wn3_fjw-b1hhmi0EqZ7dZVntYK5pM,20732
185
185
  verifiers/v1/scoring.py,sha256=-R5Cog14r_tJ6Q_oOfGr5VPAm2CCUhofYZB6B9lm4wM,5787
186
186
  verifiers/v1/session.py,sha256=J6ZPuvsg6vMTJAs9dMUMtfdjCuvp6Hom877DVdKFKlY,6912
187
187
  verifiers/v1/state.py,sha256=pcpN2V6tX7rIO5XdtdUYay1j0ktEwrMtq7w1ApfsPdE,675
@@ -252,7 +252,7 @@ verifiers/v1/gepa/config.py,sha256=gFn8Re4GoeHJs2PZ-iw4jCEZ72AiM1lNgwaA0uvJlYw,4
252
252
  verifiers/v1/gepa/dataset.py,sha256=N2QYqUljHVuAdCbUuydEIlarvAWa6LFdGKu9qsOvjro,2186
253
253
  verifiers/v1/gepa/reflection.py,sha256=ptHhx0lcDLcWhWhXuslh9T8gPVNpnX5XLPtXoneW1yA,1054
254
254
  verifiers/v1/gepa/runner.py,sha256=-LZEt0lod6NKfyxg2dvOXZjfzTv3AWwendexd1De26c,6260
255
- verifiers/v1/harnesses/__init__.py,sha256=706ES37vrh_kCbctnbgkXIb1jBtO2ozZY5klBSg8ss0,1460
255
+ verifiers/v1/harnesses/__init__.py,sha256=CDKLCWYqRbVy3NvEzavC12_DaxuoHQfw9AQ8hmlQuBQ,1624
256
256
  verifiers/v1/harnesses/bash/__init__.py,sha256=IAV4eMMqDHSl-9ANb0Q3kxmBZ22SeHZFBM448X4gQio,140
257
257
  verifiers/v1/harnesses/bash/harness.py,sha256=ZtjhsANuK3vXkf9mRCi8-sBMyPebJMBNHa58vTd2Fm8,5284
258
258
  verifiers/v1/harnesses/bash/program.py,sha256=hGv3k9jwoabL045AGiZWiX76Cca3Juk58AaMYeJTEGo,15334
@@ -263,6 +263,9 @@ verifiers/v1/harnesses/claude_code/__init__.py,sha256=JkZQylMSA3o1fvBx_NM41y_umu
263
263
  verifiers/v1/harnesses/claude_code/harness.py,sha256=98k5T8d8nFQmxIU-ytkrkrrP6UNR5NxoGhVJRLVa-fI,4015
264
264
  verifiers/v1/harnesses/codex/__init__.py,sha256=ocyBvlpSO8c3pT6D1ebrQohoXYESQcCyPT5kN3j1-N4,132
265
265
  verifiers/v1/harnesses/codex/harness.py,sha256=OG1riQdVmZzVpHENsd0UzsIbdIAxDMHgRikmo73HL5M,13071
266
+ verifiers/v1/harnesses/hermes_agent/__init__.py,sha256=k84Y8UTTf1KOw5LGqPb2dk-hYFagPevDJBOF7qsR_vo,176
267
+ verifiers/v1/harnesses/hermes_agent/harness.py,sha256=AveZi9g4pga9pYy4VAmn_KMKlJw3KhwG45JZcPxbdR4,5094
268
+ verifiers/v1/harnesses/hermes_agent/program.py,sha256=9qPLbUnhox2bGu5ypEEnBhw9QKtYwNvrbl7W_zeNMdk,229
266
269
  verifiers/v1/harnesses/kimi_code/__init__.py,sha256=1BEotSfuzmVcE7UrxHeAREM5d7J20cGkonHuowlrzu4,161
267
270
  verifiers/v1/harnesses/kimi_code/harness.py,sha256=Pc2uiBsia-uQFuXvaLMFdblE6TS-p0E2cvB6ofRrHK0,4515
268
271
  verifiers/v1/harnesses/mini_swe_agent/__init__.py,sha256=1OBu2Y-XqL72-dGhFdgB6sr5uX_peEEXT9vEjPy1ovU,182
@@ -333,8 +336,8 @@ verifiers/v1/utils/logging.py,sha256=OcMHA6NsYux3oIzjPuI95rDWmFBHNcHDjZeNIhXTX-Y
333
336
  verifiers/v1/utils/memory.py,sha256=ZkIvGk6uITAH5sKon65LifKPbvZr8mJ__PVc23FZpOQ,1835
334
337
  verifiers/v1/utils/sampling.py,sha256=52TJ5-Hmpvp4YSgKNR-LmAPVwm4RDcH-o2-BnbVTmYQ,965
335
338
  verifiers/v1/utils/version.py,sha256=-obEo_-l9-D8FLef4hYxncOe-uJpxrM1g2Hig_37Sgs,1607
336
- verifiers-0.2.2.dev58.dist-info/METADATA,sha256=dIolspvil7G19vdj_DgeI_K_JX0sxuOxXcBT1DG6-_Q,4545
337
- verifiers-0.2.2.dev58.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
338
- verifiers-0.2.2.dev58.dist-info/entry_points.txt,sha256=dF82JUYEFslR1AOW8LZfuBWeZeQoyX4wCRrduklg8-I,551
339
- verifiers-0.2.2.dev58.dist-info/licenses/LICENSE,sha256=v0RrUsdV3IDoZhrRce297IXS3xMHNJ-_LdLpFAUWb9k,1072
340
- verifiers-0.2.2.dev58.dist-info/RECORD,,
339
+ verifiers-0.2.2.dev60.dist-info/METADATA,sha256=aVyGlQcJ7KN2Qf6QMshBZTNvO6KN9rMYQNqpExl1EeY,4545
340
+ verifiers-0.2.2.dev60.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
341
+ verifiers-0.2.2.dev60.dist-info/entry_points.txt,sha256=dF82JUYEFslR1AOW8LZfuBWeZeQoyX4wCRrduklg8-I,551
342
+ verifiers-0.2.2.dev60.dist-info/licenses/LICENSE,sha256=v0RrUsdV3IDoZhrRce297IXS3xMHNJ-_LdLpFAUWb9k,1072
343
+ verifiers-0.2.2.dev60.dist-info/RECORD,,