verifiers 0.4.1.dev35__tar.gz → 0.4.1.dev37__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/PKG-INFO +1 -1
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/integration/grid.py +32 -1
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/integration/test_multi_turn.py +5 -2
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/integration/test_multi_turn_tool.py +5 -1
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/unit/test_graph.py +66 -23
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/dialects/anthropic.py +1 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/dialects/base.py +10 -1
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/dialects/chat.py +1 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/dialects/responses.py +1 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/graph.py +63 -30
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harness.py +4 -3
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/claude_code/harness.py +2 -2
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/hermes_agent/harness.py +2 -2
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/kimi_code/harness.py +9 -4
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/opencode/harness.py +11 -4
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/pi/harness.py +9 -8
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/prime_agent/harness.py +62 -62
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/interception/server.py +3 -3
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/session.py +7 -2
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/trace.py +2 -2
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/types.py +4 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/.gitignore +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/LICENSE +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/README.md +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/pyproject.toml +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/integration/conftest.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/integration/fixtures/agent_user_harness_v1.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/integration/fixtures/agent_user_v1.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/integration/fixtures/counter_tool_v1.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/integration/fixtures/duet_v1.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/integration/fixtures/echo_acp_resume_v1.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/integration/fixtures/echo_agentic_v1.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/integration/fixtures/echo_tool_compose/docker-compose.yaml +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/integration/fixtures/echo_tool_v1.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/integration/fixtures/echo_user_sim_v1.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/integration/fixtures/echo_v1.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/integration/fixtures/network_policy_v1.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/integration/fixtures/pyproject.toml +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/integration/fixtures/skill_codeword_v1.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/integration/fixtures/skills/project-codeword/SKILL.md +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/integration/fixtures/tool_response_image_v1.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/integration/test_agent_user.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/integration/test_agentic_judge.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/integration/test_compose_scoring.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/integration/test_examples.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/integration/test_limits.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/integration/test_multi_agent.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/integration/test_multimodal_tool.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/integration/test_network_policy.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/integration/test_resume_tool.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/integration/test_rubric_judge.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/integration/test_sampling.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/integration/test_shared_tool.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/integration/test_single_turn.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/integration/test_skills.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/integration/test_stateful_tool.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/integration/test_user_echo.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/integration/test_user_sim.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/unit/cli/test_replay.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/unit/conftest.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/unit/harnesses/test_core.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/unit/harnesses/test_rlm.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/unit/interception/test_tunnel.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/unit/judges/conftest.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/unit/judges/test_judge.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/unit/judges/test_reference.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/unit/judges/test_rubric.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/unit/runtimes/test_e2b.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/unit/serve/test_client.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/unit/serve/test_delta.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/unit/serve/test_pool.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/unit/test_agent.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/unit/test_dialects.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/unit/test_semantic.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/unit/test_task.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/unit/test_taskset.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/unit/test_trace.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/unit/utils/test_compile.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/unit/utils/test_loaders.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/unit/utils/test_platform.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/tests/unit/utils/test_score.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/acp/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/acp/runner.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/agent.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/cli/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/cli/dashboard/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/cli/dashboard/base.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/cli/dashboard/eval.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/cli/dashboard/replay.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/cli/dashboard/validate.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/cli/debug.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/cli/eval/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/cli/eval/hint.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/cli/eval/main.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/cli/eval/resume.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/cli/eval/runner.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/cli/gepa.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/cli/output.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/cli/replay.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/cli/resolve.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/cli/resume.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/cli/validate.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/clients/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/clients/base.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/clients/client.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/clients/eval.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/clients/train.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/configs/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/configs/agent.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/configs/cli/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/configs/cli/debug.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/configs/cli/env.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/configs/cli/eval.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/configs/cli/replay.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/configs/cli/validate.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/configs/client.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/configs/env.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/configs/harness.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/configs/judge.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/configs/retries.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/configs/runtime.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/configs/scoring.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/configs/select.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/configs/serve.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/configs/task.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/configs/taskset.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/dialects/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/env.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/envs/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/envs/agentic_judge/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/envs/agentic_judge/env.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/envs/best_of_n/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/envs/best_of_n/env.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/envs/single_agent/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/envs/single_agent/env.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/envs/user_sim/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/envs/user_sim/env.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/episode.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/errors.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/gepa/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/gepa/adapter.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/gepa/config.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/gepa/dataset.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/gepa/reflection.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/gepa/runner.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/grading.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/bash/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/bash/harness.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/browser_use/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/browser_use/harness.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/browser_use/program.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/claude_code/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/codex/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/codex/gate.mjs +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/codex/harness.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/deepseek/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/deepseek/gate.mjs +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/deepseek/harness.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/deepseek/usage.mjs +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/hermes_agent/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/hermes_agent/program.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/kimi_code/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/mini_swe_agent/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/mini_swe_agent/harness.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/mini_swe_agent/program.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/node.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/null/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/null/harness.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/openclaw/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/openclaw/harness.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/opencode/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/opencode/mcp-ready.js +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/pi/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/pi/gate.mjs +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/prime_agent/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/rlm/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/rlm/harness.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/terminus_2/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/terminus_2/harness.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/terminus_2/program.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/utils/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/utils/compaction.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/utils/core.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/utils/install.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/utils/launch.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/utils/mcp.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/utils/replay.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/interception/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/interception/base.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/interception/pool.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/interception/tunnel/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/interception/tunnel/base.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/interception/tunnel/custom.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/interception/tunnel/prime.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/judge.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/judges/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/judges/decision.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/judges/reference.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/judges/reference.txt +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/judges/rubric.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/judges/rubric.txt +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/mcp/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/mcp/launch.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/mcp/server.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/mcp/toolset.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/rollout.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/runtimes/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/runtimes/apptainer.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/runtimes/base.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/runtimes/compose.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/runtimes/container.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/runtimes/deployment.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/runtimes/docker/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/runtimes/docker/egress.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/runtimes/e2b.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/runtimes/limiters.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/runtimes/modal.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/runtimes/prime.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/runtimes/runtime.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/runtimes/strategies.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/runtimes/subprocess.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/semantic.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/serve/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/serve/client.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/serve/delta.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/serve/encoding.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/serve/pool.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/serve/server.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/serve/types.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/state.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/task.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/taskset.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/tasksets/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/tasksets/harbor/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/tasksets/harbor/taskset.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/tasksets/harbor/toolset.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/tasksets/nemo_gym/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/tasksets/nemo_gym/response.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/tasksets/nemo_gym/server.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/tasksets/nemo_gym/taskset.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/tasksets/nemo_gym/toolset.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/tasksets/openenv/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/tasksets/openenv/taskset.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/tasksets/textarena/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/tasksets/textarena/taskset.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/utils/__init__.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/utils/aio.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/utils/artifacts.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/utils/compile.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/utils/decorators.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/utils/env.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/utils/eval.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/utils/format.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/utils/generic.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/utils/git.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/utils/image.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/utils/interrupt.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/utils/loaders.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/utils/logging.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/utils/memory.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/utils/paths.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/utils/platform.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/utils/prime.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/utils/retries.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/utils/scope.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/utils/score.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/utils/trace_store.py +0 -0
- {verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/utils/version.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: verifiers
|
|
3
|
-
Version: 0.4.1.
|
|
3
|
+
Version: 0.4.1.dev37
|
|
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
|
|
@@ -32,6 +32,7 @@ prime cannot pull) stay outside the grid.
|
|
|
32
32
|
|
|
33
33
|
import shutil
|
|
34
34
|
from collections.abc import Sequence
|
|
35
|
+
from typing import get_args
|
|
35
36
|
|
|
36
37
|
import pytest
|
|
37
38
|
from pydantic import TypeAdapter
|
|
@@ -40,7 +41,7 @@ import verifiers.v1 as vf
|
|
|
40
41
|
from verifiers.v1.configs.runtime import NetworkPolicyConfig
|
|
41
42
|
from verifiers.v1.harness import Harness
|
|
42
43
|
from verifiers.v1.runtimes import RuntimeConfig, _backend_cls
|
|
43
|
-
from verifiers.v1.utils.loaders import harness_class
|
|
44
|
+
from verifiers.v1.utils.loaders import harness_class, harness_config_type
|
|
44
45
|
|
|
45
46
|
HARNESSES: list[str | dict] = [
|
|
46
47
|
"null",
|
|
@@ -191,6 +192,36 @@ def grid(
|
|
|
191
192
|
return cells
|
|
192
193
|
|
|
193
194
|
|
|
195
|
+
def transports(harness: str) -> tuple[str, ...]:
|
|
196
|
+
"""The wire formats the harness config can select (`transport`) besides its
|
|
197
|
+
default. Empty when the program speaks one format or picks its own."""
|
|
198
|
+
field = harness_config_type(harness).model_fields.get("transport")
|
|
199
|
+
if field is None:
|
|
200
|
+
return ()
|
|
201
|
+
return tuple(t for t in get_args(field.annotation) if t != field.default)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def transport_grid(
|
|
205
|
+
runtimes: Sequence[str] = ("prime",), *, needs: tuple[str, ...] = ()
|
|
206
|
+
) -> list:
|
|
207
|
+
"""`grid` cells for every non-default `transport` each harness config can select."""
|
|
208
|
+
cells = []
|
|
209
|
+
for harness in HARNESSES:
|
|
210
|
+
name = harness_id(harness)
|
|
211
|
+
for transport in transports(name):
|
|
212
|
+
for cell in grid(
|
|
213
|
+
[{"id": name, "transport": transport}], runtimes, needs=needs
|
|
214
|
+
):
|
|
215
|
+
cells.append(
|
|
216
|
+
pytest.param(
|
|
217
|
+
*cell.values,
|
|
218
|
+
marks=cell.marks,
|
|
219
|
+
id=cell.id.replace(f"{name}-in-", f"{name}-{transport}-in-"),
|
|
220
|
+
)
|
|
221
|
+
)
|
|
222
|
+
return cells
|
|
223
|
+
|
|
224
|
+
|
|
194
225
|
def skip_harness(cells: list, harness: str, reason: str) -> list:
|
|
195
226
|
"""Skip one harness's cells for a limit no capability flag declares. Each use names
|
|
196
227
|
why, so a reader sees the gap and when it can go."""
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
"""One task, many turns: the model completes a shell task (echo-agentic-v1: write a phrase
|
|
2
|
-
to a file, checked in the runtime) across the harness x runtime grid
|
|
2
|
+
to a file, checked in the runtime) across the harness x runtime grid, and on prime over
|
|
3
|
+
every other `transport` a harness config can select. A turn the harness re-sends
|
|
4
|
+
differently, beyond what its `replay_view` declares, forks the trace."""
|
|
3
5
|
|
|
4
6
|
import pytest
|
|
5
|
-
from grid import grid, mark
|
|
7
|
+
from grid import grid, mark, transport_grid
|
|
6
8
|
|
|
7
9
|
MULTI_TURN = [
|
|
8
10
|
*grid(needs=("shell",)),
|
|
11
|
+
*transport_grid(needs=("shell",)),
|
|
9
12
|
pytest.param(
|
|
10
13
|
"bash",
|
|
11
14
|
{"type": "modal", "allow": []},
|
|
@@ -28,7 +28,11 @@ async def test_tool(run, harness, harness_runtime):
|
|
|
28
28
|
# Reward 1.0 is the proof the tool ran. How a program surfaces an MCP tool to the
|
|
29
29
|
# model is its own business (rlm calls it from code; chat loops advertise it), so
|
|
30
30
|
# the advertised schema is checked on the chat loop in `test_tool_placement`.
|
|
31
|
-
assert trace.reward == 1.0
|
|
31
|
+
assert trace.reward == 1.0, "\n".join(
|
|
32
|
+
message.model_dump_json(exclude_none=True)
|
|
33
|
+
for message in trace.messages
|
|
34
|
+
if message.role in {"assistant", "tool"}
|
|
35
|
+
)
|
|
32
36
|
|
|
33
37
|
|
|
34
38
|
@pytest.mark.parametrize(
|
|
@@ -270,6 +270,30 @@ def test_reasoning_content_participates_in_graph_prefix_matching():
|
|
|
270
270
|
assert len(tool_call_nodes) == 2
|
|
271
271
|
|
|
272
272
|
|
|
273
|
+
def _replayed_trace(replay_view, sampled, replayed, dialect=None):
|
|
274
|
+
"""One tool turn sampled as `sampled` and re-sent as `replayed` on the next turn."""
|
|
275
|
+
trace = vf.Trace(
|
|
276
|
+
agent=vf.AgentInfo(config=vf.AgentConfig()),
|
|
277
|
+
task=vf.TraceTask(type="Task", data=vf.TaskData(idx=0, prompt="go")),
|
|
278
|
+
)
|
|
279
|
+
trace._replay_view = replay_view
|
|
280
|
+
user = vf.UserMessage(content="go")
|
|
281
|
+
call = vf.ToolCall(id="call_0", name="lookup", arguments="{}")
|
|
282
|
+
graph.prepare_turn(trace, [user], dialect=dialect).commit(
|
|
283
|
+
_response(vf.AssistantMessage(tool_calls=[call], **sampled))
|
|
284
|
+
)
|
|
285
|
+
graph.prepare_turn(
|
|
286
|
+
trace,
|
|
287
|
+
[
|
|
288
|
+
user,
|
|
289
|
+
vf.AssistantMessage(tool_calls=[call], **replayed),
|
|
290
|
+
vf.ToolMessage(content="result", tool_call_id="call_0"),
|
|
291
|
+
],
|
|
292
|
+
dialect=dialect,
|
|
293
|
+
).commit(_response(vf.AssistantMessage(content="done")))
|
|
294
|
+
return trace
|
|
295
|
+
|
|
296
|
+
|
|
273
297
|
@pytest.mark.parametrize(
|
|
274
298
|
"view,sampled,replayed",
|
|
275
299
|
[
|
|
@@ -284,29 +308,48 @@ def test_reasoning_content_participates_in_graph_prefix_matching():
|
|
|
284
308
|
],
|
|
285
309
|
)
|
|
286
310
|
def test_replay_view_matches_cosmetically_rewritten_turns(view, sampled, replayed):
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
)
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
311
|
+
assert _replayed_trace(None, sampled, replayed).num_branches == 2
|
|
312
|
+
trace = _replayed_trace(lambda message, _: view(message), sampled, replayed)
|
|
313
|
+
assert trace.num_branches == 1
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
@pytest.mark.parametrize(
|
|
317
|
+
"dialect,branches", [("chat_completions", 1), ("responses", 2)]
|
|
318
|
+
)
|
|
319
|
+
def test_replay_view_receives_the_request_dialect(dialect, branches):
|
|
320
|
+
def view(message, dialect):
|
|
321
|
+
return drop_blank_text(message) if dialect == "chat_completions" else message
|
|
322
|
+
|
|
323
|
+
trace = _replayed_trace(view, {"content": "\n\n"}, {"content": None}, dialect)
|
|
324
|
+
assert trace.num_branches == branches
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def test_overlapping_requests_keep_their_own_dialect():
|
|
328
|
+
trace = vf.Trace(
|
|
329
|
+
agent=vf.AgentInfo(config=vf.AgentConfig()),
|
|
330
|
+
task=vf.TraceTask(type="Task", data=vf.TaskData(idx=0, prompt="go")),
|
|
331
|
+
)
|
|
332
|
+
trace._replay_view = lambda message, dialect: (
|
|
333
|
+
drop_blank_text(message) if dialect == "chat_completions" else message
|
|
334
|
+
)
|
|
335
|
+
user = vf.UserMessage(content="go")
|
|
336
|
+
call = vf.ToolCall(id="call_0", name="lookup", arguments="{}")
|
|
337
|
+
graph.prepare_turn(trace, [user], dialect="chat_completions").commit(
|
|
338
|
+
_response(vf.AssistantMessage(content="\n\n", tool_calls=[call]))
|
|
339
|
+
)
|
|
340
|
+
replay = [
|
|
341
|
+
user,
|
|
342
|
+
vf.AssistantMessage(tool_calls=[call]),
|
|
343
|
+
vf.ToolMessage(content="result", tool_call_id="call_0"),
|
|
344
|
+
]
|
|
345
|
+
chat = graph.prepare_turn(trace, replay, dialect="chat_completions")
|
|
346
|
+
responses = graph.prepare_turn(trace, replay, dialect="responses")
|
|
347
|
+
responses.commit(_response(vf.AssistantMessage(content="other")))
|
|
348
|
+
done = chat.commit(_response(vf.AssistantMessage(content="done")))
|
|
349
|
+
|
|
350
|
+
assert chat.prefix_node_ids == [0, 1]
|
|
351
|
+
assert responses.prefix_node_ids == [0]
|
|
352
|
+
assert graph.path(trace, done)[1] == trace.nodes[1].message
|
|
310
353
|
|
|
311
354
|
|
|
312
355
|
@pytest.mark.parametrize("same_tools", [False, True])
|
|
@@ -27,7 +27,13 @@ from verifiers.v1.configs.runtime import (
|
|
|
27
27
|
intersect_network_hosts,
|
|
28
28
|
parse_network_rule,
|
|
29
29
|
)
|
|
30
|
-
from verifiers.v1.types import
|
|
30
|
+
from verifiers.v1.types import (
|
|
31
|
+
DialectName,
|
|
32
|
+
Request,
|
|
33
|
+
Response,
|
|
34
|
+
Sampling,
|
|
35
|
+
SamplingConfig,
|
|
36
|
+
)
|
|
31
37
|
|
|
32
38
|
RespT = TypeVar("RespT", bound=BaseModel)
|
|
33
39
|
RawRequest = dict[str, Any]
|
|
@@ -263,6 +269,9 @@ class Dialect(ABC, Generic[RespT]):
|
|
|
263
269
|
rewriting them. Implement a `Dialect` + register it in `dialects.DIALECTS` and a harness
|
|
264
270
|
speaking that format works end-to-end."""
|
|
265
271
|
|
|
272
|
+
name: ClassVar[DialectName]
|
|
273
|
+
"""The wire format's name, passed to `Harness.replay_view`."""
|
|
274
|
+
|
|
266
275
|
sampling_fields: ClassVar[frozenset[str]] = frozenset()
|
|
267
276
|
"""Request keys that are call settings — what shapes generation given the same
|
|
268
277
|
conversation: decoding knobs, budgets/stops, reasoning effort, output contract.
|
|
@@ -42,6 +42,7 @@ from renderers.base import RenderedTokens
|
|
|
42
42
|
from verifiers.v1.semantic import ParentLink
|
|
43
43
|
from verifiers.v1.types import (
|
|
44
44
|
AssistantMessage,
|
|
45
|
+
DialectName,
|
|
45
46
|
Message,
|
|
46
47
|
Response,
|
|
47
48
|
SamplingMask,
|
|
@@ -321,11 +322,12 @@ def _tools_hash(tools: list[Tool] | None) -> str:
|
|
|
321
322
|
return hashlib.blake2b(data, digest_size=16).hexdigest()
|
|
322
323
|
|
|
323
324
|
|
|
324
|
-
def _message_key(trace: Trace, message: Message) -> str:
|
|
325
|
-
"""`message_hash` of the message as the trace's harness replays it
|
|
326
|
-
harness re-sends with cosmetic rewrites still matches the node it
|
|
325
|
+
def _message_key(trace: Trace, message: Message, dialect: DialectName | None) -> str:
|
|
326
|
+
"""`message_hash` of the message as the trace's harness replays it over `dialect`,
|
|
327
|
+
so a turn the harness re-sends with cosmetic rewrites still matches the node it
|
|
328
|
+
came from."""
|
|
327
329
|
view = trace._replay_view
|
|
328
|
-
return message_hash(view(message) if view is not None else message)
|
|
330
|
+
return message_hash(view(message, dialect) if view is not None else message)
|
|
329
331
|
|
|
330
332
|
|
|
331
333
|
def _node_key(
|
|
@@ -333,25 +335,43 @@ def _node_key(
|
|
|
333
335
|
parent: int | None,
|
|
334
336
|
message: Message,
|
|
335
337
|
tools: list[Tool] | None = None,
|
|
338
|
+
*,
|
|
339
|
+
dialect: DialectName | None,
|
|
336
340
|
) -> tuple[int | None, str | None, str]:
|
|
337
341
|
return (
|
|
338
342
|
parent,
|
|
339
343
|
_tools_hash(tools) if parent is None else None,
|
|
340
|
-
_message_key(trace, message),
|
|
344
|
+
_message_key(trace, message, dialect),
|
|
341
345
|
)
|
|
342
346
|
|
|
343
347
|
|
|
344
|
-
def _head_index(
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
348
|
+
def _head_index(
|
|
349
|
+
trace: Trace, dialect: DialectName | None
|
|
350
|
+
) -> dict[tuple[int | None, str | None, str], int]:
|
|
351
|
+
"""Physical node key -> id under `dialect`'s replay view, built lazily per dialect
|
|
352
|
+
(and after deserialization)."""
|
|
353
|
+
index = trace._head_index.get(dialect)
|
|
354
|
+
if index is None:
|
|
355
|
+
index = trace._head_index[dialect] = {
|
|
356
|
+
_node_key(
|
|
357
|
+
trace, node.parent, node.message, node.tools, dialect=dialect
|
|
358
|
+
): nid
|
|
349
359
|
for nid, node in enumerate(trace.nodes)
|
|
350
360
|
}
|
|
351
|
-
return
|
|
361
|
+
return index
|
|
352
362
|
|
|
353
363
|
|
|
354
|
-
def
|
|
364
|
+
def _index_node(trace: Trace, node_id: int) -> None:
|
|
365
|
+
"""Point every built dialect index at a new node."""
|
|
366
|
+
node = trace.nodes[node_id]
|
|
367
|
+
for dialect, index in trace._head_index.items():
|
|
368
|
+
key = _node_key(trace, node.parent, node.message, node.tools, dialect=dialect)
|
|
369
|
+
index[key] = node_id
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
def message_prefix_len(
|
|
373
|
+
trace: Trace, prompt: list[Message], dialect: DialectName | None = None
|
|
374
|
+
) -> int:
|
|
355
375
|
"""Length of the longest message-only graph prefix matching `prompt`."""
|
|
356
376
|
children: dict[int | None, list[int]] = {}
|
|
357
377
|
for node_id, node in enumerate(trace.nodes):
|
|
@@ -360,12 +380,12 @@ def message_prefix_len(trace: Trace, prompt: list[Message]) -> int:
|
|
|
360
380
|
parents: list[int | None] = [None]
|
|
361
381
|
matched = 0
|
|
362
382
|
for message in prompt:
|
|
363
|
-
key = _message_key(trace, message)
|
|
383
|
+
key = _message_key(trace, message, dialect)
|
|
364
384
|
parents = [
|
|
365
385
|
node_id
|
|
366
386
|
for parent in parents
|
|
367
387
|
for node_id in children.get(parent, [])
|
|
368
|
-
if _message_key(trace, trace.nodes[node_id].message) == key
|
|
388
|
+
if _message_key(trace, trace.nodes[node_id].message, dialect) == key
|
|
369
389
|
]
|
|
370
390
|
if not parents:
|
|
371
391
|
break
|
|
@@ -380,6 +400,8 @@ def _matching_node(
|
|
|
380
400
|
token_ids: list[int] | None = None,
|
|
381
401
|
renderer_token_ids: list[int] | None = None,
|
|
382
402
|
tools: list[Tool] | None = None,
|
|
403
|
+
*,
|
|
404
|
+
dialect: DialectName | None,
|
|
383
405
|
) -> int | None:
|
|
384
406
|
"""Find an existing child, optionally requiring its exact token spans.
|
|
385
407
|
|
|
@@ -387,8 +409,8 @@ def _matching_node(
|
|
|
387
409
|
prefix breaks can leave older physical variants under the same key, so a token mismatch falls
|
|
388
410
|
back to a reverse scan rather than materializing a duplicate of an already-existing variant.
|
|
389
411
|
"""
|
|
390
|
-
key = _node_key(trace, parent, message, tools)
|
|
391
|
-
indexed = _head_index(trace).get(key)
|
|
412
|
+
key = _node_key(trace, parent, message, tools, dialect=dialect)
|
|
413
|
+
indexed = _head_index(trace, dialect).get(key)
|
|
392
414
|
if (
|
|
393
415
|
indexed is not None
|
|
394
416
|
and (token_ids is None or trace.nodes[indexed].token_ids == token_ids)
|
|
@@ -407,7 +429,8 @@ def _matching_node(
|
|
|
407
429
|
if (
|
|
408
430
|
(token_ids is None or node.token_ids == token_ids)
|
|
409
431
|
and (renderer_token_ids is None or node.logical_ids == renderer_token_ids)
|
|
410
|
-
and _node_key(trace, node.parent, node.message, node.tools)
|
|
432
|
+
and _node_key(trace, node.parent, node.message, node.tools, dialect=dialect)
|
|
433
|
+
== key
|
|
411
434
|
):
|
|
412
435
|
return node_id
|
|
413
436
|
return None
|
|
@@ -424,6 +447,8 @@ def _matching_prefix_node(
|
|
|
424
447
|
renderer_start: int,
|
|
425
448
|
renderer_stop: int,
|
|
426
449
|
tools: list[Tool] | None = None,
|
|
450
|
+
*,
|
|
451
|
+
dialect: DialectName | None,
|
|
427
452
|
) -> int | None:
|
|
428
453
|
"""Find the longest content-equivalent child matching inside `[start, stop]`.
|
|
429
454
|
|
|
@@ -432,8 +457,8 @@ def _matching_prefix_node(
|
|
|
432
457
|
otherwise-missing message boundary. The next attributed message's start bounds the match:
|
|
433
458
|
a sampled variant must never consume tokens that the renderer assigned to that message.
|
|
434
459
|
"""
|
|
435
|
-
key = _node_key(trace, parent, message, tools)
|
|
436
|
-
indexed = _head_index(trace).get(key)
|
|
460
|
+
key = _node_key(trace, parent, message, tools, dialect=dialect)
|
|
461
|
+
indexed = _head_index(trace, dialect).get(key)
|
|
437
462
|
candidates: list[int] = []
|
|
438
463
|
if indexed is not None:
|
|
439
464
|
candidates.append(indexed)
|
|
@@ -446,6 +471,7 @@ def _matching_prefix_node(
|
|
|
446
471
|
trace.nodes[node_id].parent,
|
|
447
472
|
trace.nodes[node_id].message,
|
|
448
473
|
trace.nodes[node_id].tools,
|
|
474
|
+
dialect=dialect,
|
|
449
475
|
)
|
|
450
476
|
== key
|
|
451
477
|
)
|
|
@@ -480,6 +506,8 @@ class PendingTurn:
|
|
|
480
506
|
tools: list[Tool]
|
|
481
507
|
prefix_node_ids: list[int]
|
|
482
508
|
path_len: int
|
|
509
|
+
dialect: DialectName | None = None
|
|
510
|
+
"""The wire format of the request, whose replay view matches its prompt."""
|
|
483
511
|
|
|
484
512
|
@property
|
|
485
513
|
def tail_start(self) -> int:
|
|
@@ -558,13 +586,14 @@ class PendingTurn:
|
|
|
558
586
|
def commit_prompt(self) -> None:
|
|
559
587
|
"""Record an input that terminated before model inference."""
|
|
560
588
|
parent = self.prefix_node_ids[-1] if self.prefix_node_ids else None
|
|
561
|
-
|
|
589
|
+
_head_index(self.trace, self.dialect)
|
|
562
590
|
for message in self.tail:
|
|
563
|
-
existing = _matching_node(
|
|
591
|
+
existing = _matching_node(
|
|
592
|
+
self.trace, parent, message, tools=self.tools, dialect=self.dialect
|
|
593
|
+
)
|
|
564
594
|
if existing is not None:
|
|
565
595
|
parent = existing
|
|
566
596
|
continue
|
|
567
|
-
previous = parent
|
|
568
597
|
self.trace.nodes.append(
|
|
569
598
|
MessageNode(
|
|
570
599
|
parent=parent,
|
|
@@ -573,7 +602,7 @@ class PendingTurn:
|
|
|
573
602
|
)
|
|
574
603
|
)
|
|
575
604
|
parent = len(self.trace.nodes) - 1
|
|
576
|
-
|
|
605
|
+
_index_node(self.trace, parent)
|
|
577
606
|
if self.tools:
|
|
578
607
|
self.trace.tools = list(
|
|
579
608
|
{
|
|
@@ -588,9 +617,11 @@ def prepare_turn(
|
|
|
588
617
|
trace: Trace,
|
|
589
618
|
prompt: list[Message],
|
|
590
619
|
tools: list[Tool] | None = None,
|
|
620
|
+
dialect: DialectName | None = None,
|
|
591
621
|
) -> PendingTurn:
|
|
592
|
-
"""Resolve a physical message-and-tools prefix without mutating the trace.
|
|
593
|
-
|
|
622
|
+
"""Resolve a physical message-and-tools prefix without mutating the trace. `dialect`
|
|
623
|
+
is the request's wire format, whose replay view matches the prompt to stored nodes."""
|
|
624
|
+
idx = _head_index(trace, dialect)
|
|
594
625
|
tools = list(tools or [])
|
|
595
626
|
tools_hash = _tools_hash(tools)
|
|
596
627
|
parent: int | None = None
|
|
@@ -614,7 +645,7 @@ def prepare_turn(
|
|
|
614
645
|
if len(children) == 1 and trace.nodes[children[0]].message == msg:
|
|
615
646
|
existing = children[0]
|
|
616
647
|
if existing is None:
|
|
617
|
-
message_key = _message_key(trace, msg)
|
|
648
|
+
message_key = _message_key(trace, msg, dialect)
|
|
618
649
|
existing = idx.get(
|
|
619
650
|
(parent, tools_hash if parent is None else None, message_key)
|
|
620
651
|
)
|
|
@@ -629,6 +660,7 @@ def prepare_turn(
|
|
|
629
660
|
tools=tools,
|
|
630
661
|
prefix_node_ids=prefix_node_ids,
|
|
631
662
|
path_len=path_len,
|
|
663
|
+
dialect=dialect,
|
|
632
664
|
)
|
|
633
665
|
|
|
634
666
|
|
|
@@ -801,7 +833,7 @@ def _commit_turn(turn: PendingTurn, response: Response) -> int:
|
|
|
801
833
|
)
|
|
802
834
|
renderer_spans = tokens.message_spans if tokens else None
|
|
803
835
|
renderer_is_content = tokens.is_content if tokens else None
|
|
804
|
-
|
|
836
|
+
_head_index(trace, turn.dialect)
|
|
805
837
|
|
|
806
838
|
prefix = turn.prefix_node_ids
|
|
807
839
|
path_len = turn.path_len
|
|
@@ -886,6 +918,7 @@ def _commit_turn(turn: PendingTurn, response: Response) -> int:
|
|
|
886
918
|
renderer_path_len,
|
|
887
919
|
next_renderer_start,
|
|
888
920
|
tools=turn.tools,
|
|
921
|
+
dialect=turn.dialect,
|
|
889
922
|
)
|
|
890
923
|
if existing is not None:
|
|
891
924
|
end += len(trace.nodes[existing].token_ids)
|
|
@@ -900,6 +933,7 @@ def _commit_turn(turn: PendingTurn, response: Response) -> int:
|
|
|
900
933
|
node_tokens if tokens is not None else None,
|
|
901
934
|
renderer_node_tokens if tokens is not None else None,
|
|
902
935
|
tools=turn.tools,
|
|
936
|
+
dialect=turn.dialect,
|
|
903
937
|
)
|
|
904
938
|
if existing is None:
|
|
905
939
|
break
|
|
@@ -914,7 +948,6 @@ def _commit_turn(turn: PendingTurn, response: Response) -> int:
|
|
|
914
948
|
# Track new nodes separately so routed-expert attribution needs only node ids, not this path.
|
|
915
949
|
new_node_ids: list[int] = []
|
|
916
950
|
for i, msg in enumerate(prompt[num_reused:], start=num_reused):
|
|
917
|
-
key = _node_key(trace, parent, msg, turn.tools)
|
|
918
951
|
start = path_len if cursor is None else cursor
|
|
919
952
|
span = spans[i] if spans and i < len(spans) else None
|
|
920
953
|
end = span[1] if span else start
|
|
@@ -938,7 +971,7 @@ def _commit_turn(turn: PendingTurn, response: Response) -> int:
|
|
|
938
971
|
)
|
|
939
972
|
)
|
|
940
973
|
parent = len(trace.nodes) - 1
|
|
941
|
-
|
|
974
|
+
_index_node(trace, parent)
|
|
942
975
|
new_node_ids.append(parent)
|
|
943
976
|
cursor = end
|
|
944
977
|
renderer_cursor = renderer_end
|
|
@@ -967,7 +1000,7 @@ def _commit_turn(turn: PendingTurn, response: Response) -> int:
|
|
|
967
1000
|
)
|
|
968
1001
|
)
|
|
969
1002
|
assistant_id = len(trace.nodes) - 1
|
|
970
|
-
|
|
1003
|
+
_index_node(trace, assistant_id)
|
|
971
1004
|
new_node_ids.append(assistant_id)
|
|
972
1005
|
|
|
973
1006
|
# Attribute this turn's expert-routing array onto the nodes created this turn (new input
|
|
@@ -18,7 +18,7 @@ from verifiers.v1.errors import (
|
|
|
18
18
|
)
|
|
19
19
|
from verifiers.v1.runtimes import ProgramResult, Runtime
|
|
20
20
|
from verifiers.v1.task import TaskData
|
|
21
|
-
from verifiers.v1.types import Message, Messages
|
|
21
|
+
from verifiers.v1.types import DialectName, Message, Messages
|
|
22
22
|
from verifiers.v1.utils.decorators import (
|
|
23
23
|
discover_decorated,
|
|
24
24
|
invoke_all,
|
|
@@ -67,8 +67,9 @@ class Harness(ABC, Generic[ConfigT]):
|
|
|
67
67
|
def __init__(self, config: ConfigT) -> None:
|
|
68
68
|
self.config = config
|
|
69
69
|
|
|
70
|
-
def replay_view(self, message: Message) -> Message:
|
|
71
|
-
"""`message` as this harness re-sends it on later requests
|
|
70
|
+
def replay_view(self, message: Message, dialect: DialectName | None) -> Message:
|
|
71
|
+
"""`message` as this harness re-sends it on later requests over `dialect` (None
|
|
72
|
+
before the first intercepted request). Override only for
|
|
72
73
|
deterministic cosmetic rewrites (e.g. dropped whitespace-only text), never for
|
|
73
74
|
content changes, so a replayed turn matches the turn it came from in the trace
|
|
74
75
|
graph instead of forking it."""
|
{verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/claude_code/harness.py
RENAMED
|
@@ -11,7 +11,7 @@ from verifiers.v1.harnesses.utils.replay import drop_blank_text
|
|
|
11
11
|
from verifiers.v1.runtimes import Runtime
|
|
12
12
|
from verifiers.v1.task import TaskData
|
|
13
13
|
from verifiers.v1.trace import Trace
|
|
14
|
-
from verifiers.v1.types import Message
|
|
14
|
+
from verifiers.v1.types import DialectName, Message
|
|
15
15
|
|
|
16
16
|
CLAUDE_ACP_DIR = "/var/tmp/vf-claude-agent-acp-{version}-{acp_version}"
|
|
17
17
|
PACKAGES_DIR = f"{CLAUDE_ACP_DIR}/packages"
|
|
@@ -44,7 +44,7 @@ class ClaudeCodeHarness(ACPHarness[ClaudeCodeHarnessConfig]):
|
|
|
44
44
|
|
|
45
45
|
# Claude Code drops whitespace-only text blocks on replay. Closed source: observed
|
|
46
46
|
# with 2.1.278, recheck on version bumps.
|
|
47
|
-
def replay_view(self, message: Message) -> Message:
|
|
47
|
+
def replay_view(self, message: Message, dialect: DialectName | None) -> Message:
|
|
48
48
|
return drop_blank_text(message)
|
|
49
49
|
|
|
50
50
|
async def setup(self, runtime: Runtime) -> None:
|
{verifiers-0.4.1.dev35 → verifiers-0.4.1.dev37}/verifiers/v1/harnesses/hermes_agent/harness.py
RENAMED
|
@@ -12,7 +12,7 @@ from verifiers.v1.runtimes import Runtime
|
|
|
12
12
|
from verifiers.v1.runtimes.base import _ENSURE_UV
|
|
13
13
|
from verifiers.v1.task import TaskData
|
|
14
14
|
from verifiers.v1.trace import Trace
|
|
15
|
-
from verifiers.v1.types import Message
|
|
15
|
+
from verifiers.v1.types import DialectName, Message
|
|
16
16
|
|
|
17
17
|
PROGRAM_SOURCE = (Path(__file__).resolve().parent / "program.py").read_text()
|
|
18
18
|
HERMES_DIR = "/var/tmp/vf-hermes-agent-{version}"
|
|
@@ -43,7 +43,7 @@ class HermesAgentHarness(ACPHarness[HermesAgentHarnessConfig]):
|
|
|
43
43
|
# replay. Recheck on version bumps:
|
|
44
44
|
# https://github.com/NousResearch/hermes-agent/blob/v2026.9.14/agent/chat_completion_helpers.py#L1444
|
|
45
45
|
# https://github.com/NousResearch/hermes-agent/blob/v2026.9.14/agent/message_sanitization.py#L545
|
|
46
|
-
def replay_view(self, message: Message) -> Message:
|
|
46
|
+
def replay_view(self, message: Message, dialect: DialectName | None) -> Message:
|
|
47
47
|
return strip_text(message)
|
|
48
48
|
|
|
49
49
|
async def setup(self, runtime: Runtime) -> None:
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
import tomllib
|
|
5
|
-
from typing import Literal
|
|
6
5
|
|
|
7
6
|
import tomli_w
|
|
8
7
|
|
|
@@ -11,9 +10,11 @@ from verifiers.v1.clients import ModelContext
|
|
|
11
10
|
from verifiers.v1.configs.harness import HarnessConfig, PinnedVersion
|
|
12
11
|
from verifiers.v1.harnesses.node import NODE_BIN_DIR, ensure_node
|
|
13
12
|
from verifiers.v1.harnesses.utils.install import ensure_installed, remove_dir
|
|
13
|
+
from verifiers.v1.harnesses.utils.replay import drop_blank_text
|
|
14
14
|
from verifiers.v1.runtimes import Runtime
|
|
15
15
|
from verifiers.v1.task import TaskData
|
|
16
16
|
from verifiers.v1.trace import Trace
|
|
17
|
+
from verifiers.v1.types import DialectName, Message
|
|
17
18
|
|
|
18
19
|
logger = logging.getLogger(__name__)
|
|
19
20
|
|
|
@@ -39,9 +40,7 @@ fi
|
|
|
39
40
|
class KimiCodeHarnessConfig(HarnessConfig):
|
|
40
41
|
version: PinnedVersion = "2.0.2"
|
|
41
42
|
"""Kimi Code release to install, pinned for reproducibility."""
|
|
42
|
-
transport:
|
|
43
|
-
"chat_completions"
|
|
44
|
-
)
|
|
43
|
+
transport: DialectName = "chat_completions"
|
|
45
44
|
"""Model API transport."""
|
|
46
45
|
|
|
47
46
|
|
|
@@ -51,6 +50,12 @@ class KimiCodeHarness(ACPHarness[KimiCodeHarnessConfig]):
|
|
|
51
50
|
SUPPORTS_SKILLS = True
|
|
52
51
|
SUPPORTS_TOOL_INTERCEPTION = True
|
|
53
52
|
|
|
53
|
+
# Kimi Code drops whitespace-only assistant text when it stores a step, so every
|
|
54
|
+
# transport replays it without. Recheck on version bumps:
|
|
55
|
+
# https://github.com/MoonshotAI/kimi-code/blob/@moonshot-ai/kimi-code@2.0.2/packages/agent-core-v2/src/agent/loop/loopService.ts#L1723
|
|
56
|
+
def replay_view(self, message: Message, dialect: DialectName | None) -> Message:
|
|
57
|
+
return drop_blank_text(message)
|
|
58
|
+
|
|
54
59
|
async def setup(self, runtime: Runtime) -> None:
|
|
55
60
|
await ensure_node(runtime)
|
|
56
61
|
logger.info(
|
|
@@ -2,16 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
from pathlib import Path
|
|
5
|
-
from typing import Literal
|
|
6
5
|
|
|
7
6
|
from verifiers.v1.acp import ACPConfig, ACPHarness
|
|
8
7
|
from verifiers.v1.clients import ModelContext
|
|
9
8
|
from verifiers.v1.configs.harness import HarnessConfig, PinnedVersion
|
|
10
9
|
from verifiers.v1.harnesses.node import ensure_node
|
|
11
10
|
from verifiers.v1.harnesses.utils.install import ensure_installed, remove_dir
|
|
11
|
+
from verifiers.v1.harnesses.utils.replay import drop_blank_text
|
|
12
12
|
from verifiers.v1.runtimes import Runtime
|
|
13
13
|
from verifiers.v1.task import TaskData
|
|
14
14
|
from verifiers.v1.trace import Trace
|
|
15
|
+
from verifiers.v1.types import DialectName, Message
|
|
15
16
|
|
|
16
17
|
OPENCODE_DIR = "/var/tmp/vf-opencode"
|
|
17
18
|
STATE_ROOT = "/tmp/vf-opencode-runs"
|
|
@@ -29,9 +30,7 @@ npm install --prefix "$VF_OPENCODE_DIR" --no-audit --no-fund --omit=dev \
|
|
|
29
30
|
class OpenCodeHarnessConfig(HarnessConfig):
|
|
30
31
|
version: PinnedVersion = "2.0.26"
|
|
31
32
|
"""OpenCode 2 release to install, pinned for reproducibility."""
|
|
32
|
-
transport:
|
|
33
|
-
"chat_completions"
|
|
34
|
-
)
|
|
33
|
+
transport: DialectName = "chat_completions"
|
|
35
34
|
"""Model API transport."""
|
|
36
35
|
disabled_tools: list[str] | None = None
|
|
37
36
|
"""OpenCode permission actions to deny, such as shell, read, or edit."""
|
|
@@ -42,6 +41,14 @@ class OpenCodeHarness(ACPHarness[OpenCodeHarnessConfig]):
|
|
|
42
41
|
SUPPORTS_MCP = True
|
|
43
42
|
SUPPORTS_SKILLS = True
|
|
44
43
|
|
|
44
|
+
# OpenCode drops whitespace-only assistant text on replay over Anthropic messages.
|
|
45
|
+
# Recheck on version bumps:
|
|
46
|
+
# https://github.com/anomalyco/opencode/blob/v2.0.26/packages/ai/src/protocols/anthropic-messages.ts#L929
|
|
47
|
+
def replay_view(self, message: Message, dialect: DialectName | None) -> Message:
|
|
48
|
+
if dialect == "anthropic_messages":
|
|
49
|
+
return drop_blank_text(message)
|
|
50
|
+
return message
|
|
51
|
+
|
|
45
52
|
async def setup(self, runtime: Runtime) -> None:
|
|
46
53
|
await ensure_node(runtime)
|
|
47
54
|
directory = f"{OPENCODE_DIR}/{self.config.version}"
|