verifiers 0.3.2.dev44__tar.gz → 0.3.2.dev46__tar.gz

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.
Files changed (214) hide show
  1. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/PKG-INFO +4 -1
  2. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/pyproject.toml +3 -0
  3. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/tests/v1/test_scoring.py +13 -0
  4. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/agent.py +26 -24
  5. verifiers-0.3.2.dev46/verifiers/v1/envs/isolated_verifier/__init__.py +7 -0
  6. verifiers-0.3.2.dev46/verifiers/v1/envs/isolated_verifier/env.py +182 -0
  7. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/rollout.py +1 -0
  8. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/task.py +21 -0
  9. verifiers-0.3.2.dev46/verifiers/v1/tasksets/harbor/env.py +82 -0
  10. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/tasksets/harbor/taskset.py +22 -20
  11. verifiers-0.3.2.dev44/verifiers/v1/tasksets/harbor/env.py +0 -127
  12. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/.gitignore +0 -0
  13. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/LICENSE +0 -0
  14. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/README.md +0 -0
  15. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/tests/v1/conftest.py +0 -0
  16. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/tests/v1/fixtures/counter_tool_v1.py +0 -0
  17. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/tests/v1/fixtures/duet_v1.py +0 -0
  18. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/tests/v1/fixtures/echo_acp_resume_v1.py +0 -0
  19. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/tests/v1/fixtures/echo_agentic_v1.py +0 -0
  20. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/tests/v1/fixtures/echo_tool_v1.py +0 -0
  21. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/tests/v1/fixtures/echo_user_sim_v1.py +0 -0
  22. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/tests/v1/fixtures/echo_v1.py +0 -0
  23. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/tests/v1/fixtures/pyproject.toml +0 -0
  24. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/tests/v1/fixtures/tool_response_image_v1.py +0 -0
  25. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/tests/v1/test_color_codeword_tasks.py +0 -0
  26. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/tests/v1/test_configs.py +0 -0
  27. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/tests/v1/test_e2e.py +0 -0
  28. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/tests/v1/test_envs.py +0 -0
  29. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/tests/v1/test_graph.py +0 -0
  30. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/tests/v1/test_judges.py +0 -0
  31. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/tests/v1/test_taskset.py +0 -0
  32. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/tests/v1/test_trace.py +0 -0
  33. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/__init__.py +0 -0
  34. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/__init__.py +0 -0
  35. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/acp/__init__.py +0 -0
  36. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/acp/runner.py +0 -0
  37. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/cli/__init__.py +0 -0
  38. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/cli/dashboard/__init__.py +0 -0
  39. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/cli/dashboard/base.py +0 -0
  40. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/cli/dashboard/eval.py +0 -0
  41. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/cli/dashboard/replay.py +0 -0
  42. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/cli/dashboard/validate.py +0 -0
  43. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/cli/debug.py +0 -0
  44. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/cli/eval/__init__.py +0 -0
  45. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/cli/eval/main.py +0 -0
  46. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/cli/eval/resume.py +0 -0
  47. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/cli/eval/runner.py +0 -0
  48. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/cli/gepa.py +0 -0
  49. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/cli/init.py +0 -0
  50. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/cli/output.py +0 -0
  51. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/cli/replay.py +0 -0
  52. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/cli/resolve.py +0 -0
  53. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/cli/resume.py +0 -0
  54. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/cli/validate.py +0 -0
  55. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/clients/__init__.py +0 -0
  56. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/clients/base.py +0 -0
  57. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/clients/client.py +0 -0
  58. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/clients/eval.py +0 -0
  59. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/clients/train.py +0 -0
  60. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/configs/__init__.py +0 -0
  61. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/configs/agent.py +0 -0
  62. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/configs/cli/__init__.py +0 -0
  63. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/configs/cli/debug.py +0 -0
  64. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/configs/cli/env.py +0 -0
  65. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/configs/cli/eval.py +0 -0
  66. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/configs/cli/init.py +0 -0
  67. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/configs/cli/replay.py +0 -0
  68. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/configs/cli/validate.py +0 -0
  69. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/configs/client.py +0 -0
  70. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/configs/env.py +0 -0
  71. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/configs/harness.py +0 -0
  72. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/configs/judge.py +0 -0
  73. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/configs/retries.py +0 -0
  74. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/configs/runtime.py +0 -0
  75. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/configs/serve.py +0 -0
  76. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/configs/task.py +0 -0
  77. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/configs/taskset.py +0 -0
  78. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/dialects/__init__.py +0 -0
  79. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/dialects/anthropic.py +0 -0
  80. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/dialects/base.py +0 -0
  81. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/dialects/chat.py +0 -0
  82. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/dialects/responses.py +0 -0
  83. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/env.py +0 -0
  84. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/envs/__init__.py +0 -0
  85. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/envs/agentic_judge/__init__.py +0 -0
  86. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/envs/agentic_judge/env.py +0 -0
  87. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/envs/best_of_n/__init__.py +0 -0
  88. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/envs/best_of_n/env.py +0 -0
  89. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/envs/shared_agentic_judge/__init__.py +0 -0
  90. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/envs/single_agent/__init__.py +0 -0
  91. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/envs/single_agent/env.py +0 -0
  92. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/envs/user_sim/__init__.py +0 -0
  93. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/envs/user_sim/env.py +0 -0
  94. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/episode.py +0 -0
  95. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/errors.py +0 -0
  96. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/gepa/__init__.py +0 -0
  97. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/gepa/adapter.py +0 -0
  98. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/gepa/config.py +0 -0
  99. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/gepa/dataset.py +0 -0
  100. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/gepa/reflection.py +0 -0
  101. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/gepa/runner.py +0 -0
  102. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/graph.py +0 -0
  103. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harness.py +0 -0
  104. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harnesses/__init__.py +0 -0
  105. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harnesses/bash/__init__.py +0 -0
  106. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harnesses/bash/harness.py +0 -0
  107. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harnesses/browser_use/__init__.py +0 -0
  108. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harnesses/browser_use/harness.py +0 -0
  109. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harnesses/browser_use/program.py +0 -0
  110. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harnesses/claude_code/__init__.py +0 -0
  111. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harnesses/claude_code/harness.py +0 -0
  112. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harnesses/codex/__init__.py +0 -0
  113. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harnesses/codex/harness.py +0 -0
  114. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harnesses/hermes_agent/__init__.py +0 -0
  115. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harnesses/hermes_agent/harness.py +0 -0
  116. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harnesses/hermes_agent/program.py +0 -0
  117. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harnesses/kimi_code/__init__.py +0 -0
  118. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harnesses/kimi_code/harness.py +0 -0
  119. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harnesses/mini_swe_agent/__init__.py +0 -0
  120. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harnesses/mini_swe_agent/harness.py +0 -0
  121. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harnesses/mini_swe_agent/program.py +0 -0
  122. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harnesses/node.py +0 -0
  123. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harnesses/null/__init__.py +0 -0
  124. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harnesses/null/harness.py +0 -0
  125. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harnesses/openclaw/__init__.py +0 -0
  126. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harnesses/openclaw/harness.py +0 -0
  127. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harnesses/pi/__init__.py +0 -0
  128. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harnesses/pi/harness.py +0 -0
  129. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harnesses/pool/__init__.py +0 -0
  130. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harnesses/pool/harness.py +0 -0
  131. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harnesses/prime_agent/__init__.py +0 -0
  132. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harnesses/prime_agent/harness.py +0 -0
  133. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harnesses/rlm/__init__.py +0 -0
  134. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harnesses/rlm/harness.py +0 -0
  135. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harnesses/terminus_2/__init__.py +0 -0
  136. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harnesses/terminus_2/harness.py +0 -0
  137. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harnesses/terminus_2/program.py +0 -0
  138. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harnesses/utils/__init__.py +0 -0
  139. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harnesses/utils/compaction.py +0 -0
  140. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harnesses/utils/core.py +0 -0
  141. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harnesses/utils/launch.py +0 -0
  142. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/harnesses/utils/mcp.py +0 -0
  143. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/interception/__init__.py +0 -0
  144. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/interception/base.py +0 -0
  145. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/interception/pool.py +0 -0
  146. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/interception/server.py +0 -0
  147. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/interception/tool.py +0 -0
  148. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/interception/tunnel/__init__.py +0 -0
  149. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/interception/tunnel/base.py +0 -0
  150. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/interception/tunnel/custom.py +0 -0
  151. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/interception/tunnel/prime.py +0 -0
  152. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/judge.py +0 -0
  153. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/judges/__init__.py +0 -0
  154. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/judges/reference.py +0 -0
  155. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/judges/reference.txt +0 -0
  156. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/judges/rubric.py +0 -0
  157. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/judges/rubric.txt +0 -0
  158. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/mcp/__init__.py +0 -0
  159. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/mcp/launch.py +0 -0
  160. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/mcp/server.py +0 -0
  161. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/mcp/toolset.py +0 -0
  162. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/runtimes/__init__.py +0 -0
  163. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/runtimes/base.py +0 -0
  164. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/runtimes/docker/__init__.py +0 -0
  165. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/runtimes/docker/egress.py +0 -0
  166. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/runtimes/limiters.py +0 -0
  167. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/runtimes/modal.py +0 -0
  168. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/runtimes/prime.py +0 -0
  169. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/runtimes/subprocess.py +0 -0
  170. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/semantic.py +0 -0
  171. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/serve/__init__.py +0 -0
  172. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/serve/client.py +0 -0
  173. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/serve/encoding.py +0 -0
  174. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/serve/pool.py +0 -0
  175. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/serve/server.py +0 -0
  176. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/serve/types.py +0 -0
  177. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/session.py +0 -0
  178. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/state.py +0 -0
  179. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/taskset.py +0 -0
  180. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/tasksets/__init__.py +0 -0
  181. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/tasksets/harbor/__init__.py +0 -0
  182. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/tasksets/lean/__init__.py +0 -0
  183. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/tasksets/lean/scoring.py +0 -0
  184. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/tasksets/lean/taskset.py +0 -0
  185. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/tasksets/nemo_gym/__init__.py +0 -0
  186. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/tasksets/nemo_gym/response.py +0 -0
  187. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/tasksets/nemo_gym/server.py +0 -0
  188. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/tasksets/nemo_gym/taskset.py +0 -0
  189. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/tasksets/nemo_gym/toolset.py +0 -0
  190. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/tasksets/openenv/__init__.py +0 -0
  191. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/tasksets/openenv/taskset.py +0 -0
  192. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/tasksets/textarena/__init__.py +0 -0
  193. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/tasksets/textarena/taskset.py +0 -0
  194. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/trace.py +0 -0
  195. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/types.py +0 -0
  196. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/utils/__init__.py +0 -0
  197. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/utils/aio.py +0 -0
  198. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/utils/artifacts.py +0 -0
  199. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/utils/compile.py +0 -0
  200. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/utils/decorators.py +0 -0
  201. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/utils/format.py +0 -0
  202. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/utils/generic.py +0 -0
  203. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/utils/git.py +0 -0
  204. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/utils/image.py +0 -0
  205. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/utils/interrupt.py +0 -0
  206. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/utils/loaders.py +0 -0
  207. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/utils/logging.py +0 -0
  208. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/utils/memory.py +0 -0
  209. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/utils/paths.py +0 -0
  210. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/utils/platform.py +0 -0
  211. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/utils/prime.py +0 -0
  212. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/utils/retries.py +0 -0
  213. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/utils/score.py +0 -0
  214. {verifiers-0.3.2.dev44 → verifiers-0.3.2.dev46}/verifiers/v1/utils/version.py +0 -0
@@ -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
@@ -40,6 +40,9 @@ dependencies = [
40
40
  "pyzmq>=27.1.0",
41
41
  "msgpack>=1.1.2",
42
42
  "httpx>=0.27.0",
43
+ "httpx2>=2.12.0",
44
+ "h11>=0.16.0",
45
+ "uvicorn>=0.52.0",
43
46
  "aiohttp>=3.14.1",
44
47
  "prime-pydantic-config[toml]>=0.4.3",
45
48
  "loguru>=0.7.0",
@@ -89,6 +89,19 @@ async def test_config_plugged_fns_merge_and_override(tmp_path) -> None:
89
89
  HookTask(HookData(idx=0, prompt="abc"), config).hooks("reward")
90
90
 
91
91
 
92
+ async def test_defer_scoring_defers_only_task_scoring() -> None:
93
+ task = HookTask(HookData(idx=0, prompt="abc"))
94
+ trace = vf.Trace(
95
+ agent=vf.AgentInfo(config=vf.AgentConfig()),
96
+ task=vf.TraceTask(type="HookTask", data=task.data),
97
+ )
98
+
99
+ await task.defer_scoring().score(trace)
100
+ assert trace.rewards == {}
101
+ await task.score(trace)
102
+ assert set(trace.rewards) == {"fmt", "lcs"}
103
+
104
+
92
105
  def test_compare_stdout_results_accepts_token_equal_text() -> None:
93
106
  assert vf.compare_stdout_results("hello world\n", "hello world\n")
94
107
 
@@ -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
@@ -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):
@@ -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.
@@ -0,0 +1,82 @@
1
+ """The harbor taskset's own env: the single solver seat, plus separate-verifier
2
+ grading for tasks that declare ``[verifier].environment_mode = "separate"``.
3
+
4
+ The default env for harbor runs (the taskset package exports it). A shared-verifier
5
+ task runs exactly as under the single-agent env: one `agent` trace, graded in the
6
+ box it worked in. A separate-verifier task is graded by `finalize` instead: the
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
+ `tests/` is staged there, and the verifier's rewards land on the solver's trace.
10
+ No second agent is involved — the verifier is the task's own `tests/test.sh`.
11
+ """
12
+
13
+ import verifiers.v1 as vf
14
+ from verifiers.v1.envs.isolated_verifier import (
15
+ IsolatedVerifierEnv,
16
+ IsolatedVerifierEnvConfig,
17
+ )
18
+ from verifiers.v1.runtimes import Runtime, RuntimeConfig
19
+ from verifiers.v1.tasksets.harbor.taskset import (
20
+ HarborTask,
21
+ verifier_box_data,
22
+ )
23
+ from verifiers.v1.utils.compile import resolve_runtime_config
24
+
25
+
26
+ class HarborEnvConfig(IsolatedVerifierEnvConfig):
27
+ """The Harbor solver plus its optional independent verifier runtime."""
28
+
29
+
30
+ class HarborEnv(IsolatedVerifierEnv, vf.Env[HarborEnvConfig]):
31
+ async def run(self, task: vf.Task, agents: vf.Agents) -> None:
32
+ if not isinstance(task, HarborTask):
33
+ raise TypeError(
34
+ f"the harbor env runs harbor tasks; got {type(task).__name__}"
35
+ )
36
+ if task.data.verifier is None:
37
+ await agents.agent.run(task)
38
+ return
39
+ # Resolve the verifier's box before the solve, so an impossible pairing
40
+ # (e.g. a restricted Prime verifier without vm=true) costs nothing
41
+ # rather than a full agent run.
42
+ self.verifier_config(task)
43
+ await agents.agent.run(task.defer_scoring(), collect_artifacts=True)
44
+
45
+ def verifier_config(self, task: HarborTask) -> RuntimeConfig:
46
+ base = (
47
+ self.config.verifier.runtime
48
+ if self.config.verifier.runtime is not None
49
+ else self.config.agent.runtime
50
+ )
51
+ return resolve_runtime_config(base, HarborTask(verifier_box_data(task.data)))
52
+
53
+ async def finalize(self, task: vf.Task, episode: vf.Episode) -> None:
54
+ """Grade a separate-verifier task in its own box, onto the solver's trace.
55
+
56
+ Provision a fresh box from the task's verifier declaration, restore the
57
+ solver's collected artifacts, stage `tests/`, run the verifier, and record
58
+ its rewards (and any extra reward.json keys as metrics) on the solver's
59
+ trace. Setup, restoration, staging, and scoring failures retry per
60
+ `verifier.retries`; the last one fails the episode."""
61
+ if not isinstance(task, HarborTask) or task.data.verifier is None:
62
+ return
63
+ solution = episode.traces[0]
64
+ if not solution.ok:
65
+ return
66
+ grader = HarborTask(verifier_box_data(task.data))
67
+ scores, solution = await self.grade(
68
+ self.verifier_config(task),
69
+ grader,
70
+ solution,
71
+ scoring_timeout_covers_attempt=True,
72
+ )
73
+ items = scores.items() if isinstance(scores, dict) else [("solved", scores)]
74
+ for name, value in items:
75
+ solution.record_reward(name, value)
76
+ episode.traces[0] = solution
77
+
78
+ async def verify(
79
+ self, task: vf.Task, solution: vf.Trace, runtime: Runtime
80
+ ) -> float | dict[str, float]:
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.