verifiers 0.4.1.dev40__tar.gz → 0.4.1.dev42__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 (276) hide show
  1. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/PKG-INFO +2 -2
  2. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/pyproject.toml +1 -1
  3. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/integration/conftest.py +4 -1
  4. verifiers-0.4.1.dev42/tests/integration/fixtures/fetch_tool_v1.py +36 -0
  5. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/integration/fixtures/pyproject.toml +1 -0
  6. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/integration/grid.py +7 -1
  7. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/integration/test_shared_tool.py +1 -1
  8. verifiers-0.4.1.dev42/tests/integration/test_tool_vms.py +88 -0
  9. verifiers-0.4.1.dev42/tests/unit/runtimes/test_private_network.py +254 -0
  10. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/cli/validate.py +66 -10
  11. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/configs/cli/validate.py +3 -0
  12. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/mcp/launch.py +28 -9
  13. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/mcp/toolset.py +3 -0
  14. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/rollout.py +25 -6
  15. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/runtimes/deployment.py +19 -5
  16. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/runtimes/prime.py +43 -16
  17. verifiers-0.4.1.dev42/verifiers/v1/runtimes/private_network.py +148 -0
  18. verifiers-0.4.1.dev42/verifiers/v1/runtimes/strategies.py +119 -0
  19. verifiers-0.4.1.dev40/verifiers/v1/runtimes/strategies.py +0 -64
  20. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/.gitignore +0 -0
  21. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/LICENSE +0 -0
  22. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/README.md +0 -0
  23. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/integration/fixtures/agent_user_harness_v1.py +0 -0
  24. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/integration/fixtures/agent_user_v1.py +0 -0
  25. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/integration/fixtures/counter_tool_v1.py +0 -0
  26. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/integration/fixtures/duet_v1.py +0 -0
  27. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/integration/fixtures/echo_acp_resume_v1.py +0 -0
  28. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/integration/fixtures/echo_agentic_v1.py +0 -0
  29. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/integration/fixtures/echo_tool_compose/docker-compose.yaml +0 -0
  30. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/integration/fixtures/echo_tool_v1.py +0 -0
  31. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/integration/fixtures/echo_user_sim_v1.py +0 -0
  32. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/integration/fixtures/echo_v1.py +0 -0
  33. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/integration/fixtures/network_policy_v1.py +0 -0
  34. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/integration/fixtures/skill_codeword_v1.py +0 -0
  35. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/integration/fixtures/skills/project-codeword/SKILL.md +0 -0
  36. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/integration/fixtures/tool_response_image_v1.py +0 -0
  37. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/integration/test_agent_user.py +0 -0
  38. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/integration/test_agentic_judge.py +0 -0
  39. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/integration/test_compose_scoring.py +0 -0
  40. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/integration/test_examples.py +0 -0
  41. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/integration/test_limits.py +0 -0
  42. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/integration/test_multi_agent.py +0 -0
  43. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/integration/test_multi_turn.py +0 -0
  44. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/integration/test_multi_turn_tool.py +0 -0
  45. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/integration/test_multimodal_tool.py +0 -0
  46. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/integration/test_network_policy.py +0 -0
  47. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/integration/test_resume_tool.py +0 -0
  48. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/integration/test_rubric_judge.py +0 -0
  49. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/integration/test_sampling.py +0 -0
  50. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/integration/test_single_turn.py +0 -0
  51. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/integration/test_skills.py +0 -0
  52. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/integration/test_stateful_tool.py +0 -0
  53. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/integration/test_user_echo.py +0 -0
  54. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/integration/test_user_sim.py +0 -0
  55. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/unit/cli/test_replay.py +0 -0
  56. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/unit/conftest.py +0 -0
  57. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/unit/harnesses/test_core.py +0 -0
  58. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/unit/harnesses/test_rlm.py +0 -0
  59. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/unit/interception/test_tunnel.py +0 -0
  60. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/unit/judges/conftest.py +0 -0
  61. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/unit/judges/test_judge.py +0 -0
  62. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/unit/judges/test_reference.py +0 -0
  63. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/unit/judges/test_rubric.py +0 -0
  64. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/unit/runtimes/test_e2b.py +0 -0
  65. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/unit/serve/test_client.py +0 -0
  66. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/unit/serve/test_delta.py +0 -0
  67. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/unit/serve/test_pool.py +0 -0
  68. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/unit/test_agent.py +0 -0
  69. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/unit/test_dialects.py +0 -0
  70. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/unit/test_graph.py +0 -0
  71. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/unit/test_semantic.py +0 -0
  72. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/unit/test_task.py +0 -0
  73. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/unit/test_taskset.py +0 -0
  74. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/unit/test_trace.py +0 -0
  75. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/unit/utils/test_compile.py +0 -0
  76. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/unit/utils/test_loaders.py +0 -0
  77. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/unit/utils/test_platform.py +0 -0
  78. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/tests/unit/utils/test_score.py +0 -0
  79. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/__init__.py +0 -0
  80. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/__init__.py +0 -0
  81. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/acp/__init__.py +0 -0
  82. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/acp/runner.py +0 -0
  83. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/agent.py +0 -0
  84. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/cli/__init__.py +0 -0
  85. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/cli/dashboard/__init__.py +0 -0
  86. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/cli/dashboard/base.py +0 -0
  87. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/cli/dashboard/eval.py +0 -0
  88. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/cli/dashboard/replay.py +0 -0
  89. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/cli/dashboard/validate.py +0 -0
  90. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/cli/debug.py +0 -0
  91. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/cli/eval/__init__.py +0 -0
  92. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/cli/eval/hint.py +0 -0
  93. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/cli/eval/main.py +0 -0
  94. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/cli/eval/resume.py +0 -0
  95. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/cli/eval/runner.py +0 -0
  96. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/cli/gepa.py +0 -0
  97. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/cli/output.py +0 -0
  98. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/cli/replay.py +0 -0
  99. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/cli/resolve.py +0 -0
  100. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/cli/resume.py +0 -0
  101. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/clients/__init__.py +0 -0
  102. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/clients/base.py +0 -0
  103. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/clients/client.py +0 -0
  104. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/clients/eval.py +0 -0
  105. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/clients/train.py +0 -0
  106. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/configs/__init__.py +0 -0
  107. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/configs/agent.py +0 -0
  108. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/configs/cli/__init__.py +0 -0
  109. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/configs/cli/debug.py +0 -0
  110. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/configs/cli/env.py +0 -0
  111. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/configs/cli/eval.py +0 -0
  112. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/configs/cli/replay.py +0 -0
  113. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/configs/client.py +0 -0
  114. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/configs/env.py +0 -0
  115. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/configs/harness.py +0 -0
  116. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/configs/judge.py +0 -0
  117. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/configs/retries.py +0 -0
  118. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/configs/runtime.py +0 -0
  119. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/configs/scoring.py +0 -0
  120. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/configs/select.py +0 -0
  121. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/configs/serve.py +0 -0
  122. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/configs/task.py +0 -0
  123. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/configs/taskset.py +0 -0
  124. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/dialects/__init__.py +0 -0
  125. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/dialects/anthropic.py +0 -0
  126. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/dialects/base.py +0 -0
  127. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/dialects/chat.py +0 -0
  128. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/dialects/responses.py +0 -0
  129. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/env.py +0 -0
  130. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/envs/__init__.py +0 -0
  131. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/envs/agentic_judge/__init__.py +0 -0
  132. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/envs/agentic_judge/env.py +0 -0
  133. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/envs/best_of_n/__init__.py +0 -0
  134. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/envs/best_of_n/env.py +0 -0
  135. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/envs/single_agent/__init__.py +0 -0
  136. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/envs/single_agent/env.py +0 -0
  137. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/envs/user_sim/__init__.py +0 -0
  138. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/envs/user_sim/env.py +0 -0
  139. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/episode.py +0 -0
  140. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/errors.py +0 -0
  141. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/gepa/__init__.py +0 -0
  142. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/gepa/adapter.py +0 -0
  143. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/gepa/config.py +0 -0
  144. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/gepa/dataset.py +0 -0
  145. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/gepa/reflection.py +0 -0
  146. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/gepa/runner.py +0 -0
  147. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/grading.py +0 -0
  148. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/graph.py +0 -0
  149. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harness.py +0 -0
  150. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/__init__.py +0 -0
  151. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/bash/__init__.py +0 -0
  152. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/bash/harness.py +0 -0
  153. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/browser_use/__init__.py +0 -0
  154. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/browser_use/harness.py +0 -0
  155. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/browser_use/program.py +0 -0
  156. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/claude_code/__init__.py +0 -0
  157. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/claude_code/harness.py +0 -0
  158. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/codex/__init__.py +0 -0
  159. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/codex/gate.mjs +0 -0
  160. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/codex/harness.py +0 -0
  161. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/deepseek/__init__.py +0 -0
  162. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/deepseek/gate.mjs +0 -0
  163. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/deepseek/harness.py +0 -0
  164. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/deepseek/usage.mjs +0 -0
  165. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/hermes_agent/__init__.py +0 -0
  166. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/hermes_agent/harness.py +0 -0
  167. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/hermes_agent/program.py +0 -0
  168. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/kimi_code/__init__.py +0 -0
  169. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/kimi_code/harness.py +0 -0
  170. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/mini_swe_agent/__init__.py +0 -0
  171. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/mini_swe_agent/harness.py +0 -0
  172. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/mini_swe_agent/program.py +0 -0
  173. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/node.py +0 -0
  174. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/null/__init__.py +0 -0
  175. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/null/harness.py +0 -0
  176. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/openclaw/__init__.py +0 -0
  177. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/openclaw/harness.py +0 -0
  178. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/opencode/__init__.py +0 -0
  179. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/opencode/harness.py +0 -0
  180. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/opencode/mcp-ready.js +0 -0
  181. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/pi/__init__.py +0 -0
  182. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/pi/gate.mjs +0 -0
  183. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/pi/harness.py +0 -0
  184. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/prime_agent/__init__.py +0 -0
  185. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/prime_agent/harness.py +0 -0
  186. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/rlm/__init__.py +0 -0
  187. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/rlm/harness.py +0 -0
  188. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/terminus_2/__init__.py +0 -0
  189. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/terminus_2/harness.py +0 -0
  190. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/terminus_2/program.py +0 -0
  191. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/utils/__init__.py +0 -0
  192. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/utils/compaction.py +0 -0
  193. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/utils/core.py +0 -0
  194. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/utils/install.py +0 -0
  195. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/utils/launch.py +0 -0
  196. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/utils/mcp.py +0 -0
  197. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/harnesses/utils/replay.py +0 -0
  198. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/interception/__init__.py +0 -0
  199. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/interception/base.py +0 -0
  200. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/interception/pool.py +0 -0
  201. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/interception/server.py +0 -0
  202. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/interception/tunnel/__init__.py +0 -0
  203. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/interception/tunnel/base.py +0 -0
  204. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/interception/tunnel/custom.py +0 -0
  205. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/interception/tunnel/prime.py +0 -0
  206. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/judge.py +0 -0
  207. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/judges/__init__.py +0 -0
  208. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/judges/decision.py +0 -0
  209. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/judges/reference.py +0 -0
  210. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/judges/reference.txt +0 -0
  211. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/judges/rubric.py +0 -0
  212. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/judges/rubric.txt +0 -0
  213. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/mcp/__init__.py +0 -0
  214. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/mcp/server.py +0 -0
  215. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/runtimes/__init__.py +0 -0
  216. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/runtimes/apptainer.py +0 -0
  217. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/runtimes/base.py +0 -0
  218. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/runtimes/compose.py +0 -0
  219. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/runtimes/container.py +0 -0
  220. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/runtimes/docker/__init__.py +0 -0
  221. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/runtimes/docker/egress.py +0 -0
  222. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/runtimes/e2b.py +0 -0
  223. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/runtimes/limiters.py +0 -0
  224. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/runtimes/modal.py +0 -0
  225. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/runtimes/runtime.py +0 -0
  226. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/runtimes/subprocess.py +0 -0
  227. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/semantic.py +0 -0
  228. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/serve/__init__.py +0 -0
  229. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/serve/client.py +0 -0
  230. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/serve/delta.py +0 -0
  231. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/serve/encoding.py +0 -0
  232. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/serve/pool.py +0 -0
  233. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/serve/server.py +0 -0
  234. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/serve/types.py +0 -0
  235. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/session.py +0 -0
  236. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/state.py +0 -0
  237. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/task.py +0 -0
  238. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/taskset.py +0 -0
  239. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/tasksets/__init__.py +0 -0
  240. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/tasksets/harbor/__init__.py +0 -0
  241. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/tasksets/harbor/taskset.py +0 -0
  242. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/tasksets/harbor/toolset.py +0 -0
  243. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/tasksets/nemo_gym/__init__.py +0 -0
  244. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/tasksets/nemo_gym/response.py +0 -0
  245. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/tasksets/nemo_gym/server.py +0 -0
  246. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/tasksets/nemo_gym/taskset.py +0 -0
  247. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/tasksets/nemo_gym/toolset.py +0 -0
  248. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/tasksets/openenv/__init__.py +0 -0
  249. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/tasksets/openenv/taskset.py +0 -0
  250. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/tasksets/textarena/__init__.py +0 -0
  251. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/tasksets/textarena/taskset.py +0 -0
  252. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/trace.py +0 -0
  253. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/types.py +0 -0
  254. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/utils/__init__.py +0 -0
  255. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/utils/aio.py +0 -0
  256. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/utils/artifacts.py +0 -0
  257. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/utils/compile.py +0 -0
  258. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/utils/decorators.py +0 -0
  259. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/utils/env.py +0 -0
  260. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/utils/eval.py +0 -0
  261. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/utils/format.py +0 -0
  262. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/utils/generic.py +0 -0
  263. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/utils/git.py +0 -0
  264. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/utils/image.py +0 -0
  265. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/utils/interrupt.py +0 -0
  266. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/utils/loaders.py +0 -0
  267. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/utils/logging.py +0 -0
  268. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/utils/memory.py +0 -0
  269. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/utils/paths.py +0 -0
  270. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/utils/platform.py +0 -0
  271. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/utils/prime.py +0 -0
  272. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/utils/retries.py +0 -0
  273. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/utils/scope.py +0 -0
  274. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/utils/score.py +0 -0
  275. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/verifiers/v1/utils/trace_store.py +0 -0
  276. {verifiers-0.4.1.dev40 → verifiers-0.4.1.dev42}/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.dev40
3
+ Version: 0.4.1.dev42
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
@@ -36,7 +36,7 @@ Requires-Dist: numpy>=2.1.0
36
36
  Requires-Dist: openai<3.0.0,>=2.54.0
37
37
  Requires-Dist: prime-pydantic-config[toml]>=0.4.3
38
38
  Requires-Dist: prime-runs>=0.1.4
39
- Requires-Dist: prime-sandboxes>=0.4.2
39
+ Requires-Dist: prime-sandboxes>=0.4.7
40
40
  Requires-Dist: prime-tunnel>=0.1.14
41
41
  Requires-Dist: pydantic>=2.12.3
42
42
  Requires-Dist: pyzmq>=27.1.0
@@ -33,7 +33,7 @@ dependencies = [
33
33
  "math-verify>=0.8.0",
34
34
  "openai>=2.54.0,<3.0.0",
35
35
  "prime-tunnel>=0.1.14",
36
- "prime-sandboxes>=0.4.2",
36
+ "prime-sandboxes>=0.4.7",
37
37
  "prime-runs>=0.1.4",
38
38
  "pydantic>=2.12.3",
39
39
  "rich>=11.0.0",
@@ -46,11 +46,14 @@ def harness_runtime(request) -> dict:
46
46
  @pytest.fixture
47
47
  def tool_runtime(request) -> dict:
48
48
  """A `taskset.task.tools` override placing the tool server: `colocated` (inside the harness's
49
- runtime), `service` (in the task's `tools` Compose service) or its own runtime, by type."""
49
+ runtime), `service` (in the task's `tools` Compose service), `service-vm` (in the task's
50
+ `tools-vm` service VM) or its own runtime, by type."""
50
51
  if request.param == "colocated":
51
52
  return {"colocated": True}
52
53
  if request.param == "service":
53
54
  return {"service": "tools"}
55
+ if request.param == "service-vm":
56
+ return {"service": "tools-vm"}
54
57
  return {"runtime": {"type": request.param}}
55
58
 
56
59
 
@@ -0,0 +1,36 @@
1
+ """fetch (v1, MCP tool): a tool server on a Prime VM of its own, under its own egress.
2
+
3
+ The integration test places a `FetchToolset` on a Prime runtime of its own
4
+ (`tools.runtime`) beside an offline workspace. The server's VM joins the workspace over
5
+ the rollout's private network and keeps the egress policy of its own runtime.
6
+ """
7
+
8
+ import urllib.request
9
+
10
+ import verifiers.v1 as vf
11
+
12
+
13
+ class FetchToolset(vf.Toolset[vf.ToolsetConfig]):
14
+ TOOL_PREFIX = "fetch"
15
+
16
+ @vf.tool
17
+ def fetch(self, url: str) -> str:
18
+ """Fetch a URL; return its HTTP status, or the type of the error."""
19
+ try:
20
+ return str(urllib.request.urlopen(url, timeout=15).status)
21
+ except (OSError, ValueError) as error: # URLError and timeouts are OSErrors
22
+ return type(error).__name__
23
+
24
+
25
+ class FetchToolTaskConfig(vf.TaskConfig):
26
+ tools: vf.ToolsetConfig = vf.ToolsetConfig()
27
+
28
+
29
+ class FetchToolTask(vf.Task[vf.TaskData, vf.State, FetchToolTaskConfig]):
30
+ @classmethod
31
+ def toolsets(cls, config: FetchToolTaskConfig) -> list[vf.Toolset]:
32
+ return [FetchToolset(config.tools)]
33
+
34
+
35
+ if __name__ == "__main__":
36
+ FetchToolset.run()
@@ -18,6 +18,7 @@ include = [
18
18
  "counter_tool_v1.py",
19
19
  "echo_acp_resume_v1.py",
20
20
  "echo_tool_v1.py",
21
+ "fetch_tool_v1.py",
21
22
  "tool_response_image_v1.py",
22
23
  "pyproject.toml",
23
24
  ]
@@ -240,9 +240,13 @@ def skip_harness(cells: list, harness: str, reason: str) -> list:
240
240
  def placements(
241
241
  harness_runtimes: tuple[str, ...] = tuple(RUNTIMES),
242
242
  tool_runtimes: tuple[str, ...] = ("colocated", *RUNTIMES),
243
+ *,
244
+ shared: bool = False,
243
245
  ) -> list:
244
246
  """`(harness_runtime, tool_runtime)` cells for a tool server placed colocated in the
245
- harness's box, in a Compose service beside it (`service`), or in its own runtime."""
247
+ harness's box, in a Compose service beside it (`service`), or in its own runtime
248
+ (on Prime beside a Prime harness, a VM on the rollout's private network). A `shared`
249
+ server runs once per eval, so it joins no rollout's network."""
246
250
  cells = []
247
251
  for harness_runtime in harness_runtimes:
248
252
  for tool_runtime in tool_runtimes:
@@ -264,6 +268,8 @@ def placements(
264
268
  elif (
265
269
  tool_runtime not in ("colocated", "service")
266
270
  and not runtime_cls(tool_runtime).supports_expose
271
+ # Beside a Prime harness, a rollout's Prime server joins its private network.
272
+ and not (not shared and tool_runtime == harness_runtime == "prime")
267
273
  ):
268
274
  marks.append(
269
275
  pytest.mark.skip(
@@ -8,7 +8,7 @@ from grid import HARNESS_RUNTIMES, placements
8
8
 
9
9
  @pytest.mark.parametrize(
10
10
  "harness_runtime,tool_runtime",
11
- placements(tool_runtimes=tuple(HARNESS_RUNTIMES)),
11
+ placements(tool_runtimes=tuple(HARNESS_RUNTIMES), shared=True),
12
12
  indirect=True,
13
13
  )
14
14
  async def test_shared_tool(run, harness_runtime, tool_runtime):
@@ -0,0 +1,88 @@
1
+ """A tool server on a Prime runtime of its own runs on a VM of its own, reachable from
2
+ the restricted workspace over the rollout's private network, under the egress policy of
3
+ its own runtime: unrestricted, or none at all."""
4
+
5
+ import json
6
+ import os
7
+
8
+ import pytest
9
+ from fetch_tool_v1 import FetchToolTask, FetchToolTaskConfig
10
+
11
+ import verifiers.v1 as vf
12
+ from verifiers.v1.configs.agent import TimeoutConfig
13
+
14
+ pytestmark = pytest.mark.skipif(
15
+ not os.environ.get("PRIME_API_KEY"), reason="needs PRIME_API_KEY"
16
+ )
17
+
18
+ FETCH = """
19
+ import sys, urllib.error, urllib.request
20
+ try:
21
+ print(urllib.request.urlopen(sys.argv[1], timeout=15).status)
22
+ except urllib.error.HTTPError as error:
23
+ print(error.code)
24
+ except Exception as error:
25
+ print(type(error).__name__)
26
+ """
27
+
28
+ # The workspace reaches the tool server (any HTTP status) but not the internet.
29
+ PROBE = """
30
+ import json, sys, urllib.error, urllib.request
31
+
32
+ def fetch(url):
33
+ try:
34
+ return urllib.request.urlopen(url, timeout=15).status
35
+ except urllib.error.HTTPError as error:
36
+ return error.code
37
+ except Exception as error:
38
+ return type(error).__name__
39
+
40
+ json.dump(
41
+ {"tool": fetch(sys.argv[1]), "public": fetch("https://example.com")},
42
+ open("/tmp/probe.json", "w"),
43
+ )
44
+ """
45
+
46
+
47
+ class ProbeHarness(vf.Harness):
48
+ SUPPORTS_MCP = True
49
+
50
+ async def launch(self, ctx, trace, runtime, endpoint, secret, mcp_urls, data):
51
+ trace.info["url"] = mcp_urls["fetch"]
52
+ return await runtime.run_program(["python", "-c", PROBE, mcp_urls["fetch"]], {})
53
+
54
+
55
+ class ProbeTask(FetchToolTask):
56
+ async def finalize(self, trace, runtime, deployment):
57
+ trace.info["workspace"] = json.loads(await runtime.read("/tmp/probe.json"))
58
+ tool = await deployment.target("fetch").run(
59
+ ["python", "-c", FETCH, "https://example.com"], {}
60
+ )
61
+ trace.info["tool"] = tool.stdout.strip()
62
+
63
+
64
+ @pytest.mark.parametrize(("allow", "egress"), [(["*"], "200"), ([], "URLError")])
65
+ async def test_a_tool_vm_keeps_its_own_egress_beside_a_restricted_workspace(
66
+ allow, egress
67
+ ):
68
+ tools = vf.ToolsetConfig(
69
+ runtime=vf.PrimeConfig(labels=["vf-integration"], allow=allow)
70
+ )
71
+ task = ProbeTask(
72
+ vf.TaskData(prompt="Probe the network", network_allow=[]),
73
+ FetchToolTaskConfig(tools=tools),
74
+ )
75
+ config = vf.AgentConfig(
76
+ model="unused",
77
+ runtime=vf.PrimeConfig(labels=["vf-integration"]),
78
+ timeout=TimeoutConfig(setup=900, finalize=120, scoring=60),
79
+ )
80
+ async with vf.Agent(config) as agent:
81
+ agent.harness = ProbeHarness(vf.HarnessConfig())
82
+ trace = await agent.run(task)
83
+ assert trace.ok, trace.errors
84
+ assert ".sandbox.internal:" in trace.info["url"]
85
+ workspace = trace.info["workspace"]
86
+ assert isinstance(workspace["tool"], int), workspace
87
+ assert workspace["public"] == "URLError"
88
+ assert trace.info["tool"] == egress
@@ -0,0 +1,254 @@
1
+ import asyncio
2
+ from contextlib import asynccontextmanager
3
+ from types import SimpleNamespace
4
+
5
+ import pytest
6
+
7
+ import verifiers.v1 as vf
8
+ from verifiers.v1.errors import ToolsetError
9
+ from verifiers.v1.mcp import launch
10
+ from verifiers.v1.runtimes import (
11
+ DockerConfig,
12
+ PrimeBackend,
13
+ PrimeConfig,
14
+ prime,
15
+ private_network,
16
+ )
17
+ from verifiers.v1.runtimes.strategies import (
18
+ _ColocatedDeployment,
19
+ _ContainerDeployment,
20
+ _deployment_strategy,
21
+ _PrivateNetworkDeployment,
22
+ )
23
+
24
+ ATLAS = PrimeConfig(image="atlas:1", cpu=4, memory=10) # open egress by default
25
+ FORTH = PrimeConfig(image="forth:1", allow=[])
26
+
27
+
28
+ def toolset(name, **config):
29
+ return SimpleNamespace(server_name=name, config=vf.ToolsetConfig(**config))
30
+
31
+
32
+ def test_tool_servers_on_prime_runtimes_beside_a_prime_workspace_get_vms():
33
+ toolsets = [
34
+ toolset("atlas", runtime=ATLAS),
35
+ toolset("search"), # the evaluator
36
+ toolset("files", colocated=True, runtime=ATLAS), # the workspace
37
+ toolset("remote", url="https://tools.example/mcp"),
38
+ toolset("gym", service="tools"), # a Compose service
39
+ toolset("local", runtime=DockerConfig()), # a published port
40
+ ]
41
+ vms = private_network.tool_vms
42
+ assert vms(toolsets, PrimeConfig(), composed=False, borrowed=False) == {
43
+ "atlas": ATLAS
44
+ }
45
+ # Without a Prime workspace of the rollout's own, servers keep their published port.
46
+ for workspace, composed, borrowed in [
47
+ (DockerConfig(), False, False),
48
+ (PrimeConfig(), True, False),
49
+ (PrimeConfig(), False, True),
50
+ ]:
51
+ assert vms(toolsets, workspace, composed=composed, borrowed=borrowed) == {}
52
+ with pytest.raises(ValueError, match="cannot be named 'main'"):
53
+ vms(
54
+ [toolset("main", runtime=ATLAS)],
55
+ PrimeConfig(),
56
+ composed=False,
57
+ borrowed=False,
58
+ )
59
+
60
+
61
+ def test_the_deployment_joins_tool_vms_over_a_private_network():
62
+ assert isinstance(
63
+ _deployment_strategy(None, PrimeConfig(), None, {"atlas": ATLAS}),
64
+ _PrivateNetworkDeployment,
65
+ )
66
+ assert isinstance(
67
+ _deployment_strategy(None, PrimeConfig(), None, {}), _ColocatedDeployment
68
+ )
69
+ spec = vf.ComposeSpec("task")
70
+ assert isinstance(
71
+ _deployment_strategy(None, PrimeConfig(), spec), _ContainerDeployment
72
+ )
73
+
74
+
75
+ def test_the_private_network_needs_a_prime_workspace():
76
+ workspace = SimpleNamespace(config=DockerConfig())
77
+
78
+ async def enter():
79
+ async with private_network.private_network(workspace, {"atlas": ATLAS}, None):
80
+ pass
81
+
82
+ with pytest.raises(TypeError, match="needs the workspace on Prime, not docker"):
83
+ asyncio.run(enter())
84
+
85
+
86
+ class FakeLanClient:
87
+ def __init__(self, events):
88
+ self.events = events
89
+
90
+ async def create_lan_network(self, name, rules):
91
+ self.events.append(("network", [(rule.from_, rule.to) for rule in rules]))
92
+ return SimpleNamespace(id="net1")
93
+
94
+ async def delete_lan_network(self, network_id):
95
+ self.events.append("network deleted")
96
+
97
+
98
+ def boot(monkeypatch, members):
99
+ """Enter `private_network` with a fake LAN client and Prime VMs that record their
100
+ boot and stop there."""
101
+ events = []
102
+
103
+ @asynccontextmanager
104
+ async def leased_client():
105
+ yield FakeLanClient(events)
106
+
107
+ async def start(self):
108
+ events.append(("boot", self.name, self.config, self.lan))
109
+ raise RuntimeError("booted")
110
+
111
+ monkeypatch.setattr(private_network, "leased_client", leased_client)
112
+ monkeypatch.setattr(prime, "ensure_prime_auth", lambda: None)
113
+ monkeypatch.setattr(PrimeBackend, "start", start)
114
+ workspace = PrimeBackend(PrimeConfig(allow=[]), name="ws")
115
+
116
+ async def enter():
117
+ async with private_network.private_network(workspace, members, workspace.start):
118
+ pass
119
+
120
+ with pytest.raises(RuntimeError, match="booted"):
121
+ asyncio.run(enter())
122
+ return events
123
+
124
+
125
+ def test_each_vm_boots_its_server_runtime_as_given(monkeypatch):
126
+ events = boot(monkeypatch, {"atlas": ATLAS, "forth": FORTH})
127
+ vms = {e[1]: e[2] for e in events if isinstance(e, tuple) and e[0] == "boot"}
128
+
129
+ def shape(config):
130
+ return config.image, config.cpu, config.memory, config.allow
131
+
132
+ assert shape(vms["ws-vf-tool-0"]) == ("atlas:1", 4, 10, ["*"])
133
+ assert shape(vms["ws-vf-tool-1"]) == ("forth:1", FORTH.cpu, FORTH.memory, [])
134
+ assert vms["ws"].allow == [] # the workspace keeps its own policy
135
+
136
+
137
+ def test_the_network_starts_with_its_rules_and_every_vm_joins_as_it_boots(monkeypatch):
138
+ events = boot(monkeypatch, {"atlas": ATLAS, "forth": FORTH})
139
+ assert events[0] == (
140
+ "network",
141
+ [("tag:vf-workspace", "tag:vf-tool-0"), ("tag:vf-workspace", "tag:vf-tool-1")],
142
+ )
143
+ boots = {name: lan for kind, name, _, lan in events[1:-1] if kind == "boot"}
144
+ assert boots == {
145
+ "ws": ("net1", ["vf-workspace"]),
146
+ "ws-vf-tool-0": ("net1", ["vf-tool-0"]),
147
+ "ws-vf-tool-1": ("net1", ["vf-tool-1"]),
148
+ }
149
+ assert events[-1] == "network deleted"
150
+
151
+
152
+ def test_a_tool_vm_keeps_only_its_state_channel_and_the_harness_its_routes():
153
+ applied = []
154
+
155
+ def backend(name):
156
+ async def prepare_execution(routes):
157
+ applied.append((name, routes))
158
+
159
+ def host_url(url):
160
+ return url.replace("http://127.0.0.1:9000", "https://tunnel.example")
161
+
162
+ return SimpleNamespace(prepare_execution=prepare_execution, host_url=host_url)
163
+
164
+ strategy = _PrivateNetworkDeployment()
165
+ strategy._service_backends = [backend("forth")]
166
+ deployment = SimpleNamespace(harness_backend=backend("main"))
167
+ # The harness reaches the model and a remote tool server; the tool VM needs neither.
168
+ routes = ["https://tunnel.example/v1", "https://tools.example/mcp"]
169
+ asyncio.run(
170
+ strategy.prepare_execution(
171
+ deployment, routes, state_route="http://127.0.0.1:9000/state"
172
+ )
173
+ )
174
+ assert sorted(applied) == [
175
+ ("forth", ["https://tunnel.example/state"]),
176
+ ("main", routes),
177
+ ]
178
+
179
+
180
+ def test_the_workspace_stops_before_tool_evidence_and_is_released_regardless():
181
+ events = []
182
+
183
+ async def stop():
184
+ events.append("workspace stopped")
185
+
186
+ async def aclose():
187
+ events.append("tool VMs released")
188
+ raise RuntimeError("network deletion failed")
189
+
190
+ strategy = _PrivateNetworkDeployment()
191
+ strategy._services = SimpleNamespace(aclose=aclose)
192
+ deployment = SimpleNamespace(execution_backend=SimpleNamespace(stop=stop))
193
+
194
+ # As Compose stops its workspace, before finalize_services reads the targets.
195
+ asyncio.run(strategy.stop_execution(deployment))
196
+ assert events == ["workspace stopped"]
197
+
198
+ # A failed tool VM or network release neither skips the workspace's nor raises.
199
+ events.clear()
200
+ asyncio.run(strategy.prepare_verifier(deployment))
201
+ assert events == ["workspace stopped", "tool VMs released"]
202
+
203
+
204
+ def served_url(monkeypatch, server, **kwargs):
205
+ async def serve_in_runtime(server, runtime, **options):
206
+ assert options["listen_all"]
207
+ return 1234
208
+
209
+ monkeypatch.setattr(launch, "serve_in_runtime", serve_in_runtime)
210
+
211
+ async def url():
212
+ async with launch._serve(server, **kwargs) as served:
213
+ return served.url
214
+
215
+ return asyncio.run(url())
216
+
217
+
218
+ def test_a_tool_server_on_its_own_vm_is_reached_by_its_lan_name(monkeypatch):
219
+ runtime = SimpleNamespace(is_local=False, host_url=lambda url: url)
220
+ # Its restricted runtime is enforced on its own VM, so it is not refused.
221
+ server = toolset("forth", runtime=FORTH)
222
+ assert (
223
+ served_url(
224
+ monkeypatch,
225
+ server,
226
+ hosts={"forth": "sb1.sandbox.internal"},
227
+ placements={"forth": runtime},
228
+ )
229
+ == "http://sb1.sandbox.internal:1234/mcp"
230
+ )
231
+ # A Compose service keeps its name.
232
+ compose = toolset("echo", service="tools")
233
+ assert served_url(monkeypatch, compose, services={"tools": runtime}) == (
234
+ "http://tools:1234/mcp"
235
+ )
236
+
237
+
238
+ @pytest.mark.parametrize("service", ["main", "forth"])
239
+ def test_an_explicit_service_names_only_a_compose_service(monkeypatch, service):
240
+ # The workspace and another server's VM are deployment targets, not services.
241
+ runtime = SimpleNamespace(is_local=False, host_url=lambda url: url)
242
+ with pytest.raises(ToolsetError, match="Compose services define it"):
243
+ served_url(
244
+ monkeypatch,
245
+ toolset("echo", service=service),
246
+ services={},
247
+ hosts={"forth": "sb1.sandbox.internal"},
248
+ placements={"forth": runtime},
249
+ )
250
+
251
+
252
+ def test_a_restricted_runtime_without_a_private_network_is_refused(monkeypatch):
253
+ with pytest.raises(ToolsetError, match="joins the rollout's private network"):
254
+ served_url(monkeypatch, toolset("forth", runtime=FORTH))
@@ -31,11 +31,14 @@ from verifiers.v1.cli.resolve import (
31
31
  from verifiers.v1.cli.resume import distribute
32
32
  from verifiers.v1.configs.cli.validate import ValidateConfig
33
33
  from verifiers.v1.errors import TaskError, boundary
34
+ from verifiers.v1.grading import bind_scoring_env, grade_task, verifier_runtime
34
35
  from verifiers.v1.runtimes import make_backend
36
+ from verifiers.v1.runtimes.deployment import Deployment
35
37
  from verifiers.v1.state import state_cls
36
38
  from verifiers.v1.task import Task
37
39
  from verifiers.v1.trace import Trace, TraceTask
38
40
  from verifiers.v1.utils.aio import run_shielded
41
+ from verifiers.v1.utils.artifacts import collect
39
42
  from verifiers.v1.utils.compile import resolve_runtime_config
40
43
  from verifiers.v1.utils.decorators import invoke
41
44
  from verifiers.v1.utils.interrupt import install_interrupt
@@ -244,17 +247,48 @@ async def _gold(task: Task, trace: Trace, runtime) -> bool | None:
244
247
  return await task.validate(runtime)
245
248
 
246
249
 
247
- async def _score_untouched(task: Task, trace: Trace, runtime) -> bool | None:
250
+ async def _score_untouched(
251
+ task: Task, trace: Trace, runtime, deployment: Deployment, config: ValidateConfig
252
+ ) -> bool | None:
248
253
  """Finalize and score the freshly set-up task exactly as a rollout would, with no
249
254
  agent turn in between. Valid iff it does not already pass; `None` when the task
250
255
  declares no reward to check."""
256
+ policy = task.scoring
257
+ isolated = policy.mode == "isolated"
258
+ grader = task.grading_task(runtime, scoring=policy)
259
+ grading_config = (
260
+ verifier_runtime(grader, runtime, config.runtime, policy) if isolated else None
261
+ )
262
+ if not isolated and policy.runtime is not None:
263
+ raise TaskError("verifier runtime overrides require isolated verification")
251
264
  async with (
252
265
  asyncio.timeout(task.data.timeout.finalize),
253
266
  boundary(TaskError, "task finalize"),
254
267
  ):
255
- await invoke(task.finalize, {"trace": trace, "runtime": runtime})
256
- async with asyncio.timeout(task.data.timeout.scoring):
257
- await task.score(trace, runtime)
268
+ await invoke(
269
+ task.finalize,
270
+ {"trace": trace, "runtime": runtime, "deployment": deployment},
271
+ )
272
+ if isolated:
273
+ if not trace.state.artifacts:
274
+ trace.state.artifacts = await collect(
275
+ runtime, task.data.artifacts, max_bytes=task.data.artifact_max_bytes
276
+ )
277
+ await deployment.stop_execution()
278
+ await task.finalize_services(
279
+ trace,
280
+ {name: deployment.target(name) for name in deployment.target_backends},
281
+ )
282
+ if isolated:
283
+ timeouts = task.data.timeout
284
+ if config.timeout.setup is not None:
285
+ timeouts = timeouts.model_copy(update={"setup": config.timeout.setup})
286
+ await grade_task(grader, trace, grading_config, timeouts, policy, deployment)
287
+ else:
288
+ async with asyncio.timeout(task.data.timeout.scoring):
289
+ runtime = bind_scoring_env(runtime, policy)
290
+ await invoke(grader.stage_verifier, {"trace": trace, "runtime": runtime})
291
+ await grader.score(trace, runtime, deployment)
258
292
  if not trace.rewards:
259
293
  return None
260
294
  return trace.reward < SOLVED
@@ -278,8 +312,13 @@ async def _run_check(task: Task, config: ValidateConfig, mode: str) -> ResultRow
278
312
  valid: bool | None = False
279
313
  exc = None
280
314
  trace: Trace | None = None
315
+ deployment: Deployment | None = None
281
316
  try:
282
- backend.env = dict(task.runtime_env())
317
+ deployment = backend.deploy(
318
+ spec=task.deployment_spec(),
319
+ env=task.runtime_env(),
320
+ config=config.deployment,
321
+ )
283
322
  trace = Trace(
284
323
  task=TraceTask(
285
324
  type=type(task).__name__, data=task.data, key=task.key, hash=task.hash
@@ -292,20 +331,37 @@ async def _run_check(task: Task, config: ValidateConfig, mode: str) -> ResultRow
292
331
  trainable=False,
293
332
  ),
294
333
  )
295
- await backend.start()
296
- runtime = backend.bind()
334
+ await deployment.start()
335
+ runtime = deployment.workspace
336
+ await runtime.backend.prepare_setup()
297
337
  await asyncio.wait_for(
298
- invoke(task.setup, {"trace": trace, "runtime": runtime}),
338
+ invoke(
339
+ task.setup,
340
+ {"trace": trace, "runtime": runtime, "deployment": deployment},
341
+ ),
299
342
  setup_timeout,
300
343
  )
301
344
  valid = await asyncio.wait_for(
302
- CHECKS[mode](task, trace, runtime), config.timeout.total
345
+ invoke(
346
+ CHECKS[mode],
347
+ {
348
+ "task": task,
349
+ "trace": trace,
350
+ "runtime": runtime,
351
+ "deployment": deployment,
352
+ "config": config,
353
+ },
354
+ ),
355
+ config.timeout.total,
303
356
  )
304
357
  except Exception as e: # noqa: BLE001 - validation reports plugin failures per task
305
358
  exc = e
306
359
  finally:
307
360
  try:
308
- await backend.stop()
361
+ if deployment is not None:
362
+ await deployment.close()
363
+ else:
364
+ await backend.stop()
309
365
  except Exception:
310
366
  logger.warning(
311
367
  "runtime teardown failed (task %s)", task.data.idx, exc_info=True
@@ -10,6 +10,7 @@ from verifiers.v1.configs.cli.eval import RunConfig
10
10
  from verifiers.v1.configs.select import SelectCLIConfig
11
11
  from verifiers.v1.configs.taskset import TasksetConfig
12
12
  from verifiers.v1.runtimes import PrimeConfig, RuntimeConfig
13
+ from verifiers.v1.runtimes.deployment import DeploymentConfig
13
14
 
14
15
 
15
16
  class CheckTimeoutConfig(BaseConfig):
@@ -30,6 +31,8 @@ class ValidateConfig(BaseConfig):
30
31
  `select.limit`, `-s` sets `select.shuffle`)."""
31
32
  runtime: RuntimeConfig = PrimeConfig()
32
33
  """Where each task's validation hooks run."""
34
+ deployment: DeploymentConfig = DeploymentConfig()
35
+ """Task service deployment, including local Compose trust."""
33
36
  timeout: CheckTimeoutConfig = CheckTimeoutConfig()
34
37
  only_setup: bool = False
35
38
  """Run only the setup check: `Task.setup`."""