verifiers 0.3.2.dev126__tar.gz → 0.3.2.dev127__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.dev126 → verifiers-0.3.2.dev127}/PKG-INFO +1 -1
- verifiers-0.3.2.dev127/verifiers/v1/configs/runtime.py +126 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/dialects/anthropic.py +72 -145
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/dialects/base.py +121 -69
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/dialects/chat.py +49 -54
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/dialects/responses.py +80 -163
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/runtimes/modal.py +3 -6
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/task.py +2 -2
- verifiers-0.3.2.dev126/verifiers/v1/configs/runtime.py +0 -72
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/.gitignore +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/LICENSE +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/README.md +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/pyproject.toml +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/tests/v1/conftest.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/tests/v1/fixtures/counter_tool_v1.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/tests/v1/fixtures/duet_v1.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/tests/v1/fixtures/echo_acp_resume_v1.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/tests/v1/fixtures/echo_agentic_v1.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/tests/v1/fixtures/echo_tool_v1.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/tests/v1/fixtures/echo_user_sim_v1.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/tests/v1/fixtures/echo_v1.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/tests/v1/fixtures/pyproject.toml +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/tests/v1/fixtures/tool_response_image_v1.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/tests/v1/test_color_codeword_tasks.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/tests/v1/test_configs.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/tests/v1/test_e2e.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/tests/v1/test_envs.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/tests/v1/test_graph.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/tests/v1/test_judges.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/tests/v1/test_platform_run.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/tests/v1/test_scoring.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/tests/v1/test_serve_delta.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/tests/v1/test_taskset.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/tests/v1/test_trace.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/acp/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/acp/runner.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/agent.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/cli/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/cli/dashboard/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/cli/dashboard/base.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/cli/dashboard/eval.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/cli/dashboard/replay.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/cli/dashboard/validate.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/cli/debug.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/cli/eval/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/cli/eval/hint.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/cli/eval/main.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/cli/eval/resume.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/cli/eval/runner.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/cli/gepa.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/cli/init.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/cli/output.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/cli/replay.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/cli/resolve.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/cli/resume.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/cli/validate.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/clients/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/clients/base.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/clients/client.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/clients/eval.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/clients/train.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/configs/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/configs/agent.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/configs/cli/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/configs/cli/debug.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/configs/cli/env.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/configs/cli/eval.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/configs/cli/init.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/configs/cli/replay.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/configs/cli/validate.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/configs/client.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/configs/env.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/configs/harness.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/configs/judge.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/configs/retries.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/configs/serve.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/configs/task.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/configs/taskset.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/dialects/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/env.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/envs/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/envs/agentic_judge/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/envs/agentic_judge/env.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/envs/best_of_n/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/envs/best_of_n/env.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/envs/isolated_verifier/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/envs/isolated_verifier/env.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/envs/shared_agentic_judge/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/envs/single_agent/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/envs/single_agent/env.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/envs/user_sim/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/envs/user_sim/env.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/episode.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/errors.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/gepa/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/gepa/adapter.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/gepa/config.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/gepa/dataset.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/gepa/reflection.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/gepa/runner.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/graph.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harness.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/bash/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/bash/harness.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/browser_use/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/browser_use/harness.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/browser_use/program.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/claude_code/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/claude_code/harness.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/codex/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/codex/gate.mjs +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/codex/harness.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/hermes_agent/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/hermes_agent/harness.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/hermes_agent/program.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/kimi_code/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/kimi_code/harness.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/mini_swe_agent/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/mini_swe_agent/harness.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/mini_swe_agent/program.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/node.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/null/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/null/harness.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/openclaw/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/openclaw/harness.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/pi/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/pi/gate.mjs +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/pi/harness.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/prime_agent/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/prime_agent/harness.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/rlm/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/rlm/harness.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/terminus_2/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/terminus_2/harness.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/terminus_2/program.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/utils/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/utils/compaction.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/utils/core.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/utils/install.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/utils/launch.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/utils/mcp.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/interception/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/interception/base.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/interception/pool.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/interception/server.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/interception/tunnel/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/interception/tunnel/base.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/interception/tunnel/custom.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/interception/tunnel/prime.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/judge.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/judges/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/judges/reference.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/judges/reference.txt +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/judges/rubric.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/judges/rubric.txt +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/mcp/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/mcp/launch.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/mcp/server.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/mcp/toolset.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/rollout.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/runtimes/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/runtimes/apptainer.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/runtimes/base.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/runtimes/container.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/runtimes/docker/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/runtimes/docker/egress.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/runtimes/limiters.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/runtimes/prime.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/runtimes/subprocess.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/semantic.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/serve/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/serve/client.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/serve/delta.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/serve/encoding.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/serve/pool.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/serve/server.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/serve/types.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/session.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/state.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/taskset.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/harbor/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/harbor/env.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/harbor/taskset.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/harbor/toolset.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/lean/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/lean/scoring.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/lean/taskset.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/nemo_gym/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/nemo_gym/response.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/nemo_gym/server.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/nemo_gym/taskset.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/nemo_gym/toolset.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/openenv/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/openenv/taskset.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/textarena/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/textarena/taskset.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/trace.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/types.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/utils/__init__.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/utils/aio.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/utils/artifacts.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/utils/compile.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/utils/decorators.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/utils/format.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/utils/generic.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/utils/git.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/utils/image.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/utils/interrupt.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/utils/loaders.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/utils/logging.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/utils/memory.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/utils/paths.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/utils/platform.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/utils/prime.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/utils/retries.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/verifiers/v1/utils/score.py +0 -0
- {verifiers-0.3.2.dev126 → verifiers-0.3.2.dev127}/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.dev127
|
|
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,126 @@
|
|
|
1
|
+
"""Shared configuration for execution-time network policy."""
|
|
2
|
+
|
|
3
|
+
from fnmatch import fnmatchcase
|
|
4
|
+
from glob import has_magic
|
|
5
|
+
from itertools import product
|
|
6
|
+
from typing import Self
|
|
7
|
+
from urllib.parse import SplitResult, urlsplit
|
|
8
|
+
|
|
9
|
+
from pydantic import Field, model_validator
|
|
10
|
+
from pydantic_config import BaseConfig
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def parse_network_rule(rule: str) -> tuple[SplitResult, str, int | None]:
|
|
14
|
+
"""Normalize host/port while retaining URL syntax for backend validation."""
|
|
15
|
+
parsed = urlsplit(rule if "://" in rule else f"//{rule}")
|
|
16
|
+
port = parsed.port
|
|
17
|
+
if parsed.scheme and port is None:
|
|
18
|
+
port = 443 if parsed.scheme == "https" else 80
|
|
19
|
+
return parsed, (parsed.hostname or "").lower().rstrip("."), port
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def network_rule_matches(rule: str, scheme: str, host: str, port: int) -> bool:
|
|
23
|
+
"""Match a network-policy host pattern or URL origin. Paths are ignored."""
|
|
24
|
+
try:
|
|
25
|
+
parsed, pattern, rule_port = parse_network_rule(rule)
|
|
26
|
+
except ValueError:
|
|
27
|
+
return False
|
|
28
|
+
if not pattern or (parsed.scheme and parsed.scheme != scheme):
|
|
29
|
+
return False
|
|
30
|
+
if rule_port is not None and rule_port != port:
|
|
31
|
+
return False
|
|
32
|
+
host = host.lower().rstrip(".")
|
|
33
|
+
return fnmatchcase(host, pattern) or (
|
|
34
|
+
pattern.startswith("*.") and host == pattern[2:]
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def intersect_network_hosts(left: str, right: str) -> str:
|
|
39
|
+
"""Return the narrower host pattern, rejecting unprovable glob overlaps."""
|
|
40
|
+
for parent, child in ((left, right), (right, left)):
|
|
41
|
+
if parent in ("*", child):
|
|
42
|
+
return child
|
|
43
|
+
# Matching a pattern as text proves containment only for domain suffixes.
|
|
44
|
+
if has_magic(child) and not (
|
|
45
|
+
parent.startswith("*.") and not has_magic(parent[2:])
|
|
46
|
+
):
|
|
47
|
+
continue
|
|
48
|
+
if fnmatchcase(child, parent) or child == parent.removeprefix("*."):
|
|
49
|
+
return child
|
|
50
|
+
if all(has_magic(host) for host in (left, right)) and any(
|
|
51
|
+
has_magic(host.removeprefix("*.")) for host in (left, right)
|
|
52
|
+
):
|
|
53
|
+
raise ValueError(f"cannot intersect network hosts {left!r} and {right!r}")
|
|
54
|
+
return ""
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class NetworkPolicyConfig(BaseConfig):
|
|
58
|
+
"""Shared execution-time policy surface for runtimes that support it."""
|
|
59
|
+
|
|
60
|
+
allow: list[str] = Field(default_factory=lambda: ["*"])
|
|
61
|
+
"""Destinations allowed during execution; `*` is unrestricted and `[]` is
|
|
62
|
+
framework-only."""
|
|
63
|
+
block: list[str] = Field(default_factory=list)
|
|
64
|
+
"""Destinations denied during execution; any `*` makes the policy framework-only."""
|
|
65
|
+
|
|
66
|
+
@model_validator(mode="after")
|
|
67
|
+
def validate_network_policy(self) -> Self:
|
|
68
|
+
if not self.allow or "*" in self.block:
|
|
69
|
+
# Empty allowlists and wildcard blocks both mean framework-only access.
|
|
70
|
+
self.allow = []
|
|
71
|
+
self.block = ["*"]
|
|
72
|
+
elif self.allow != ["*"] and self.block:
|
|
73
|
+
raise ValueError(
|
|
74
|
+
"non-empty concrete allow and block egress lists are mutually exclusive"
|
|
75
|
+
)
|
|
76
|
+
return self
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def network_restricted(self) -> bool:
|
|
80
|
+
return "*" not in self.allow or bool(self.block)
|
|
81
|
+
|
|
82
|
+
def permits(self, scheme: str, host: str, port: int) -> bool:
|
|
83
|
+
"""Whether the destination is allowed by the configured egress rules."""
|
|
84
|
+
return not any(
|
|
85
|
+
network_rule_matches(rule, scheme, host, port) for rule in self.block
|
|
86
|
+
) and any(network_rule_matches(rule, scheme, host, port) for rule in self.allow)
|
|
87
|
+
|
|
88
|
+
def with_task_network_policy(self, allow: list[str], block: list[str]) -> Self:
|
|
89
|
+
values = self.model_dump()
|
|
90
|
+
# Intersection must not erase syntax the runtime cannot enforce.
|
|
91
|
+
task = type(self).model_validate({**values, "allow": allow, "block": block})
|
|
92
|
+
allow, block = task.allow, task.block
|
|
93
|
+
if "*" in allow:
|
|
94
|
+
allow = self.allow
|
|
95
|
+
elif "*" not in self.allow:
|
|
96
|
+
intersection = []
|
|
97
|
+
for left, right in product(allow, self.allow):
|
|
98
|
+
if left == right:
|
|
99
|
+
intersection.append(left)
|
|
100
|
+
continue
|
|
101
|
+
a, a_host, a_port = parse_network_rule(left)
|
|
102
|
+
b, b_host, b_port = parse_network_rule(right)
|
|
103
|
+
# Bare paths can be provider-specific CIDRs, not URL paths to discard.
|
|
104
|
+
if any(
|
|
105
|
+
not url.hostname or (url.path and not url.scheme) for url in (a, b)
|
|
106
|
+
):
|
|
107
|
+
raise ValueError(
|
|
108
|
+
f"cannot intersect network rules {left!r} and {right!r}"
|
|
109
|
+
)
|
|
110
|
+
if (
|
|
111
|
+
len({a.scheme, b.scheme} - {""}) > 1
|
|
112
|
+
or len({a_port, b_port} - {None}) > 1
|
|
113
|
+
):
|
|
114
|
+
continue
|
|
115
|
+
host = intersect_network_hosts(a_host, b_host)
|
|
116
|
+
if not host:
|
|
117
|
+
continue
|
|
118
|
+
scheme = a.scheme or b.scheme
|
|
119
|
+
port = a_port if a_port is not None else b_port
|
|
120
|
+
authority = f"[{host}]" if ":" in host else host
|
|
121
|
+
if port is not None:
|
|
122
|
+
authority = f"{authority}:{port}"
|
|
123
|
+
intersection.append(f"{scheme}://{authority}" if scheme else authority)
|
|
124
|
+
allow = list(dict.fromkeys(intersection))
|
|
125
|
+
block = list(dict.fromkeys([*block, *self.block]))
|
|
126
|
+
return type(self).model_validate({**values, "allow": allow, "block": block})
|
|
@@ -18,9 +18,9 @@ from verifiers.v1.configs.runtime import NetworkPolicyConfig
|
|
|
18
18
|
from verifiers.v1.dialects.base import (
|
|
19
19
|
Dialect,
|
|
20
20
|
RawRequest,
|
|
21
|
+
RequestFilter,
|
|
21
22
|
StreamParser,
|
|
22
23
|
append_user_notice,
|
|
23
|
-
blocked_url,
|
|
24
24
|
parse_sse_event,
|
|
25
25
|
provider_domains,
|
|
26
26
|
)
|
|
@@ -129,89 +129,66 @@ def parse_content(content) -> str | list[ContentPart]:
|
|
|
129
129
|
return parts
|
|
130
130
|
|
|
131
131
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
) -> str | None:
|
|
135
|
-
if isinstance(value, list):
|
|
136
|
-
for index, item in enumerate(value):
|
|
137
|
-
if blocked := blocked_content_path(
|
|
138
|
-
item, f"{path}[{index}]", policy, blocked_urls
|
|
139
|
-
):
|
|
140
|
-
return blocked
|
|
141
|
-
return None
|
|
142
|
-
if not isinstance(value, dict):
|
|
143
|
-
return None
|
|
132
|
+
class AnthropicRequestFilter(RequestFilter):
|
|
133
|
+
wrappers = _CONTENT_WRAPPERS
|
|
144
134
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
source.get("content"), f"{source_path}.content", policy, blocked_urls
|
|
135
|
+
def tool(self, tool, path: str) -> dict | None:
|
|
136
|
+
kind = tool.get("type") if isinstance(tool, dict) else None
|
|
137
|
+
if isinstance(kind, str) and _WEB_TOOL_TYPE(kind):
|
|
138
|
+
callers = tool.get("allowed_callers")
|
|
139
|
+
filter_key = "blocked_domains" if self.policy.block else "allowed_domains"
|
|
140
|
+
other_key = "allowed_domains" if self.policy.block else "blocked_domains"
|
|
141
|
+
domains = (
|
|
142
|
+
provider_domains(self.policy, tool.get(filter_key))
|
|
143
|
+
# Anthropic does not support combining allow and block filters.
|
|
144
|
+
if tool.get(other_key) in (None, [])
|
|
145
|
+
and (
|
|
146
|
+
callers is None or isinstance(callers, list) and "direct" in callers
|
|
147
|
+
)
|
|
148
|
+
else []
|
|
160
149
|
)
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
150
|
+
if domains:
|
|
151
|
+
web_tool = {**tool, filter_key: domains, "allowed_callers": ["direct"]}
|
|
152
|
+
web_tool.pop(other_key, None)
|
|
153
|
+
return web_tool
|
|
154
|
+
if isinstance(tool, dict) and (
|
|
155
|
+
(isinstance(kind, str) and _CLIENT_TOOL_TYPE(kind))
|
|
156
|
+
or (kind in (None, "custom") and "input_schema" in tool)
|
|
157
|
+
):
|
|
158
|
+
return tool
|
|
159
|
+
self.capabilities.append(f"{path}.type")
|
|
160
|
+
return None
|
|
161
|
+
|
|
162
|
+
def blocked_part(self, value: dict, path: str) -> str | None:
|
|
163
|
+
kind = value.get("type")
|
|
164
|
+
if kind in ("image", "document"):
|
|
165
|
+
source_path = f"{path}.source"
|
|
166
|
+
source = value.get("source") or {}
|
|
167
|
+
if not isinstance(source, dict):
|
|
168
|
+
return source_path
|
|
169
|
+
source_kind = source.get("type")
|
|
170
|
+
if source_kind == "content":
|
|
171
|
+
return self.blocked(source.get("content"), f"{source_path}.content")
|
|
172
|
+
if source_kind == "url" and self.blocked_url(source.get("url")):
|
|
164
173
|
return f"{source_path}.url"
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
)
|
|
184
|
-
return None if kind in _SAFE_CONTENT_TYPES else f"{path}.type"
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
def mediate_content(
|
|
188
|
-
value, path: str, policy: NetworkPolicyConfig, blocked_urls: list[str]
|
|
189
|
-
):
|
|
190
|
-
if not isinstance(value, list):
|
|
191
|
-
blocked = blocked_content_path(value, path, policy, blocked_urls)
|
|
192
|
-
return ("", [blocked]) if blocked else (value, [])
|
|
193
|
-
|
|
194
|
-
mediated = []
|
|
195
|
-
capabilities = []
|
|
196
|
-
for index, block in enumerate(value):
|
|
197
|
-
item_path = f"{path}[{index}]"
|
|
198
|
-
if isinstance(block, dict) and block.get("type") in _CONTENT_WRAPPERS:
|
|
199
|
-
if blocked := blocked_content_path(
|
|
200
|
-
{**block, "content": []}, item_path, policy, blocked_urls
|
|
201
|
-
):
|
|
202
|
-
capabilities.append(blocked)
|
|
203
|
-
continue
|
|
204
|
-
content, removed = mediate_content(
|
|
205
|
-
block.get("content"), f"{item_path}.content", policy, blocked_urls
|
|
206
|
-
)
|
|
207
|
-
if removed:
|
|
208
|
-
block["content"] = content or ""
|
|
209
|
-
capabilities.extend(removed)
|
|
210
|
-
elif blocked := blocked_content_path(block, item_path, policy, blocked_urls):
|
|
211
|
-
capabilities.append(blocked)
|
|
212
|
-
continue
|
|
213
|
-
mediated.append(block)
|
|
214
|
-
return mediated, capabilities
|
|
174
|
+
if source_kind == "file":
|
|
175
|
+
return (
|
|
176
|
+
f"{source_path}.file_id"
|
|
177
|
+
if source.get("file_id")
|
|
178
|
+
else f"{source_path}.type"
|
|
179
|
+
)
|
|
180
|
+
if source_kind not in ("base64", "text", "url"):
|
|
181
|
+
return f"{source_path}.type"
|
|
182
|
+
|
|
183
|
+
if kind in (
|
|
184
|
+
"container_upload",
|
|
185
|
+
"code_execution_output",
|
|
186
|
+
"bash_code_execution_output",
|
|
187
|
+
) and value.get("file_id"):
|
|
188
|
+
return f"{path}.file_id"
|
|
189
|
+
if kind in self.wrappers:
|
|
190
|
+
return self.blocked(value.get("content"), f"{path}.content")
|
|
191
|
+
return None if kind in _SAFE_CONTENT_TYPES else f"{path}.type"
|
|
215
192
|
|
|
216
193
|
|
|
217
194
|
def content_to_wire(content) -> str | list[dict]:
|
|
@@ -464,80 +441,29 @@ class AnthropicDialect(Dialect[AnthropicMessage]):
|
|
|
464
441
|
self, body: RawRequest, policy: NetworkPolicyConfig
|
|
465
442
|
) -> tuple[RawRequest, list[str]]:
|
|
466
443
|
mediated = body
|
|
467
|
-
|
|
468
|
-
|
|
444
|
+
request_filter = AnthropicRequestFilter(policy)
|
|
445
|
+
capabilities = request_filter.capabilities
|
|
469
446
|
|
|
470
447
|
for key in ("container", "mcp_servers"):
|
|
471
448
|
if mediated.pop(key, None):
|
|
472
449
|
capabilities.append(key)
|
|
473
450
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
if system:
|
|
480
|
-
mediated["system"] = system
|
|
481
|
-
else:
|
|
482
|
-
mediated.pop("system")
|
|
451
|
+
if (
|
|
452
|
+
request_filter.content(mediated, "system", "system")
|
|
453
|
+
and not mediated["system"]
|
|
454
|
+
):
|
|
455
|
+
mediated.pop("system")
|
|
483
456
|
|
|
484
457
|
for message_index, message in enumerate(mediated.get("messages") or []):
|
|
485
458
|
if not isinstance(message, dict):
|
|
486
459
|
continue
|
|
487
|
-
content
|
|
488
|
-
message
|
|
460
|
+
request_filter.content(
|
|
461
|
+
message,
|
|
462
|
+
"content",
|
|
489
463
|
f"messages[{message_index}].content",
|
|
490
|
-
policy,
|
|
491
|
-
blocked_urls,
|
|
492
464
|
)
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
message["content"] = content or ""
|
|
496
|
-
|
|
497
|
-
raw_tools = mediated.get("tools")
|
|
498
|
-
tool_items = raw_tools if isinstance(raw_tools, list) else []
|
|
499
|
-
if raw_tools is not None and not isinstance(raw_tools, list):
|
|
500
|
-
capabilities.append("tools")
|
|
501
|
-
tools = []
|
|
502
|
-
for index, tool in enumerate(tool_items):
|
|
503
|
-
kind = tool.get("type") if isinstance(tool, dict) else None
|
|
504
|
-
if (
|
|
505
|
-
isinstance(tool, dict)
|
|
506
|
-
and isinstance(kind, str)
|
|
507
|
-
and _WEB_TOOL_TYPE(kind)
|
|
508
|
-
):
|
|
509
|
-
callers = tool.get("allowed_callers")
|
|
510
|
-
filter_key = "blocked_domains" if policy.block else "allowed_domains"
|
|
511
|
-
other_key = "allowed_domains" if policy.block else "blocked_domains"
|
|
512
|
-
domains = (
|
|
513
|
-
provider_domains(policy, tool.get(filter_key))
|
|
514
|
-
# Anthropic does not support combining allow and block filters.
|
|
515
|
-
if tool.get(other_key) in (None, [])
|
|
516
|
-
and (
|
|
517
|
-
callers is None
|
|
518
|
-
or isinstance(callers, list)
|
|
519
|
-
and "direct" in callers
|
|
520
|
-
)
|
|
521
|
-
else []
|
|
522
|
-
)
|
|
523
|
-
if domains:
|
|
524
|
-
web_tool = {
|
|
525
|
-
**tool,
|
|
526
|
-
filter_key: domains,
|
|
527
|
-
"allowed_callers": ["direct"],
|
|
528
|
-
}
|
|
529
|
-
web_tool.pop(other_key, None)
|
|
530
|
-
tools.append(web_tool)
|
|
531
|
-
continue
|
|
532
|
-
capabilities.append(f"tools[{index}].type")
|
|
533
|
-
continue
|
|
534
|
-
if isinstance(tool, dict) and (
|
|
535
|
-
(isinstance(kind, str) and _CLIENT_TOOL_TYPE(kind))
|
|
536
|
-
or (kind in (None, "custom") and "input_schema" in tool)
|
|
537
|
-
):
|
|
538
|
-
tools.append(tool)
|
|
539
|
-
continue
|
|
540
|
-
capabilities.append(f"tools[{index}].type")
|
|
465
|
+
|
|
466
|
+
tools = request_filter.tools(mediated.get("tools"))
|
|
541
467
|
if "tools" in mediated:
|
|
542
468
|
mediated["tools"] = tools
|
|
543
469
|
|
|
@@ -562,7 +488,8 @@ class AnthropicDialect(Dialect[AnthropicMessage]):
|
|
|
562
488
|
|
|
563
489
|
if capabilities:
|
|
564
490
|
append_user_notice(
|
|
565
|
-
mediated.setdefault("messages", []),
|
|
491
|
+
mediated.setdefault("messages", []),
|
|
492
|
+
blocked_urls=request_filter.blocked_urls,
|
|
566
493
|
)
|
|
567
494
|
return mediated, capabilities
|
|
568
495
|
|
|
@@ -15,13 +15,17 @@ import json
|
|
|
15
15
|
import logging
|
|
16
16
|
from abc import ABC, abstractmethod
|
|
17
17
|
from collections.abc import Callable, Mapping
|
|
18
|
+
from glob import has_magic
|
|
18
19
|
from typing import Any, ClassVar, Generic, TypeVar
|
|
19
|
-
from urllib.parse import urlsplit
|
|
20
20
|
|
|
21
21
|
from pydantic import AnyHttpUrl, BaseModel, ValidationError
|
|
22
22
|
from pydantic_core import from_json
|
|
23
23
|
|
|
24
|
-
from verifiers.v1.configs.runtime import
|
|
24
|
+
from verifiers.v1.configs.runtime import (
|
|
25
|
+
NetworkPolicyConfig,
|
|
26
|
+
intersect_network_hosts,
|
|
27
|
+
parse_network_rule,
|
|
28
|
+
)
|
|
25
29
|
from verifiers.v1.types import Request, Response, Sampling, SamplingConfig
|
|
26
30
|
|
|
27
31
|
RespT = TypeVar("RespT", bound=BaseModel)
|
|
@@ -36,21 +40,90 @@ CAPABILITY_NOTICE = (
|
|
|
36
40
|
)
|
|
37
41
|
|
|
38
42
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
43
|
+
class RequestFilter:
|
|
44
|
+
"""One request's omissions and blocked URLs; subclasses define native wire rules."""
|
|
45
|
+
|
|
46
|
+
wrappers: tuple[str, ...] = ()
|
|
47
|
+
|
|
48
|
+
def __init__(self, policy: NetworkPolicyConfig):
|
|
49
|
+
self.policy = policy
|
|
50
|
+
self.blocked_urls: list[str] = []
|
|
51
|
+
self.capabilities: list[str] = []
|
|
52
|
+
|
|
53
|
+
def blocked_url(self, value: object) -> bool:
|
|
54
|
+
if not isinstance(value, str):
|
|
55
|
+
return True
|
|
56
|
+
if value.lower().startswith("data:"):
|
|
57
|
+
return False
|
|
58
|
+
try:
|
|
59
|
+
url = AnyHttpUrl(value)
|
|
60
|
+
except ValidationError:
|
|
61
|
+
return True
|
|
62
|
+
blocked = not self.policy.permits(url.scheme, url.host.strip("[]"), url.port)
|
|
63
|
+
if blocked:
|
|
64
|
+
self.blocked_urls.append(value)
|
|
65
|
+
return blocked
|
|
66
|
+
|
|
67
|
+
def blocked(self, value, path: str) -> str | None:
|
|
68
|
+
"""Find the first forbidden part, treating nested content as one unit."""
|
|
69
|
+
if isinstance(value, list):
|
|
70
|
+
for index, item in enumerate(value):
|
|
71
|
+
if blocked := self.blocked(item, f"{path}[{index}]"):
|
|
72
|
+
return blocked
|
|
73
|
+
return None
|
|
74
|
+
if not isinstance(value, dict):
|
|
75
|
+
return None
|
|
76
|
+
caller = value.get("caller")
|
|
77
|
+
if caller is not None and not (
|
|
78
|
+
isinstance(caller, dict) and caller.get("type") == "direct"
|
|
79
|
+
):
|
|
80
|
+
return f"{path}.caller.type"
|
|
81
|
+
return self.blocked_part(value, path)
|
|
82
|
+
|
|
83
|
+
def blocked_part(self, value: dict, path: str) -> str | None:
|
|
84
|
+
raise NotImplementedError
|
|
85
|
+
|
|
86
|
+
def mediate(self, value, path: str):
|
|
87
|
+
"""Remove forbidden parts while retaining supported wrapper blocks."""
|
|
88
|
+
if not isinstance(value, list):
|
|
89
|
+
if blocked := self.blocked(value, path):
|
|
90
|
+
self.capabilities.append(blocked)
|
|
91
|
+
return ""
|
|
92
|
+
return value
|
|
93
|
+
mediated = []
|
|
94
|
+
for index, block in enumerate(value):
|
|
95
|
+
item_path = f"{path}[{index}]"
|
|
96
|
+
wrapper = isinstance(block, dict) and block.get("type") in self.wrappers
|
|
97
|
+
scan = {**block, "content": []} if wrapper else block
|
|
98
|
+
if blocked := self.blocked(scan, item_path):
|
|
99
|
+
self.capabilities.append(blocked)
|
|
100
|
+
continue
|
|
101
|
+
if wrapper:
|
|
102
|
+
self.content(block, "content", f"{item_path}.content")
|
|
103
|
+
mediated.append(block)
|
|
104
|
+
return mediated
|
|
105
|
+
|
|
106
|
+
def content(self, parent: dict, key: str, path: str) -> bool:
|
|
107
|
+
"""Rewrite a content field only when filtering removes something."""
|
|
108
|
+
before = len(self.capabilities)
|
|
109
|
+
content = self.mediate(parent.get(key), path)
|
|
110
|
+
changed = len(self.capabilities) != before
|
|
111
|
+
if changed:
|
|
112
|
+
parent[key] = content or ""
|
|
113
|
+
return changed
|
|
114
|
+
|
|
115
|
+
def tools(self, value, path: str = "tools") -> list[dict]:
|
|
116
|
+
if value is not None and not isinstance(value, list):
|
|
117
|
+
self.capabilities.append(path)
|
|
118
|
+
return []
|
|
119
|
+
tools = []
|
|
120
|
+
for index, tool in enumerate(value or []):
|
|
121
|
+
if (filtered := self.tool(tool, f"{path}[{index}]")) is not None:
|
|
122
|
+
tools.append(filtered)
|
|
123
|
+
return tools
|
|
124
|
+
|
|
125
|
+
def tool(self, value, path: str) -> dict | None:
|
|
126
|
+
raise NotImplementedError
|
|
54
127
|
|
|
55
128
|
|
|
56
129
|
def provider_domains(
|
|
@@ -62,75 +135,54 @@ def provider_domains(
|
|
|
62
135
|
Empty results mean the policy cannot be represented; never send an empty filter.
|
|
63
136
|
"""
|
|
64
137
|
rules = policy.block or policy.allow
|
|
65
|
-
if not
|
|
138
|
+
if requested is not None and not isinstance(requested, list):
|
|
66
139
|
return []
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
140
|
+
hosts, requested_domains = [], []
|
|
141
|
+
for entries, output, is_filter in (
|
|
142
|
+
(rules, hosts, False),
|
|
143
|
+
(requested or [], requested_domains, True),
|
|
144
|
+
):
|
|
145
|
+
for rule in entries:
|
|
146
|
+
if not isinstance(rule, str):
|
|
147
|
+
return []
|
|
148
|
+
try:
|
|
149
|
+
url, host, port = parse_network_rule(rule)
|
|
150
|
+
except ValueError:
|
|
151
|
+
return []
|
|
152
|
+
if is_filter and (
|
|
153
|
+
url.username is not None or url.path or url.query or url.fragment
|
|
154
|
+
):
|
|
155
|
+
return []
|
|
156
|
+
domain = host if is_filter else host.removeprefix("*.")
|
|
157
|
+
if (
|
|
158
|
+
url.scheme
|
|
159
|
+
or port is not None
|
|
160
|
+
or not domain
|
|
161
|
+
or has_magic(domain)
|
|
162
|
+
or not domain.isascii()
|
|
163
|
+
):
|
|
164
|
+
return []
|
|
165
|
+
output.append(host)
|
|
87
166
|
for host in hosts:
|
|
88
167
|
if not host.startswith("*.") and not (
|
|
89
168
|
policy.block
|
|
90
169
|
and any(
|
|
91
170
|
wildcard.startswith("*.")
|
|
92
|
-
and (host ==
|
|
171
|
+
and intersect_network_hosts(wildcard, host) == host
|
|
93
172
|
for wildcard in hosts
|
|
94
173
|
)
|
|
95
174
|
):
|
|
96
175
|
return []
|
|
97
|
-
domains = list(dict.fromkeys(
|
|
176
|
+
domains = list(dict.fromkeys(host.removeprefix("*.") for host in hosts))
|
|
98
177
|
if requested is None:
|
|
99
178
|
return domains
|
|
100
|
-
if not isinstance(requested, list):
|
|
101
|
-
return []
|
|
102
|
-
requested_domains = []
|
|
103
|
-
for domain in requested:
|
|
104
|
-
if not isinstance(domain, str):
|
|
105
|
-
return []
|
|
106
|
-
try:
|
|
107
|
-
url = urlsplit(domain if "://" in domain else f"//{domain}")
|
|
108
|
-
port = url.port
|
|
109
|
-
except ValueError:
|
|
110
|
-
return []
|
|
111
|
-
host = (url.hostname or "").lower().rstrip(".")
|
|
112
|
-
if (
|
|
113
|
-
url.scheme
|
|
114
|
-
or port is not None
|
|
115
|
-
or url.username is not None
|
|
116
|
-
or url.path
|
|
117
|
-
or url.query
|
|
118
|
-
or url.fragment
|
|
119
|
-
or not host
|
|
120
|
-
or any(char in host for char in "*?[]")
|
|
121
|
-
or not host.isascii()
|
|
122
|
-
):
|
|
123
|
-
return []
|
|
124
|
-
requested_domains.append(host)
|
|
125
179
|
if policy.block:
|
|
126
180
|
return list(dict.fromkeys([*domains, *requested_domains]))
|
|
127
181
|
intersection = []
|
|
128
182
|
for allowed in domains:
|
|
129
183
|
for requested_domain in requested_domains:
|
|
130
|
-
if
|
|
131
|
-
intersection.append(
|
|
132
|
-
elif requested_domain.endswith(f".{allowed}"):
|
|
133
|
-
intersection.append(requested_domain)
|
|
184
|
+
if host := intersect_network_hosts(f"*.{allowed}", f"*.{requested_domain}"):
|
|
185
|
+
intersection.append(host.removeprefix("*."))
|
|
134
186
|
return list(dict.fromkeys(intersection))
|
|
135
187
|
|
|
136
188
|
|