verifiers 0.3.2.dev171__tar.gz → 0.3.2.dev185__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.3.2.dev171 → verifiers-0.3.2.dev185}/PKG-INFO +1 -1
- verifiers-0.3.2.dev185/tests/v1/fixtures/delivery_behaviors.py +196 -0
- verifiers-0.3.2.dev185/tests/v1/test_delivery_behaviors.py +288 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/__init__.py +4 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/acp/__init__.py +67 -9
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/acp/runner.py +58 -29
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/agent.py +276 -7
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/configs/client.py +4 -1
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/configs/judge.py +2 -2
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harness.py +6 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/interception/server.py +3 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/judges/__init__.py +3 -0
- verifiers-0.3.2.dev185/verifiers/v1/judges/decision.py +105 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/rollout.py +15 -1
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/session.py +2 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/.gitignore +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/LICENSE +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/README.md +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/pyproject.toml +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/conftest.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/fixtures/agent_user_v1.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/fixtures/counter_tool_v1.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/fixtures/duet_v1.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/fixtures/echo_acp_resume_v1.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/fixtures/echo_agentic_v1.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/fixtures/echo_tool_v1.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/fixtures/echo_user_sim_v1.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/fixtures/echo_v1.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/fixtures/pyproject.toml +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/fixtures/tool_response_image_v1.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/test_color_codeword_tasks.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/test_configs.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/test_e2e.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/test_envs.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/test_graph.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/test_judges.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/test_platform_run.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/test_scoring.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/test_serve_delta.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/test_taskset.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/test_trace.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/cli/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/cli/dashboard/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/cli/dashboard/base.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/cli/dashboard/eval.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/cli/dashboard/replay.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/cli/dashboard/validate.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/cli/debug.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/cli/eval/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/cli/eval/hint.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/cli/eval/main.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/cli/eval/resume.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/cli/eval/runner.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/cli/gepa.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/cli/init.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/cli/output.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/cli/replay.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/cli/resolve.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/cli/resume.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/cli/validate.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/clients/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/clients/base.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/clients/client.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/clients/eval.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/clients/train.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/configs/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/configs/agent.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/configs/cli/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/configs/cli/debug.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/configs/cli/env.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/configs/cli/eval.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/configs/cli/init.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/configs/cli/replay.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/configs/cli/validate.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/configs/env.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/configs/harness.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/configs/retries.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/configs/runtime.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/configs/select.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/configs/serve.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/configs/task.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/configs/taskset.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/dialects/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/dialects/anthropic.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/dialects/base.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/dialects/chat.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/dialects/responses.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/env.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/envs/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/envs/agentic_judge/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/envs/agentic_judge/env.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/envs/best_of_n/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/envs/best_of_n/env.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/envs/isolated_verifier/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/envs/isolated_verifier/env.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/envs/shared_agentic_judge/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/envs/single_agent/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/envs/single_agent/env.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/envs/user_sim/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/envs/user_sim/env.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/episode.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/errors.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/gepa/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/gepa/adapter.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/gepa/config.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/gepa/dataset.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/gepa/reflection.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/gepa/runner.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/graph.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/bash/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/bash/harness.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/browser_use/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/browser_use/harness.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/browser_use/program.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/claude_code/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/claude_code/harness.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/codex/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/codex/gate.mjs +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/codex/harness.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/hermes_agent/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/hermes_agent/harness.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/hermes_agent/program.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/kimi_code/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/kimi_code/harness.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/mini_swe_agent/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/mini_swe_agent/harness.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/mini_swe_agent/program.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/node.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/null/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/null/harness.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/openclaw/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/openclaw/harness.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/pi/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/pi/gate.mjs +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/pi/harness.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/prime_agent/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/prime_agent/harness.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/rlm/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/rlm/harness.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/terminus_2/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/terminus_2/harness.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/terminus_2/program.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/utils/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/utils/compaction.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/utils/core.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/utils/install.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/utils/launch.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/utils/mcp.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/interception/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/interception/base.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/interception/pool.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/interception/tunnel/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/interception/tunnel/base.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/interception/tunnel/custom.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/interception/tunnel/prime.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/judge.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/judges/reference.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/judges/reference.txt +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/judges/rubric.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/judges/rubric.txt +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/mcp/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/mcp/launch.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/mcp/server.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/mcp/toolset.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/runtimes/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/runtimes/apptainer.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/runtimes/base.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/runtimes/container.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/runtimes/docker/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/runtimes/docker/egress.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/runtimes/e2b.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/runtimes/limiters.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/runtimes/modal.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/runtimes/prime.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/runtimes/subprocess.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/semantic.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/serve/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/serve/client.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/serve/delta.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/serve/encoding.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/serve/pool.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/serve/server.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/serve/types.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/state.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/task.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/taskset.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/tasksets/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/tasksets/harbor/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/tasksets/harbor/compose.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/tasksets/harbor/env.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/tasksets/harbor/taskset.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/tasksets/harbor/toolset.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/tasksets/nemo_gym/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/tasksets/nemo_gym/response.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/tasksets/nemo_gym/server.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/tasksets/nemo_gym/taskset.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/tasksets/nemo_gym/toolset.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/tasksets/openenv/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/tasksets/openenv/taskset.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/tasksets/textarena/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/tasksets/textarena/taskset.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/trace.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/types.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/__init__.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/aio.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/artifacts.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/compile.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/decorators.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/format.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/generic.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/git.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/image.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/interrupt.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/loaders.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/logging.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/memory.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/paths.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/platform.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/prime.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/retries.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/scope.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/score.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/trace_store.py +0 -0
- {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/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.
|
|
3
|
+
Version: 0.3.2.dev185
|
|
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
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
"""Scripted inference and event log for the real-harness delivery examples."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import json
|
|
5
|
+
import time
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
|
|
8
|
+
from starlette.responses import JSONResponse, StreamingResponse
|
|
9
|
+
|
|
10
|
+
LABELS = ("MESSAGE_ALPHA", "MESSAGE_BETA")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def labels(value):
|
|
14
|
+
text = json.dumps(value)
|
|
15
|
+
return sorted((label for label in LABELS if label in text), key=text.index)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class Scenario:
|
|
20
|
+
harness: str
|
|
21
|
+
state: str
|
|
22
|
+
events: list = field(default_factory=list)
|
|
23
|
+
calls: list = field(default_factory=list)
|
|
24
|
+
model_entered: asyncio.Event = field(default_factory=asyncio.Event)
|
|
25
|
+
model_release: asyncio.Event = field(default_factory=asyncio.Event)
|
|
26
|
+
tool_entered: asyncio.Event = field(default_factory=asyncio.Event)
|
|
27
|
+
tool_release: asyncio.Event = field(default_factory=asyncio.Event)
|
|
28
|
+
response_sent: asyncio.Event = field(default_factory=asyncio.Event)
|
|
29
|
+
endpoint: str = ""
|
|
30
|
+
hold_call: int = 1
|
|
31
|
+
|
|
32
|
+
def record(self, event, **data):
|
|
33
|
+
self.events.append({"index": len(self.events), "event": event, **data})
|
|
34
|
+
|
|
35
|
+
async def barrier(self, request):
|
|
36
|
+
self.record("tool_started")
|
|
37
|
+
self.tool_entered.set()
|
|
38
|
+
await self.tool_release.wait()
|
|
39
|
+
self.record("tool_finished")
|
|
40
|
+
return JSONResponse({"result": "work finished"})
|
|
41
|
+
|
|
42
|
+
async def respond(self, request):
|
|
43
|
+
data = await request.json()
|
|
44
|
+
number = len(self.calls) + 1
|
|
45
|
+
visible = labels(data.get("messages", data.get("input", [])))
|
|
46
|
+
self.calls.append({"number": number, "messages": visible})
|
|
47
|
+
self.record("model_request", call=number, messages=visible)
|
|
48
|
+
if number == self.hold_call:
|
|
49
|
+
self.model_entered.set()
|
|
50
|
+
text = "Observed: " + (", ".join(visible) or "opening prompt") + "."
|
|
51
|
+
tool = None
|
|
52
|
+
if number == 1 and self.state in ("tool", "wait"):
|
|
53
|
+
if self.state == "wait":
|
|
54
|
+
tool = ("wait", {"timeout": 2})
|
|
55
|
+
elif self.harness == "rlm":
|
|
56
|
+
tool = (
|
|
57
|
+
"ipython",
|
|
58
|
+
{
|
|
59
|
+
"code": f"import urllib.request\nprint(urllib.request.urlopen('{self.endpoint}/barrier').read().decode())"
|
|
60
|
+
},
|
|
61
|
+
)
|
|
62
|
+
else:
|
|
63
|
+
tool = (
|
|
64
|
+
"Bash",
|
|
65
|
+
{
|
|
66
|
+
"command": f"curl -fsS {self.endpoint}/barrier",
|
|
67
|
+
"description": "Wait for the demonstration work to finish",
|
|
68
|
+
},
|
|
69
|
+
)
|
|
70
|
+
if self.harness == "rlm":
|
|
71
|
+
if number == self.hold_call and self.state in (
|
|
72
|
+
"model",
|
|
73
|
+
"idle",
|
|
74
|
+
"idle-burst",
|
|
75
|
+
):
|
|
76
|
+
await self.model_release.wait()
|
|
77
|
+
message = {"role": "assistant", "content": text}
|
|
78
|
+
if tool:
|
|
79
|
+
message = {
|
|
80
|
+
"role": "assistant",
|
|
81
|
+
"content": None,
|
|
82
|
+
"tool_calls": [
|
|
83
|
+
{
|
|
84
|
+
"id": f"tool_{number}",
|
|
85
|
+
"type": "function",
|
|
86
|
+
"function": {
|
|
87
|
+
"name": tool[0],
|
|
88
|
+
"arguments": json.dumps(tool[1]),
|
|
89
|
+
},
|
|
90
|
+
}
|
|
91
|
+
],
|
|
92
|
+
}
|
|
93
|
+
self.record("model_response", call=number, tool=tool[0] if tool else None)
|
|
94
|
+
if number == 1:
|
|
95
|
+
self.response_sent.set()
|
|
96
|
+
return JSONResponse(
|
|
97
|
+
{
|
|
98
|
+
"id": f"chatcmpl-{number}",
|
|
99
|
+
"object": "chat.completion",
|
|
100
|
+
"created": int(time.time()),
|
|
101
|
+
"model": data["model"],
|
|
102
|
+
"choices": [
|
|
103
|
+
{
|
|
104
|
+
"index": 0,
|
|
105
|
+
"message": message,
|
|
106
|
+
"finish_reason": "tool_calls" if tool else "stop",
|
|
107
|
+
}
|
|
108
|
+
],
|
|
109
|
+
"usage": {
|
|
110
|
+
"prompt_tokens": 100,
|
|
111
|
+
"completion_tokens": 10,
|
|
112
|
+
"total_tokens": 110,
|
|
113
|
+
},
|
|
114
|
+
}
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
async def stream():
|
|
118
|
+
def event(kind, **fields):
|
|
119
|
+
return (
|
|
120
|
+
"event: "
|
|
121
|
+
+ kind
|
|
122
|
+
+ "\ndata: "
|
|
123
|
+
+ json.dumps({"type": kind, **fields})
|
|
124
|
+
+ "\n\n"
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
yield event(
|
|
128
|
+
"message_start",
|
|
129
|
+
message={
|
|
130
|
+
"id": f"msg_{number}",
|
|
131
|
+
"type": "message",
|
|
132
|
+
"role": "assistant",
|
|
133
|
+
"model": data["model"],
|
|
134
|
+
"content": [],
|
|
135
|
+
"stop_reason": None,
|
|
136
|
+
"stop_sequence": None,
|
|
137
|
+
"usage": {"input_tokens": 100, "output_tokens": 0},
|
|
138
|
+
},
|
|
139
|
+
)
|
|
140
|
+
try:
|
|
141
|
+
if number == self.hold_call and self.state in (
|
|
142
|
+
"model",
|
|
143
|
+
"idle",
|
|
144
|
+
"idle-burst",
|
|
145
|
+
):
|
|
146
|
+
await self.model_release.wait()
|
|
147
|
+
if tool:
|
|
148
|
+
yield event(
|
|
149
|
+
"content_block_start",
|
|
150
|
+
index=0,
|
|
151
|
+
content_block={
|
|
152
|
+
"type": "tool_use",
|
|
153
|
+
"id": f"tool_{number}",
|
|
154
|
+
"name": tool[0],
|
|
155
|
+
"input": {},
|
|
156
|
+
},
|
|
157
|
+
)
|
|
158
|
+
yield event(
|
|
159
|
+
"content_block_delta",
|
|
160
|
+
index=0,
|
|
161
|
+
delta={
|
|
162
|
+
"type": "input_json_delta",
|
|
163
|
+
"partial_json": json.dumps(tool[1]),
|
|
164
|
+
},
|
|
165
|
+
)
|
|
166
|
+
else:
|
|
167
|
+
yield event(
|
|
168
|
+
"content_block_start",
|
|
169
|
+
index=0,
|
|
170
|
+
content_block={"type": "text", "text": ""},
|
|
171
|
+
)
|
|
172
|
+
yield event(
|
|
173
|
+
"content_block_delta",
|
|
174
|
+
index=0,
|
|
175
|
+
delta={"type": "text_delta", "text": text},
|
|
176
|
+
)
|
|
177
|
+
yield event("content_block_stop", index=0)
|
|
178
|
+
yield event(
|
|
179
|
+
"message_delta",
|
|
180
|
+
delta={
|
|
181
|
+
"stop_reason": "tool_use" if tool else "end_turn",
|
|
182
|
+
"stop_sequence": None,
|
|
183
|
+
},
|
|
184
|
+
usage={"output_tokens": 10},
|
|
185
|
+
)
|
|
186
|
+
yield event("message_stop")
|
|
187
|
+
self.record(
|
|
188
|
+
"model_response", call=number, tool=tool[0] if tool else None
|
|
189
|
+
)
|
|
190
|
+
if number == 1:
|
|
191
|
+
self.response_sent.set()
|
|
192
|
+
except asyncio.CancelledError:
|
|
193
|
+
self.record("model_stream_cancelled", call=number)
|
|
194
|
+
raise
|
|
195
|
+
|
|
196
|
+
return StreamingResponse(stream(), media_type="text/event-stream")
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
"""Executable delivery examples using real harness processes and scripted inference.
|
|
2
|
+
|
|
3
|
+
Put `rlm`, `claude-agent-acp`, `claude`, and `node` on PATH, then run:
|
|
4
|
+
VF_DELIVERY_BEHAVIORS=1 uv run pytest tests/v1/test_delivery_behaviors.py -v -s
|
|
5
|
+
|
|
6
|
+
For nano-rlm alone, add `-k rlm`; neither subset needs PRIME_API_KEY.
|
|
7
|
+
VF_DELIVERY_OUTPUT optionally selects a directory for timeline.json and normal
|
|
8
|
+
Verifiers traces.jsonl files (one run per case). No model API key is required.
|
|
9
|
+
`idle` sends A after the opening turn finishes, then B during A's resumed work.
|
|
10
|
+
`idle-burst` submits A and B together after the opening turn finishes.
|
|
11
|
+
Other cases submit A and B during the same held model/tool operation. `queue`
|
|
12
|
+
means an ordinary user message deferred until the current turn yields.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import asyncio
|
|
16
|
+
import json
|
|
17
|
+
import os
|
|
18
|
+
import shutil
|
|
19
|
+
import socket
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
|
|
22
|
+
import pytest
|
|
23
|
+
import uvicorn
|
|
24
|
+
from delivery_behaviors import LABELS, Scenario, labels
|
|
25
|
+
from starlette.applications import Starlette
|
|
26
|
+
from starlette.routing import Route
|
|
27
|
+
|
|
28
|
+
import verifiers.v1 as vf
|
|
29
|
+
from verifiers.v1.acp import ACPHarness
|
|
30
|
+
from verifiers.v1.episode import Episode, EvalRunInfo
|
|
31
|
+
from verifiers.v1.harnesses.claude_code.harness import (
|
|
32
|
+
ClaudeCodeHarness,
|
|
33
|
+
ClaudeCodeHarnessConfig,
|
|
34
|
+
)
|
|
35
|
+
from verifiers.v1.harnesses.rlm.harness import RLMHarness, RLMHarnessConfig
|
|
36
|
+
|
|
37
|
+
pytestmark = [
|
|
38
|
+
pytest.mark.asyncio,
|
|
39
|
+
pytest.mark.skipif(
|
|
40
|
+
os.environ.get("VF_DELIVERY_BEHAVIORS") != "1",
|
|
41
|
+
reason="opt-in real-harness behavioral examples",
|
|
42
|
+
),
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@pytest.mark.parametrize("kind", ["rlm", "claude-code"])
|
|
47
|
+
@pytest.mark.parametrize("state", ["idle-burst", "idle", "model", "tool", "wait"])
|
|
48
|
+
@pytest.mark.parametrize(
|
|
49
|
+
"modes",
|
|
50
|
+
[("steer", "steer"), ("steer", "queue"), ("queue", "steer"), ("queue", "queue")],
|
|
51
|
+
ids=["steer-steer", "steer-message", "message-steer", "message-message"],
|
|
52
|
+
)
|
|
53
|
+
async def test_delivery_behavior(kind, state, modes, tmp_path, monkeypatch):
|
|
54
|
+
if state == "wait" and kind != "rlm":
|
|
55
|
+
pytest.skip("wait is a nano-rlm native tool")
|
|
56
|
+
commands = ["rlm"] if kind == "rlm" else ["node", "claude-agent-acp", "claude"]
|
|
57
|
+
bins = {command: shutil.which(command) for command in commands}
|
|
58
|
+
if not all(bins.values()):
|
|
59
|
+
pytest.fail(f"install these harness executables first: {commands}")
|
|
60
|
+
scenario = Scenario(kind, state, hold_call=2 if state.startswith("idle") else 1)
|
|
61
|
+
sock = socket.socket()
|
|
62
|
+
sock.bind(("127.0.0.1", 0))
|
|
63
|
+
scenario.endpoint = f"http://127.0.0.1:{sock.getsockname()[1]}"
|
|
64
|
+
server = uvicorn.Server(
|
|
65
|
+
uvicorn.Config(
|
|
66
|
+
Starlette(
|
|
67
|
+
routes=[
|
|
68
|
+
Route("/v1/chat/completions", scenario.respond, methods=["POST"]),
|
|
69
|
+
Route("/v1/messages", scenario.respond, methods=["POST"]),
|
|
70
|
+
Route("/barrier", scenario.barrier),
|
|
71
|
+
]
|
|
72
|
+
),
|
|
73
|
+
log_level="error",
|
|
74
|
+
)
|
|
75
|
+
)
|
|
76
|
+
serving = asyncio.create_task(server.serve(sockets=[sock]))
|
|
77
|
+
monkeypatch.setenv("DELIVERY_SCRIPTED_KEY", "local-scripted")
|
|
78
|
+
base = RLMHarness if kind == "rlm" else ClaudeCodeHarness
|
|
79
|
+
|
|
80
|
+
class LocalHarness(base):
|
|
81
|
+
NEEDS_CONTAINER = False
|
|
82
|
+
|
|
83
|
+
async def setup(self, runtime):
|
|
84
|
+
await ACPHarness.setup(self, runtime)
|
|
85
|
+
|
|
86
|
+
async def prepare_acp(self, *args, **kwargs):
|
|
87
|
+
config = await super().prepare_acp(*args, **kwargs)
|
|
88
|
+
if kind == "rlm":
|
|
89
|
+
config.command = [bins["rlm"], "--acp"]
|
|
90
|
+
else:
|
|
91
|
+
config.command = [bins["node"], bins["claude-agent-acp"]]
|
|
92
|
+
config.env["CLAUDE_CODE_EXECUTABLE"] = bins["claude"]
|
|
93
|
+
return config
|
|
94
|
+
|
|
95
|
+
config = (
|
|
96
|
+
RLMHarnessConfig(id="rlm", builtin_skills=[], max_depth=0, compaction=False)
|
|
97
|
+
if kind == "rlm"
|
|
98
|
+
else ClaudeCodeHarnessConfig(id="claude-code")
|
|
99
|
+
)
|
|
100
|
+
agent = vf.make_agent(
|
|
101
|
+
vf.AgentConfig(
|
|
102
|
+
harness=config,
|
|
103
|
+
model="scripted-delivery" if kind == "rlm" else "claude-sonnet-4-6",
|
|
104
|
+
client={
|
|
105
|
+
"type": "eval",
|
|
106
|
+
"base_url": scenario.endpoint + "/v1",
|
|
107
|
+
"api_key_var": "DELIVERY_SCRIPTED_KEY",
|
|
108
|
+
},
|
|
109
|
+
runtime={"type": "subprocess"},
|
|
110
|
+
max_turns=8,
|
|
111
|
+
timeout={"rollout": 45},
|
|
112
|
+
)
|
|
113
|
+
)
|
|
114
|
+
agent.harness = LocalHarness(config)
|
|
115
|
+
name = f"delivery-{kind}-{state}-{'-'.join(modes)}"
|
|
116
|
+
task = vf.Task(
|
|
117
|
+
vf.TaskData(
|
|
118
|
+
idx=0,
|
|
119
|
+
name=name,
|
|
120
|
+
prompt="Report which incoming messages you have received.",
|
|
121
|
+
system_prompt="Behavioral demonstration with scripted inference. Each reply states which messages are visible in that model request.",
|
|
122
|
+
)
|
|
123
|
+
)
|
|
124
|
+
sends = []
|
|
125
|
+
opening = None
|
|
126
|
+
try:
|
|
127
|
+
async with asyncio.timeout(50), agent, agent.interaction(task) as interaction:
|
|
128
|
+
real_step = interaction._run.step
|
|
129
|
+
|
|
130
|
+
async def step(messages=None, **kwargs):
|
|
131
|
+
scenario.record(
|
|
132
|
+
"turn_started",
|
|
133
|
+
messages=labels(
|
|
134
|
+
[m.model_dump() for m in messages] if messages else []
|
|
135
|
+
),
|
|
136
|
+
)
|
|
137
|
+
result = await real_step(messages, **kwargs)
|
|
138
|
+
scenario.record("turn_yielded")
|
|
139
|
+
return result
|
|
140
|
+
|
|
141
|
+
interaction._run.step = step
|
|
142
|
+
|
|
143
|
+
async def send(index):
|
|
144
|
+
scenario.record("submitted", message=LABELS[index], mode=modes[index])
|
|
145
|
+
receipt = await interaction.send(
|
|
146
|
+
LABELS[index], mode=modes[index], message_id=LABELS[index]
|
|
147
|
+
)
|
|
148
|
+
scenario.record("receipt", message=LABELS[index], **receipt)
|
|
149
|
+
return receipt
|
|
150
|
+
|
|
151
|
+
opening = asyncio.create_task(interaction.turn())
|
|
152
|
+
if state == "idle-burst":
|
|
153
|
+
await opening
|
|
154
|
+
scenario.record("agent_idle")
|
|
155
|
+
sends.extend(asyncio.create_task(send(i)) for i in range(2))
|
|
156
|
+
await scenario.model_entered.wait()
|
|
157
|
+
elif state == "idle":
|
|
158
|
+
await opening
|
|
159
|
+
scenario.record("agent_idle")
|
|
160
|
+
sends.append(asyncio.create_task(send(0)))
|
|
161
|
+
# A sequential caller can await the wake-up receipt and still
|
|
162
|
+
# steer the next message into the held, resumed turn.
|
|
163
|
+
receipt = await sends[0]
|
|
164
|
+
assert (
|
|
165
|
+
await interaction.send(
|
|
166
|
+
LABELS[0], mode=modes[0], message_id=LABELS[0]
|
|
167
|
+
)
|
|
168
|
+
== receipt
|
|
169
|
+
)
|
|
170
|
+
await scenario.model_entered.wait()
|
|
171
|
+
assert not scenario.model_release.is_set()
|
|
172
|
+
assert not any(
|
|
173
|
+
e["event"] == "turn_yielded"
|
|
174
|
+
for e in scenario.events[
|
|
175
|
+
next(
|
|
176
|
+
e["index"]
|
|
177
|
+
for e in scenario.events
|
|
178
|
+
if e["event"] == "agent_idle"
|
|
179
|
+
)
|
|
180
|
+
+ 1 :
|
|
181
|
+
]
|
|
182
|
+
)
|
|
183
|
+
sends.append(asyncio.create_task(send(1)))
|
|
184
|
+
else:
|
|
185
|
+
if state == "model":
|
|
186
|
+
await scenario.model_entered.wait()
|
|
187
|
+
elif state == "tool":
|
|
188
|
+
await scenario.tool_entered.wait()
|
|
189
|
+
else:
|
|
190
|
+
await scenario.response_sent.wait()
|
|
191
|
+
# Let the harness enter its native wait after reading the response.
|
|
192
|
+
await asyncio.sleep(0.2)
|
|
193
|
+
sends.extend(asyncio.create_task(send(i)) for i in range(2))
|
|
194
|
+
# Keep the operation held while both ACP requests traverse the real processes.
|
|
195
|
+
await asyncio.sleep(0.25)
|
|
196
|
+
scenario.record("release_operation")
|
|
197
|
+
scenario.model_release.set()
|
|
198
|
+
scenario.tool_release.set()
|
|
199
|
+
await opening
|
|
200
|
+
receipts = await asyncio.gather(*sends)
|
|
201
|
+
# Delivery receipts no longer wait for completion. Keep the session
|
|
202
|
+
# open until its owned turns finish so the trace includes the replies.
|
|
203
|
+
if interaction._message_drain is not None:
|
|
204
|
+
await interaction._message_drain
|
|
205
|
+
assert all(r["outcome"] == "injected" for r in receipts)
|
|
206
|
+
assert set(scenario.calls[-1]["messages"]) == set(LABELS)
|
|
207
|
+
for label, mode in zip(LABELS, modes, strict=True):
|
|
208
|
+
submitted = next(
|
|
209
|
+
e["index"]
|
|
210
|
+
for e in scenario.events
|
|
211
|
+
if e["event"] == "submitted" and e["message"] == label
|
|
212
|
+
)
|
|
213
|
+
seen = next(
|
|
214
|
+
e["index"]
|
|
215
|
+
for e in scenario.events
|
|
216
|
+
if e["event"] == "model_request" and label in e["messages"]
|
|
217
|
+
)
|
|
218
|
+
assert seen > submitted
|
|
219
|
+
if mode == "steer" and (
|
|
220
|
+
not state.startswith("idle")
|
|
221
|
+
or (state == "idle" and label == LABELS[1])
|
|
222
|
+
):
|
|
223
|
+
assert not any(
|
|
224
|
+
e["event"] == "turn_yielded"
|
|
225
|
+
for e in scenario.events[submitted:seen]
|
|
226
|
+
)
|
|
227
|
+
if (
|
|
228
|
+
mode == "queue"
|
|
229
|
+
and state != "idle-burst"
|
|
230
|
+
and not (state == "idle" and label == LABELS[0])
|
|
231
|
+
):
|
|
232
|
+
assert any(
|
|
233
|
+
e["event"] == "turn_yielded"
|
|
234
|
+
for e in scenario.events[submitted:seen]
|
|
235
|
+
)
|
|
236
|
+
if (
|
|
237
|
+
kind == "rlm"
|
|
238
|
+
and not state.startswith("idle")
|
|
239
|
+
and modes == ("steer", "steer")
|
|
240
|
+
):
|
|
241
|
+
assert scenario.calls[1]["messages"] == list(LABELS)
|
|
242
|
+
if not state.startswith("idle") and modes == ("queue", "queue"):
|
|
243
|
+
assert next(c for c in scenario.calls if c["messages"])[
|
|
244
|
+
"messages"
|
|
245
|
+
] == list(LABELS)
|
|
246
|
+
interaction.trace.info.update(
|
|
247
|
+
scenario=name,
|
|
248
|
+
scripted_inference=True,
|
|
249
|
+
executables=bins,
|
|
250
|
+
events=scenario.events,
|
|
251
|
+
model_inputs=scenario.calls,
|
|
252
|
+
)
|
|
253
|
+
assert interaction.trace.ok, interaction.trace.errors
|
|
254
|
+
finally:
|
|
255
|
+
scenario.model_release.set()
|
|
256
|
+
scenario.tool_release.set()
|
|
257
|
+
for pending in [opening, *sends]:
|
|
258
|
+
if pending is not None and not pending.done():
|
|
259
|
+
pending.cancel()
|
|
260
|
+
await asyncio.gather(
|
|
261
|
+
*(p for p in [opening, *sends] if p is not None), return_exceptions=True
|
|
262
|
+
)
|
|
263
|
+
server.should_exit = True
|
|
264
|
+
await serving
|
|
265
|
+
output = Path(os.environ.get("VF_DELIVERY_OUTPUT", tmp_path)) / name
|
|
266
|
+
output.mkdir(parents=True, exist_ok=True)
|
|
267
|
+
(output / "timeline.json").write_text(
|
|
268
|
+
json.dumps(
|
|
269
|
+
{
|
|
270
|
+
"scenario": name,
|
|
271
|
+
"events": scenario.events,
|
|
272
|
+
"model_inputs": scenario.calls,
|
|
273
|
+
},
|
|
274
|
+
indent=2,
|
|
275
|
+
)
|
|
276
|
+
)
|
|
277
|
+
if "interaction" in locals():
|
|
278
|
+
trace = interaction.trace
|
|
279
|
+
episode = Episode(
|
|
280
|
+
task=trace.task,
|
|
281
|
+
traces=[trace],
|
|
282
|
+
ok=trace.ok,
|
|
283
|
+
run=EvalRunInfo(id=name, name=name),
|
|
284
|
+
)
|
|
285
|
+
(output / "traces.jsonl").write_text(episode.model_dump_json() + "\n")
|
|
286
|
+
print(
|
|
287
|
+
"\n" + name + ": " + " → ".join(str(c["messages"]) for c in scenario.calls)
|
|
288
|
+
)
|
|
@@ -66,6 +66,8 @@ from verifiers.v1.harness import Harness, HarnessSession
|
|
|
66
66
|
from verifiers.v1.judge import Judge, JudgeResponse, JudgeView
|
|
67
67
|
from verifiers.v1.judges import (
|
|
68
68
|
Criterion,
|
|
69
|
+
DecisionJudge,
|
|
70
|
+
DecisionJudgeConfig,
|
|
69
71
|
ReferenceJudge,
|
|
70
72
|
ReferenceJudgeConfig,
|
|
71
73
|
RubricJudge,
|
|
@@ -358,6 +360,8 @@ __all__ = [ # noqa: RUF022 - grouped by public API area
|
|
|
358
360
|
"Judges",
|
|
359
361
|
"JudgeResponse",
|
|
360
362
|
"JudgeView",
|
|
363
|
+
"DecisionJudge",
|
|
364
|
+
"DecisionJudgeConfig",
|
|
361
365
|
"ReferenceJudge",
|
|
362
366
|
"ReferenceJudgeConfig",
|
|
363
367
|
"RubricJudge",
|
|
@@ -231,6 +231,10 @@ class ACPHarnessSession(HarnessSession):
|
|
|
231
231
|
self._stderr_tail = bytearray()
|
|
232
232
|
self._stderr_task: asyncio.Task[None] | None = None
|
|
233
233
|
self._lock = asyncio.Lock()
|
|
234
|
+
self._write_lock = asyncio.Lock()
|
|
235
|
+
self._responses: dict[int, asyncio.Future] = {}
|
|
236
|
+
self._request_id = 0
|
|
237
|
+
self._response_task: asyncio.Task | None = None
|
|
234
238
|
|
|
235
239
|
async def _start(self) -> None:
|
|
236
240
|
self._stderr_tail.clear()
|
|
@@ -242,8 +246,57 @@ class ACPHarnessSession(HarnessSession):
|
|
|
242
246
|
process = await self.runtime.open_process(program, self.config.env)
|
|
243
247
|
self._process = process
|
|
244
248
|
self._reader = _PacketReader(process.stdout)
|
|
249
|
+
self._response_task = asyncio.create_task(self._read_responses(self._reader))
|
|
245
250
|
self._stderr_task = asyncio.create_task(self._drain_stderr(process.stderr))
|
|
246
251
|
|
|
252
|
+
async def _read_responses(self, reader: _PacketReader) -> None:
|
|
253
|
+
try:
|
|
254
|
+
while True:
|
|
255
|
+
response = await reader.read()
|
|
256
|
+
future = self._responses.get(response.get("id"))
|
|
257
|
+
if future is not None and not future.done():
|
|
258
|
+
future.set_result(response)
|
|
259
|
+
except BaseException as error: # noqa: BLE001 - settle callers on disconnect or shutdown
|
|
260
|
+
for future in self._responses.values():
|
|
261
|
+
if not future.done():
|
|
262
|
+
future.set_exception(
|
|
263
|
+
RuntimeError(f"ACP connection closed: {error}")
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
async def _request(self, operation: str, **payload: Any) -> JsonObject:
|
|
267
|
+
process = self._process
|
|
268
|
+
if process is None or self._response_task is None or self._response_task.done():
|
|
269
|
+
raise RuntimeError("ACP process is not running")
|
|
270
|
+
self._request_id += 1
|
|
271
|
+
request_id = self._request_id
|
|
272
|
+
future = asyncio.get_running_loop().create_future()
|
|
273
|
+
self._responses[request_id] = future
|
|
274
|
+
try:
|
|
275
|
+
async with self._write_lock:
|
|
276
|
+
await process.write(
|
|
277
|
+
_packet({"id": request_id, "operation": operation, **payload})
|
|
278
|
+
)
|
|
279
|
+
return await future
|
|
280
|
+
finally:
|
|
281
|
+
self._responses.pop(request_id, None)
|
|
282
|
+
if not future.done():
|
|
283
|
+
future.cancel()
|
|
284
|
+
|
|
285
|
+
async def steer(self, message: str, *, message_id: str | None = None) -> dict:
|
|
286
|
+
if self._closed:
|
|
287
|
+
raise RuntimeError("ACP session is closed")
|
|
288
|
+
if not isinstance(message, str) or not message.strip():
|
|
289
|
+
raise ValueError("steering requires a nonempty user message")
|
|
290
|
+
if self._process is None:
|
|
291
|
+
return {"outcome": "promptRequired", "reason": "noRunningTurn"}
|
|
292
|
+
response = await self._request("steer", message=message, message_id=message_id)
|
|
293
|
+
if not response.get("ok"):
|
|
294
|
+
raise RuntimeError(response.get("error") or "ACP steering failed")
|
|
295
|
+
result = response.get("result")
|
|
296
|
+
if not isinstance(result, dict):
|
|
297
|
+
raise TypeError("invalid ACP steering receipt")
|
|
298
|
+
return result
|
|
299
|
+
|
|
247
300
|
async def _drain_stderr(self, stream: AsyncIterator[bytes]) -> None:
|
|
248
301
|
async for chunk in stream:
|
|
249
302
|
self._stderr_tail.extend(chunk)
|
|
@@ -295,10 +348,7 @@ class ACPHarnessSession(HarnessSession):
|
|
|
295
348
|
assert self._reader is not None
|
|
296
349
|
calls_before = len(self.trace.calls)
|
|
297
350
|
try:
|
|
298
|
-
await self.
|
|
299
|
-
_packet({"operation": "prompt", "config": config})
|
|
300
|
-
)
|
|
301
|
-
response = await self._reader.read()
|
|
351
|
+
response = await self._request("prompt", config=config)
|
|
302
352
|
except BaseException:
|
|
303
353
|
await run_shielded(self._stop(graceful=False))
|
|
304
354
|
raise
|
|
@@ -323,17 +373,18 @@ class ACPHarnessSession(HarnessSession):
|
|
|
323
373
|
return result
|
|
324
374
|
|
|
325
375
|
async def _stop(self, *, graceful: bool) -> dict[str, Any]:
|
|
326
|
-
process
|
|
327
|
-
|
|
376
|
+
process = self._process
|
|
377
|
+
response_task = self._response_task
|
|
328
378
|
stderr_task, self._stderr_task = self._stderr_task, None
|
|
329
379
|
if process is None:
|
|
330
380
|
return {}
|
|
331
381
|
response_metadata: dict[str, Any] = {}
|
|
332
382
|
try:
|
|
333
|
-
if graceful
|
|
383
|
+
if graceful:
|
|
334
384
|
with contextlib.suppress(BaseException):
|
|
335
|
-
await
|
|
336
|
-
|
|
385
|
+
response = await asyncio.wait_for(
|
|
386
|
+
self._request("shutdown"), timeout=10
|
|
387
|
+
)
|
|
337
388
|
result = response.get("result")
|
|
338
389
|
if response.get("ok") and isinstance(result, dict):
|
|
339
390
|
metadata = result.get("response_metadata")
|
|
@@ -353,6 +404,13 @@ class ACPHarnessSession(HarnessSession):
|
|
|
353
404
|
except TimeoutError:
|
|
354
405
|
continue
|
|
355
406
|
finally:
|
|
407
|
+
self._process = None
|
|
408
|
+
self._reader = None
|
|
409
|
+
self._response_task = None
|
|
410
|
+
if response_task is not None:
|
|
411
|
+
response_task.cancel()
|
|
412
|
+
with contextlib.suppress(BaseException):
|
|
413
|
+
await response_task
|
|
356
414
|
if stderr_task is not None:
|
|
357
415
|
if not stderr_task.done():
|
|
358
416
|
stderr_task.cancel()
|