verifiers 0.4.1.dev27__tar.gz → 0.4.1.dev29__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.
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/PKG-INFO +1 -1
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/integration/test_agent_user.py +6 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/integration/test_agentic_judge.py +2 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/integration/test_examples.py +14 -9
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/integration/test_limits.py +2 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/integration/test_multi_agent.py +3 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/integration/test_multi_turn.py +1 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/integration/test_multi_turn_tool.py +2 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/integration/test_multimodal_tool.py +1 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/integration/test_network_policy.py +6 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/integration/test_resume_tool.py +1 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/integration/test_rubric_judge.py +1 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/integration/test_shared_tool.py +1 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/integration/test_single_turn.py +1 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/integration/test_skills.py +1 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/integration/test_stateful_tool.py +1 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/integration/test_user_echo.py +2 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/integration/test_user_sim.py +2 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/unit/test_graph.py +40 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/graph.py +23 -12
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harness.py +8 -1
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/bash/harness.py +14 -3
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/claude_code/harness.py +7 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/hermes_agent/harness.py +9 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/pi/harness.py +7 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/prime_agent/harness.py +7 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/rlm/harness.py +3 -0
- verifiers-0.4.1.dev29/verifiers/v1/harnesses/utils/replay.py +30 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/rollout.py +1 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/trace.py +3 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/.gitignore +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/LICENSE +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/README.md +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/pyproject.toml +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/integration/conftest.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/integration/fixtures/agent_user_harness_v1.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/integration/fixtures/agent_user_v1.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/integration/fixtures/counter_tool_v1.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/integration/fixtures/duet_v1.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/integration/fixtures/echo_acp_resume_v1.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/integration/fixtures/echo_agentic_v1.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/integration/fixtures/echo_tool_v1.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/integration/fixtures/echo_user_sim_v1.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/integration/fixtures/echo_v1.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/integration/fixtures/network_policy_v1.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/integration/fixtures/pyproject.toml +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/integration/fixtures/skill_codeword_v1.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/integration/fixtures/skills/project-codeword/SKILL.md +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/integration/fixtures/tool_response_image_v1.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/integration/grid.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/integration/test_compose_scoring.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/integration/test_sampling.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/unit/cli/test_replay.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/unit/conftest.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/unit/harnesses/test_core.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/unit/harnesses/test_rlm.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/unit/interception/test_tunnel.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/unit/judges/conftest.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/unit/judges/test_judge.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/unit/judges/test_reference.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/unit/judges/test_rubric.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/unit/runtimes/test_e2b.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/unit/serve/test_client.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/unit/serve/test_delta.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/unit/serve/test_pool.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/unit/test_agent.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/unit/test_dialects.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/unit/test_semantic.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/unit/test_task.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/unit/test_taskset.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/unit/test_trace.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/unit/utils/test_compile.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/unit/utils/test_loaders.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/unit/utils/test_platform.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/tests/unit/utils/test_score.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/acp/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/acp/runner.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/agent.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/cli/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/cli/dashboard/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/cli/dashboard/base.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/cli/dashboard/eval.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/cli/dashboard/replay.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/cli/dashboard/validate.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/cli/debug.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/cli/eval/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/cli/eval/hint.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/cli/eval/main.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/cli/eval/resume.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/cli/eval/runner.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/cli/gepa.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/cli/output.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/cli/replay.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/cli/resolve.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/cli/resume.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/cli/validate.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/clients/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/clients/base.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/clients/client.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/clients/eval.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/clients/train.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/configs/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/configs/agent.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/configs/cli/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/configs/cli/debug.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/configs/cli/env.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/configs/cli/eval.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/configs/cli/replay.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/configs/cli/validate.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/configs/client.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/configs/env.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/configs/harness.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/configs/judge.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/configs/retries.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/configs/runtime.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/configs/scoring.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/configs/select.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/configs/serve.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/configs/task.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/configs/taskset.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/dialects/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/dialects/anthropic.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/dialects/base.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/dialects/chat.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/dialects/responses.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/env.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/envs/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/envs/agentic_judge/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/envs/agentic_judge/env.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/envs/best_of_n/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/envs/best_of_n/env.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/envs/single_agent/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/envs/single_agent/env.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/envs/user_sim/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/envs/user_sim/env.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/episode.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/errors.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/gepa/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/gepa/adapter.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/gepa/config.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/gepa/dataset.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/gepa/reflection.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/gepa/runner.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/grading.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/bash/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/browser_use/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/browser_use/harness.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/browser_use/program.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/claude_code/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/codex/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/codex/gate.mjs +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/codex/harness.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/hermes_agent/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/hermes_agent/program.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/kimi_code/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/kimi_code/harness.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/mini_swe_agent/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/mini_swe_agent/harness.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/mini_swe_agent/program.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/node.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/null/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/null/harness.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/openclaw/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/openclaw/harness.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/opencode/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/opencode/harness.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/opencode/mcp-ready.js +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/pi/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/pi/gate.mjs +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/prime_agent/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/rlm/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/terminus_2/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/terminus_2/harness.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/terminus_2/program.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/utils/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/utils/compaction.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/utils/core.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/utils/install.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/utils/launch.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/utils/mcp.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/interception/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/interception/base.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/interception/pool.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/interception/server.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/interception/tunnel/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/interception/tunnel/base.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/interception/tunnel/custom.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/interception/tunnel/prime.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/judge.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/judges/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/judges/decision.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/judges/reference.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/judges/reference.txt +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/judges/rubric.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/judges/rubric.txt +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/mcp/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/mcp/launch.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/mcp/server.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/mcp/toolset.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/runtimes/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/runtimes/apptainer.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/runtimes/base.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/runtimes/compose.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/runtimes/container.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/runtimes/deployment.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/runtimes/docker/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/runtimes/docker/egress.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/runtimes/e2b.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/runtimes/limiters.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/runtimes/modal.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/runtimes/prime.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/runtimes/runtime.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/runtimes/strategies.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/runtimes/subprocess.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/semantic.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/serve/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/serve/client.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/serve/delta.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/serve/encoding.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/serve/pool.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/serve/server.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/serve/types.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/session.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/state.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/task.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/taskset.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/tasksets/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/tasksets/harbor/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/tasksets/harbor/taskset.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/tasksets/harbor/toolset.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/tasksets/nemo_gym/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/tasksets/nemo_gym/response.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/tasksets/nemo_gym/server.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/tasksets/nemo_gym/taskset.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/tasksets/nemo_gym/toolset.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/tasksets/openenv/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/tasksets/openenv/taskset.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/tasksets/textarena/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/tasksets/textarena/taskset.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/types.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/utils/__init__.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/utils/aio.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/utils/artifacts.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/utils/compile.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/utils/decorators.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/utils/env.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/utils/eval.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/utils/format.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/utils/generic.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/utils/git.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/utils/image.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/utils/interrupt.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/utils/loaders.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/utils/logging.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/utils/memory.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/utils/paths.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/utils/platform.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/utils/prime.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/utils/retries.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/utils/scope.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/utils/score.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/utils/trace_store.py +0 -0
- {verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/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.
|
|
3
|
+
Version: 0.4.1.dev29
|
|
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
|
|
@@ -52,5 +52,11 @@ async def test_agent_user_harness(run, harness, harness_runtime, scripted_model)
|
|
|
52
52
|
client=scripted_model,
|
|
53
53
|
)
|
|
54
54
|
(trace,) = episode.traces
|
|
55
|
+
# mini-swe-agent only acts through tool calls: it drops a tool-less reply (the
|
|
56
|
+
# scripted "OK") from its history and resends only its format error, so the
|
|
57
|
+
# trace forks by design. Recheck on version bumps:
|
|
58
|
+
# https://github.com/SWE-agent/mini-swe-agent/blob/v2.4.6/src/minisweagent/agents/default.py#L114
|
|
59
|
+
if harness.id != "mini-swe-agent":
|
|
60
|
+
assert trace.num_branches == 1
|
|
55
61
|
assert trace.ok, [error.message for error in trace.errors]
|
|
56
62
|
assert trace.num_turns >= 1
|
|
@@ -30,6 +30,7 @@ async def test_agentic_judge_shared_verifier(run, tmp_path):
|
|
|
30
30
|
rollout_timeout=600,
|
|
31
31
|
)
|
|
32
32
|
traces = episode.traces
|
|
33
|
+
assert all(trace.num_branches == 1 for trace in traces)
|
|
33
34
|
assert sorted(t.agent.name for t in traces) == ["judge", "solver"]
|
|
34
35
|
(solver,) = [t for t in traces if t.agent.name == "solver"]
|
|
35
36
|
(judge,) = [t for t in traces if t.agent.name == "judge"]
|
|
@@ -75,6 +76,7 @@ async def test_env_id_agentic_judge(run, tmp_path):
|
|
|
75
76
|
rollout_timeout=600,
|
|
76
77
|
)
|
|
77
78
|
traces = episode.traces
|
|
79
|
+
assert all(trace.num_branches == 1 for trace in traces)
|
|
78
80
|
assert sorted(t.agent.name for t in traces) == ["judge", "solver"]
|
|
79
81
|
(solver,) = [t for t in traces if t.agent.name == "solver"]
|
|
80
82
|
(judge,) = [t for t in traces if t.agent.name == "judge"]
|
|
@@ -14,6 +14,8 @@ import pytest
|
|
|
14
14
|
from conftest import CI_DECISION_JUDGE, CI_MODEL, CI_REASONING_EFFORT
|
|
15
15
|
from grid import agent_seats
|
|
16
16
|
|
|
17
|
+
import verifiers.v1 as vf
|
|
18
|
+
|
|
17
19
|
EVAL_TIMEOUT = 600 # 10 minutes for a capped eval (-n 1 -r 1)
|
|
18
20
|
|
|
19
21
|
ENVIRONMENTS = Path(__file__).parent.parent.parent / "environments"
|
|
@@ -107,26 +109,29 @@ def test_eval(taskset: str, tmp_path: Path):
|
|
|
107
109
|
f"eval {taskset} failed: {(proc.stderr or proc.stdout)[-2000:]}"
|
|
108
110
|
)
|
|
109
111
|
episodes = [
|
|
110
|
-
|
|
112
|
+
vf.WireEpisode.model_validate_json(line)
|
|
111
113
|
for line in (tmp_path / "run" / "traces.jsonl").read_text().splitlines()
|
|
112
114
|
]
|
|
113
|
-
assert episodes and all(episode
|
|
114
|
-
error
|
|
115
|
+
assert episodes and all(episode.ok for episode in episodes), [
|
|
116
|
+
error.message[:500]
|
|
115
117
|
for episode in episodes
|
|
116
|
-
for holder in (episode, *episode
|
|
117
|
-
for error in holder.
|
|
118
|
+
for holder in (episode, *episode.traces)
|
|
119
|
+
for error in holder.errors or []
|
|
118
120
|
]
|
|
121
|
+
assert all(
|
|
122
|
+
trace.num_branches == 1 for episode in episodes for trace in episode.traces
|
|
123
|
+
)
|
|
119
124
|
if taskset == "interception" and os.getenv("PRIME_API_KEY"):
|
|
120
125
|
assert len(episodes) == 4
|
|
121
126
|
judged = [
|
|
122
127
|
trace
|
|
123
128
|
for episode in episodes
|
|
124
|
-
for trace in episode
|
|
125
|
-
if trace
|
|
129
|
+
for trace in episode.traces
|
|
130
|
+
if trace.info.get("judge_calls")
|
|
126
131
|
]
|
|
127
132
|
assert len(judged) == 1
|
|
128
133
|
assert (
|
|
129
|
-
judged[0]
|
|
134
|
+
judged[0].info["judge_calls"][0]["request"]["model"]
|
|
130
135
|
== CI_DECISION_JUDGE["model"]
|
|
131
136
|
)
|
|
132
|
-
assert judged[0]
|
|
137
|
+
assert judged[0].response_rewrites
|
|
@@ -23,6 +23,7 @@ async def test_cap(run, cap, stop_condition):
|
|
|
23
23
|
env={"agent": cap},
|
|
24
24
|
)
|
|
25
25
|
(trace,) = episode.traces
|
|
26
|
+
assert trace.num_branches == 1
|
|
26
27
|
assert trace.stop_condition == stop_condition
|
|
27
28
|
assert trace.is_truncated
|
|
28
29
|
assert trace.num_turns == 1
|
|
@@ -50,5 +51,6 @@ async def test_max_turns(run, harness, harness_runtime):
|
|
|
50
51
|
max_tokens=8192,
|
|
51
52
|
)
|
|
52
53
|
(trace,) = episode.traces
|
|
54
|
+
assert trace.num_branches == 1
|
|
53
55
|
assert trace.stop_condition == "max_turns", [e.message for e in trace.errors]
|
|
54
56
|
assert trace.num_turns == 1
|
|
@@ -14,6 +14,7 @@ async def test_multi_agent_env(run):
|
|
|
14
14
|
max_turns=2,
|
|
15
15
|
)
|
|
16
16
|
traces = episode.traces
|
|
17
|
+
assert all(trace.num_branches == 1 for trace in traces)
|
|
17
18
|
assert len(traces) == 2 # one episode, one trace per role
|
|
18
19
|
assert sorted(t.agent.name for t in traces) == ["a", "b"]
|
|
19
20
|
(b,) = [t for t in traces if t.agent.name == "b"]
|
|
@@ -42,6 +43,7 @@ async def test_env_id_best_of_n(run):
|
|
|
42
43
|
max_turns=2,
|
|
43
44
|
)
|
|
44
45
|
traces = episode.traces
|
|
46
|
+
assert all(trace.num_branches == 1 for trace in traces)
|
|
45
47
|
assert len(traces) == 2 # one episode, two attempts
|
|
46
48
|
assert all(t.agent.name == "agent" and t.ok for t in traces)
|
|
47
49
|
assert any(t.metrics["best"] == 1.0 for t in traces)
|
|
@@ -62,6 +64,7 @@ async def test_kuhn_poker_self_play(run):
|
|
|
62
64
|
rollout_timeout=300,
|
|
63
65
|
)
|
|
64
66
|
traces = episode.traces
|
|
67
|
+
assert all(trace.num_branches == 1 for trace in traces)
|
|
65
68
|
assert sorted(t.agent.name for t in traces) == ["player0", "player1"]
|
|
66
69
|
payoffs = {t.agent.name: t.rewards["payoff"].score for t in traces}
|
|
67
70
|
assert payoffs["player0"] + payoffs["player1"] == 0 # zero-sum
|
|
@@ -25,6 +25,7 @@ async def test_multi_turn(run, harness, harness_runtime):
|
|
|
25
25
|
max_tokens=8192,
|
|
26
26
|
)
|
|
27
27
|
(trace,) = episode.traces
|
|
28
|
+
assert trace.num_branches == 1
|
|
28
29
|
assert trace.ok, [error.message for error in trace.errors]
|
|
29
30
|
assert trace.num_turns >= 1 # ran a command, then finished
|
|
30
31
|
assert trace.reward == 1.0
|
|
@@ -20,6 +20,7 @@ async def test_tool(run, harness, harness_runtime):
|
|
|
20
20
|
taskset_overrides={"task": {"tools": {"colocated": True}}},
|
|
21
21
|
)
|
|
22
22
|
(trace,) = episode.traces
|
|
23
|
+
assert trace.num_branches == 1
|
|
23
24
|
assert trace.ok, [error.message for error in trace.errors]
|
|
24
25
|
assert trace.num_turns >= 1
|
|
25
26
|
# Reward 1.0 is the proof the tool ran. How a program surfaces an MCP tool to the
|
|
@@ -41,6 +42,7 @@ async def test_tool_placement(run, harness_runtime, tool_runtime):
|
|
|
41
42
|
taskset_overrides={"task": {"tools": tool_runtime}},
|
|
42
43
|
)
|
|
43
44
|
(trace,) = episode.traces
|
|
45
|
+
assert trace.num_branches == 1
|
|
44
46
|
assert trace.ok, [error.message for error in trace.errors]
|
|
45
47
|
assert trace.num_turns >= 2 # tool call + answer
|
|
46
48
|
assert trace.reward == 1.0
|
|
@@ -54,6 +54,12 @@ async def test_network_policy(run, harness, harness_runtime, notice):
|
|
|
54
54
|
assert set(by_name) == set(NOTICES)
|
|
55
55
|
for name, episode in by_name.items():
|
|
56
56
|
[trace] = episode.traces
|
|
57
|
+
# mini-swe-agent only acts through tool calls: it drops a tool-less reply
|
|
58
|
+
# (the probe output as a final answer) from its history, so the trace forks
|
|
59
|
+
# by design. Recheck on version bumps:
|
|
60
|
+
# https://github.com/SWE-agent/mini-swe-agent/blob/v2.4.6/src/minisweagent/agents/default.py#L114
|
|
61
|
+
if harness.id != "mini-swe-agent":
|
|
62
|
+
assert trace.num_branches == 1
|
|
57
63
|
assert trace.ok, [error.message for error in trace.errors]
|
|
58
64
|
prompt = prompt_text(trace)
|
|
59
65
|
if notice and NOTICES[name]:
|
|
@@ -19,6 +19,7 @@ async def test_resume_tool(run, harness, harness_runtime):
|
|
|
19
19
|
rollout_timeout=600,
|
|
20
20
|
)
|
|
21
21
|
(trace,) = episode.traces
|
|
22
|
+
assert trace.num_branches == 1
|
|
22
23
|
assert trace.ok, [error.message for error in trace.errors]
|
|
23
24
|
assert trace.stop_condition == "user_closed"
|
|
24
25
|
assert trace.rewards["resumed"].score == 1.0
|
|
@@ -31,6 +31,7 @@ async def test_rubric_judge(run, tmp_path, judge_id):
|
|
|
31
31
|
max_turns=2,
|
|
32
32
|
)
|
|
33
33
|
(trace,) = episode.traces
|
|
34
|
+
assert trace.num_branches == 1
|
|
34
35
|
assert trace.ok
|
|
35
36
|
assert trace.rewards[judge_id].score > 0 # the judge's verdict landed in the reward
|
|
36
37
|
if judge_id == "rubric":
|
|
@@ -22,6 +22,7 @@ async def test_shared_tool(run, harness_runtime, tool_runtime):
|
|
|
22
22
|
taskset_overrides={"tools": tool_runtime},
|
|
23
23
|
)
|
|
24
24
|
traces = [trace for episode in episodes for trace in episode.traces]
|
|
25
|
+
assert all(trace.num_branches == 1 for trace in traces)
|
|
25
26
|
assert len(traces) == 2
|
|
26
27
|
for trace in traces:
|
|
27
28
|
assert trace.ok, [error.message for error in trace.errors]
|
|
@@ -23,6 +23,7 @@ async def test_single_turn(run, harness, harness_runtime):
|
|
|
23
23
|
max_turns=2,
|
|
24
24
|
)
|
|
25
25
|
(trace,) = episode.traces
|
|
26
|
+
assert trace.num_branches == 1
|
|
26
27
|
assert trace.ok, [error.message for error in trace.errors]
|
|
27
28
|
assert trace.num_turns == 1
|
|
28
29
|
assert trace.stop_condition == "agent_completed"
|
|
@@ -20,6 +20,7 @@ async def test_tool_state(run, harness_runtime, tool_runtime):
|
|
|
20
20
|
taskset_overrides={"task": {"tools": tool_runtime}},
|
|
21
21
|
)
|
|
22
22
|
(trace,) = episode.traces
|
|
23
|
+
assert trace.num_branches == 1
|
|
23
24
|
assert trace.ok, [error.message for error in trace.errors]
|
|
24
25
|
assert trace.num_turns >= 2 # at least two tool calls accumulated
|
|
25
26
|
assert trace.reward == 1.0
|
|
@@ -25,6 +25,7 @@ async def test_user(run, harness, harness_runtime):
|
|
|
25
25
|
max_tokens=8192,
|
|
26
26
|
)
|
|
27
27
|
(trace,) = episode.traces
|
|
28
|
+
assert trace.num_branches == 1
|
|
28
29
|
assert trace.ok, [error.message for error in trace.errors]
|
|
29
30
|
assert trace.num_turns >= 2 # genuinely multi-turn
|
|
30
31
|
assert trace.stop_condition == "user_closed" # leaving the interaction ended it
|
|
@@ -67,3 +68,4 @@ async def test_interaction(live_ctx):
|
|
|
67
68
|
assert trace is not None and trace.errors == []
|
|
68
69
|
assert trace.stop_condition == "user_closed" # closing the interaction ended it
|
|
69
70
|
assert trace.num_turns == 2
|
|
71
|
+
assert trace.num_branches == 1
|
|
@@ -17,6 +17,7 @@ async def test_env_id_user_sim(run):
|
|
|
17
17
|
rollout_timeout=300,
|
|
18
18
|
)
|
|
19
19
|
traces = episode.traces
|
|
20
|
+
assert all(trace.num_branches == 1 for trace in traces)
|
|
20
21
|
assert sorted(t.agent.name for t in traces) == ["assistant", "user"]
|
|
21
22
|
(assistant,) = [t for t in traces if t.agent.name == "assistant"]
|
|
22
23
|
(user,) = [t for t in traces if t.agent.name == "user"]
|
|
@@ -53,6 +54,7 @@ async def test_env_id_user_sim_with_tools(run):
|
|
|
53
54
|
rollout_timeout=300,
|
|
54
55
|
)
|
|
55
56
|
traces = episode.traces
|
|
57
|
+
assert all(trace.num_branches == 1 for trace in traces)
|
|
56
58
|
(assistant,) = [t for t in traces if t.agent.name == "assistant"]
|
|
57
59
|
(user,) = [t for t in traces if t.agent.name == "user"]
|
|
58
60
|
assert assistant.ok and user.ok
|
|
@@ -5,6 +5,7 @@ import pytest
|
|
|
5
5
|
|
|
6
6
|
import verifiers.v1 as vf
|
|
7
7
|
from verifiers.v1 import graph
|
|
8
|
+
from verifiers.v1.harnesses.utils.replay import drop_blank_text, strip_text
|
|
8
9
|
from verifiers.v1.types import TurnTokens
|
|
9
10
|
|
|
10
11
|
|
|
@@ -269,6 +270,45 @@ def test_reasoning_content_participates_in_graph_prefix_matching():
|
|
|
269
270
|
assert len(tool_call_nodes) == 2
|
|
270
271
|
|
|
271
272
|
|
|
273
|
+
@pytest.mark.parametrize(
|
|
274
|
+
"view,sampled,replayed",
|
|
275
|
+
[
|
|
276
|
+
(drop_blank_text, {"content": "\n\n"}, {"content": None}),
|
|
277
|
+
(
|
|
278
|
+
drop_blank_text,
|
|
279
|
+
{"content": "\n\n", "reasoning_content": "plan"},
|
|
280
|
+
{"reasoning_content": "plan"},
|
|
281
|
+
),
|
|
282
|
+
(strip_text, {"content": "Calling it.\n\n"}, {"content": "Calling it."}),
|
|
283
|
+
(strip_text, {"content": None}, {"reasoning_content": " "}),
|
|
284
|
+
],
|
|
285
|
+
)
|
|
286
|
+
def test_replay_view_matches_cosmetically_rewritten_turns(view, sampled, replayed):
|
|
287
|
+
def run(replay_view):
|
|
288
|
+
trace = vf.Trace(
|
|
289
|
+
agent=vf.AgentInfo(config=vf.AgentConfig()),
|
|
290
|
+
task=vf.TraceTask(type="Task", data=vf.TaskData(idx=0, prompt="go")),
|
|
291
|
+
)
|
|
292
|
+
trace._replay_view = replay_view
|
|
293
|
+
user = vf.UserMessage(content="go")
|
|
294
|
+
call = vf.ToolCall(id="call_0", name="lookup", arguments="{}")
|
|
295
|
+
graph.prepare_turn(trace, [user]).commit(
|
|
296
|
+
_response(vf.AssistantMessage(tool_calls=[call], **sampled))
|
|
297
|
+
)
|
|
298
|
+
graph.prepare_turn(
|
|
299
|
+
trace,
|
|
300
|
+
[
|
|
301
|
+
user,
|
|
302
|
+
vf.AssistantMessage(tool_calls=[call], **replayed),
|
|
303
|
+
vf.ToolMessage(content="result", tool_call_id="call_0"),
|
|
304
|
+
],
|
|
305
|
+
).commit(_response(vf.AssistantMessage(content="done")))
|
|
306
|
+
return trace
|
|
307
|
+
|
|
308
|
+
assert run(None).num_branches == 2
|
|
309
|
+
assert run(view).num_branches == 1
|
|
310
|
+
|
|
311
|
+
|
|
272
312
|
@pytest.mark.parametrize("same_tools", [False, True])
|
|
273
313
|
def test_parallel_commits_reconcile_shared_prompt_prefix(same_tools):
|
|
274
314
|
"""Two requests prepared from the same graph snapshot share any common prompt prefix that
|
|
@@ -321,13 +321,23 @@ def _tools_hash(tools: list[Tool] | None) -> str:
|
|
|
321
321
|
return hashlib.blake2b(data, digest_size=16).hexdigest()
|
|
322
322
|
|
|
323
323
|
|
|
324
|
+
def _message_key(trace: Trace, message: Message) -> str:
|
|
325
|
+
"""`message_hash` of the message as the trace's harness replays it, so a turn the
|
|
326
|
+
harness re-sends with cosmetic rewrites still matches the node it came from."""
|
|
327
|
+
view = trace._replay_view
|
|
328
|
+
return message_hash(view(message) if view is not None else message)
|
|
329
|
+
|
|
330
|
+
|
|
324
331
|
def _node_key(
|
|
325
|
-
|
|
332
|
+
trace: Trace,
|
|
333
|
+
parent: int | None,
|
|
334
|
+
message: Message,
|
|
335
|
+
tools: list[Tool] | None = None,
|
|
326
336
|
) -> tuple[int | None, str | None, str]:
|
|
327
337
|
return (
|
|
328
338
|
parent,
|
|
329
339
|
_tools_hash(tools) if parent is None else None,
|
|
330
|
-
|
|
340
|
+
_message_key(trace, message),
|
|
331
341
|
)
|
|
332
342
|
|
|
333
343
|
|
|
@@ -335,7 +345,7 @@ def _head_index(trace: Trace) -> dict[tuple[int | None, str | None, str], int]:
|
|
|
335
345
|
"""Physical node key -> id, rebuilt lazily after deserialization."""
|
|
336
346
|
if not trace._head_index and trace.nodes:
|
|
337
347
|
trace._head_index = {
|
|
338
|
-
_node_key(node.parent, node.message, node.tools): nid
|
|
348
|
+
_node_key(trace, node.parent, node.message, node.tools): nid
|
|
339
349
|
for nid, node in enumerate(trace.nodes)
|
|
340
350
|
}
|
|
341
351
|
return trace._head_index
|
|
@@ -350,12 +360,12 @@ def message_prefix_len(trace: Trace, prompt: list[Message]) -> int:
|
|
|
350
360
|
parents: list[int | None] = [None]
|
|
351
361
|
matched = 0
|
|
352
362
|
for message in prompt:
|
|
353
|
-
key =
|
|
363
|
+
key = _message_key(trace, message)
|
|
354
364
|
parents = [
|
|
355
365
|
node_id
|
|
356
366
|
for parent in parents
|
|
357
367
|
for node_id in children.get(parent, [])
|
|
358
|
-
if
|
|
368
|
+
if _message_key(trace, trace.nodes[node_id].message) == key
|
|
359
369
|
]
|
|
360
370
|
if not parents:
|
|
361
371
|
break
|
|
@@ -377,7 +387,7 @@ def _matching_node(
|
|
|
377
387
|
prefix breaks can leave older physical variants under the same key, so a token mismatch falls
|
|
378
388
|
back to a reverse scan rather than materializing a duplicate of an already-existing variant.
|
|
379
389
|
"""
|
|
380
|
-
key = _node_key(parent, message, tools)
|
|
390
|
+
key = _node_key(trace, parent, message, tools)
|
|
381
391
|
indexed = _head_index(trace).get(key)
|
|
382
392
|
if (
|
|
383
393
|
indexed is not None
|
|
@@ -397,7 +407,7 @@ def _matching_node(
|
|
|
397
407
|
if (
|
|
398
408
|
(token_ids is None or node.token_ids == token_ids)
|
|
399
409
|
and (renderer_token_ids is None or node.logical_ids == renderer_token_ids)
|
|
400
|
-
and _node_key(node.parent, node.message, node.tools) == key
|
|
410
|
+
and _node_key(trace, node.parent, node.message, node.tools) == key
|
|
401
411
|
):
|
|
402
412
|
return node_id
|
|
403
413
|
return None
|
|
@@ -422,7 +432,7 @@ def _matching_prefix_node(
|
|
|
422
432
|
otherwise-missing message boundary. The next attributed message's start bounds the match:
|
|
423
433
|
a sampled variant must never consume tokens that the renderer assigned to that message.
|
|
424
434
|
"""
|
|
425
|
-
key = _node_key(parent, message, tools)
|
|
435
|
+
key = _node_key(trace, parent, message, tools)
|
|
426
436
|
indexed = _head_index(trace).get(key)
|
|
427
437
|
candidates: list[int] = []
|
|
428
438
|
if indexed is not None:
|
|
@@ -432,6 +442,7 @@ def _matching_prefix_node(
|
|
|
432
442
|
for node_id in range(len(trace.nodes) - 1, -1, -1)
|
|
433
443
|
if node_id != indexed
|
|
434
444
|
and _node_key(
|
|
445
|
+
trace,
|
|
435
446
|
trace.nodes[node_id].parent,
|
|
436
447
|
trace.nodes[node_id].message,
|
|
437
448
|
trace.nodes[node_id].tools,
|
|
@@ -562,7 +573,7 @@ class PendingTurn:
|
|
|
562
573
|
)
|
|
563
574
|
)
|
|
564
575
|
parent = len(self.trace.nodes) - 1
|
|
565
|
-
index[_node_key(previous, message, self.tools)] = parent
|
|
576
|
+
index[_node_key(self.trace, previous, message, self.tools)] = parent
|
|
566
577
|
if self.tools:
|
|
567
578
|
self.trace.tools = list(
|
|
568
579
|
{
|
|
@@ -603,7 +614,7 @@ def prepare_turn(
|
|
|
603
614
|
if len(children) == 1 and trace.nodes[children[0]].message == msg:
|
|
604
615
|
existing = children[0]
|
|
605
616
|
if existing is None:
|
|
606
|
-
message_key =
|
|
617
|
+
message_key = _message_key(trace, msg)
|
|
607
618
|
existing = idx.get(
|
|
608
619
|
(parent, tools_hash if parent is None else None, message_key)
|
|
609
620
|
)
|
|
@@ -903,7 +914,7 @@ def _commit_turn(turn: PendingTurn, response: Response) -> int:
|
|
|
903
914
|
# Track new nodes separately so routed-expert attribution needs only node ids, not this path.
|
|
904
915
|
new_node_ids: list[int] = []
|
|
905
916
|
for i, msg in enumerate(prompt[num_reused:], start=num_reused):
|
|
906
|
-
key = _node_key(parent, msg, turn.tools)
|
|
917
|
+
key = _node_key(trace, parent, msg, turn.tools)
|
|
907
918
|
start = path_len if cursor is None else cursor
|
|
908
919
|
span = spans[i] if spans and i < len(spans) else None
|
|
909
920
|
end = span[1] if span else start
|
|
@@ -956,7 +967,7 @@ def _commit_turn(turn: PendingTurn, response: Response) -> int:
|
|
|
956
967
|
)
|
|
957
968
|
)
|
|
958
969
|
assistant_id = len(trace.nodes) - 1
|
|
959
|
-
idx[_node_key(parent, response.message, turn.tools)] = assistant_id
|
|
970
|
+
idx[_node_key(trace, parent, response.message, turn.tools)] = assistant_id
|
|
960
971
|
new_node_ids.append(assistant_id)
|
|
961
972
|
|
|
962
973
|
# Attribute this turn's expert-routing array onto the nodes created this turn (new input
|
|
@@ -18,7 +18,7 @@ from verifiers.v1.errors import (
|
|
|
18
18
|
)
|
|
19
19
|
from verifiers.v1.runtimes import ProgramResult, Runtime
|
|
20
20
|
from verifiers.v1.task import TaskData
|
|
21
|
-
from verifiers.v1.types import Messages
|
|
21
|
+
from verifiers.v1.types import Message, Messages
|
|
22
22
|
from verifiers.v1.utils.decorators import (
|
|
23
23
|
discover_decorated,
|
|
24
24
|
invoke_all,
|
|
@@ -67,6 +67,13 @@ class Harness(ABC, Generic[ConfigT]):
|
|
|
67
67
|
def __init__(self, config: ConfigT) -> None:
|
|
68
68
|
self.config = config
|
|
69
69
|
|
|
70
|
+
def replay_view(self, message: Message) -> Message:
|
|
71
|
+
"""`message` as this harness re-sends it on later requests. Override only for
|
|
72
|
+
deterministic cosmetic rewrites (e.g. dropped whitespace-only text), never for
|
|
73
|
+
content changes, so a replayed turn matches the turn it came from in the trace
|
|
74
|
+
graph instead of forking it."""
|
|
75
|
+
return message
|
|
76
|
+
|
|
70
77
|
def resolve_prompt(
|
|
71
78
|
self, task: TaskData
|
|
72
79
|
) -> tuple[str | None, str | Messages | None]:
|
|
@@ -79,9 +79,20 @@ class BashHarness(Harness[BashHarnessConfig]):
|
|
|
79
79
|
fragments.append(EDIT_SYSTEM_PROMPT)
|
|
80
80
|
if self.config.search:
|
|
81
81
|
fragments.append(SEARCH_PROMPT)
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
82
|
+
harness_prompt = " ".join(fragments)
|
|
83
|
+
system_prompt = "\n\n".join(p for p in (harness_prompt, system_prompt) if p)
|
|
84
|
+
# Default resume replays the transcript. If there was no task system
|
|
85
|
+
# prompt, that transcript already contains this harness prompt.
|
|
86
|
+
if (
|
|
87
|
+
data.system_prompt is None
|
|
88
|
+
and prompt is not None
|
|
89
|
+
and not isinstance(prompt, str)
|
|
90
|
+
and any(
|
|
91
|
+
message.role == "system" and message.content == harness_prompt
|
|
92
|
+
for message in prompt
|
|
93
|
+
)
|
|
94
|
+
):
|
|
95
|
+
system_prompt = None
|
|
85
96
|
env = {**self.config.resolved_env}
|
|
86
97
|
args = ["--bash"]
|
|
87
98
|
if tool_interception_url:
|
{verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/claude_code/harness.py
RENAMED
|
@@ -7,9 +7,11 @@ from verifiers.v1.clients import ModelContext
|
|
|
7
7
|
from verifiers.v1.configs.harness import HarnessConfig, PinnedVersion
|
|
8
8
|
from verifiers.v1.harnesses.node import NODE_BIN_DIR, ensure_node
|
|
9
9
|
from verifiers.v1.harnesses.utils.install import ensure_installed, remove_dir
|
|
10
|
+
from verifiers.v1.harnesses.utils.replay import drop_blank_text
|
|
10
11
|
from verifiers.v1.runtimes import Runtime
|
|
11
12
|
from verifiers.v1.task import TaskData
|
|
12
13
|
from verifiers.v1.trace import Trace
|
|
14
|
+
from verifiers.v1.types import Message
|
|
13
15
|
|
|
14
16
|
CLAUDE_ACP_DIR = "/var/tmp/vf-claude-agent-acp-{version}-{acp_version}"
|
|
15
17
|
PACKAGES_DIR = f"{CLAUDE_ACP_DIR}/packages"
|
|
@@ -40,6 +42,11 @@ class ClaudeCodeHarness(ACPHarness[ClaudeCodeHarnessConfig]):
|
|
|
40
42
|
SUPPORTS_SKILLS = True
|
|
41
43
|
SUPPORTS_TOOL_INTERCEPTION = True
|
|
42
44
|
|
|
45
|
+
# Claude Code drops whitespace-only text blocks on replay. Closed source: observed
|
|
46
|
+
# with 2.1.278, recheck on version bumps.
|
|
47
|
+
def replay_view(self, message: Message) -> Message:
|
|
48
|
+
return drop_blank_text(message)
|
|
49
|
+
|
|
43
50
|
async def setup(self, runtime: Runtime) -> None:
|
|
44
51
|
await ensure_node(runtime)
|
|
45
52
|
versions = {"version": self.config.version, "acp_version": ACP_VERSION}
|
{verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/hermes_agent/harness.py
RENAMED
|
@@ -7,10 +7,12 @@ from verifiers.v1.acp import ACPConfig, ACPHarness
|
|
|
7
7
|
from verifiers.v1.clients import ModelContext
|
|
8
8
|
from verifiers.v1.configs.harness import HarnessConfig, PinnedVersion
|
|
9
9
|
from verifiers.v1.harnesses.utils.install import ensure_installed, remove_dir
|
|
10
|
+
from verifiers.v1.harnesses.utils.replay import strip_text
|
|
10
11
|
from verifiers.v1.runtimes import Runtime
|
|
11
12
|
from verifiers.v1.runtimes.base import _ENSURE_UV
|
|
12
13
|
from verifiers.v1.task import TaskData
|
|
13
14
|
from verifiers.v1.trace import Trace
|
|
15
|
+
from verifiers.v1.types import Message
|
|
14
16
|
|
|
15
17
|
PROGRAM_SOURCE = (Path(__file__).resolve().parent / "program.py").read_text()
|
|
16
18
|
HERMES_DIR = "/var/tmp/vf-hermes-agent-{version}"
|
|
@@ -37,6 +39,13 @@ class HermesAgentHarness(ACPHarness[HermesAgentHarnessConfig]):
|
|
|
37
39
|
SUPPORTS_MCP = True
|
|
38
40
|
SUPPORTS_SKILLS = True
|
|
39
41
|
|
|
42
|
+
# Hermes stores assistant text stripped and pads missing reasoning with " " on
|
|
43
|
+
# replay. Recheck on version bumps:
|
|
44
|
+
# https://github.com/NousResearch/hermes-agent/blob/v2026.9.14/agent/chat_completion_helpers.py#L1444
|
|
45
|
+
# https://github.com/NousResearch/hermes-agent/blob/v2026.9.14/agent/message_sanitization.py#L545
|
|
46
|
+
def replay_view(self, message: Message) -> Message:
|
|
47
|
+
return strip_text(message)
|
|
48
|
+
|
|
40
49
|
async def setup(self, runtime: Runtime) -> None:
|
|
41
50
|
# Hermes needs its source-tree assets and supports editable installs only.
|
|
42
51
|
directory = HERMES_DIR.format(version=self.config.version)
|
|
@@ -11,9 +11,11 @@ from verifiers.v1.clients import ModelContext
|
|
|
11
11
|
from verifiers.v1.configs.harness import HarnessConfig, PinnedVersion, skill_destination
|
|
12
12
|
from verifiers.v1.harnesses.node import NODE_BIN_DIR, ensure_node
|
|
13
13
|
from verifiers.v1.harnesses.utils.install import ensure_installed, remove_dir
|
|
14
|
+
from verifiers.v1.harnesses.utils.replay import drop_blank_text
|
|
14
15
|
from verifiers.v1.runtimes import Runtime
|
|
15
16
|
from verifiers.v1.task import TaskData
|
|
16
17
|
from verifiers.v1.trace import Trace
|
|
18
|
+
from verifiers.v1.types import Message
|
|
17
19
|
|
|
18
20
|
logger = logging.getLogger(__name__)
|
|
19
21
|
|
|
@@ -65,6 +67,11 @@ class PiHarness(ACPHarness[PiHarnessConfig]):
|
|
|
65
67
|
# so the installed skills are passed explicitly via `--skill` at launch.
|
|
66
68
|
SUPPORTS_SKILLS = True
|
|
67
69
|
|
|
70
|
+
# pi-ai drops whitespace-only assistant text on replay. Recheck on version bumps:
|
|
71
|
+
# https://github.com/earendil-works/pi/blob/v0.86.1/packages/ai/src/api/openai-completions.ts#L1290
|
|
72
|
+
def replay_view(self, message: Message) -> Message:
|
|
73
|
+
return drop_blank_text(message)
|
|
74
|
+
|
|
68
75
|
async def setup(self, runtime: Runtime) -> None:
|
|
69
76
|
await ensure_node(runtime)
|
|
70
77
|
logger.info(
|
{verifiers-0.4.1.dev27 → verifiers-0.4.1.dev29}/verifiers/v1/harnesses/prime_agent/harness.py
RENAMED
|
@@ -10,9 +10,11 @@ from verifiers.v1.clients import ModelContext
|
|
|
10
10
|
from verifiers.v1.configs.harness import HarnessConfig, skill_destination
|
|
11
11
|
from verifiers.v1.harnesses.node import NODE_BIN_DIR, ensure_node
|
|
12
12
|
from verifiers.v1.harnesses.utils.install import ensure_installed, remove_dir
|
|
13
|
+
from verifiers.v1.harnesses.utils.replay import drop_blank_text
|
|
13
14
|
from verifiers.v1.runtimes import Runtime
|
|
14
15
|
from verifiers.v1.task import TaskData
|
|
15
16
|
from verifiers.v1.trace import Trace
|
|
17
|
+
from verifiers.v1.types import Message
|
|
16
18
|
|
|
17
19
|
logger = logging.getLogger(__name__)
|
|
18
20
|
|
|
@@ -99,6 +101,11 @@ class PrimeAgentHarness(ACPHarness[PrimeAgentHarnessConfig]):
|
|
|
99
101
|
SUPPORTS_MCP = True
|
|
100
102
|
SUPPORTS_SKILLS = True
|
|
101
103
|
|
|
104
|
+
# Its pi-ai fork drops whitespace-only assistant text on replay. Recheck on bumps:
|
|
105
|
+
# https://github.com/PrimeIntellect-ai/prime-agent/blob/v0.9.5/packages/ai/src/providers/openai-completions.ts#L895
|
|
106
|
+
def replay_view(self, message: Message) -> Message:
|
|
107
|
+
return drop_blank_text(message)
|
|
108
|
+
|
|
102
109
|
def acp_turn_result(self, trace: Trace, result: ACPTurn) -> None:
|
|
103
110
|
events = [
|
|
104
111
|
event
|
|
@@ -165,6 +165,9 @@ class RLMHarness(ACPHarness[RLMHarnessConfig]):
|
|
|
165
165
|
"{ apt-get update -qq && apt-get install -y -qq git; } && "
|
|
166
166
|
f"rm -rf {checkout} && git clone https://{RLM_REPO} {checkout} && "
|
|
167
167
|
f"git -C {checkout} checkout {shlex.quote(self.config.version)} && "
|
|
168
|
+
# The editable install needs the files, not the history; agents searching
|
|
169
|
+
# their workspace for a repository must not find the harness's own.
|
|
170
|
+
f"rm -rf {checkout}/.git && "
|
|
168
171
|
f"{home_env}UV_INSTALL_DIR={directory}/bin UV_TOOL_BIN_DIR={directory}/bin "
|
|
169
172
|
f"UV_TOOL_DIR={directory}/tools "
|
|
170
173
|
f"RLM_CHECKOUT_PATH={checkout} bash {checkout}/install.sh && "
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Cosmetic rewrites agent CLIs apply when they replay an earlier assistant turn, for
|
|
2
|
+
`Harness.replay_view` overrides. Each function is idempotent and only normalizes
|
|
3
|
+
whitespace, so the replayed and the sampled form of a turn map to the same view."""
|
|
4
|
+
|
|
5
|
+
from verifiers.v1.types import AssistantMessage, Message
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def drop_blank_text(message: Message) -> Message:
|
|
9
|
+
"""Clients that filter text blocks with `text.trim()` drop whitespace-only
|
|
10
|
+
assistant text, such as the "\\n\\n" some models emit before a tool call."""
|
|
11
|
+
if (
|
|
12
|
+
isinstance(message, AssistantMessage)
|
|
13
|
+
and isinstance(message.content, str)
|
|
14
|
+
and not message.content.strip()
|
|
15
|
+
):
|
|
16
|
+
return message.model_copy(update={"content": None})
|
|
17
|
+
return message
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def strip_text(message: Message) -> Message:
|
|
21
|
+
"""Clients that store assistant text stripped, and pad a missing reasoning field
|
|
22
|
+
with a single space on replay."""
|
|
23
|
+
if not isinstance(message, AssistantMessage):
|
|
24
|
+
return message
|
|
25
|
+
update: dict = {}
|
|
26
|
+
if isinstance(message.content, str):
|
|
27
|
+
update["content"] = message.content.strip() or None
|
|
28
|
+
if message.reasoning_content is not None and not message.reasoning_content.strip():
|
|
29
|
+
update["reasoning_content"] = None
|
|
30
|
+
return message.model_copy(update=update) if update else message
|
|
@@ -433,6 +433,9 @@ class Trace(BaseModel, Generic[DataT, StateT, AgentConfigT]):
|
|
|
433
433
|
|
|
434
434
|
_head_index: dict = PrivateAttr(default_factory=dict)
|
|
435
435
|
"""Physical node key -> node id for the graph builder."""
|
|
436
|
+
_replay_view: Any = PrivateAttr(default=None)
|
|
437
|
+
"""The harness's `replay_view`, applied to both sides when the graph builder matches
|
|
438
|
+
a replayed message against a stored node."""
|
|
436
439
|
_on_change: Any = PrivateAttr(default=None)
|
|
437
440
|
_pending: dict[int, list[Message]] = PrivateAttr(default_factory=dict)
|
|
438
441
|
"""The messages of the request in flight that no node holds yet (the harness's tool
|
|
File without changes
|
|
File without changes
|