verifiers 0.4.1.dev54__tar.gz → 0.4.1.dev58__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 (277) hide show
  1. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/PKG-INFO +1 -1
  2. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/pyproject.toml +2 -0
  3. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/test_compose_scoring.py +43 -9
  4. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/unit/test_runtimes.py +1 -1
  5. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/__init__.py +2 -0
  6. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/envs/agentic_judge/env.py +9 -9
  7. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/errors.py +5 -0
  8. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/rlm/harness.py +1 -1
  9. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/judges/rubric.py +69 -24
  10. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/rollout.py +17 -0
  11. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/runtimes/compose.py +21 -2
  12. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/runtimes/strategies.py +24 -2
  13. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/tasksets/harbor/taskset.py +5 -1
  14. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/.gitignore +0 -0
  15. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/LICENSE +0 -0
  16. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/README.md +0 -0
  17. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/conftest.py +0 -0
  18. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/fixtures/agent_user.py +0 -0
  19. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/fixtures/agent_user_harness.py +0 -0
  20. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/fixtures/counter_tool.py +0 -0
  21. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/fixtures/duet.py +0 -0
  22. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/fixtures/echo.py +0 -0
  23. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/fixtures/echo_acp_resume.py +0 -0
  24. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/fixtures/echo_agentic.py +0 -0
  25. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/fixtures/echo_tool.py +0 -0
  26. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/fixtures/echo_tool_compose/docker-compose.yaml +0 -0
  27. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/fixtures/echo_user_sim.py +0 -0
  28. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/fixtures/fetch_tool.py +0 -0
  29. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/fixtures/network_policy.py +0 -0
  30. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/fixtures/parallel.py +0 -0
  31. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/fixtures/pyproject.toml +0 -0
  32. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/fixtures/skill_codeword.py +0 -0
  33. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/fixtures/skills/project-codeword/SKILL.md +0 -0
  34. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/fixtures/tool_response_image.py +0 -0
  35. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/grid.py +0 -0
  36. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/test_agent_user.py +0 -0
  37. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/test_agentic_judge.py +0 -0
  38. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/test_examples.py +0 -0
  39. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/test_limits.py +0 -0
  40. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/test_multi_agent.py +0 -0
  41. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/test_multi_turn.py +0 -0
  42. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/test_multi_turn_tool.py +0 -0
  43. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/test_multimodal_tool.py +0 -0
  44. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/test_network_policy.py +0 -0
  45. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/test_parallel.py +0 -0
  46. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/test_resume_tool.py +0 -0
  47. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/test_rubric_judge.py +0 -0
  48. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/test_sampling.py +0 -0
  49. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/test_scoring_network.py +0 -0
  50. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/test_shared_tool.py +0 -0
  51. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/test_single_turn.py +0 -0
  52. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/test_skills.py +0 -0
  53. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/test_stateful_tool.py +0 -0
  54. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/test_tool_vms.py +0 -0
  55. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/test_user_echo.py +0 -0
  56. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/integration/test_user_sim.py +0 -0
  57. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/unit/cli/test_replay.py +0 -0
  58. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/unit/conftest.py +0 -0
  59. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/unit/harnesses/test_core.py +0 -0
  60. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/unit/harnesses/test_rlm.py +0 -0
  61. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/unit/interception/test_tunnel.py +0 -0
  62. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/unit/judges/conftest.py +0 -0
  63. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/unit/judges/test_judge.py +0 -0
  64. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/unit/judges/test_reference.py +0 -0
  65. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/unit/judges/test_rubric.py +0 -0
  66. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/unit/runtimes/test_private_network.py +0 -0
  67. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/unit/serve/test_client.py +0 -0
  68. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/unit/serve/test_delta.py +0 -0
  69. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/unit/serve/test_pool.py +0 -0
  70. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/unit/test_agent.py +0 -0
  71. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/unit/test_dialects.py +0 -0
  72. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/unit/test_graph.py +0 -0
  73. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/unit/test_harbor_grading_network.py +0 -0
  74. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/unit/test_scoring_network.py +0 -0
  75. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/unit/test_semantic.py +0 -0
  76. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/unit/test_task.py +0 -0
  77. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/unit/test_taskset.py +0 -0
  78. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/unit/test_trace.py +0 -0
  79. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/unit/utils/test_compile.py +0 -0
  80. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/unit/utils/test_loaders.py +0 -0
  81. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/unit/utils/test_platform.py +0 -0
  82. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/tests/unit/utils/test_score.py +0 -0
  83. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/__init__.py +0 -0
  84. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/acp/__init__.py +0 -0
  85. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/acp/runner.py +0 -0
  86. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/agent.py +0 -0
  87. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/cli/__init__.py +0 -0
  88. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/cli/dashboard/__init__.py +0 -0
  89. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/cli/dashboard/base.py +0 -0
  90. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/cli/dashboard/eval.py +0 -0
  91. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/cli/dashboard/replay.py +0 -0
  92. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/cli/dashboard/validate.py +0 -0
  93. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/cli/debug.py +0 -0
  94. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/cli/eval/__init__.py +0 -0
  95. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/cli/eval/hint.py +0 -0
  96. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/cli/eval/main.py +0 -0
  97. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/cli/eval/resume.py +0 -0
  98. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/cli/eval/runner.py +0 -0
  99. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/cli/gepa.py +0 -0
  100. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/cli/output.py +0 -0
  101. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/cli/replay.py +0 -0
  102. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/cli/resolve.py +0 -0
  103. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/cli/resume.py +0 -0
  104. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/cli/validate.py +0 -0
  105. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/clients/__init__.py +0 -0
  106. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/clients/base.py +0 -0
  107. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/clients/client.py +0 -0
  108. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/clients/eval.py +0 -0
  109. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/clients/train.py +0 -0
  110. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/configs/__init__.py +0 -0
  111. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/configs/agent.py +0 -0
  112. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/configs/cli/__init__.py +0 -0
  113. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/configs/cli/debug.py +0 -0
  114. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/configs/cli/env.py +0 -0
  115. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/configs/cli/eval.py +0 -0
  116. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/configs/cli/replay.py +0 -0
  117. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/configs/cli/validate.py +0 -0
  118. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/configs/client.py +0 -0
  119. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/configs/env.py +0 -0
  120. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/configs/harness.py +0 -0
  121. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/configs/judge.py +0 -0
  122. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/configs/retries.py +0 -0
  123. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/configs/runtime.py +0 -0
  124. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/configs/scoring.py +0 -0
  125. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/configs/select.py +0 -0
  126. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/configs/serve.py +0 -0
  127. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/configs/task.py +0 -0
  128. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/configs/taskset.py +0 -0
  129. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/dialects/__init__.py +0 -0
  130. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/dialects/anthropic.py +0 -0
  131. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/dialects/base.py +0 -0
  132. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/dialects/chat.py +0 -0
  133. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/dialects/responses.py +0 -0
  134. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/env.py +0 -0
  135. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/envs/__init__.py +0 -0
  136. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/envs/agentic_judge/__init__.py +0 -0
  137. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/envs/best_of_n/__init__.py +0 -0
  138. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/envs/best_of_n/env.py +0 -0
  139. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/envs/single_agent/__init__.py +0 -0
  140. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/envs/single_agent/env.py +0 -0
  141. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/envs/user_sim/__init__.py +0 -0
  142. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/envs/user_sim/env.py +0 -0
  143. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/episode.py +0 -0
  144. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/gepa/__init__.py +0 -0
  145. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/gepa/adapter.py +0 -0
  146. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/gepa/config.py +0 -0
  147. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/gepa/dataset.py +0 -0
  148. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/gepa/reflection.py +0 -0
  149. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/gepa/runner.py +0 -0
  150. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/grading.py +0 -0
  151. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/graph.py +0 -0
  152. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harness.py +0 -0
  153. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/__init__.py +0 -0
  154. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/bash/__init__.py +0 -0
  155. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/bash/harness.py +0 -0
  156. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/browser_use/__init__.py +0 -0
  157. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/browser_use/harness.py +0 -0
  158. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/browser_use/program.py +0 -0
  159. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/claude_code/__init__.py +0 -0
  160. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/claude_code/harness.py +0 -0
  161. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/codex/__init__.py +0 -0
  162. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/codex/gate.mjs +0 -0
  163. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/codex/harness.py +0 -0
  164. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/deepseek/__init__.py +0 -0
  165. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/deepseek/gate.mjs +0 -0
  166. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/deepseek/harness.py +0 -0
  167. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/deepseek/usage.mjs +0 -0
  168. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/hermes_agent/__init__.py +0 -0
  169. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/hermes_agent/harness.py +0 -0
  170. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/hermes_agent/program.py +0 -0
  171. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/kimi_code/__init__.py +0 -0
  172. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/kimi_code/harness.py +0 -0
  173. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/mini_swe_agent/__init__.py +0 -0
  174. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/mini_swe_agent/harness.py +0 -0
  175. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/mini_swe_agent/program.py +0 -0
  176. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/node.py +0 -0
  177. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/null/__init__.py +0 -0
  178. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/null/harness.py +0 -0
  179. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/openclaw/__init__.py +0 -0
  180. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/openclaw/harness.py +0 -0
  181. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/opencode/__init__.py +0 -0
  182. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/opencode/harness.py +0 -0
  183. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/opencode/mcp-ready.js +0 -0
  184. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/pi/__init__.py +0 -0
  185. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/pi/gate.mjs +0 -0
  186. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/pi/harness.py +0 -0
  187. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/prime_agent/__init__.py +0 -0
  188. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/prime_agent/harness.py +0 -0
  189. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/rlm/__init__.py +0 -0
  190. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/terminus_2/__init__.py +0 -0
  191. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/terminus_2/harness.py +0 -0
  192. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/terminus_2/program.py +0 -0
  193. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/utils/__init__.py +0 -0
  194. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/utils/compaction.py +0 -0
  195. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/utils/core.py +0 -0
  196. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/utils/install.py +0 -0
  197. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/utils/launch.py +0 -0
  198. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/utils/mcp.py +0 -0
  199. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/harnesses/utils/replay.py +0 -0
  200. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/interception/__init__.py +0 -0
  201. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/interception/base.py +0 -0
  202. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/interception/pool.py +0 -0
  203. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/interception/server.py +0 -0
  204. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/interception/tunnel/__init__.py +0 -0
  205. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/interception/tunnel/base.py +0 -0
  206. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/interception/tunnel/custom.py +0 -0
  207. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/interception/tunnel/prime.py +0 -0
  208. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/judge.py +0 -0
  209. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/judges/__init__.py +0 -0
  210. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/judges/decision.py +0 -0
  211. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/judges/reference.py +0 -0
  212. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/judges/reference.txt +0 -0
  213. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/judges/rubric.txt +0 -0
  214. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/mcp/__init__.py +0 -0
  215. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/mcp/launch.py +0 -0
  216. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/mcp/server.py +0 -0
  217. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/mcp/toolset.py +0 -0
  218. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/runtimes/__init__.py +0 -0
  219. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/runtimes/backends/custom.py +0 -0
  220. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/runtimes/backends/docker.py +0 -0
  221. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/runtimes/backends/prime.py +0 -0
  222. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/runtimes/backends/subprocess.py +0 -0
  223. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/runtimes/base.py +0 -0
  224. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/runtimes/deployment.py +0 -0
  225. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/runtimes/egress.py +0 -0
  226. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/runtimes/limiters.py +0 -0
  227. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/runtimes/private_network.py +0 -0
  228. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/runtimes/runtime.py +0 -0
  229. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/semantic.py +0 -0
  230. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/serve/__init__.py +0 -0
  231. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/serve/client.py +0 -0
  232. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/serve/delta.py +0 -0
  233. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/serve/encoding.py +0 -0
  234. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/serve/pool.py +0 -0
  235. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/serve/server.py +0 -0
  236. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/serve/types.py +0 -0
  237. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/session.py +0 -0
  238. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/state.py +0 -0
  239. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/task.py +0 -0
  240. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/taskset.py +0 -0
  241. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/tasksets/__init__.py +0 -0
  242. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/tasksets/harbor/__init__.py +0 -0
  243. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/tasksets/harbor/toolset.py +0 -0
  244. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/tasksets/nemo_gym/__init__.py +0 -0
  245. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/tasksets/nemo_gym/response.py +0 -0
  246. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/tasksets/nemo_gym/server.py +0 -0
  247. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/tasksets/nemo_gym/taskset.py +0 -0
  248. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/tasksets/nemo_gym/toolset.py +0 -0
  249. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/tasksets/openenv/__init__.py +0 -0
  250. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/tasksets/openenv/taskset.py +0 -0
  251. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/tasksets/textarena/__init__.py +0 -0
  252. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/tasksets/textarena/taskset.py +0 -0
  253. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/trace.py +0 -0
  254. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/types.py +0 -0
  255. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/utils/__init__.py +0 -0
  256. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/utils/aio.py +0 -0
  257. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/utils/artifacts.py +0 -0
  258. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/utils/compile.py +0 -0
  259. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/utils/decorators.py +0 -0
  260. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/utils/env.py +0 -0
  261. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/utils/eval.py +0 -0
  262. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/utils/format.py +0 -0
  263. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/utils/generic.py +0 -0
  264. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/utils/git.py +0 -0
  265. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/utils/image.py +0 -0
  266. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/utils/interrupt.py +0 -0
  267. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/utils/loaders.py +0 -0
  268. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/utils/logging.py +0 -0
  269. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/utils/memory.py +0 -0
  270. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/utils/paths.py +0 -0
  271. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/utils/platform.py +0 -0
  272. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/utils/prime.py +0 -0
  273. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/utils/retries.py +0 -0
  274. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/utils/scope.py +0 -0
  275. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/utils/score.py +0 -0
  276. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/utils/trace_store.py +0 -0
  277. {verifiers-0.4.1.dev54 → verifiers-0.4.1.dev58}/verifiers/v1/utils/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: verifiers
3
- Version: 0.4.1.dev54
3
+ Version: 0.4.1.dev58
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
@@ -131,6 +131,8 @@ bash-interception = { path = "environments/bash_interception", editable = true }
131
131
  web-search-interception = { path = "environments/web_search_interception", editable = true }
132
132
 
133
133
  [tool.uv.exclude-newer-package]
134
+ # CA bundle: security fixes (e.g. 2026.10.19 for the eMudhra compromise) must not wait out the cooldown
135
+ certifi = false
134
136
  # Bounded cutoffs for the explicitly requested tool upgrades.
135
137
  # Full UTC timestamps: bare dates resolve to local-tz midnight and churn the
136
138
  # lockfile across timezones.
@@ -42,6 +42,9 @@ services:
42
42
 
43
43
  class ComposeHarness(vf.Harness):
44
44
  async def launch(self, ctx, trace, runtime, endpoint, secret, mcp_urls, data):
45
+ if "build-main" in data.tags:
46
+ result = await runtime.run(["test", "-f", "/built-from-dockerfile"], {})
47
+ assert result.exit_code == 0, "workspace did not use its Dockerfile"
45
48
  return await runtime.run(
46
49
  [
47
50
  "python",
@@ -65,6 +68,19 @@ class ComposeTask(HarborTask):
65
68
  assert all(
66
69
  backend._host_backend.stopped for backend in SOLVERS[trace.id]
67
70
  ), "solver services survived into grading"
71
+ if "build-main" in self.data.tags:
72
+ result = await runtime.run(
73
+ [
74
+ "sh",
75
+ "-c",
76
+ "test -f /built-from-dockerfile && test ! -e /app/answer",
77
+ ],
78
+ {},
79
+ )
80
+ assert result.exit_code == 0, (
81
+ "verifier was not rebuilt from clean inputs"
82
+ )
83
+ trace.info["verifier_rebuilt_from_dockerfile"] = True
68
84
  trace.info["solver_released_before_grading"] = True
69
85
 
70
86
  async def finalize_services(self, trace, targets):
@@ -80,24 +96,30 @@ class ComposeTask(HarborTask):
80
96
 
81
97
 
82
98
  @pytest.mark.parametrize(
83
- ("mode", "collect_fails", "scoring_timeout"),
99
+ ("mode", "collect_fails", "scoring_timeout", "build_main"),
84
100
  [
85
- ("shared", True, False),
86
- ("isolated", False, False),
87
- ("isolated", True, False),
88
- ("isolated", False, True),
101
+ ("shared", True, False, False),
102
+ ("isolated", False, False, False),
103
+ ("isolated", True, False, False),
104
+ ("isolated", False, True, False),
105
+ ("shared", True, False, True),
106
+ ("isolated", False, False, True),
89
107
  ],
90
108
  ids=[
91
109
  "shared-skips-sidecars",
92
110
  "isolated-restores-sidecars",
93
111
  "isolated-collect-failure",
94
112
  "isolated-scoring-timeout",
113
+ "shared-builds-main",
114
+ "isolated-builds-main",
95
115
  ],
96
116
  )
97
117
  @pytest.mark.prime
98
118
  @pytest.mark.skipif(not os.environ.get("PRIME_API_KEY"), reason="needs PRIME_API_KEY")
99
- async def test_compose_scoring(tmp_path, mode, collect_fails, scoring_timeout):
100
- task = compose_task(tmp_path, collect_fails, scoring_timeout, mode)
119
+ async def test_compose_scoring(
120
+ tmp_path, mode, collect_fails, scoring_timeout, build_main
121
+ ):
122
+ task = compose_task(tmp_path, collect_fails, scoring_timeout, mode, build_main)
101
123
  config = vf.AgentConfig(
102
124
  model="unused",
103
125
  runtime=vf.PrimeConfig(labels=["vf-ci"]),
@@ -128,12 +150,23 @@ async def test_compose_scoring(tmp_path, mode, collect_fails, scoring_timeout):
128
150
  assert bool(trace.info.get("solver_released_before_grading")) == (
129
151
  mode == "isolated"
130
152
  )
153
+ assert bool(trace.info.get("verifier_rebuilt_from_dockerfile")) == (
154
+ mode == "isolated" and build_main
155
+ )
131
156
 
132
157
 
133
- def compose_task(tmp_path, collect_fails: bool, scoring_timeout: bool, mode: str):
158
+ def compose_task(
159
+ tmp_path, collect_fails: bool, scoring_timeout: bool, mode: str, build_main: bool
160
+ ):
134
161
  environment = tmp_path / "environment"
135
162
  environment.mkdir()
136
- (environment / "docker-compose.yaml").write_text(COMPOSE)
163
+ compose = COMPOSE
164
+ if build_main:
165
+ compose = compose.replace(f" image: {IMAGE}\n", "", 1)
166
+ (environment / "Dockerfile").write_text(
167
+ f"FROM {IMAGE}\nRUN touch /built-from-dockerfile\n"
168
+ )
169
+ (environment / "docker-compose.yaml").write_text(compose)
137
170
  tests = tmp_path / "tests"
138
171
  tests.mkdir()
139
172
  (tests / "test.sh").write_text(
@@ -146,6 +179,7 @@ def compose_task(tmp_path, collect_fails: bool, scoring_timeout: bool, mode: str
146
179
  return ComposeTask(
147
180
  HarborData(
148
181
  prompt="Copy the sidecar answer",
182
+ tags=["build-main"] if build_main else [],
149
183
  task_dir=str(tmp_path),
150
184
  env={"COLLECTION_SCOPE": "workspace"},
151
185
  # Exercise agent overrides in both directions, not just task defaults.
@@ -300,7 +300,7 @@ async def test_compose(runtime, tmp_path) -> None:
300
300
  spec = ComposeSpec(directory=str(tmp_path), file="docker-compose.yaml")
301
301
  async with (
302
302
  asyncio.timeout(TIMEOUT),
303
- compose_services(runtime, spec, {}, trust_compose=True) as (services, _, _),
303
+ compose_services(runtime, spec, {}, trust_compose=True) as (services, _, _, _),
304
304
  ):
305
305
  assert set(services) == {"main", "sidecar"}
306
306
  resolved = await services["main"].bind().run(["getent", "hosts", "sidecar"], {})
@@ -60,6 +60,7 @@ from verifiers.v1.errors import (
60
60
  TaskError,
61
61
  ToolsetError,
62
62
  TunnelError,
63
+ UnscoredError,
63
64
  )
64
65
  from verifiers.v1.grading import grade_submission
65
66
  from verifiers.v1.graph import MessageNode
@@ -283,6 +284,7 @@ __all__ = [ # noqa: RUF022 - grouped by public API area
283
284
  "ToolsetError",
284
285
  "SandboxError",
285
286
  "TaskError",
287
+ "UnscoredError",
286
288
  "InterceptionError",
287
289
  "TunnelError",
288
290
  # clients
@@ -18,8 +18,10 @@ from verifiers.v1.configs.scoring import resolve_scoring
18
18
  from verifiers.v1.judges.rubric import (
19
19
  Criterion,
20
20
  RubricVerdicts,
21
+ criterion_metrics,
21
22
  load_criteria,
22
23
  score_verdicts,
24
+ weighted_score,
23
25
  )
24
26
  from verifiers.v1.utils.compile import validate_pairing
25
27
 
@@ -45,10 +47,7 @@ SOLVED = Criterion(
45
47
 
46
48
 
47
49
  def _verdict_section(criteria: list[Criterion]) -> str:
48
- listing = "\n".join(
49
- f"- {c.name}: {c.text} (answer one of, worst to best: {', '.join(c.choices)})"
50
- for c in criteria
51
- )
50
+ listing = "\n".join(c.render() for c in criteria)
52
51
  return f"""\
53
52
  ## Your verdict
54
53
 
@@ -300,15 +299,16 @@ class AgenticJudgeEnv(vf.Env[AgenticJudgeEnvConfig]):
300
299
  verdicts = RubricVerdicts.model_validate(verdict.info.get("verdict")).verdicts
301
300
  criteria = self.config.task.criteria()
302
301
  scores = score_verdicts(verdicts, criteria, "the rubric's")
303
- for criterion in criteria:
304
- solution.record_metric(f"judge/{criterion.name}", scores[criterion.name])
302
+ solution.record_metrics(criterion_metrics("judge", verdicts, criteria, scores))
305
303
  if self.config.score.task_weight != 1.0:
306
304
  for reward in solution.rewards.values():
307
305
  if reward is not None:
308
306
  reward.weight *= self.config.score.task_weight
309
- total = sum(criterion.weight for criterion in criteria)
310
- reward = sum(c.weight * scores[c.name] for c in criteria) / total
311
- solution.record_reward("judge", reward, weight=self.config.score.judge_weight)
307
+ solution.record_reward(
308
+ "judge",
309
+ weighted_score(scores, criteria),
310
+ weight=self.config.score.judge_weight,
311
+ )
312
312
 
313
313
  async def run(self, task: vf.Task, agents: vf.Agents) -> None:
314
314
  policy = resolve_scoring(task.scoring, self.config.solver.scoring)
@@ -69,6 +69,11 @@ class TaskError(RolloutError):
69
69
  """Task-authored code raised — `setup`, `finalize`, or a `@reward`/`@metric`."""
70
70
 
71
71
 
72
+ class UnscoredError(RolloutError):
73
+ """Scoring completed but a reward came out `nan`, e.g. a judge's answer that scores
74
+ nothing: the rollout carries no reward signal, so consumers drop it like a failure."""
75
+
76
+
72
77
  class InterceptionError(RolloutError):
73
78
  """Communication with the host interception server failed."""
74
79
 
@@ -69,7 +69,7 @@ class CompactionConfig(BaseConfig):
69
69
  class RLMHarnessConfig(HarnessConfig):
70
70
  dialect: Literal["chat_completions"] = "chat_completions"
71
71
  version: str = Field(
72
- default="77c3489d1a7f918b75c31e195480ed3556af60a5", min_length=1
72
+ default="4cd7f399f5ba79c350d693af63144e2609062235", min_length=1
73
73
  )
74
74
  """Git ref (branch, tag, or commit) of nano-rlm to install. Must know every
75
75
  field this harness puts on the wire, i.e. be at least the default ref."""
@@ -2,6 +2,7 @@
2
2
 
3
3
  import asyncio
4
4
  import json
5
+ import math
5
6
  import re
6
7
  import tomllib
7
8
  from functools import cached_property
@@ -35,10 +36,6 @@ JSON_SUFFIX = (
35
36
  )
36
37
 
37
38
 
38
- def normalize_choice(choice: str, choices: list[str]) -> float:
39
- return choices.index(choice) / (len(choices) - 1)
40
-
41
-
42
39
  def first_verdicts_object(text: str) -> dict | None:
43
40
  """The first balanced JSON object in `text` that carries a `verdicts` key. Scanning for the
44
41
  key (not just the first `{`) skips prose and, crucially, an echoed format example — the one
@@ -63,17 +60,44 @@ class Criterion(BaseModel):
63
60
  text: str
64
61
  weight: CriterionWeight = 1.0
65
62
  """The criterion's share of the reward (overridable per name via `weights` in config)."""
66
- choices: list[str] = Field(default_factory=lambda: ["no", "yes"], min_length=2)
67
- """Allowed answers, ordered **worst → best**: the first scores 0.0, the last 1.0, the rest
68
- evenly spaced by rank. Default `["no", "yes"]` is a binary check. Needs >= 2, no duplicates."""
63
+ choices: list[str] | dict[str, float] = Field(
64
+ default_factory=lambda: ["no", "yes"], min_length=2
65
+ )
66
+ """Allowed answers. A list is ordered **worst → best**: the first scores 0.0, the last 1.0,
67
+ the rest evenly spaced by rank. A mapping gives each answer its own finite score, in any
68
+ order; `nan` scores nothing, which leaves the rollout unscored when the criterion
69
+ carries weight. Default `["no", "yes"]` is a binary check. Needs >= 2, no duplicates."""
69
70
 
70
71
  @field_validator("choices")
71
72
  @classmethod
72
- def _check_choices(cls, v: list[str]) -> list[str]:
73
- if len(set(v)) != len(v):
73
+ def _check_choices(
74
+ cls, v: list[str] | dict[str, float]
75
+ ) -> list[str] | dict[str, float]:
76
+ if isinstance(v, list) and len(set(v)) != len(v):
74
77
  raise ValueError(f"`choices` has duplicate options: {v}")
78
+ if isinstance(v, dict) and any(math.isinf(score) for score in v.values()):
79
+ raise ValueError(f"`choices` scores must be finite or nan: {v}")
75
80
  return v
76
81
 
82
+ def score(self, choice: str) -> float:
83
+ """The score of `choice`, one of `choices`; `nan` when it scores nothing."""
84
+ if isinstance(self.choices, dict):
85
+ return self.choices[choice]
86
+ return self.choices.index(choice) / (len(self.choices) - 1)
87
+
88
+ def render(self) -> str:
89
+ """The criterion as a prompt line, with its allowed answers."""
90
+ order = "" if isinstance(self.choices, dict) else ", worst to best"
91
+ return f"- {self.name}: {self.text} (answer one of{order}: {', '.join(self.choices)})"
92
+
93
+
94
+ def weighted_score(scores: dict[str, float], criteria: list[Criterion]) -> float:
95
+ """The criteria's weighted mean score; `nan` when a weighted criterion scored nothing.
96
+ Zero-weight criteria never contribute, not even their `nan`."""
97
+ weighted = [c for c in criteria if c.weight]
98
+ total = sum(c.weight for c in weighted)
99
+ return sum(c.weight * scores[c.name] for c in weighted) / total
100
+
77
101
 
78
102
  CRITERIA_ADAPTER = TypeAdapter(list[Criterion])
79
103
 
@@ -174,15 +198,38 @@ def score_verdicts(
174
198
  )
175
199
  scores: dict[str, float] = {}
176
200
  for name, answer in answers.items():
177
- choices = by_criterion[name].choices
178
- if answer not in choices:
201
+ criterion = by_criterion[name]
202
+ if answer not in criterion.choices:
179
203
  raise ValueError(
180
- f"judge answered {answer!r} for '{name}', expected one of {choices}"
204
+ f"judge answered {answer!r} for '{name}', expected one of "
205
+ f"{list(criterion.choices)}"
181
206
  )
182
- scores[name] = normalize_choice(answer, choices)
207
+ scores[name] = criterion.score(answer)
183
208
  return scores
184
209
 
185
210
 
211
+ def criterion_metrics(
212
+ prefix: str,
213
+ verdicts: list[CriterionVerdict],
214
+ criteria: list[Criterion],
215
+ scores: dict[str, float],
216
+ ) -> dict[str, float]:
217
+ """Each criterion's score as `<prefix>/<name>`, unless it scored nothing, and for a
218
+ criterion with scored choices (a mapping) an indicator per choice, `<prefix>/<name>/<choice>`,
219
+ since several choices can share a score."""
220
+ answers = {verdict.name: verdict.verdict for verdict in verdicts}
221
+ metrics: dict[str, float] = {}
222
+ for criterion in criteria:
223
+ if not math.isnan(scores[criterion.name]):
224
+ metrics[f"{prefix}/{criterion.name}"] = scores[criterion.name]
225
+ if isinstance(criterion.choices, dict):
226
+ for choice in criterion.choices:
227
+ metrics[f"{prefix}/{criterion.name}/{choice}"] = float(
228
+ answers[criterion.name] == choice
229
+ )
230
+ return metrics
231
+
232
+
186
233
  class RubricJudge(Judge[RubricVerdicts, RubricJudgeConfig]):
187
234
  prompt = RUBRIC_PROMPT
188
235
  schema = RubricVerdicts
@@ -194,9 +241,6 @@ class RubricJudge(Judge[RubricVerdicts, RubricJudgeConfig]):
194
241
  async def grade_batch(
195
242
  self, task: TaskData, trace: Trace, batch: list[Criterion]
196
243
  ) -> dict[str, float]:
197
- def render(c: Criterion) -> str:
198
- return f"- {c.name}: {c.text} (answer one of, worst to best: {', '.join(c.choices)})"
199
-
200
244
  reference = ""
201
245
  if self.config.answer_field:
202
246
  answer = getattr(task, self.config.answer_field, None)
@@ -222,7 +266,7 @@ class RubricJudge(Judge[RubricVerdicts, RubricJudgeConfig]):
222
266
  fields = {
223
267
  "question": judge_question(task, self.config.question_field),
224
268
  "response": judge_response(trace, self.config.view),
225
- "criteria": "\n".join(render(c) for c in batch),
269
+ "criteria": "\n".join(c.render() for c in batch),
226
270
  "reference": reference,
227
271
  }
228
272
  if self.config.structured_output:
@@ -238,7 +282,11 @@ class RubricJudge(Judge[RubricVerdicts, RubricJudgeConfig]):
238
282
  )
239
283
  verdicts = RubricVerdicts.model_validate(obj).verdicts
240
284
  # A malformed verdict is a judge failure and must error the rollout, not score the model.
241
- return score_verdicts(verdicts, batch, "the batch's")
285
+ scores = score_verdicts(verdicts, batch, "the batch's")
286
+ trace.record_metrics(
287
+ criterion_metrics(self.reward_name, verdicts, batch, scores)
288
+ )
289
+ return scores
242
290
 
243
291
  async def score(self, task: TaskData, trace: Trace) -> float:
244
292
  criteria = self.criteria
@@ -264,12 +312,7 @@ class RubricJudge(Judge[RubricVerdicts, RubricJudgeConfig]):
264
312
  by_name: dict[str, float] = {}
265
313
  for batch_verdicts in results:
266
314
  by_name.update(batch_verdicts)
267
- for criterion in criteria:
268
- trace.record_metric(
269
- f"{self.reward_name}/{criterion.name}", by_name[criterion.name]
270
- )
271
- total = sum(criterion.weight for criterion in criteria)
272
- return sum(c.weight * by_name[c.name] for c in criteria) / total
315
+ return weighted_score(by_name, criteria)
273
316
 
274
317
 
275
318
  __all__ = [
@@ -278,4 +321,6 @@ __all__ = [
278
321
  "RubricJudge",
279
322
  "RubricJudgeConfig",
280
323
  "RubricVerdicts",
324
+ "criterion_metrics",
325
+ "weighted_score",
281
326
  ]
@@ -3,6 +3,7 @@
3
3
  import asyncio
4
4
  import contextlib
5
5
  import logging
6
+ import math
6
7
  import time
7
8
  from collections.abc import Awaitable, Callable
8
9
  from contextlib import AsyncExitStack
@@ -19,6 +20,7 @@ from verifiers.v1.errors import (
19
20
  SandboxError,
20
21
  TaskError,
21
22
  ToolsetError,
23
+ UnscoredError,
22
24
  boundary,
23
25
  )
24
26
  from verifiers.v1.grading import (
@@ -908,6 +910,21 @@ class Rollout:
908
910
  # the metrics read what it handed over and left behind.
909
911
  await self._close_harness()
910
912
  await self._score_harness(runtime, self._metrics_deadline())
913
+ # A nan reward scores nothing: keep it unscored (None) and fail the
914
+ # rollout after scoring, so it is never retried as a scoring failure.
915
+ unscored = [
916
+ name
917
+ for name, reward in trace.rewards.items()
918
+ if reward is not None and math.isnan(reward.score)
919
+ ]
920
+ if unscored:
921
+ for name in unscored:
922
+ trace.rewards[name] = None
923
+ self.fail(
924
+ UnscoredError(
925
+ f"reward {', '.join(unscored)} scored nothing (nan)"
926
+ )
927
+ )
911
928
  trace.timing.scoring.end = time.time()
912
929
  trace.notify()
913
930
  except Exception as e: # noqa: BLE001 - finalize boundary records every rollout failure
@@ -85,7 +85,12 @@ async def compose_services(
85
85
  trust_compose: bool = False,
86
86
  setup_timeout: float | None = None,
87
87
  ) -> AsyncIterator[
88
- tuple[dict[str, DockerBackend], Callable[[], Awaitable[str]], RuntimeBackend | None]
88
+ tuple[
89
+ dict[str, DockerBackend],
90
+ Callable[[], Awaitable[str]],
91
+ RuntimeBackend | None,
92
+ str | None,
93
+ ]
89
94
  ]:
90
95
  """Own one Compose attempt and lend its services until the context exits."""
91
96
  local = isinstance(config, DockerConfig)
@@ -186,9 +191,20 @@ async def compose_services(
186
191
  }
187
192
  }
188
193
  }
189
- if {"image", "build"} & authored["services"].get(spec.workspace, {}).keys():
194
+ main = authored["services"].get(spec.workspace, {})
195
+ if {"image", "build"} & main.keys():
190
196
  # A template default must not replace an authored image or skip its build.
191
197
  del base["services"][spec.workspace]["image"]
198
+ elif (
199
+ not spec.image_override
200
+ and (
201
+ directory / "task" / Path(spec.file).parent / "Dockerfile"
202
+ ).is_file()
203
+ ):
204
+ main = base["services"][spec.workspace]
205
+ del main["image"]
206
+ main["build"] = {"context": "${CONTEXT_DIR}"}
207
+ main["pull_policy"] = "build"
192
208
  (directory / "base.json").write_text(json.dumps(base))
193
209
  (directory / "env.json").write_text(
194
210
  json.dumps({"services": {spec.workspace: {"environment": environment}}})
@@ -334,6 +350,9 @@ async def compose_services(
334
350
  backends,
335
351
  partial(compose, "stop", spec.workspace, timeout=60),
336
352
  host_backend,
353
+ backends[spec.workspace].config.image
354
+ if services[spec.workspace].get("build")
355
+ else None,
337
356
  )
338
357
  finally:
339
358
  await run_shielded(stack.aclose())
@@ -2,7 +2,7 @@
2
2
 
3
3
  import asyncio
4
4
  import logging
5
- from contextlib import AsyncExitStack
5
+ from contextlib import AsyncExitStack, asynccontextmanager
6
6
  from functools import partial
7
7
 
8
8
  from verifiers.v1.runtimes.compose import compose_services
@@ -37,13 +37,19 @@ class _ContainerDeployment(_DeploymentStrategy):
37
37
  if d.borrowed_runtime is not None:
38
38
  raise ValueError("a composed deployment cannot borrow a single target")
39
39
  self._services = await d.enter(AsyncExitStack())
40
- services, self._stop_main, _ = await self._services.enter_async_context(
40
+ (
41
+ services,
42
+ self._stop_main,
43
+ host,
44
+ built_image,
45
+ ) = await self._services.enter_async_context(
41
46
  compose_services(
42
47
  d.backend.config, d.spec, d.env, trust_compose=d.config.trust_compose
43
48
  )
44
49
  )
45
50
  d.target_backends = dict(services)
46
51
  d.execution_backend = d.harness_backend = services[d.spec.workspace]
52
+ self._remote_built_image = built_image if host is not None else None
47
53
 
48
54
  async def stop_execution(self, d):
49
55
  await self._stop_main()
@@ -57,6 +63,22 @@ class _ContainerDeployment(_DeploymentStrategy):
57
63
  "solver services teardown failed before grading", exc_info=True
58
64
  )
59
65
 
66
+ @asynccontextmanager
67
+ async def verifier(self, deployment, config, env):
68
+ if self._remote_built_image is None or config.image != self._remote_built_image:
69
+ async with super().verifier(deployment, config, env) as backend:
70
+ yield backend
71
+ return
72
+ # Rebuild from trusted task inputs on a fresh host: the solver's Docker tag
73
+ # is local to its released VM, and its modified container must never travel.
74
+ async with compose_services(
75
+ config,
76
+ deployment.spec,
77
+ env,
78
+ trust_compose=deployment.config.trust_compose,
79
+ ) as (services, _, _, _):
80
+ yield services[deployment.spec.workspace]
81
+
60
82
 
61
83
  class _PrivateNetworkDeployment(_DeploymentStrategy):
62
84
  async def start(self, d):
@@ -708,7 +708,11 @@ def resolve_image(
708
708
  import yaml
709
709
 
710
710
  main = yaml.safe_load(compose.read_text())["services"].get("main", {})
711
- if main.get("image") or "build" in main:
711
+ if (
712
+ main.get("image")
713
+ or "build" in main
714
+ or (task_dir / "environment/Dockerfile").is_file()
715
+ ):
712
716
  return None
713
717
  section = "verifier.environment" if verifier else "environment"
714
718
  dockerfile = "tests/Dockerfile" if verifier else "environment/Dockerfile"
File without changes