verifiers 0.3.2.dev92__tar.gz → 0.3.2.dev94__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 (215) hide show
  1. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/PKG-INFO +1 -1
  2. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/tests/v1/test_envs.py +1 -1
  3. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/clients/train.py +2 -3
  4. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/graph.py +97 -27
  5. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/interception/server.py +10 -6
  6. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/session.py +4 -4
  7. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/trace.py +5 -1
  8. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/.gitignore +0 -0
  9. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/LICENSE +0 -0
  10. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/README.md +0 -0
  11. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/pyproject.toml +0 -0
  12. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/tests/v1/conftest.py +0 -0
  13. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/tests/v1/fixtures/counter_tool_v1.py +0 -0
  14. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/tests/v1/fixtures/duet_v1.py +0 -0
  15. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/tests/v1/fixtures/echo_acp_resume_v1.py +0 -0
  16. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/tests/v1/fixtures/echo_agentic_v1.py +0 -0
  17. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/tests/v1/fixtures/echo_tool_v1.py +0 -0
  18. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/tests/v1/fixtures/echo_user_sim_v1.py +0 -0
  19. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/tests/v1/fixtures/echo_v1.py +0 -0
  20. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/tests/v1/fixtures/pyproject.toml +0 -0
  21. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/tests/v1/fixtures/tool_response_image_v1.py +0 -0
  22. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/tests/v1/test_color_codeword_tasks.py +0 -0
  23. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/tests/v1/test_configs.py +0 -0
  24. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/tests/v1/test_e2e.py +0 -0
  25. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/tests/v1/test_graph.py +0 -0
  26. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/tests/v1/test_judges.py +0 -0
  27. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/tests/v1/test_platform_run.py +0 -0
  28. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/tests/v1/test_scoring.py +0 -0
  29. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/tests/v1/test_taskset.py +0 -0
  30. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/tests/v1/test_trace.py +0 -0
  31. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/__init__.py +0 -0
  32. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/cli/commands/init.py +0 -0
  33. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/__init__.py +0 -0
  34. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/acp/__init__.py +0 -0
  35. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/acp/runner.py +0 -0
  36. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/agent.py +0 -0
  37. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/cli/__init__.py +0 -0
  38. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/cli/dashboard/__init__.py +0 -0
  39. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/cli/dashboard/base.py +0 -0
  40. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/cli/dashboard/eval.py +0 -0
  41. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/cli/dashboard/replay.py +0 -0
  42. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/cli/dashboard/validate.py +0 -0
  43. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/cli/debug.py +0 -0
  44. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/cli/eval/__init__.py +0 -0
  45. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/cli/eval/main.py +0 -0
  46. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/cli/eval/resume.py +0 -0
  47. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/cli/eval/runner.py +0 -0
  48. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/cli/gepa.py +0 -0
  49. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/cli/init.py +0 -0
  50. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/cli/output.py +0 -0
  51. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/cli/replay.py +0 -0
  52. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/cli/resolve.py +0 -0
  53. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/cli/resume.py +0 -0
  54. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/cli/validate.py +0 -0
  55. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/clients/__init__.py +0 -0
  56. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/clients/base.py +0 -0
  57. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/clients/client.py +0 -0
  58. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/clients/eval.py +0 -0
  59. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/configs/__init__.py +0 -0
  60. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/configs/agent.py +0 -0
  61. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/configs/cli/__init__.py +0 -0
  62. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/configs/cli/debug.py +0 -0
  63. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/configs/cli/env.py +0 -0
  64. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/configs/cli/eval.py +0 -0
  65. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/configs/cli/init.py +0 -0
  66. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/configs/cli/replay.py +0 -0
  67. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/configs/cli/validate.py +0 -0
  68. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/configs/client.py +0 -0
  69. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/configs/env.py +0 -0
  70. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/configs/harness.py +0 -0
  71. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/configs/judge.py +0 -0
  72. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/configs/retries.py +0 -0
  73. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/configs/runtime.py +0 -0
  74. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/configs/serve.py +0 -0
  75. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/configs/task.py +0 -0
  76. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/configs/taskset.py +0 -0
  77. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/dialects/__init__.py +0 -0
  78. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/dialects/anthropic.py +0 -0
  79. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/dialects/base.py +0 -0
  80. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/dialects/chat.py +0 -0
  81. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/dialects/responses.py +0 -0
  82. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/env.py +0 -0
  83. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/envs/__init__.py +0 -0
  84. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/envs/agentic_judge/__init__.py +0 -0
  85. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/envs/agentic_judge/env.py +0 -0
  86. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/envs/best_of_n/__init__.py +0 -0
  87. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/envs/best_of_n/env.py +0 -0
  88. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/envs/isolated_verifier/__init__.py +0 -0
  89. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/envs/isolated_verifier/env.py +0 -0
  90. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/envs/shared_agentic_judge/__init__.py +0 -0
  91. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/envs/single_agent/__init__.py +0 -0
  92. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/envs/single_agent/env.py +0 -0
  93. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/envs/user_sim/__init__.py +0 -0
  94. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/envs/user_sim/env.py +0 -0
  95. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/episode.py +0 -0
  96. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/errors.py +0 -0
  97. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/gepa/__init__.py +0 -0
  98. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/gepa/adapter.py +0 -0
  99. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/gepa/config.py +0 -0
  100. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/gepa/dataset.py +0 -0
  101. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/gepa/reflection.py +0 -0
  102. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/gepa/runner.py +0 -0
  103. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/harness.py +0 -0
  104. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/harnesses/__init__.py +0 -0
  105. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/harnesses/bash/__init__.py +0 -0
  106. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/harnesses/bash/harness.py +0 -0
  107. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/harnesses/browser_use/__init__.py +0 -0
  108. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/harnesses/browser_use/harness.py +0 -0
  109. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/harnesses/browser_use/program.py +0 -0
  110. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/harnesses/claude_code/__init__.py +0 -0
  111. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/harnesses/claude_code/harness.py +0 -0
  112. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/harnesses/codex/__init__.py +0 -0
  113. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/harnesses/codex/harness.py +0 -0
  114. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/harnesses/hermes_agent/__init__.py +0 -0
  115. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/harnesses/hermes_agent/harness.py +0 -0
  116. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/harnesses/hermes_agent/program.py +0 -0
  117. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/harnesses/kimi_code/__init__.py +0 -0
  118. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/harnesses/kimi_code/harness.py +0 -0
  119. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/harnesses/mini_swe_agent/__init__.py +0 -0
  120. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/harnesses/mini_swe_agent/harness.py +0 -0
  121. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/harnesses/mini_swe_agent/program.py +0 -0
  122. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/harnesses/node.py +0 -0
  123. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/harnesses/null/__init__.py +0 -0
  124. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/harnesses/null/harness.py +0 -0
  125. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/harnesses/openclaw/__init__.py +0 -0
  126. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/harnesses/openclaw/harness.py +0 -0
  127. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/harnesses/pi/__init__.py +0 -0
  128. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/harnesses/pi/harness.py +0 -0
  129. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/harnesses/prime_agent/__init__.py +0 -0
  130. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/harnesses/prime_agent/harness.py +0 -0
  131. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/harnesses/rlm/__init__.py +0 -0
  132. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/harnesses/rlm/harness.py +0 -0
  133. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/harnesses/terminus_2/__init__.py +0 -0
  134. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/harnesses/terminus_2/harness.py +0 -0
  135. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/harnesses/terminus_2/program.py +0 -0
  136. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/harnesses/utils/__init__.py +0 -0
  137. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/harnesses/utils/compaction.py +0 -0
  138. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/harnesses/utils/core.py +0 -0
  139. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/harnesses/utils/install.py +0 -0
  140. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/harnesses/utils/launch.py +0 -0
  141. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/harnesses/utils/mcp.py +0 -0
  142. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/interception/__init__.py +0 -0
  143. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/interception/base.py +0 -0
  144. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/interception/pool.py +0 -0
  145. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/interception/tool.py +0 -0
  146. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/interception/tunnel/__init__.py +0 -0
  147. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/interception/tunnel/base.py +0 -0
  148. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/interception/tunnel/custom.py +0 -0
  149. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/interception/tunnel/prime.py +0 -0
  150. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/judge.py +0 -0
  151. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/judges/__init__.py +0 -0
  152. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/judges/reference.py +0 -0
  153. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/judges/reference.txt +0 -0
  154. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/judges/rubric.py +0 -0
  155. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/judges/rubric.txt +0 -0
  156. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/mcp/__init__.py +0 -0
  157. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/mcp/launch.py +0 -0
  158. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/mcp/server.py +0 -0
  159. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/mcp/toolset.py +0 -0
  160. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/rollout.py +0 -0
  161. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/runtimes/__init__.py +0 -0
  162. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/runtimes/base.py +0 -0
  163. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/runtimes/docker/__init__.py +0 -0
  164. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/runtimes/docker/egress.py +0 -0
  165. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/runtimes/limiters.py +0 -0
  166. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/runtimes/modal.py +0 -0
  167. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/runtimes/prime.py +0 -0
  168. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/runtimes/subprocess.py +0 -0
  169. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/semantic.py +0 -0
  170. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/serve/__init__.py +0 -0
  171. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/serve/client.py +0 -0
  172. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/serve/encoding.py +0 -0
  173. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/serve/pool.py +0 -0
  174. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/serve/server.py +0 -0
  175. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/serve/types.py +0 -0
  176. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/state.py +0 -0
  177. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/task.py +0 -0
  178. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/taskset.py +0 -0
  179. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/tasksets/__init__.py +0 -0
  180. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/tasksets/harbor/__init__.py +0 -0
  181. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/tasksets/harbor/env.py +0 -0
  182. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/tasksets/harbor/taskset.py +0 -0
  183. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/tasksets/harbor/toolset.py +0 -0
  184. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/tasksets/lean/__init__.py +0 -0
  185. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/tasksets/lean/scoring.py +0 -0
  186. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/tasksets/lean/taskset.py +0 -0
  187. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/tasksets/nemo_gym/__init__.py +0 -0
  188. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/tasksets/nemo_gym/response.py +0 -0
  189. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/tasksets/nemo_gym/server.py +0 -0
  190. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/tasksets/nemo_gym/taskset.py +0 -0
  191. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/tasksets/nemo_gym/toolset.py +0 -0
  192. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/tasksets/openenv/__init__.py +0 -0
  193. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/tasksets/openenv/taskset.py +0 -0
  194. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/tasksets/textarena/__init__.py +0 -0
  195. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/tasksets/textarena/taskset.py +0 -0
  196. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/types.py +0 -0
  197. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/utils/__init__.py +0 -0
  198. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/utils/aio.py +0 -0
  199. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/utils/artifacts.py +0 -0
  200. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/utils/compile.py +0 -0
  201. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/utils/decorators.py +0 -0
  202. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/utils/format.py +0 -0
  203. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/utils/generic.py +0 -0
  204. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/utils/git.py +0 -0
  205. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/utils/image.py +0 -0
  206. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/utils/interrupt.py +0 -0
  207. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/utils/loaders.py +0 -0
  208. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/utils/logging.py +0 -0
  209. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/utils/memory.py +0 -0
  210. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/utils/paths.py +0 -0
  211. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/utils/platform.py +0 -0
  212. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/utils/prime.py +0 -0
  213. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/utils/retries.py +0 -0
  214. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/utils/score.py +0 -0
  215. {verifiers-0.3.2.dev92 → verifiers-0.3.2.dev94}/verifiers/v1/utils/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: verifiers
3
- Version: 0.3.2.dev92
3
+ Version: 0.3.2.dev94
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
@@ -69,7 +69,7 @@ def test_eval(taskset: str):
69
69
  "uv", "run", "--no-sync", "eval", taskset,
70
70
  *model,
71
71
  "-n", "1", "-r", "1", *caps,
72
- "--sampling.max-tokens", "512", "--no-rich",
72
+ "--sampling.max-tokens", "512", "--no-rich", "--no-push",
73
73
  ] # fmt: skip
74
74
  try:
75
75
  proc = subprocess.run(
@@ -16,7 +16,7 @@ from renderers.base import ToolCallParseStatus, is_multimodal
16
16
  from verifiers.v1.clients.base import build_async_openai
17
17
  from verifiers.v1.clients.client import SESSION_ID_HEADER, Client
18
18
  from verifiers.v1.configs.client import TrainClientConfig
19
- from verifiers.v1.dialects import FINISH_REASONS, ChatDialect, Dialect, parse_tools
19
+ from verifiers.v1.dialects import FINISH_REASONS, ChatDialect, Dialect
20
20
  from verifiers.v1.dialects.chat import message_to_wire
21
21
  from verifiers.v1.errors import ProviderError, model_error
22
22
  from verifiers.v1.graph import PendingTurn
@@ -343,10 +343,9 @@ class TrainClient(Client):
343
343
  f"{type(dialect).__name__}. Use the proxy client for this dialect, or add "
344
344
  f"renderer support for it."
345
345
  )
346
- # Intercepted turns already own the typed prompt, so only their tools need parsing here.
347
346
  if turn is not None:
348
347
  prompt = turn.prompt
349
- tools = parse_tools(body.get("tools"))
348
+ tools = turn.tools
350
349
  else:
351
350
  request = dialect.parse_request(body)
352
351
  prompt = request.messages
@@ -87,6 +87,8 @@ class MessageNode(BaseModel):
87
87
 
88
88
  parent: int | None = None
89
89
  """Index into `Trace.nodes` of the predecessor message; None for a root."""
90
+ tools: list[Tool] = Field(default_factory=list, exclude_if=lambda tools: not tools)
91
+ """Tools rendered into this branch's prompt. Populated only on root nodes."""
90
92
  semantic_parents: list[ParentLink] = Field(default_factory=list)
91
93
  """Additional harness-declared parents in the semantic execution graph.
92
94
 
@@ -277,9 +279,8 @@ def message_hash(message: Message) -> str:
277
279
  """Stable content hash on the fields that round-trip through a prompt — role, content
278
280
  (None and "" equal), assistant reasoning content when present, assistant tool calls,
279
281
  opaque continuation state, tool call id. Two messages hash equal iff they're the same
280
- conversational message, so a re-stated prefix message dedups to one node. The dedup key
281
- for sharing a prefix across turns/branches; salt-free so it is identical across processes
282
- and after deserialization."""
282
+ conversational message, so a re-stated prefix message dedups to one node. The message part
283
+ of the prefix key; salt-free so it is identical across processes and after deserialization."""
283
284
  digest = hashlib.blake2b(digest_size=16)
284
285
 
285
286
  def add(value: str) -> None:
@@ -350,21 +351,60 @@ def message_hash(message: Message) -> str:
350
351
  return digest.hexdigest()
351
352
 
352
353
 
353
- def _head_index(trace: Trace) -> dict[tuple[int | None, str], int]:
354
- """`(parent, msg_hash) -> node_id`, rebuilt lazily from `nodes` after deserialization."""
354
+ def _tools_hash(tools: list[Tool] | None) -> str:
355
+ payload = [tool.model_dump(mode="json", exclude_none=True) for tool in tools or []]
356
+ data = json.dumps(payload, sort_keys=True, separators=(",", ":")).encode()
357
+ return hashlib.blake2b(data, digest_size=16).hexdigest()
358
+
359
+
360
+ def _node_key(
361
+ parent: int | None, message: Message, tools: list[Tool] | None = None
362
+ ) -> tuple[int | None, str | None, str]:
363
+ return (
364
+ parent,
365
+ _tools_hash(tools) if parent is None else None,
366
+ message_hash(message),
367
+ )
368
+
369
+
370
+ def _head_index(trace: Trace) -> dict[tuple[int | None, str | None, str], int]:
371
+ """Physical node key -> id, rebuilt lazily after deserialization."""
355
372
  if not trace._head_index and trace.nodes:
356
373
  trace._head_index = {
357
- (node.parent, message_hash(node.message)): nid
374
+ _node_key(node.parent, node.message, node.tools): nid
358
375
  for nid, node in enumerate(trace.nodes)
359
376
  }
360
377
  return trace._head_index
361
378
 
362
379
 
380
+ def message_prefix_len(trace: Trace, prompt: list[Message]) -> int:
381
+ """Length of the longest message-only graph prefix matching `prompt`."""
382
+ children: dict[int | None, list[int]] = {}
383
+ for node_id, node in enumerate(trace.nodes):
384
+ children.setdefault(node.parent, []).append(node_id)
385
+
386
+ parents: list[int | None] = [None]
387
+ matched = 0
388
+ for message in prompt:
389
+ key = message_hash(message)
390
+ parents = [
391
+ node_id
392
+ for parent in parents
393
+ for node_id in children.get(parent, [])
394
+ if message_hash(trace.nodes[node_id].message) == key
395
+ ]
396
+ if not parents:
397
+ break
398
+ matched += 1
399
+ return matched
400
+
401
+
363
402
  def _matching_node(
364
403
  trace: Trace,
365
404
  parent: int | None,
366
405
  message: Message,
367
406
  token_ids: list[int] | None = None,
407
+ tools: list[Tool] | None = None,
368
408
  ) -> int | None:
369
409
  """Find an existing child, optionally requiring its exact physical token span.
370
410
 
@@ -372,7 +412,7 @@ def _matching_node(
372
412
  prefix breaks can leave older physical variants under the same key, so a token mismatch falls
373
413
  back to a reverse scan rather than materializing a duplicate of an already-existing variant.
374
414
  """
375
- key = (parent, message_hash(message))
415
+ key = _node_key(parent, message, tools)
376
416
  indexed = _head_index(trace).get(key)
377
417
  if indexed is not None and (
378
418
  token_ids is None or trace.nodes[indexed].token_ids == token_ids
@@ -387,7 +427,7 @@ def _matching_node(
387
427
  if (
388
428
  node.parent == parent
389
429
  and node.token_ids == token_ids
390
- and message_hash(node.message) == key[1]
430
+ and _node_key(node.parent, node.message, node.tools) == key
391
431
  ):
392
432
  return node_id
393
433
  return None
@@ -400,6 +440,7 @@ def _matching_prefix_node(
400
440
  prompt_ids: list[int],
401
441
  start: int,
402
442
  stop: int,
443
+ tools: list[Tool] | None = None,
403
444
  ) -> int | None:
404
445
  """Find the longest content-equivalent child matching inside `[start, stop]`.
405
446
 
@@ -408,7 +449,7 @@ def _matching_prefix_node(
408
449
  otherwise-missing message boundary. The next attributed message's start bounds the match:
409
450
  a sampled variant must never consume tokens that the renderer assigned to that message.
410
451
  """
411
- key = (parent, message_hash(message))
452
+ key = _node_key(parent, message, tools)
412
453
  indexed = _head_index(trace).get(key)
413
454
  candidates: list[int] = []
414
455
  if indexed is not None:
@@ -417,8 +458,12 @@ def _matching_prefix_node(
417
458
  node_id
418
459
  for node_id in range(len(trace.nodes) - 1, -1, -1)
419
460
  if node_id != indexed
420
- and trace.nodes[node_id].parent == parent
421
- and message_hash(trace.nodes[node_id].message) == key[1]
461
+ and _node_key(
462
+ trace.nodes[node_id].parent,
463
+ trace.nodes[node_id].message,
464
+ trace.nodes[node_id].tools,
465
+ )
466
+ == key
422
467
  )
423
468
  matches = [
424
469
  node_id
@@ -443,6 +488,7 @@ class PendingTurn:
443
488
 
444
489
  trace: Trace
445
490
  prompt: list[Message]
491
+ tools: list[Tool]
446
492
  prefix_node_ids: list[int]
447
493
  path_len: int
448
494
 
@@ -498,33 +544,43 @@ class PendingTurn:
498
544
  for span in tail_spans
499
545
  ]
500
546
 
501
- def commit(self, response: Response, tools: list[Tool] | None = None) -> int:
547
+ def commit(self, response: Response) -> int:
502
548
  """Add this turn to the graph; returns the committed assistant node's id."""
503
549
  assistant_id = _commit_turn(self, response)
504
- if tools:
505
- self.trace.tools = tools
550
+ self.trace.tools = self.tools
506
551
  return assistant_id
507
552
 
508
- def commit_prompt(self, tools: list[Tool] | None = None) -> None:
553
+ def commit_prompt(self) -> None:
509
554
  """Record an input that terminated before model inference."""
510
555
  parent = self.prefix_node_ids[-1] if self.prefix_node_ids else None
511
556
  index = _head_index(self.trace)
512
557
  for message in self.tail:
513
- existing = _matching_node(self.trace, parent, message)
558
+ existing = _matching_node(self.trace, parent, message, tools=self.tools)
514
559
  if existing is not None:
515
560
  parent = existing
516
561
  continue
517
562
  previous = parent
518
- self.trace.nodes.append(MessageNode(parent=parent, message=message))
563
+ self.trace.nodes.append(
564
+ MessageNode(
565
+ parent=parent,
566
+ message=message,
567
+ tools=self.tools if parent is None else [],
568
+ )
569
+ )
519
570
  parent = len(self.trace.nodes) - 1
520
- index[(previous, message_hash(message))] = parent
521
- if tools:
522
- self.trace.tools = tools
571
+ index[_node_key(previous, message, self.tools)] = parent
572
+ self.trace.tools = self.tools
523
573
 
524
574
 
525
- def prepare_turn(trace: Trace, prompt: list[Message]) -> PendingTurn:
526
- """Resolve `prompt` against the trace graph without mutating it."""
575
+ def prepare_turn(
576
+ trace: Trace,
577
+ prompt: list[Message],
578
+ tools: list[Tool] | None = None,
579
+ ) -> PendingTurn:
580
+ """Resolve a physical message-and-tools prefix without mutating the trace."""
527
581
  idx = _head_index(trace)
582
+ tools = list(tools or [])
583
+ tools_hash = _tools_hash(tools)
528
584
  parent: int | None = None
529
585
  path_len = 0
530
586
  prefix_node_ids: list[int] = []
@@ -537,15 +593,19 @@ def prepare_turn(trace: Trace, prompt: list[Message]) -> PendingTurn:
537
593
  ):
538
594
  children = [
539
595
  node_id
540
- for (node_parent, _), node_id in idx.items()
596
+ for (node_parent, node_tools_hash, _), node_id in idx.items()
541
597
  if node_parent == parent
598
+ and (parent is not None or node_tools_hash == tools_hash)
542
599
  ]
543
600
  # Repeated image URLs are cheaper to compare than to encode and hash again.
544
601
  # Only scan short, unambiguous parents; all other cases use the stable index.
545
602
  if len(children) == 1 and trace.nodes[children[0]].message == msg:
546
603
  existing = children[0]
547
604
  if existing is None:
548
- existing = idx.get((parent, message_hash(msg)))
605
+ message_key = message_hash(msg)
606
+ existing = idx.get(
607
+ (parent, tools_hash if parent is None else None, message_key)
608
+ )
549
609
  if existing is None:
550
610
  break
551
611
  prefix_node_ids.append(existing)
@@ -554,6 +614,7 @@ def prepare_turn(trace: Trace, prompt: list[Message]) -> PendingTurn:
554
614
  return PendingTurn(
555
615
  trace=trace,
556
616
  prompt=prompt,
617
+ tools=tools,
557
618
  prefix_node_ids=prefix_node_ids,
558
619
  path_len=path_len,
559
620
  )
@@ -718,7 +779,13 @@ def _commit_turn(turn: PendingTurn, response: Response) -> int:
718
779
  len(prompt_ids),
719
780
  )
720
781
  existing = _matching_prefix_node(
721
- trace, parent, prompt[i], prompt_ids, path_len, next_start
782
+ trace,
783
+ parent,
784
+ prompt[i],
785
+ prompt_ids,
786
+ path_len,
787
+ next_start,
788
+ turn.tools,
722
789
  )
723
790
  if existing is not None:
724
791
  end += len(trace.nodes[existing].token_ids)
@@ -729,6 +796,7 @@ def _commit_turn(turn: PendingTurn, response: Response) -> int:
729
796
  parent,
730
797
  prompt[i],
731
798
  node_tokens if tokens is not None else None,
799
+ turn.tools,
732
800
  )
733
801
  if existing is None:
734
802
  break
@@ -746,7 +814,7 @@ def _commit_turn(turn: PendingTurn, response: Response) -> int:
746
814
  if multi_modal_data is not None:
747
815
  mm_path = [(nid, prompt[i]) for i, nid in enumerate(prefix)]
748
816
  for i, msg in enumerate(prompt[num_reused:], start=num_reused):
749
- key = (parent, message_hash(msg))
817
+ key = _node_key(parent, msg, turn.tools)
750
818
  start = path_len if cursor is None else cursor
751
819
  span = spans[i] if spans and i < len(spans) else None
752
820
  end = span[1] if span else start
@@ -756,6 +824,7 @@ def _commit_turn(turn: PendingTurn, response: Response) -> int:
756
824
  # potentially huge token slices a second time.
757
825
  MessageNode.model_construct(
758
826
  parent=parent,
827
+ tools=turn.tools if parent is None else [],
759
828
  message=msg,
760
829
  token_ids=node_tokens,
761
830
  mask=[False] * len(node_tokens),
@@ -776,6 +845,7 @@ def _commit_turn(turn: PendingTurn, response: Response) -> int:
776
845
  trace.nodes.append(
777
846
  MessageNode.model_construct(
778
847
  parent=parent,
848
+ tools=turn.tools if parent is None else [],
779
849
  message=response.message,
780
850
  sampled=True,
781
851
  token_ids=[*gen_prompt, *comp_ids],
@@ -789,7 +859,7 @@ def _commit_turn(turn: PendingTurn, response: Response) -> int:
789
859
  )
790
860
  # Register the assistant so the next turn's prompt (which restates it) reuses this node.
791
861
  assistant_id = len(trace.nodes) - 1
792
- idx[(parent, message_hash(response.message))] = assistant_id
862
+ idx[_node_key(parent, response.message, turn.tools)] = assistant_id
793
863
  new_node_ids.append(assistant_id)
794
864
 
795
865
  # Attribute this turn's images onto the input nodes that introduced them (by content part).
@@ -638,8 +638,10 @@ class InterceptionServer(Interception):
638
638
  except BaseException:
639
639
  raise
640
640
  if stopped is not None:
641
- turn = graph.prepare_turn(session.trace, model_request.messages)
642
- turn.commit_prompt(model_request.tools)
641
+ turn = graph.prepare_turn(
642
+ session.trace, model_request.messages, model_request.tools
643
+ )
644
+ turn.commit_prompt()
643
645
  session.trace.stop(stopped)
644
646
  return web.json_response(
645
647
  dialect.error_body(f"rollout stopped: {stopped}"),
@@ -651,7 +653,9 @@ class InterceptionServer(Interception):
651
653
  # Restricted mediation can mutate the body without reporting policy paths.
652
654
  if request_rewrites or session.network_policy.network_restricted:
653
655
  model_request = dialect.parse_request(body)
654
- turn = graph.prepare_turn(session.trace, model_request.messages)
656
+ turn = graph.prepare_turn(
657
+ session.trace, model_request.messages, model_request.tools
658
+ )
655
659
  except ValueError as error:
656
660
  return web.json_response(dialect.error_body(str(error)), status=400)
657
661
  except RolloutError as error:
@@ -739,7 +743,7 @@ class InterceptionServer(Interception):
739
743
  ),
740
744
  status=400,
741
745
  )
742
- node = turn.commit(call_response, model_request.tools)
746
+ node = turn.commit(call_response)
743
747
  session.consume_prepared(turn.tail)
744
748
  session.trace.response_rewrites.extend(response_rewrites)
745
749
  if stopped is not None:
@@ -905,7 +909,7 @@ class InterceptionServer(Interception):
905
909
  ),
906
910
  status=409 if session.released else 400,
907
911
  )
908
- node = turn.commit(response, model_request.tools)
912
+ node = turn.commit(response)
909
913
  session.consume_prepared(turn.tail)
910
914
  session.trace.response_rewrites.extend(response_rewrites)
911
915
  if stopped is not None:
@@ -1013,7 +1017,7 @@ class InterceptionServer(Interception):
1013
1017
  raise parser_error
1014
1018
  response = parser.finish()
1015
1019
  if not session.released and not session.stopped:
1016
- node = turn.commit(response, model_request.tools)
1020
+ node = turn.commit(response)
1017
1021
  session.consume_prepared(turn.tail)
1018
1022
  logger.debug("intercept stream turn: id=%s", session.trace.id)
1019
1023
  elif session.stopped:
@@ -162,9 +162,9 @@ class RolloutSession:
162
162
  prepared: set[int] = set()
163
163
  candidates: set[int] = set()
164
164
  if self.request_interceptors:
165
- turn = graph.prepare_turn(self.trace, request.messages)
165
+ tail_start = graph.message_prefix_len(self.trace, request.messages)
166
166
  prepared_users = self.prepared_users.copy()
167
- for position in range(turn.tail_start, len(request.messages)):
167
+ for position in range(tail_start, len(request.messages)):
168
168
  message = request.messages[position]
169
169
  if isinstance(message, UserMessage):
170
170
  candidates.add(position)
@@ -350,7 +350,7 @@ class RolloutSession:
350
350
  request, records, stopped = await self.rewrite_request(
351
351
  Request(
352
352
  messages=[*branch.messages, *previous, message],
353
- tools=self.trace.tools or None,
353
+ tools=branch.tools or None,
354
354
  )
355
355
  )
356
356
  candidate = request.messages[-1]
@@ -358,7 +358,7 @@ class RolloutSession:
358
358
  self.trace.request_rewrites.extend(records)
359
359
  if stopped is not None:
360
360
  committed = request.messages if phase == "after" else request.messages[:-1]
361
- turn = graph.prepare_turn(self.trace, committed)
361
+ turn = graph.prepare_turn(self.trace, committed, request.tools)
362
362
  turn.commit_prompt()
363
363
  self.consume_prepared(turn.tail)
364
364
  self.trace.stop(stopped)
@@ -206,6 +206,10 @@ class Branch(BaseModel):
206
206
  def messages(self) -> Messages:
207
207
  return [n.message for n in self.nodes]
208
208
 
209
+ @property
210
+ def tools(self) -> list[Tool]:
211
+ return self.nodes[0].tools if self.nodes else []
212
+
209
213
  @property
210
214
  def token_ids(self) -> list[int]:
211
215
  """Training input IDs formed by concatenating node token spans."""
@@ -442,7 +446,7 @@ class Trace(BaseModel, Generic[DataT, StateT, AgentConfigT]):
442
446
  timing: Timing = Field(default_factory=Timing)
443
447
 
444
448
  _head_index: dict = PrivateAttr(default_factory=dict)
445
- """`(parent, msg_hash) -> node_id` for the graph builder."""
449
+ """Physical node key -> node id for the graph builder."""
446
450
 
447
451
  @field_serializer("mm_token_type_id_map")
448
452
  def serialize_mm_token_type_id_map(self, mapping: dict[int, int]) -> dict[str, int]:
File without changes