verifiers 0.3.2.dev72__tar.gz → 0.3.2.dev74__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.dev72 → verifiers-0.3.2.dev74}/PKG-INFO +2 -1
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/pyproject.toml +2 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/tests/v1/conftest.py +1 -0
- verifiers-0.3.2.dev74/tests/v1/test_platform_run.py +104 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/cli/dashboard/eval.py +15 -7
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/cli/eval/main.py +0 -4
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/cli/eval/runner.py +69 -34
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/configs/cli/eval.py +26 -2
- verifiers-0.3.2.dev74/verifiers/v1/utils/platform.py +178 -0
- verifiers-0.3.2.dev72/verifiers/v1/utils/platform.py +0 -317
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/.gitignore +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/LICENSE +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/README.md +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/tests/v1/fixtures/counter_tool_v1.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/tests/v1/fixtures/duet_v1.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/tests/v1/fixtures/echo_acp_resume_v1.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/tests/v1/fixtures/echo_agentic_v1.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/tests/v1/fixtures/echo_tool_v1.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/tests/v1/fixtures/echo_user_sim_v1.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/tests/v1/fixtures/echo_v1.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/tests/v1/fixtures/pyproject.toml +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/tests/v1/fixtures/tool_response_image_v1.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/tests/v1/test_color_codeword_tasks.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/tests/v1/test_configs.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/tests/v1/test_e2e.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/tests/v1/test_envs.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/tests/v1/test_graph.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/tests/v1/test_judges.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/tests/v1/test_scoring.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/tests/v1/test_taskset.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/tests/v1/test_trace.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/cli/commands/init.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/acp/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/acp/runner.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/agent.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/cli/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/cli/dashboard/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/cli/dashboard/base.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/cli/dashboard/replay.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/cli/dashboard/validate.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/cli/debug.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/cli/eval/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/cli/eval/resume.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/cli/gepa.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/cli/init.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/cli/output.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/cli/replay.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/cli/resolve.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/cli/resume.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/cli/validate.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/clients/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/clients/base.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/clients/client.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/clients/eval.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/clients/train.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/configs/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/configs/agent.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/configs/cli/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/configs/cli/debug.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/configs/cli/env.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/configs/cli/init.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/configs/cli/replay.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/configs/cli/validate.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/configs/client.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/configs/env.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/configs/harness.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/configs/judge.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/configs/retries.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/configs/runtime.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/configs/serve.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/configs/task.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/configs/taskset.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/dialects/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/dialects/anthropic.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/dialects/base.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/dialects/chat.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/dialects/responses.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/env.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/envs/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/envs/agentic_judge/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/envs/agentic_judge/env.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/envs/best_of_n/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/envs/best_of_n/env.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/envs/isolated_verifier/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/envs/isolated_verifier/env.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/envs/shared_agentic_judge/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/envs/single_agent/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/envs/single_agent/env.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/envs/user_sim/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/envs/user_sim/env.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/episode.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/errors.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/gepa/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/gepa/adapter.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/gepa/config.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/gepa/dataset.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/gepa/reflection.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/gepa/runner.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/graph.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harness.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/bash/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/bash/harness.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/browser_use/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/browser_use/harness.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/browser_use/program.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/claude_code/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/claude_code/harness.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/codex/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/codex/harness.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/hermes_agent/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/hermes_agent/harness.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/hermes_agent/program.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/kimi_code/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/kimi_code/harness.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/mini_swe_agent/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/mini_swe_agent/harness.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/mini_swe_agent/program.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/node.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/null/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/null/harness.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/openclaw/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/openclaw/harness.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/pi/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/pi/harness.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/pool/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/pool/harness.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/prime_agent/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/prime_agent/harness.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/rlm/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/rlm/harness.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/terminus_2/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/terminus_2/harness.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/terminus_2/program.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/utils/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/utils/compaction.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/utils/core.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/utils/install.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/utils/launch.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/harnesses/utils/mcp.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/interception/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/interception/base.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/interception/pool.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/interception/server.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/interception/tool.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/interception/tunnel/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/interception/tunnel/base.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/interception/tunnel/custom.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/interception/tunnel/prime.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/judge.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/judges/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/judges/reference.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/judges/reference.txt +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/judges/rubric.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/judges/rubric.txt +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/mcp/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/mcp/launch.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/mcp/server.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/mcp/toolset.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/rollout.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/runtimes/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/runtimes/base.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/runtimes/docker/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/runtimes/docker/egress.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/runtimes/limiters.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/runtimes/modal.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/runtimes/prime.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/runtimes/subprocess.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/semantic.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/serve/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/serve/client.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/serve/encoding.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/serve/pool.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/serve/server.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/serve/types.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/session.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/state.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/task.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/taskset.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/tasksets/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/tasksets/harbor/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/tasksets/harbor/env.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/tasksets/harbor/taskset.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/tasksets/lean/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/tasksets/lean/scoring.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/tasksets/lean/taskset.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/tasksets/nemo_gym/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/tasksets/nemo_gym/response.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/tasksets/nemo_gym/server.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/tasksets/nemo_gym/taskset.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/tasksets/nemo_gym/toolset.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/tasksets/openenv/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/tasksets/openenv/taskset.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/tasksets/textarena/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/tasksets/textarena/taskset.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/trace.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/types.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/utils/__init__.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/utils/aio.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/utils/artifacts.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/utils/compile.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/utils/decorators.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/utils/format.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/utils/generic.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/utils/git.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/utils/image.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/utils/interrupt.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/utils/loaders.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/utils/logging.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/utils/memory.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/utils/paths.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/utils/prime.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/utils/retries.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/verifiers/v1/utils/score.py +0 -0
- {verifiers-0.3.2.dev72 → verifiers-0.3.2.dev74}/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.dev74
|
|
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
|
|
@@ -35,6 +35,7 @@ Requires-Dist: msgpack>=1.1.2
|
|
|
35
35
|
Requires-Dist: numpy>=2.1.0
|
|
36
36
|
Requires-Dist: openai<3.0.0,>=2.54.0
|
|
37
37
|
Requires-Dist: prime-pydantic-config[toml]>=0.4.3
|
|
38
|
+
Requires-Dist: prime-runs>=0.1.2
|
|
38
39
|
Requires-Dist: prime-sandboxes>=0.2.39
|
|
39
40
|
Requires-Dist: prime-tunnel>=0.1.8
|
|
40
41
|
Requires-Dist: pydantic>=2.12.3
|
|
@@ -34,6 +34,7 @@ dependencies = [
|
|
|
34
34
|
"openai>=2.54.0,<3.0.0",
|
|
35
35
|
"prime-tunnel>=0.1.8",
|
|
36
36
|
"prime-sandboxes>=0.2.39",
|
|
37
|
+
"prime-runs>=0.1.2",
|
|
37
38
|
"pydantic>=2.12.3",
|
|
38
39
|
"rich>=11.0.0",
|
|
39
40
|
"tenacity>=8.5.0",
|
|
@@ -146,6 +147,7 @@ ty = "2026-07-28T00:00:00Z"
|
|
|
146
147
|
# PrimeIntellect-published on PyPI (trusted publisher)
|
|
147
148
|
prime-tunnel = false
|
|
148
149
|
prime-sandboxes = false
|
|
150
|
+
prime-runs = false
|
|
149
151
|
prime-pydantic-config = false
|
|
150
152
|
renderers = false
|
|
151
153
|
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""`open_run`: the eval's run on the platform, and `run.attach` for hosted evaluations."""
|
|
2
|
+
|
|
3
|
+
import prime_runs as pr
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
from verifiers.v1.configs.cli.eval import EvalConfig
|
|
7
|
+
from verifiers.v1.utils import platform
|
|
8
|
+
from verifiers.v1.utils.platform import PushState, open_run
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class _FakeRun:
|
|
12
|
+
"""Stands in for `pr.Run`: an id, a URL, nothing else the tests reach for."""
|
|
13
|
+
|
|
14
|
+
def __init__(self, run_id: str, mode: str) -> None:
|
|
15
|
+
self.id = run_id
|
|
16
|
+
self.mode = mode
|
|
17
|
+
self.url = (
|
|
18
|
+
None if mode == "disabled" else f"https://app.example/evaluations/{run_id}"
|
|
19
|
+
)
|
|
20
|
+
self.finished = False
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@pytest.fixture
|
|
24
|
+
def init_calls(monkeypatch):
|
|
25
|
+
"""Record every `pr.init` call; the fake returns a run keyed by the attach id."""
|
|
26
|
+
calls: list[dict] = []
|
|
27
|
+
|
|
28
|
+
def fake_init(**kwargs):
|
|
29
|
+
calls.append(kwargs)
|
|
30
|
+
return _FakeRun(kwargs.get("id") or "eval-created", kwargs["mode"])
|
|
31
|
+
|
|
32
|
+
monkeypatch.setattr(platform.pr, "init", fake_init)
|
|
33
|
+
monkeypatch.delenv(pr.MODE_ENV, raising=False)
|
|
34
|
+
return calls
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_attach_needs_push():
|
|
38
|
+
with pytest.raises(ValueError, match="needs push"):
|
|
39
|
+
EvalConfig(run={"attach": "eval-hosted"}, push=False)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_a_run_without_attach_is_created_on_the_platform(init_calls):
|
|
43
|
+
config = EvalConfig(push=True)
|
|
44
|
+
state = PushState()
|
|
45
|
+
|
|
46
|
+
run = open_run(config, state, num_examples=3)
|
|
47
|
+
|
|
48
|
+
assert init_calls[0]["mode"] == "online"
|
|
49
|
+
assert init_calls[0]["id"] is None
|
|
50
|
+
assert run.id == config.run.id == "eval-created"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_attach_hands_the_launchers_id_to_the_sdk(init_calls):
|
|
54
|
+
config = EvalConfig(run={"attach": "eval-hosted"})
|
|
55
|
+
state = PushState()
|
|
56
|
+
|
|
57
|
+
run = open_run(config, state, num_examples=3)
|
|
58
|
+
|
|
59
|
+
assert init_calls[0]["mode"] == "online"
|
|
60
|
+
assert init_calls[0]["id"] == "eval-hosted"
|
|
61
|
+
# The launcher's id is the run's one id: traces and the run dir carry it too.
|
|
62
|
+
assert run.id == config.run.id == "eval-hosted"
|
|
63
|
+
assert state.error is None
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_a_failed_attach_fails_the_eval_instead_of_falling_back(monkeypatch):
|
|
67
|
+
def refuse(**kwargs):
|
|
68
|
+
raise pr.UnauthorizedError("bad key")
|
|
69
|
+
|
|
70
|
+
monkeypatch.setattr(platform.pr, "init", refuse)
|
|
71
|
+
monkeypatch.delenv(pr.MODE_ENV, raising=False)
|
|
72
|
+
config = EvalConfig(run={"attach": "eval-hosted"})
|
|
73
|
+
|
|
74
|
+
with pytest.raises(RuntimeError, match="could not attach to run 'eval-hosted'"):
|
|
75
|
+
open_run(config, PushState(), num_examples=3)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def test_a_failed_open_without_attach_still_falls_back_to_a_local_run(monkeypatch):
|
|
79
|
+
calls: list[dict] = []
|
|
80
|
+
|
|
81
|
+
def flaky(**kwargs):
|
|
82
|
+
calls.append(kwargs)
|
|
83
|
+
if kwargs["mode"] == "online":
|
|
84
|
+
raise pr.UnauthorizedError("bad key")
|
|
85
|
+
return _FakeRun("local-run", kwargs["mode"])
|
|
86
|
+
|
|
87
|
+
monkeypatch.setattr(platform.pr, "init", flaky)
|
|
88
|
+
monkeypatch.delenv(pr.MODE_ENV, raising=False)
|
|
89
|
+
state = PushState()
|
|
90
|
+
|
|
91
|
+
run = open_run(EvalConfig(push=True), state, num_examples=3)
|
|
92
|
+
|
|
93
|
+
assert [call["mode"] for call in calls] == ["online", "disabled"]
|
|
94
|
+
assert run.mode == "disabled"
|
|
95
|
+
assert state.error is not None and "UnauthorizedError" in state.error
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_the_sdk_kill_switch_refuses_an_attach(init_calls, monkeypatch):
|
|
99
|
+
monkeypatch.setenv(pr.MODE_ENV, "disabled")
|
|
100
|
+
config = EvalConfig(run={"attach": "eval-hosted"})
|
|
101
|
+
|
|
102
|
+
with pytest.raises(RuntimeError, match="PRIME_RUNS_MODE=disabled"):
|
|
103
|
+
open_run(config, PushState(), num_examples=3)
|
|
104
|
+
assert init_calls == []
|
|
@@ -281,17 +281,25 @@ def Overview(config: EvalConfig) -> Table:
|
|
|
281
281
|
|
|
282
282
|
|
|
283
283
|
def _push_footer(push: "PushState | None") -> Group | None:
|
|
284
|
-
"""The `--push`
|
|
285
|
-
|
|
286
|
-
|
|
284
|
+
"""The `--push` line under the rollouts: dim with the run's URL while it streams,
|
|
285
|
+
white once pushed, red when it failed. `None` when `--push` is off or the run stayed
|
|
286
|
+
local."""
|
|
287
287
|
if push is None or not push.started:
|
|
288
288
|
return None
|
|
289
|
-
if
|
|
290
|
-
|
|
291
|
-
elif push.url:
|
|
289
|
+
if push.error and push.url:
|
|
290
|
+
# The run exists and holds what streamed up; only closing it out failed.
|
|
292
291
|
line = Text(f"Traces pushed ({push.url})", style="white", overflow="fold")
|
|
293
|
-
|
|
292
|
+
line.append(f" not closed out: {push.error}", style="red")
|
|
293
|
+
elif push.error:
|
|
294
294
|
line = Text(f"Trace push failed ({push.error})", style="red", overflow="fold")
|
|
295
|
+
elif push.incomplete and push.url:
|
|
296
|
+
# Closed out, but the uploader lost records: what landed is there, say what didn't.
|
|
297
|
+
line = Text(f"Traces pushed ({push.url})", style="white", overflow="fold")
|
|
298
|
+
line.append(f" incomplete: {push.incomplete}", style="red")
|
|
299
|
+
elif not push.finished:
|
|
300
|
+
line = Text(f"Pushing traces ({push.url})", style="dim", overflow="fold")
|
|
301
|
+
else:
|
|
302
|
+
line = Text(f"Traces pushed ({push.url})", style="white", overflow="fold")
|
|
295
303
|
return Group(Rule(style="dim"), line)
|
|
296
304
|
|
|
297
305
|
|
|
@@ -134,10 +134,6 @@ def main(argv: list[str] | None = None) -> None:
|
|
|
134
134
|
# Graceful cleanup has already run (each rollout's `finally`); partial results are on
|
|
135
135
|
# disk. Exit on the conventional Ctrl-C code without a traceback.
|
|
136
136
|
raise SystemExit(130)
|
|
137
|
-
if config.push and config.rich is None:
|
|
138
|
-
from verifiers.v1.utils.platform import push_traces
|
|
139
|
-
|
|
140
|
-
push_traces(episodes, config)
|
|
141
137
|
if (
|
|
142
138
|
config.rich is None
|
|
143
139
|
): # --rich is the whole output; otherwise dump each trace as JSON
|
|
@@ -13,8 +13,8 @@ import asyncio
|
|
|
13
13
|
import contextlib
|
|
14
14
|
import logging
|
|
15
15
|
import time
|
|
16
|
-
from collections.abc import AsyncIterator, Awaitable, Callable
|
|
17
|
-
from typing import cast
|
|
16
|
+
from collections.abc import AsyncIterator, Awaitable, Callable, Iterable
|
|
17
|
+
from typing import TypeVar, cast
|
|
18
18
|
|
|
19
19
|
from verifiers.v1.cli.dashboard import dashboard
|
|
20
20
|
from verifiers.v1.cli.eval import resume
|
|
@@ -30,9 +30,36 @@ from verifiers.v1.configs.cli.eval import EvalConfig
|
|
|
30
30
|
from verifiers.v1.configs.serve import ServeConfig
|
|
31
31
|
from verifiers.v1.env import Env, RunSlot
|
|
32
32
|
from verifiers.v1.episode import Episode, EvalRunInfo
|
|
33
|
+
from verifiers.v1.utils.aio import run_shielded
|
|
34
|
+
from verifiers.v1.utils.platform import (
|
|
35
|
+
PushState,
|
|
36
|
+
abort_run,
|
|
37
|
+
finish_run,
|
|
38
|
+
log_episodes,
|
|
39
|
+
open_run,
|
|
40
|
+
)
|
|
33
41
|
|
|
34
42
|
logger = logging.getLogger(__name__)
|
|
35
43
|
|
|
44
|
+
T = TypeVar("T")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
async def gather_rollouts(rollouts: Iterable[Awaitable[T]]) -> list[T]:
|
|
48
|
+
"""`asyncio.gather`, but one rollout failing cancels the rest and waits for them
|
|
49
|
+
to unwind, so nothing keeps uploading into a run the caller is already closing.
|
|
50
|
+
Not a `TaskGroup`: that wraps errors in an `ExceptionGroup`, and `main` would no
|
|
51
|
+
longer see a `KeyboardInterrupt` as Ctrl-C."""
|
|
52
|
+
tasks = [asyncio.ensure_future(rollout) for rollout in rollouts]
|
|
53
|
+
try:
|
|
54
|
+
return await asyncio.gather(*tasks)
|
|
55
|
+
except BaseException:
|
|
56
|
+
for task in tasks:
|
|
57
|
+
task.cancel()
|
|
58
|
+
# return_exceptions: wait for every task, not just the first to cancel.
|
|
59
|
+
await asyncio.gather(*tasks, return_exceptions=True)
|
|
60
|
+
raise
|
|
61
|
+
|
|
62
|
+
|
|
36
63
|
RunSlotFn = Callable[[RunSlot], Awaitable[Episode]]
|
|
37
64
|
OnComplete = Callable[[Episode], Awaitable[None]]
|
|
38
65
|
|
|
@@ -203,47 +230,55 @@ async def run_eval(config: EvalConfig) -> list[Episode]:
|
|
|
203
230
|
asyncio.Semaphore(config.max_concurrent) if config.max_concurrent else None
|
|
204
231
|
)
|
|
205
232
|
write_lock = asyncio.Lock()
|
|
233
|
+
push_state = PushState()
|
|
234
|
+
|
|
235
|
+
# Opened before the first rollout so every episode streams as it lands.
|
|
236
|
+
run = open_run(config, push_state, num_examples=len(tasks))
|
|
237
|
+
# Resumed rollouts are part of this run too.
|
|
238
|
+
log_episodes(run, finished)
|
|
206
239
|
|
|
207
240
|
async def on_complete(episode: Episode) -> None:
|
|
208
241
|
episode.record_run(EvalRunInfo(id=config.run.id, name=config.run.name))
|
|
209
242
|
await append_episode(out, episode, write_lock)
|
|
243
|
+
await asyncio.to_thread(log_episodes, run, [episode])
|
|
210
244
|
|
|
211
245
|
backend = (
|
|
212
246
|
_in_process(env, config, semaphore, on_complete)
|
|
213
247
|
if env is not None
|
|
214
248
|
else _server(config, config.serve, semaphore, on_complete)
|
|
215
249
|
)
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
250
|
+
# The run is closed out whatever breaks, backend setup and teardown included.
|
|
251
|
+
try:
|
|
252
|
+
async with backend as run_slot:
|
|
253
|
+
# The display slots: in-process ones are the env's own (it fills their live
|
|
254
|
+
# traces); a served rollout's is a client-side stand-in its worker never sees.
|
|
255
|
+
planned = [
|
|
256
|
+
slot
|
|
257
|
+
for task, n in plan
|
|
258
|
+
for slot in (
|
|
259
|
+
env.slots(task, n)
|
|
260
|
+
if env is not None
|
|
261
|
+
else [RunSlot(task) for _ in range(n)]
|
|
262
|
+
)
|
|
263
|
+
]
|
|
264
|
+
slots = [RunSlot.finished(episode) for episode in finished] + planned
|
|
265
|
+
display = (
|
|
266
|
+
dashboard(slots, config, start, push=push_state)
|
|
267
|
+
if config.rich is not None
|
|
268
|
+
else contextlib.nullcontext()
|
|
226
269
|
)
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
episodes = finished + list(results)
|
|
242
|
-
if (
|
|
243
|
-
push_state is not None
|
|
244
|
-
): # upload off the event loop so the view keeps refreshing
|
|
245
|
-
from verifiers.v1.utils.platform import push_traces
|
|
246
|
-
|
|
247
|
-
push_state.started = True
|
|
248
|
-
await asyncio.to_thread(push_traces, episodes, config, push_state)
|
|
270
|
+
async with display:
|
|
271
|
+
results = await gather_rollouts(run_slot(slot) for slot in planned)
|
|
272
|
+
episodes = finished + list(results)
|
|
273
|
+
# Drain and close out off the event loop so the view keeps refreshing.
|
|
274
|
+
# Shielded: a Ctrl-C here must not cancel the close-out before the
|
|
275
|
+
# worker picks it up (a cancelled executor item never runs), so it
|
|
276
|
+
# runs to completion first and the interrupt is re-raised after —
|
|
277
|
+
# by which point the run is finished and `abort_run` has nothing to do.
|
|
278
|
+
await run_shielded(
|
|
279
|
+
asyncio.to_thread(finish_run, run, episodes, push_state)
|
|
280
|
+
)
|
|
281
|
+
except BaseException as e:
|
|
282
|
+
await asyncio.to_thread(abort_run, run, e, push_state)
|
|
283
|
+
raise
|
|
249
284
|
return episodes
|
|
@@ -48,13 +48,29 @@ class RunConfig(BaseConfig):
|
|
|
48
48
|
"""Run directory name — the run writes to `output_dir / dir`. Defaults to `run.name`;
|
|
49
49
|
set it only when the directory should differ from the display name."""
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
attach: str | None = None
|
|
52
|
+
"""Stream into a run the launcher already created on the platform instead of opening a
|
|
53
|
+
new one — its evaluation id. Hosted evaluations pass the sandbox's `$EVALUATION_ID`
|
|
54
|
+
here; the platform owns that run's record and status, and a run that cannot be
|
|
55
|
+
attached to is an error rather than a local fallback. Requires `push`."""
|
|
56
|
+
|
|
57
|
+
_id: str | None = PrivateAttr(default=None)
|
|
53
58
|
|
|
54
59
|
@property
|
|
55
60
|
def id(self) -> str:
|
|
61
|
+
"""The run's one id, assigned by `open_run` from the prime-runs handle: the
|
|
62
|
+
platform's evaluation id online, the SDK's local id otherwise. The run mints
|
|
63
|
+
none of its own, so the run dir, every trace and the dashboard agree."""
|
|
64
|
+
if self._id is None:
|
|
65
|
+
raise RuntimeError("the run has no id until `open_run` has opened it")
|
|
56
66
|
return self._id
|
|
57
67
|
|
|
68
|
+
def assign_id(self, run_id: str) -> None:
|
|
69
|
+
"""Called once by `open_run`, before the first rollout."""
|
|
70
|
+
if self._id is not None and self._id != run_id:
|
|
71
|
+
raise RuntimeError(f"the run already has id {self._id!r}")
|
|
72
|
+
self._id = run_id
|
|
73
|
+
|
|
58
74
|
|
|
59
75
|
class EvalConfig(BaseConfig):
|
|
60
76
|
env: SerializeAsAny[EnvConfig] = SingleAgentEnvConfig()
|
|
@@ -135,3 +151,11 @@ class EvalConfig(BaseConfig):
|
|
|
135
151
|
if self.run.dir is None:
|
|
136
152
|
self.run.dir = self.run.name
|
|
137
153
|
return self
|
|
154
|
+
|
|
155
|
+
@model_validator(mode="after")
|
|
156
|
+
def attach_needs_push(self):
|
|
157
|
+
if self.run.attach and not self.push:
|
|
158
|
+
raise ValueError(
|
|
159
|
+
"run.attach names a run on the platform, so it needs push (drop --no-push)"
|
|
160
|
+
)
|
|
161
|
+
return self
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"""The eval's run on the Prime Intellect platform (`--no-push` to keep it local)."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import logging
|
|
5
|
+
import os
|
|
6
|
+
from collections.abc import Mapping
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
import prime_runs as pr
|
|
11
|
+
from prime_runs.projection import (
|
|
12
|
+
build_samples, # noqa: F401 - prime-rl imports it from here
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
from verifiers.v1.configs.cli.eval import EvalConfig
|
|
16
|
+
from verifiers.v1.episode import Episode
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class PushState:
|
|
23
|
+
"""The dashboard's view of the run: reads through to it, owns no I/O."""
|
|
24
|
+
|
|
25
|
+
run: pr.Run | None = None
|
|
26
|
+
error: str | None = None
|
|
27
|
+
incomplete: str | None = None
|
|
28
|
+
"""Set when the run closed out but the uploader lost records on the way: the run
|
|
29
|
+
exists and holds what did land, so the footer says so rather than "failed"."""
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def url(self) -> str | None:
|
|
33
|
+
return self.run.url if self.run is not None else None
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def finished(self) -> bool:
|
|
37
|
+
return self.run is not None and self.run.finished
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def started(self) -> bool:
|
|
41
|
+
"""A live run, or a reason there isn't one."""
|
|
42
|
+
return self.error is not None or self.url is not None
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def open_run(config: EvalConfig, state: PushState, *, num_examples: int) -> pr.Run:
|
|
46
|
+
"""Open the run this eval streams into, before the first rollout, and give the
|
|
47
|
+
config the run's id. A run that cannot be opened is logged and replaced by a
|
|
48
|
+
disabled one; the eval goes on. With `run.attach` the run already exists on the
|
|
49
|
+
platform (a hosted evaluation's launcher created it and is waiting on it), so
|
|
50
|
+
there is no local fallback: failing to attach fails the eval."""
|
|
51
|
+
attach = config.run.attach
|
|
52
|
+
identity: dict[str, Any] = {
|
|
53
|
+
"name": config.run.name,
|
|
54
|
+
# Resolved by name via the hub's get-or-create; no taskset, nothing to attach to.
|
|
55
|
+
"environments": [config.env.taskset.id] if config.env.taskset.id else [],
|
|
56
|
+
"model": config.model,
|
|
57
|
+
"framework": "verifiers",
|
|
58
|
+
# The v0 keys the dashboard's lists read. The config itself is a follow-up:
|
|
59
|
+
# a dump or the launched file can carry credentials and needs masking first.
|
|
60
|
+
"config": {
|
|
61
|
+
"model": config.model,
|
|
62
|
+
"num_examples": num_examples,
|
|
63
|
+
"rollouts_per_example": config.num_rollouts,
|
|
64
|
+
},
|
|
65
|
+
}
|
|
66
|
+
if config.push and os.getenv(pr.MODE_ENV, "").strip().lower() == "disabled":
|
|
67
|
+
if attach:
|
|
68
|
+
raise RuntimeError(
|
|
69
|
+
f"run.attach={attach!r} names a run on the platform, but "
|
|
70
|
+
f"{pr.MODE_ENV}=disabled would keep this eval local"
|
|
71
|
+
)
|
|
72
|
+
# The SDK's own kill switch; the explicit `mode="online"` below would override it.
|
|
73
|
+
logger.info("--push: %s=disabled; running without a platform run", pr.MODE_ENV)
|
|
74
|
+
elif config.push:
|
|
75
|
+
try:
|
|
76
|
+
state.run = pr.init(mode="online", id=attach, **identity)
|
|
77
|
+
except Exception as e:
|
|
78
|
+
if attach:
|
|
79
|
+
# The launcher's run would sit at running until it times out; a local
|
|
80
|
+
# run nobody reads is not a substitute.
|
|
81
|
+
raise RuntimeError(
|
|
82
|
+
f"--run.attach: could not attach to run {attach!r} ({type(e).__name__}: {e})"
|
|
83
|
+
) from e
|
|
84
|
+
logger.warning(
|
|
85
|
+
"--push: could not open the run (%s: %s); running without it",
|
|
86
|
+
type(e).__name__,
|
|
87
|
+
e,
|
|
88
|
+
)
|
|
89
|
+
state.error = f"{type(e).__name__}: {e}"
|
|
90
|
+
if state.run is None:
|
|
91
|
+
state.run = pr.init(mode="disabled", **identity)
|
|
92
|
+
# The run's one id: the platform's when online, the SDK's local one otherwise.
|
|
93
|
+
# The SDK keys every upload to it regardless; this is for the local records.
|
|
94
|
+
config.run.assign_id(state.run.id)
|
|
95
|
+
return state.run
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def log_episodes(run: pr.Run, episodes: list[Episode]) -> None:
|
|
99
|
+
"""Hand finished episodes to the run, best effort: the SDK already keeps upload
|
|
100
|
+
failures on its own thread, so this only guards the hand-off itself. A problem
|
|
101
|
+
here is the platform's, never the eval's."""
|
|
102
|
+
if not episodes:
|
|
103
|
+
return
|
|
104
|
+
try:
|
|
105
|
+
run.log_episodes(episodes)
|
|
106
|
+
except Exception as e: # noqa: BLE001 - the rollouts are on disk; report, don't raise
|
|
107
|
+
logger.warning(
|
|
108
|
+
"--push: could not queue %d episode(s) (%s: %s)",
|
|
109
|
+
len(episodes),
|
|
110
|
+
type(e).__name__,
|
|
111
|
+
e,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def finish_run(run: pr.Run, episodes: list[Episode], state: PushState) -> None:
|
|
116
|
+
"""Drain, write the run's aggregates, close it out. Blocking: call it off the loop."""
|
|
117
|
+
try:
|
|
118
|
+
summary = pr.metrics.from_episodes(episodes)
|
|
119
|
+
except Exception as e: # noqa: BLE001 - close the run even without its headline
|
|
120
|
+
logger.warning(
|
|
121
|
+
"--push: could not aggregate the run's metrics (%s: %s)",
|
|
122
|
+
type(e).__name__,
|
|
123
|
+
e,
|
|
124
|
+
)
|
|
125
|
+
summary = None
|
|
126
|
+
_close(run, state, summary=summary)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def abort_run(run: pr.Run, error: BaseException, state: PushState) -> None:
|
|
130
|
+
"""Close the run out after the eval broke, so it doesn't sit at running. Not
|
|
131
|
+
for a break during `finish_run`: that close-out completes on its own thread
|
|
132
|
+
and the SDK lets the first `finish()` decide the status — it sets `run.status`
|
|
133
|
+
as it starts, so an in-flight one is visible here before it is `finished`."""
|
|
134
|
+
if run.finished or run.status is not pr.RunStatus.RUNNING:
|
|
135
|
+
return
|
|
136
|
+
if isinstance(error, (KeyboardInterrupt, asyncio.CancelledError)):
|
|
137
|
+
status, message = pr.RunStatus.CANCELLED, "interrupted"
|
|
138
|
+
else:
|
|
139
|
+
status, message = pr.RunStatus.FAILED, f"{type(error).__name__}: {error}"
|
|
140
|
+
_close(run, state, status=status, error=message)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _close(
|
|
144
|
+
run: pr.Run,
|
|
145
|
+
state: PushState,
|
|
146
|
+
summary: Mapping[str, Any] | None = None,
|
|
147
|
+
status: pr.RunStatus = pr.RunStatus.COMPLETED,
|
|
148
|
+
error: str | None = None,
|
|
149
|
+
) -> None:
|
|
150
|
+
"""`run.finish()`, best effort: the results are on disk, so nothing here may raise."""
|
|
151
|
+
try:
|
|
152
|
+
run.finish(summary, status=status, error=error)
|
|
153
|
+
except Exception as e: # noqa: BLE001 - the run is over; report, don't raise
|
|
154
|
+
logger.warning(
|
|
155
|
+
"--push: could not close out the run (%s: %s)", type(e).__name__, e
|
|
156
|
+
)
|
|
157
|
+
if state.error is None:
|
|
158
|
+
state.error = f"{type(e).__name__}: {e}"
|
|
159
|
+
else:
|
|
160
|
+
state.incomplete = _losses(run)
|
|
161
|
+
if state.incomplete:
|
|
162
|
+
logger.warning("--push: %s, but %s", status.value, state.incomplete)
|
|
163
|
+
if run.url:
|
|
164
|
+
# The run's own status: `finish()` is a no-op once another caller closed it.
|
|
165
|
+
logger.info("--push: %s -> %s", run.status.value, run.url)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _losses(run: pr.Run) -> str | None:
|
|
169
|
+
"""What the uploader could not store, or `None`. A sink that switched itself off
|
|
170
|
+
quietly (Prime Traces outside the beta) is not a loss and is not counted here."""
|
|
171
|
+
parts = [
|
|
172
|
+
f"{count} record(s) not stored by the {sink} sink"
|
|
173
|
+
for sink, count in sorted(run.failed_records.items())
|
|
174
|
+
if count
|
|
175
|
+
]
|
|
176
|
+
if run.dropped_records:
|
|
177
|
+
parts.append(f"{run.dropped_records} record(s) never queued (uploader overrun)")
|
|
178
|
+
return "; ".join(parts) or None
|