verifiers 0.3.2.dev125__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.dev125 → verifiers-0.3.2.dev127}/PKG-INFO +1 -1
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/acp/__init__.py +15 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/acp/runner.py +68 -4
- verifiers-0.3.2.dev127/verifiers/v1/configs/runtime.py +126 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/dialects/anthropic.py +72 -145
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/dialects/base.py +121 -69
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/dialects/chat.py +49 -54
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/dialects/responses.py +80 -163
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/graph.py +11 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harness.py +4 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/claude_code/harness.py +14 -0
- verifiers-0.3.2.dev127/verifiers/v1/harnesses/codex/gate.mjs +34 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/codex/harness.py +46 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/kimi_code/harness.py +14 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/null/harness.py +5 -0
- verifiers-0.3.2.dev127/verifiers/v1/harnesses/pi/gate.mjs +11 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/pi/harness.py +18 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/utils/core.py +20 -28
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/interception/server.py +30 -12
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/rollout.py +1 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/runtimes/modal.py +3 -6
- verifiers-0.3.2.dev127/verifiers/v1/session.py +605 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/task.py +2 -2
- verifiers-0.3.2.dev125/verifiers/v1/configs/runtime.py +0 -72
- verifiers-0.3.2.dev125/verifiers/v1/interception/tool.py +0 -12
- verifiers-0.3.2.dev125/verifiers/v1/session.py +0 -418
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/.gitignore +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/LICENSE +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/README.md +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/pyproject.toml +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/conftest.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/fixtures/counter_tool_v1.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/fixtures/duet_v1.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/fixtures/echo_acp_resume_v1.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/fixtures/echo_agentic_v1.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/fixtures/echo_tool_v1.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/fixtures/echo_user_sim_v1.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/fixtures/echo_v1.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/fixtures/pyproject.toml +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/fixtures/tool_response_image_v1.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/test_color_codeword_tasks.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/test_configs.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/test_e2e.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/test_envs.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/test_graph.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/test_judges.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/test_platform_run.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/test_scoring.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/test_serve_delta.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/test_taskset.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/test_trace.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/agent.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/cli/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/cli/dashboard/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/cli/dashboard/base.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/cli/dashboard/eval.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/cli/dashboard/replay.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/cli/dashboard/validate.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/cli/debug.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/cli/eval/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/cli/eval/hint.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/cli/eval/main.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/cli/eval/resume.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/cli/eval/runner.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/cli/gepa.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/cli/init.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/cli/output.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/cli/replay.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/cli/resolve.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/cli/resume.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/cli/validate.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/clients/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/clients/base.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/clients/client.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/clients/eval.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/clients/train.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/configs/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/configs/agent.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/configs/cli/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/configs/cli/debug.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/configs/cli/env.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/configs/cli/eval.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/configs/cli/init.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/configs/cli/replay.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/configs/cli/validate.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/configs/client.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/configs/env.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/configs/harness.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/configs/judge.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/configs/retries.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/configs/serve.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/configs/task.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/configs/taskset.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/dialects/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/env.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/envs/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/envs/agentic_judge/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/envs/agentic_judge/env.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/envs/best_of_n/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/envs/best_of_n/env.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/envs/isolated_verifier/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/envs/isolated_verifier/env.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/envs/shared_agentic_judge/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/envs/single_agent/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/envs/single_agent/env.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/envs/user_sim/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/envs/user_sim/env.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/episode.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/errors.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/gepa/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/gepa/adapter.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/gepa/config.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/gepa/dataset.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/gepa/reflection.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/gepa/runner.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/bash/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/bash/harness.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/browser_use/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/browser_use/harness.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/browser_use/program.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/claude_code/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/codex/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/hermes_agent/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/hermes_agent/harness.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/hermes_agent/program.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/kimi_code/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/mini_swe_agent/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/mini_swe_agent/harness.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/mini_swe_agent/program.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/node.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/null/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/openclaw/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/openclaw/harness.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/pi/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/prime_agent/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/prime_agent/harness.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/rlm/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/rlm/harness.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/terminus_2/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/terminus_2/harness.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/terminus_2/program.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/utils/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/utils/compaction.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/utils/install.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/utils/launch.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/utils/mcp.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/interception/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/interception/base.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/interception/pool.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/interception/tunnel/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/interception/tunnel/base.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/interception/tunnel/custom.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/interception/tunnel/prime.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/judge.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/judges/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/judges/reference.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/judges/reference.txt +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/judges/rubric.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/judges/rubric.txt +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/mcp/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/mcp/launch.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/mcp/server.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/mcp/toolset.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/runtimes/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/runtimes/apptainer.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/runtimes/base.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/runtimes/container.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/runtimes/docker/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/runtimes/docker/egress.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/runtimes/limiters.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/runtimes/prime.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/runtimes/subprocess.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/semantic.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/serve/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/serve/client.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/serve/delta.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/serve/encoding.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/serve/pool.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/serve/server.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/serve/types.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/state.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/taskset.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/harbor/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/harbor/env.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/harbor/taskset.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/harbor/toolset.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/lean/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/lean/scoring.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/lean/taskset.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/nemo_gym/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/nemo_gym/response.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/nemo_gym/server.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/nemo_gym/taskset.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/nemo_gym/toolset.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/openenv/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/openenv/taskset.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/textarena/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/textarena/taskset.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/trace.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/types.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/utils/__init__.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/utils/aio.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/utils/artifacts.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/utils/compile.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/utils/decorators.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/utils/format.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/utils/generic.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/utils/git.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/utils/image.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/utils/interrupt.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/utils/loaders.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/utils/logging.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/utils/memory.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/utils/paths.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/utils/platform.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/utils/prime.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/utils/retries.py +0 -0
- {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/utils/score.py +0 -0
- {verifiers-0.3.2.dev125 → 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
|
|
@@ -85,6 +85,13 @@ class ACPHarness(Harness[ConfigT]):
|
|
|
85
85
|
)
|
|
86
86
|
trace.add_semantic_edges(edge_set)
|
|
87
87
|
|
|
88
|
+
async def gate_tools(
|
|
89
|
+
self, config: ACPConfig, runtime: Runtime, url: str, secret: str
|
|
90
|
+
) -> None:
|
|
91
|
+
"""Configure the agent to ask before every tool call, so each request reaches the
|
|
92
|
+
runner's gate (`/tool` at `url`, keyed by `secret`). A harness that advertises
|
|
93
|
+
`SUPPORTS_TOOL_INTERCEPTION` implements this with the agent's own config."""
|
|
94
|
+
|
|
88
95
|
@abstractmethod
|
|
89
96
|
async def prepare_acp(
|
|
90
97
|
self,
|
|
@@ -116,6 +123,8 @@ class ACPHarness(Harness[ConfigT]):
|
|
|
116
123
|
config = await self.prepare_acp(
|
|
117
124
|
ctx, trace, runtime, endpoint, secret, mcp_urls, data
|
|
118
125
|
)
|
|
126
|
+
if tool_interception_url is not None:
|
|
127
|
+
await self.gate_tools(config, runtime, tool_interception_url, secret)
|
|
119
128
|
return ACPHarnessSession(
|
|
120
129
|
self,
|
|
121
130
|
ctx,
|
|
@@ -268,6 +277,12 @@ class ACPHarnessSession(HarnessSession):
|
|
|
268
277
|
"system_prompt": self.config.system_prompt or "",
|
|
269
278
|
"session_meta": self.config.session_meta or {},
|
|
270
279
|
"client_capabilities": self.config.client_capabilities or {},
|
|
280
|
+
"tool_interception": {
|
|
281
|
+
"url": self.tool_interception_url,
|
|
282
|
+
"secret": self.secret,
|
|
283
|
+
}
|
|
284
|
+
if self.tool_interception_url
|
|
285
|
+
else None,
|
|
271
286
|
}
|
|
272
287
|
async with self._lock:
|
|
273
288
|
if self._closed:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# /// script
|
|
2
2
|
# requires-python = ">=3.10,<3.15"
|
|
3
|
-
# dependencies = ["agent-client-protocol==0.12.1"]
|
|
3
|
+
# dependencies = ["agent-client-protocol==0.12.1", "httpx"]
|
|
4
4
|
# ///
|
|
5
5
|
"""Run harness segments through an ACP agent."""
|
|
6
6
|
|
|
@@ -14,6 +14,7 @@ from contextlib import AsyncExitStack, suppress
|
|
|
14
14
|
from dataclasses import asdict, dataclass
|
|
15
15
|
from typing import Any
|
|
16
16
|
|
|
17
|
+
import httpx
|
|
17
18
|
from acp import (
|
|
18
19
|
PROTOCOL_VERSION,
|
|
19
20
|
Client,
|
|
@@ -45,8 +46,42 @@ class ACPTurn:
|
|
|
45
46
|
update_metadata: list[dict[str, Any]]
|
|
46
47
|
|
|
47
48
|
|
|
49
|
+
class ToolGate:
|
|
50
|
+
"""The rollout's `/tool` gate, asked before every tool call the agent wants to run."""
|
|
51
|
+
|
|
52
|
+
def __init__(self, url: str, secret: str) -> None:
|
|
53
|
+
self.url = url
|
|
54
|
+
self.client = httpx.AsyncClient(
|
|
55
|
+
headers={"Authorization": f"Bearer {secret}"},
|
|
56
|
+
timeout=httpx.Timeout(120, connect=5),
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
async def decision(self, tool_call_id: str, arguments: Any) -> str:
|
|
60
|
+
try:
|
|
61
|
+
# pi-acp wraps extension confirmations in a separate UI permission call.
|
|
62
|
+
if (
|
|
63
|
+
tool_call_id.startswith("pi-ui-")
|
|
64
|
+
and isinstance(arguments, dict)
|
|
65
|
+
and arguments.get("method") == "confirm"
|
|
66
|
+
):
|
|
67
|
+
tool_call_id, arguments = (
|
|
68
|
+
arguments["title"],
|
|
69
|
+
json.loads(arguments["message"]),
|
|
70
|
+
)
|
|
71
|
+
response = await self.client.post(
|
|
72
|
+
self.url, json={"tool_call_id": tool_call_id, "arguments": arguments}
|
|
73
|
+
)
|
|
74
|
+
response.raise_for_status()
|
|
75
|
+
return response.json()["action"]
|
|
76
|
+
except Exception as error: # noqa: BLE001 - an unreachable gate lets nothing run
|
|
77
|
+
print(f"tool gate denied {tool_call_id}: {error}", file=sys.stderr)
|
|
78
|
+
return "deny"
|
|
79
|
+
|
|
80
|
+
|
|
48
81
|
class VerifiersACPClient(Client):
|
|
49
82
|
def __init__(self) -> None:
|
|
83
|
+
self.gate: ToolGate | None = None
|
|
84
|
+
self.prompt_task: asyncio.Task | None = None
|
|
50
85
|
self.visible_reply = ""
|
|
51
86
|
self.message_id: str | None = None
|
|
52
87
|
self.stop_reason: str | None = None
|
|
@@ -90,9 +125,23 @@ class VerifiersACPClient(Client):
|
|
|
90
125
|
options: list[PermissionOption],
|
|
91
126
|
**kwargs: Any,
|
|
92
127
|
) -> RequestPermissionResponse:
|
|
128
|
+
"""Ask the rollout before execution: deny rejects one call; stop cancels the turn."""
|
|
129
|
+
kinds = ("allow_once", "allow_always")
|
|
130
|
+
if self.gate is not None:
|
|
131
|
+
decision = await self.gate.decision(
|
|
132
|
+
tool_call.tool_call_id, tool_call.raw_input
|
|
133
|
+
)
|
|
134
|
+
if decision == "stop":
|
|
135
|
+
self.stop_reason = "cancelled"
|
|
136
|
+
if self.prompt_task is not None:
|
|
137
|
+
self.prompt_task.cancel()
|
|
138
|
+
return RequestPermissionResponse(
|
|
139
|
+
outcome=DeniedOutcome(outcome="cancelled")
|
|
140
|
+
)
|
|
141
|
+
if decision != "allow":
|
|
142
|
+
kinds = ("reject_once", "reject_always")
|
|
93
143
|
option = next(
|
|
94
|
-
(item for item in options if item.kind
|
|
95
|
-
None,
|
|
144
|
+
(item for kind in kinds for item in options if item.kind == kind), None
|
|
96
145
|
)
|
|
97
146
|
outcome = (
|
|
98
147
|
AllowedOutcome(outcome="selected", option_id=option.option_id)
|
|
@@ -165,12 +214,21 @@ async def prompt(
|
|
|
165
214
|
if not blocks:
|
|
166
215
|
raise ValueError("ACP prompt has no content")
|
|
167
216
|
try:
|
|
168
|
-
|
|
217
|
+
client.prompt_task = asyncio.create_task(
|
|
218
|
+
connection.prompt(session_id=session_id, prompt=blocks)
|
|
219
|
+
)
|
|
220
|
+
response = await client.prompt_task
|
|
169
221
|
client.stop_reason = response.stop_reason
|
|
170
222
|
client.response_metadata = dict(response.field_meta or {})
|
|
223
|
+
except asyncio.CancelledError:
|
|
224
|
+
if client.stop_reason != "cancelled":
|
|
225
|
+
raise
|
|
226
|
+
await connection.cancel(session_id=session_id)
|
|
171
227
|
except RequestError as error:
|
|
172
228
|
detail = error.data.get("details") if isinstance(error.data, dict) else None
|
|
173
229
|
raise RuntimeError(detail or str(error)) from error
|
|
230
|
+
finally:
|
|
231
|
+
client.prompt_task = None
|
|
174
232
|
return client.turn_result()
|
|
175
233
|
|
|
176
234
|
|
|
@@ -222,6 +280,9 @@ class ACPSession:
|
|
|
222
280
|
self.is_new = True
|
|
223
281
|
|
|
224
282
|
async def run(self, config: dict) -> ACPTurn:
|
|
283
|
+
gate = config.get("tool_interception")
|
|
284
|
+
if gate and self.client.gate is None:
|
|
285
|
+
self.client.gate = ToolGate(gate["url"], gate["secret"])
|
|
225
286
|
if self.connection is None:
|
|
226
287
|
await self.start(config)
|
|
227
288
|
assert self.session_id is not None
|
|
@@ -253,6 +314,9 @@ class ACPSession:
|
|
|
253
314
|
try:
|
|
254
315
|
await self.stack.aclose()
|
|
255
316
|
finally:
|
|
317
|
+
if self.client.gate is not None:
|
|
318
|
+
await self.client.gate.client.aclose()
|
|
319
|
+
self.client.gate = None
|
|
256
320
|
self._reset()
|
|
257
321
|
return response_metadata
|
|
258
322
|
|
|
@@ -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
|
|