verifiers 0.4.2.dev36__tar.gz → 0.4.2.dev45__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 (267) hide show
  1. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/PKG-INFO +1 -1
  2. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/unit/harnesses/test_rlm.py +18 -2
  3. verifiers-0.4.2.dev45/tests/unit/test_eval_client_cache.py +161 -0
  4. verifiers-0.4.2.dev45/tests/unit/test_prompt_cache.py +451 -0
  5. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/clients/eval.py +8 -0
  6. verifiers-0.4.2.dev45/verifiers/v1/clients/prompt_cache.py +350 -0
  7. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/configs/client.py +3 -0
  8. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/rlm/harness.py +8 -0
  9. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/.gitignore +0 -0
  10. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/LICENSE +0 -0
  11. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/README.md +0 -0
  12. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/pyproject.toml +0 -0
  13. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/conftest.py +0 -0
  14. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/fixtures/agent_user.py +0 -0
  15. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/fixtures/agent_user_harness.py +0 -0
  16. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/fixtures/counter_tool.py +0 -0
  17. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/fixtures/duet.py +0 -0
  18. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/fixtures/echo.py +0 -0
  19. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/fixtures/echo_acp_resume.py +0 -0
  20. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/fixtures/echo_agentic.py +0 -0
  21. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/fixtures/echo_tool.py +0 -0
  22. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/fixtures/echo_user_sim.py +0 -0
  23. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/fixtures/fetch_tool.py +0 -0
  24. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/fixtures/network_policy.py +0 -0
  25. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/fixtures/parallel.py +0 -0
  26. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/fixtures/pyproject.toml +0 -0
  27. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/fixtures/skill_codeword.py +0 -0
  28. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/fixtures/skills/project-codeword/SKILL.md +0 -0
  29. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/fixtures/tool_error.py +0 -0
  30. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/fixtures/tool_response_image.py +0 -0
  31. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/grid.py +0 -0
  32. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/test_agent_user.py +0 -0
  33. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/test_agentic_judge.py +0 -0
  34. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/test_examples.py +0 -0
  35. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/test_limits.py +0 -0
  36. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/test_mounts.py +0 -0
  37. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/test_multi_agent.py +0 -0
  38. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/test_multi_turn.py +0 -0
  39. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/test_multi_turn_tool.py +0 -0
  40. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/test_multimodal_tool.py +0 -0
  41. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/test_native_compose.py +0 -0
  42. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/test_network_policy.py +0 -0
  43. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/test_parallel.py +0 -0
  44. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/test_resume_tool.py +0 -0
  45. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/test_rubric_judge.py +0 -0
  46. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/test_sampling.py +0 -0
  47. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/test_scoring_network.py +0 -0
  48. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/test_service_scoring.py +0 -0
  49. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/test_shared_tool.py +0 -0
  50. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/test_single_turn.py +0 -0
  51. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/test_skills.py +0 -0
  52. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/test_stateful_tool.py +0 -0
  53. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/test_tool_vms.py +0 -0
  54. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/test_user_echo.py +0 -0
  55. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/integration/test_user_sim.py +0 -0
  56. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/unit/conftest.py +0 -0
  57. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/unit/harnesses/test_core.py +0 -0
  58. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/unit/interception/test_tunnel.py +0 -0
  59. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/unit/judges/conftest.py +0 -0
  60. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/unit/judges/test_judge.py +0 -0
  61. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/unit/judges/test_reference.py +0 -0
  62. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/unit/judges/test_rubric.py +0 -0
  63. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/unit/runtimes/test_private_network.py +0 -0
  64. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/unit/test_agent.py +0 -0
  65. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/unit/test_api.py +0 -0
  66. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/unit/test_best_of_n_judge.py +0 -0
  67. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/unit/test_delta.py +0 -0
  68. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/unit/test_dialects.py +0 -0
  69. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/unit/test_graph.py +0 -0
  70. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/unit/test_harbor_grading_network.py +0 -0
  71. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/unit/test_retry_ids.py +0 -0
  72. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/unit/test_runtimes.py +0 -0
  73. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/unit/test_scoring_network.py +0 -0
  74. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/unit/test_semantic.py +0 -0
  75. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/unit/test_task.py +0 -0
  76. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/unit/test_taskset.py +0 -0
  77. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/unit/test_token_arrays.py +0 -0
  78. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/unit/test_trace.py +0 -0
  79. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/unit/utils/test_artifacts.py +0 -0
  80. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/unit/utils/test_compile.py +0 -0
  81. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/unit/utils/test_loaders.py +0 -0
  82. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/tests/unit/utils/test_score.py +0 -0
  83. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/__init__.py +0 -0
  84. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/__init__.py +0 -0
  85. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/acp/__init__.py +0 -0
  86. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/acp/runner.py +0 -0
  87. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/agent.py +0 -0
  88. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/cli/__init__.py +0 -0
  89. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/cli/debug.py +0 -0
  90. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/cli/output.py +0 -0
  91. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/cli/resolve.py +0 -0
  92. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/cli/resume.py +0 -0
  93. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/cli/run.py +0 -0
  94. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/cli/validate.py +0 -0
  95. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/clients/__init__.py +0 -0
  96. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/clients/base.py +0 -0
  97. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/clients/client.py +0 -0
  98. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/clients/train.py +0 -0
  99. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/configs/__init__.py +0 -0
  100. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/configs/agent.py +0 -0
  101. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/configs/cli/__init__.py +0 -0
  102. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/configs/cli/debug.py +0 -0
  103. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/configs/cli/env.py +0 -0
  104. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/configs/cli/run.py +0 -0
  105. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/configs/cli/validate.py +0 -0
  106. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/configs/deployment.py +0 -0
  107. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/configs/env.py +0 -0
  108. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/configs/harness.py +0 -0
  109. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/configs/interception.py +0 -0
  110. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/configs/judge.py +0 -0
  111. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/configs/retries.py +0 -0
  112. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/configs/runtime.py +0 -0
  113. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/configs/sampling.py +0 -0
  114. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/configs/scoring.py +0 -0
  115. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/configs/select.py +0 -0
  116. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/configs/task.py +0 -0
  117. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/configs/taskset.py +0 -0
  118. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/delta.py +0 -0
  119. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/dialects/__init__.py +0 -0
  120. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/dialects/anthropic.py +0 -0
  121. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/dialects/base.py +0 -0
  122. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/dialects/chat.py +0 -0
  123. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/dialects/responses.py +0 -0
  124. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/env.py +0 -0
  125. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/envs/__init__.py +0 -0
  126. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/envs/agentic_judge/__init__.py +0 -0
  127. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/envs/agentic_judge/env.py +0 -0
  128. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/envs/best_of_n/__init__.py +0 -0
  129. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/envs/best_of_n/env.py +0 -0
  130. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/envs/best_of_n_judge/__init__.py +0 -0
  131. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/envs/best_of_n_judge/env.py +0 -0
  132. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/envs/single_agent/__init__.py +0 -0
  133. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/envs/single_agent/env.py +0 -0
  134. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/envs/user_sim/__init__.py +0 -0
  135. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/envs/user_sim/env.py +0 -0
  136. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/episode.py +0 -0
  137. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/errors.py +0 -0
  138. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/grading.py +0 -0
  139. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/graph.py +0 -0
  140. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harness.py +0 -0
  141. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/__init__.py +0 -0
  142. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/bash/__init__.py +0 -0
  143. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/bash/harness.py +0 -0
  144. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/browser_use/__init__.py +0 -0
  145. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/browser_use/harness.py +0 -0
  146. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/browser_use/program.py +0 -0
  147. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/claude_code/__init__.py +0 -0
  148. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/claude_code/harness.py +0 -0
  149. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/codex/__init__.py +0 -0
  150. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/codex/gate.mjs +0 -0
  151. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/codex/harness.py +0 -0
  152. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/deepseek/__init__.py +0 -0
  153. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/deepseek/gate.mjs +0 -0
  154. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/deepseek/harness.py +0 -0
  155. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/deepseek/usage.mjs +0 -0
  156. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/hermes_agent/__init__.py +0 -0
  157. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/hermes_agent/harness.py +0 -0
  158. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/hermes_agent/program.py +0 -0
  159. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/kimi_code/__init__.py +0 -0
  160. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/kimi_code/harness.py +0 -0
  161. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/mini_swe_agent/__init__.py +0 -0
  162. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/mini_swe_agent/harness.py +0 -0
  163. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/mini_swe_agent/program.py +0 -0
  164. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/node.py +0 -0
  165. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/null/__init__.py +0 -0
  166. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/null/harness.py +0 -0
  167. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/openclaw/__init__.py +0 -0
  168. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/openclaw/harness.py +0 -0
  169. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/opencode/__init__.py +0 -0
  170. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/opencode/harness.py +0 -0
  171. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/opencode/mcp-ready.js +0 -0
  172. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/pi/__init__.py +0 -0
  173. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/pi/gate.mjs +0 -0
  174. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/pi/harness.py +0 -0
  175. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/prime_agent/__init__.py +0 -0
  176. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/prime_agent/harness.py +0 -0
  177. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/rlm/__init__.py +0 -0
  178. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/stirrup/__init__.py +0 -0
  179. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/stirrup/harness.py +0 -0
  180. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/stirrup/program.py +0 -0
  181. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/terminus_2/__init__.py +0 -0
  182. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/terminus_2/harness.py +0 -0
  183. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/terminus_2/program.py +0 -0
  184. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/utils/__init__.py +0 -0
  185. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/utils/compaction.py +0 -0
  186. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/utils/core.py +0 -0
  187. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/utils/install.py +0 -0
  188. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/utils/launch.py +0 -0
  189. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/utils/mcp.py +0 -0
  190. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/harnesses/utils/replay.py +0 -0
  191. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/interception/__init__.py +0 -0
  192. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/interception/base.py +0 -0
  193. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/interception/pool.py +0 -0
  194. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/interception/server.py +0 -0
  195. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/interception/tunnel/__init__.py +0 -0
  196. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/interception/tunnel/base.py +0 -0
  197. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/interception/tunnel/custom.py +0 -0
  198. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/interception/tunnel/prime.py +0 -0
  199. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/judge.py +0 -0
  200. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/judges/__init__.py +0 -0
  201. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/judges/decision.py +0 -0
  202. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/judges/reference.py +0 -0
  203. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/judges/reference.txt +0 -0
  204. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/judges/rubric.py +0 -0
  205. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/judges/rubric.txt +0 -0
  206. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/mcp/__init__.py +0 -0
  207. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/mcp/launch.py +0 -0
  208. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/mcp/server.py +0 -0
  209. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/mcp/toolset.py +0 -0
  210. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/rollout.py +0 -0
  211. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/runtimes/__init__.py +0 -0
  212. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/runtimes/backends/custom.py +0 -0
  213. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/runtimes/backends/docker.py +0 -0
  214. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/runtimes/backends/prime.py +0 -0
  215. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/runtimes/backends/subprocess.py +0 -0
  216. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/runtimes/base.py +0 -0
  217. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/runtimes/compose.py +0 -0
  218. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/runtimes/deployment.py +0 -0
  219. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/runtimes/egress.py +0 -0
  220. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/runtimes/limiters.py +0 -0
  221. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/runtimes/native_compose.py +0 -0
  222. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/runtimes/private_network.py +0 -0
  223. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/runtimes/runtime.py +0 -0
  224. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/runtimes/strategies.py +0 -0
  225. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/semantic.py +0 -0
  226. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/session.py +0 -0
  227. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/state.py +0 -0
  228. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/task.py +0 -0
  229. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/taskset.py +0 -0
  230. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/tasksets/__init__.py +0 -0
  231. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/tasksets/harbor/__init__.py +0 -0
  232. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/tasksets/harbor/taskset.py +0 -0
  233. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/tasksets/harbor/toolset.py +0 -0
  234. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/tasksets/nemo_gym/__init__.py +0 -0
  235. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/tasksets/nemo_gym/response.py +0 -0
  236. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/tasksets/nemo_gym/server.py +0 -0
  237. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/tasksets/nemo_gym/taskset.py +0 -0
  238. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/tasksets/nemo_gym/toolset.py +0 -0
  239. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/tasksets/openenv/__init__.py +0 -0
  240. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/tasksets/openenv/taskset.py +0 -0
  241. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/tasksets/textarena/__init__.py +0 -0
  242. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/tasksets/textarena/taskset.py +0 -0
  243. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/trace.py +0 -0
  244. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/types.py +0 -0
  245. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/utils/__init__.py +0 -0
  246. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/utils/aio.py +0 -0
  247. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/utils/artifacts.py +0 -0
  248. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/utils/compile.py +0 -0
  249. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/utils/decorators.py +0 -0
  250. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/utils/env.py +0 -0
  251. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/utils/eval.py +0 -0
  252. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/utils/format.py +0 -0
  253. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/utils/generic.py +0 -0
  254. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/utils/git.py +0 -0
  255. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/utils/image.py +0 -0
  256. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/utils/interrupt.py +0 -0
  257. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/utils/loaders.py +0 -0
  258. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/utils/logging.py +0 -0
  259. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/utils/memory.py +0 -0
  260. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/utils/network.py +0 -0
  261. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/utils/paths.py +0 -0
  262. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/utils/prime.py +0 -0
  263. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/utils/retries.py +0 -0
  264. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/utils/scope.py +0 -0
  265. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/utils/score.py +0 -0
  266. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/verifiers/v1/utils/trace_store.py +0 -0
  267. {verifiers-0.4.2.dev36 → verifiers-0.4.2.dev45}/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.2.dev36
3
+ Version: 0.4.2.dev45
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
@@ -57,12 +57,28 @@ def test_rlm_stop_reason_survives_completion_and_serialization(reason, condition
57
57
  assert restored.is_format_violation is (condition == "format_violation")
58
58
 
59
59
 
60
- def _sent_policy(config: RLMHarnessConfig, runtime_config) -> dict:
60
+ def _sent_contract(config: RLMHarnessConfig, runtime_config) -> dict:
61
61
  runtime = SimpleNamespace(config=runtime_config, env={})
62
62
  metadata = RLMHarness(config)._runtime_metadata(
63
63
  SimpleNamespace(model="m"), _trace(), runtime, "http://endpoint", "s", None
64
64
  )
65
- return metadata[RLM_RUNTIME_METADATA_KEY]["policy"]
65
+ return metadata[RLM_RUNTIME_METADATA_KEY]
66
+
67
+
68
+ def _sent_policy(config: RLMHarnessConfig, runtime_config) -> dict:
69
+ return _sent_contract(config, runtime_config)["policy"]
70
+
71
+
72
+ @pytest.mark.parametrize("max_retries", [0, 30])
73
+ def test_rlm_sends_provider_max_retries_when_set(max_retries):
74
+ config = RLMHarnessConfig(id="rlm", provider_max_retries=max_retries)
75
+ provider = _sent_contract(config, DockerConfig())["provider"]
76
+ assert provider["max_retries"] == max_retries
77
+
78
+
79
+ def test_rlm_leaves_provider_max_retries_to_nano_rlm_by_default():
80
+ provider = _sent_contract(RLMHarnessConfig(id="rlm"), DockerConfig())["provider"]
81
+ assert "max_retries" not in provider
66
82
 
67
83
 
68
84
  @pytest.mark.parametrize(
@@ -0,0 +1,161 @@
1
+ """EvalClient wiring for Anthropic prompt-cache markers."""
2
+
3
+ import json
4
+ from copy import deepcopy
5
+ from types import SimpleNamespace
6
+ from unittest.mock import AsyncMock
7
+
8
+ import pytest
9
+ from pydantic import ValidationError
10
+
11
+ from verifiers.v1.clients.eval import EvalClient
12
+ from verifiers.v1.configs.client import EvalClientConfig
13
+ from verifiers.v1.configs.sampling import SamplingConfig
14
+ from verifiers.v1.dialects.chat import ChatDialect
15
+
16
+ BASE_URL = "https://api.pinference.ai/api/v1"
17
+
18
+
19
+ def _body() -> dict:
20
+ return {
21
+ "model": "anthropic/claude-opus-4.8",
22
+ "messages": [
23
+ {"role": "system", "content": "stable instructions"},
24
+ {"role": "user", "content": "dynamic rollout turn"},
25
+ {"role": "assistant", "content": "working"},
26
+ {"role": "user", "content": "continue"},
27
+ ],
28
+ "tools": [
29
+ {
30
+ "type": "function",
31
+ "function": {"name": "read_file", "parameters": {}},
32
+ }
33
+ ],
34
+ }
35
+
36
+
37
+ def _response() -> SimpleNamespace:
38
+ return SimpleNamespace(
39
+ content=json.dumps(
40
+ {
41
+ "id": "chatcmpl_test",
42
+ "object": "chat.completion",
43
+ "created": 0,
44
+ "model": "anthropic/claude-opus-4.8",
45
+ "choices": [
46
+ {
47
+ "index": 0,
48
+ "message": {"role": "assistant", "content": "ok"},
49
+ "finish_reason": "stop",
50
+ }
51
+ ],
52
+ "usage": {
53
+ "prompt_tokens": 1,
54
+ "completion_tokens": 1,
55
+ "total_tokens": 2,
56
+ },
57
+ }
58
+ ).encode()
59
+ )
60
+
61
+
62
+ class _StreamResponse:
63
+ status_code = 200
64
+
65
+ def __init__(self) -> None:
66
+ self.headers = {"content-type": "text/event-stream"}
67
+
68
+ async def aiter_bytes(self):
69
+ yield b'data: {"choices":[{"delta":{"content":"ok"}}]}\n\n'
70
+
71
+ async def aclose(self):
72
+ return None
73
+
74
+
75
+ @pytest.mark.asyncio
76
+ async def test_get_response_sends_configured_retention() -> None:
77
+ client = EvalClient(EvalClientConfig(base_url=BASE_URL, cache_retention="long"))
78
+ client._request = AsyncMock(return_value=_response()) # type: ignore[method-assign]
79
+ body = _body()
80
+ original = deepcopy(body)
81
+
82
+ await client.get_response(ChatDialect(), body, SamplingConfig())
83
+
84
+ sent = client._request.await_args.args[1]
85
+ assert sent["tools"][-1]["cache_control"] == {
86
+ "type": "ephemeral",
87
+ "ttl": "1h",
88
+ }
89
+ assert sent["messages"][-1]["content"][0]["cache_control"]["ttl"] == "1h"
90
+ assert body == original
91
+
92
+
93
+ @pytest.mark.asyncio
94
+ async def test_relay_sends_configured_retention() -> None:
95
+ client = EvalClient(EvalClientConfig(base_url=BASE_URL, cache_retention="long"))
96
+ client._request = AsyncMock(return_value=_StreamResponse()) # type: ignore[method-assign]
97
+ body = _body()
98
+ original = deepcopy(body)
99
+
100
+ await client.relay(ChatDialect(), body, session_id="trace-1")
101
+
102
+ sent = client._request.await_args.args[1]
103
+ assert sent["tools"][-1]["cache_control"] == {
104
+ "type": "ephemeral",
105
+ "ttl": "1h",
106
+ }
107
+ assert body == original
108
+
109
+
110
+ @pytest.mark.asyncio
111
+ async def test_default_retention_sends_short_markers(
112
+ monkeypatch: pytest.MonkeyPatch,
113
+ ) -> None:
114
+ monkeypatch.delenv("PI_CACHE_RETENTION", raising=False)
115
+ client = EvalClient(EvalClientConfig(base_url=BASE_URL))
116
+ client._request = AsyncMock(return_value=_response()) # type: ignore[method-assign]
117
+ body = _body()
118
+
119
+ await client.get_response(ChatDialect(), body, SamplingConfig())
120
+
121
+ sent = client._request.await_args.args[1]
122
+ assert sent["tools"][-1]["cache_control"] == {"type": "ephemeral"}
123
+ assert "ttl" not in json.dumps(sent)
124
+
125
+
126
+ @pytest.mark.asyncio
127
+ async def test_none_retention_passes_through() -> None:
128
+ client = EvalClient(EvalClientConfig(base_url=BASE_URL, cache_retention="none"))
129
+ client._request = AsyncMock(return_value=_response()) # type: ignore[method-assign]
130
+ body = _body()
131
+ original = deepcopy(body)
132
+
133
+ await client.get_response(ChatDialect(), body, SamplingConfig())
134
+
135
+ sent = client._request.await_args.args[1]
136
+ assert "cache_control" not in json.dumps(sent)
137
+ assert body == original
138
+
139
+
140
+ @pytest.mark.asyncio
141
+ async def test_non_gateway_base_url_passes_through() -> None:
142
+ client = EvalClient(
143
+ EvalClientConfig(
144
+ base_url="https://api.anthropic.com/v1",
145
+ cache_retention="long",
146
+ )
147
+ )
148
+ client._request = AsyncMock(return_value=_response()) # type: ignore[method-assign]
149
+ body = _body()
150
+ original = deepcopy(body)
151
+
152
+ await client.get_response(ChatDialect(), body, SamplingConfig())
153
+
154
+ sent = client._request.await_args.args[1]
155
+ assert "cache_control" not in json.dumps(sent)
156
+ assert body == original
157
+
158
+
159
+ def test_config_rejects_unknown_retention() -> None:
160
+ with pytest.raises(ValidationError):
161
+ EvalClientConfig(cache_retention="forever") # type: ignore[arg-type]
@@ -0,0 +1,451 @@
1
+ """Gateway prompt-cache markers for Anthropic models on evaluator relays."""
2
+
3
+ from copy import deepcopy
4
+
5
+ import pytest
6
+
7
+ from verifiers.v1.clients.prompt_cache import maybe_add_prompt_cache_control
8
+ from verifiers.v1.dialects.anthropic import AnthropicDialect
9
+ from verifiers.v1.dialects.chat import ChatDialect
10
+ from verifiers.v1.dialects.responses import ResponsesDialect
11
+
12
+ CACHE = {"type": "ephemeral"}
13
+
14
+
15
+ def _anthropic_body(history: bool = False) -> dict:
16
+ messages = [
17
+ {
18
+ "role": "user",
19
+ "content": [{"type": "text", "text": "dynamic rollout turn"}],
20
+ }
21
+ ]
22
+ if history:
23
+ messages += [
24
+ {"role": "assistant", "content": "working"},
25
+ {"role": "user", "content": "continue"},
26
+ ]
27
+ return {
28
+ "model": "anthropic/claude-opus-4.8",
29
+ "system": [{"type": "text", "text": "stable instructions"}],
30
+ "tools": [
31
+ {
32
+ "name": "read_file",
33
+ "description": "Read a file",
34
+ "input_schema": {"type": "object", "properties": {}},
35
+ }
36
+ ],
37
+ "messages": messages,
38
+ }
39
+
40
+
41
+ def _chat_body(history: bool = False) -> dict:
42
+ messages = [
43
+ {"role": "system", "content": "stable instructions"},
44
+ {"role": "user", "content": "dynamic rollout turn"},
45
+ ]
46
+ if history:
47
+ messages += [
48
+ {"role": "assistant", "content": "working"},
49
+ {"role": "user", "content": "continue"},
50
+ ]
51
+ return {
52
+ "model": "anthropic/claude-opus-4.8",
53
+ "messages": messages,
54
+ "tools": [
55
+ {
56
+ "type": "function",
57
+ "function": {"name": "read_file", "parameters": {}},
58
+ }
59
+ ],
60
+ }
61
+
62
+
63
+ @pytest.mark.parametrize(
64
+ "base_url",
65
+ ["https://api.pinference.ai/api/v1", "https://openrouter.ai/api/v1"],
66
+ )
67
+ def test_anthropic_one_shot_marks_only_static_prefix(base_url: str) -> None:
68
+ body = _anthropic_body()
69
+ original = deepcopy(body)
70
+
71
+ updated = maybe_add_prompt_cache_control(AnthropicDialect(), body, base_url)
72
+
73
+ assert updated is not body
74
+ assert body == original
75
+ assert updated["tools"][-1]["cache_control"] == CACHE
76
+ assert updated["system"][-1]["cache_control"] == CACHE
77
+ assert "cache_control" not in updated["messages"][-1]["content"][0]
78
+ assert "ttl" not in str(updated)
79
+
80
+
81
+ def test_long_retention_emits_one_hour_ttl() -> None:
82
+ body = _anthropic_body()
83
+
84
+ updated = maybe_add_prompt_cache_control(
85
+ AnthropicDialect(),
86
+ body,
87
+ "https://api.pinference.ai/api/v1",
88
+ cache_retention="long",
89
+ )
90
+
91
+ assert updated["tools"][-1]["cache_control"] == {
92
+ "type": "ephemeral",
93
+ "ttl": "1h",
94
+ }
95
+ assert updated["system"][-1]["cache_control"] == {
96
+ "type": "ephemeral",
97
+ "ttl": "1h",
98
+ }
99
+
100
+
101
+ def test_none_retention_disables_markers() -> None:
102
+ body = _anthropic_body()
103
+ original = deepcopy(body)
104
+
105
+ updated = maybe_add_prompt_cache_control(
106
+ AnthropicDialect(),
107
+ body,
108
+ "https://api.pinference.ai/api/v1",
109
+ cache_retention="none",
110
+ )
111
+
112
+ assert updated is body
113
+ assert body == original
114
+
115
+
116
+ def test_env_long_retention_promotes_but_explicit_short_wins(
117
+ monkeypatch: pytest.MonkeyPatch,
118
+ ) -> None:
119
+ monkeypatch.setenv("PI_CACHE_RETENTION", "long")
120
+ body = _anthropic_body()
121
+
122
+ promoted = maybe_add_prompt_cache_control(
123
+ AnthropicDialect(), body, "https://api.pinference.ai/api/v1"
124
+ )
125
+ assert promoted["tools"][-1]["cache_control"]["ttl"] == "1h"
126
+
127
+ explicit = maybe_add_prompt_cache_control(
128
+ AnthropicDialect(),
129
+ body,
130
+ "https://api.pinference.ai/api/v1",
131
+ cache_retention="short",
132
+ )
133
+ assert explicit["tools"][-1]["cache_control"] == CACHE
134
+
135
+
136
+ def test_anthropic_multi_turn_marks_latest_message() -> None:
137
+ body = _anthropic_body(history=True)
138
+
139
+ updated = maybe_add_prompt_cache_control(
140
+ AnthropicDialect(), body, "https://api.pinference.ai/api/v1"
141
+ )
142
+
143
+ assert updated["messages"][-1]["content"][0]["cache_control"] == CACHE
144
+ assert "ttl" not in str(updated)
145
+
146
+
147
+ def test_chat_marks_static_prefix_and_latest_turn() -> None:
148
+ body = _chat_body(history=True)
149
+ original = deepcopy(body)
150
+
151
+ updated = maybe_add_prompt_cache_control(
152
+ ChatDialect(), body, "https://api.pinference.ai/api/v1"
153
+ )
154
+
155
+ assert body == original
156
+ assert updated["tools"][-1]["cache_control"] == CACHE
157
+ assert updated["messages"][0]["content"][0]["cache_control"] == CACHE
158
+ assert updated["messages"][-1]["content"][0]["cache_control"] == CACHE
159
+ assert "ttl" not in str(updated)
160
+
161
+
162
+ def test_anthropic_server_tool_falls_back_to_custom_tool() -> None:
163
+ body = _anthropic_body()
164
+ body["tools"] = [
165
+ {
166
+ "name": "read_file",
167
+ "description": "Read a file",
168
+ "input_schema": {"type": "object", "properties": {}},
169
+ },
170
+ {
171
+ "type": "web_search_20250305",
172
+ "name": "web_search",
173
+ "max_uses": 5,
174
+ },
175
+ ]
176
+
177
+ updated = maybe_add_prompt_cache_control(
178
+ AnthropicDialect(), body, "https://api.pinference.ai/api/v1"
179
+ )
180
+
181
+ assert updated["tools"][0]["cache_control"] == CACHE
182
+ assert "cache_control" not in updated["tools"][1]
183
+ assert updated["system"][-1]["cache_control"] == CACHE
184
+
185
+
186
+ def test_malformed_chat_roles_do_not_raise() -> None:
187
+ body = _chat_body(history=True)
188
+ body["messages"][0]["role"] = []
189
+ body["messages"][-1]["role"] = []
190
+
191
+ updated = maybe_add_prompt_cache_control(
192
+ ChatDialect(), body, "https://api.pinference.ai/api/v1"
193
+ )
194
+
195
+ assert isinstance(updated, dict)
196
+
197
+
198
+ def test_malformed_tool_type_does_not_raise() -> None:
199
+ body = _anthropic_body()
200
+ body["tools"] = [{"type": [], "name": "bad", "input_schema": {"type": "object"}}]
201
+
202
+ updated = maybe_add_prompt_cache_control(
203
+ AnthropicDialect(), body, "https://api.pinference.ai/api/v1"
204
+ )
205
+
206
+ assert "cache_control" not in updated["tools"][0]
207
+ assert updated["system"][-1]["cache_control"] == CACHE
208
+
209
+
210
+ def test_malformed_anthropic_block_types_do_not_raise() -> None:
211
+ body = _anthropic_body(history=True)
212
+ body["messages"][-1]["content"] = [{"type": [], "text": "bad"}]
213
+
214
+ updated = maybe_add_prompt_cache_control(
215
+ AnthropicDialect(), body, "https://api.pinference.ai/api/v1"
216
+ )
217
+
218
+ assert isinstance(updated, dict)
219
+
220
+
221
+ def _collect_markers(value):
222
+ if isinstance(value, dict):
223
+ marker = value.get("cache_control")
224
+ if isinstance(marker, dict) and marker.get("type") == "ephemeral":
225
+ yield marker
226
+ for item in value.values():
227
+ yield from _collect_markers(item)
228
+ elif isinstance(value, list):
229
+ for item in value:
230
+ yield from _collect_markers(item)
231
+
232
+
233
+ @pytest.mark.parametrize(
234
+ ("dialect", "body"),
235
+ [
236
+ (AnthropicDialect(), _anthropic_body(history=True)),
237
+ (ChatDialect(), _chat_body(history=True)),
238
+ ],
239
+ )
240
+ def test_long_retention_marks_every_generated_marker(dialect, body) -> None:
241
+ updated = maybe_add_prompt_cache_control(
242
+ dialect,
243
+ body,
244
+ "https://api.pinference.ai/api/v1",
245
+ cache_retention="long",
246
+ )
247
+
248
+ markers = list(_collect_markers(updated))
249
+ assert len(markers) == 3
250
+ assert all(marker == {"type": "ephemeral", "ttl": "1h"} for marker in markers)
251
+
252
+
253
+ @pytest.mark.parametrize(
254
+ ("dialect", "body"),
255
+ [
256
+ (AnthropicDialect(), _anthropic_body(history=True)),
257
+ (ChatDialect(), _chat_body(history=True)),
258
+ ],
259
+ )
260
+ def test_short_retention_marks_three_blocks_without_ttl(dialect, body) -> None:
261
+ updated = maybe_add_prompt_cache_control(
262
+ dialect, body, "https://api.pinference.ai/api/v1"
263
+ )
264
+
265
+ markers = list(_collect_markers(updated))
266
+ assert len(markers) == 3
267
+ assert all(marker == {"type": "ephemeral"} for marker in markers)
268
+
269
+
270
+ def test_tool_use_input_is_not_a_caller_marker() -> None:
271
+ body = _anthropic_body(history=True)
272
+ body["messages"] = [
273
+ {"role": "user", "content": "Find the bug."},
274
+ {
275
+ "role": "assistant",
276
+ "content": [
277
+ {
278
+ "type": "tool_use",
279
+ "id": "call_1",
280
+ "name": "lookup",
281
+ "input": {"cache_control": "data"},
282
+ }
283
+ ],
284
+ },
285
+ {
286
+ "role": "user",
287
+ "content": [
288
+ {
289
+ "type": "tool_result",
290
+ "tool_use_id": "call_1",
291
+ "content": "result",
292
+ }
293
+ ],
294
+ },
295
+ ]
296
+
297
+ updated = maybe_add_prompt_cache_control(
298
+ AnthropicDialect(), body, "https://api.pinference.ai/api/v1"
299
+ )
300
+
301
+ markers = list(_collect_markers(updated))
302
+ assert len(markers) == 3
303
+ assert any(
304
+ block.get("cache_control") == CACHE
305
+ for block in updated["messages"][-1]["content"]
306
+ )
307
+
308
+
309
+ def test_chat_assistant_prefill_is_not_marked() -> None:
310
+ body = _chat_body(history=True)
311
+ body["messages"].append({"role": "assistant", "content": "prefill"})
312
+
313
+ updated = maybe_add_prompt_cache_control(
314
+ ChatDialect(), body, "https://api.pinference.ai/api/v1"
315
+ )
316
+
317
+ assert updated["messages"][-1] == {"role": "assistant", "content": "prefill"}
318
+ assert "cache_control" not in updated["messages"][-1]
319
+
320
+
321
+ def test_env_retention_none_disables_and_whitespace_long_promotes(
322
+ monkeypatch: pytest.MonkeyPatch,
323
+ ) -> None:
324
+ body = _anthropic_body()
325
+ original = deepcopy(body)
326
+ monkeypatch.setenv("PI_CACHE_RETENTION", "none")
327
+
328
+ disabled = maybe_add_prompt_cache_control(
329
+ AnthropicDialect(), body, "https://api.pinference.ai/api/v1"
330
+ )
331
+ assert disabled is body
332
+ assert body == original
333
+
334
+ monkeypatch.setenv("PI_CACHE_RETENTION", " long ")
335
+ promoted = maybe_add_prompt_cache_control(
336
+ AnthropicDialect(), body, "https://api.pinference.ai/api/v1"
337
+ )
338
+ assert promoted["tools"][-1]["cache_control"]["ttl"] == "1h"
339
+
340
+
341
+ def test_nested_tool_result_marker_suppresses_injection() -> None:
342
+ body = _anthropic_body(history=True)
343
+ body["messages"][-1]["content"] = [
344
+ {
345
+ "type": "tool_result",
346
+ "content": [
347
+ {
348
+ "type": "text",
349
+ "text": "nested",
350
+ "cache_control": {"type": "ephemeral"},
351
+ }
352
+ ],
353
+ }
354
+ ]
355
+ original = deepcopy(body)
356
+
357
+ updated = maybe_add_prompt_cache_control(
358
+ AnthropicDialect(), body, "https://api.pinference.ai/api/v1"
359
+ )
360
+
361
+ assert updated is body
362
+ assert body == original
363
+
364
+
365
+ def test_input_schema_property_named_cache_control_is_not_a_marker() -> None:
366
+ body = _anthropic_body()
367
+ body["tools"] = [
368
+ {
369
+ "name": "lookup",
370
+ "input_schema": {
371
+ "type": "object",
372
+ "properties": {"cache_control": {"type": "string"}},
373
+ },
374
+ }
375
+ ]
376
+
377
+ updated = maybe_add_prompt_cache_control(
378
+ AnthropicDialect(), body, "https://api.pinference.ai/api/v1"
379
+ )
380
+
381
+ assert updated["tools"][-1]["cache_control"] == CACHE
382
+
383
+
384
+ def test_claude_substring_is_not_an_anthropic_model() -> None:
385
+ body = _chat_body()
386
+ body["model"] = "org/qwen-claude-distill"
387
+ original = deepcopy(body)
388
+
389
+ updated = maybe_add_prompt_cache_control(
390
+ ChatDialect(), body, "https://api.pinference.ai/api/v1"
391
+ )
392
+
393
+ assert updated is body
394
+ assert body == original
395
+
396
+
397
+ def test_caller_marker_and_ttl_are_left_alone() -> None:
398
+ body = _anthropic_body()
399
+ body["system"] = [
400
+ {
401
+ "type": "text",
402
+ "text": "stable instructions",
403
+ "cache_control": {"type": "ephemeral", "ttl": "1h"},
404
+ }
405
+ ]
406
+ original = deepcopy(body)
407
+
408
+ updated = maybe_add_prompt_cache_control(
409
+ AnthropicDialect(), body, "https://api.pinference.ai/api/v1"
410
+ )
411
+
412
+ assert updated is body
413
+ assert body == original
414
+ assert "cache_control" not in body["tools"][-1]
415
+
416
+
417
+ def test_non_gateway_host_is_untouched() -> None:
418
+ body = _anthropic_body()
419
+ original = deepcopy(body)
420
+
421
+ updated = maybe_add_prompt_cache_control(
422
+ AnthropicDialect(), body, "https://api.anthropic.com/v1"
423
+ )
424
+
425
+ assert updated is body
426
+ assert body == original
427
+
428
+
429
+ def test_non_anthropic_model_is_untouched() -> None:
430
+ body = _chat_body()
431
+ body["model"] = "openai/gpt-5-mini"
432
+ original = deepcopy(body)
433
+
434
+ updated = maybe_add_prompt_cache_control(
435
+ ChatDialect(), body, "https://api.pinference.ai/api/v1"
436
+ )
437
+
438
+ assert updated is body
439
+ assert body == original
440
+
441
+
442
+ def test_responses_requests_are_not_rewritten() -> None:
443
+ body = {"model": "anthropic/claude-opus-4.8", "input": "Find the bug."}
444
+ original = deepcopy(body)
445
+
446
+ updated = maybe_add_prompt_cache_control(
447
+ ResponsesDialect(), body, "https://api.pinference.ai/api/v1"
448
+ )
449
+
450
+ assert updated is body
451
+ assert body == original
@@ -9,6 +9,7 @@ from pydantic_core import from_json, to_json
9
9
 
10
10
  from verifiers.v1.clients.base import build_async_httpx, join_url
11
11
  from verifiers.v1.clients.client import SESSION_ID_HEADER, Client, RelayReply
12
+ from verifiers.v1.clients.prompt_cache import maybe_add_prompt_cache_control
12
13
  from verifiers.v1.configs.client import (
13
14
  BaseClientConfig,
14
15
  resolve_api_key,
@@ -70,6 +71,7 @@ class EvalClient(Client):
70
71
  def __init__(self, config: BaseClientConfig) -> None:
71
72
  self.base_url = config.base_url
72
73
  self.api_key = resolve_api_key(config)
74
+ self.cache_retention = getattr(config, "cache_retention", None)
73
75
  # Keep endpoint headers separate so they can override intercepted request headers before
74
76
  # the dialect's provider authentication is applied.
75
77
  self.headers = resolve_headers(config)
@@ -84,6 +86,9 @@ class EvalClient(Client):
84
86
  turn: PendingTurn | None = None,
85
87
  headers: Mapping[str, str] | None = None,
86
88
  ) -> Response:
89
+ body = maybe_add_prompt_cache_control(
90
+ dialect, body, self.base_url, self.cache_retention
91
+ )
87
92
  resp = await self._request(
88
93
  join_url(self.base_url, dialect.upstream_path),
89
94
  body,
@@ -177,6 +182,9 @@ class EvalClient(Client):
177
182
  ) -> RelayReply:
178
183
  # Relay complete SSE events so the interception server can safely insert keepalives
179
184
  # between them. Error responses are mapped before any event is handed back.
185
+ body = maybe_add_prompt_cache_control(
186
+ dialect, body, self.base_url, self.cache_retention
187
+ )
180
188
  resp = await self._request(
181
189
  join_url(self.base_url, dialect.upstream_path),
182
190
  body,