benchflow 0.3.3__tar.gz → 0.3.4__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.
- {benchflow-0.3.3 → benchflow-0.3.4}/PKG-INFO +6 -6
- {benchflow-0.3.3 → benchflow-0.3.4}/README.md +5 -5
- {benchflow-0.3.3 → benchflow-0.3.4}/pyproject.toml +1 -1
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/__init__.py +106 -17
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/_agent_env.py +1 -1
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/_agent_setup.py +1 -0
- benchflow-0.3.4/src/benchflow/_run.py +34 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/_scene.py +31 -5
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/_snapshot.py +16 -9
- benchflow-0.3.4/src/benchflow/_types.py +94 -0
- benchflow-0.3.4/src/benchflow/adapters/__init__.py +25 -0
- benchflow-0.3.4/src/benchflow/adapters/inspect_ai.py +63 -0
- benchflow-0.3.4/src/benchflow/adapters/ors.py +68 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/agents/__init__.py +0 -4
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/agents/pi_acp_launcher.py +2 -2
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/agents/providers.py +1 -1
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/cli/main.py +76 -187
- benchflow-0.3.3/src/benchflow/job.py → benchflow-0.3.4/src/benchflow/evaluation.py +57 -95
- benchflow-0.3.4/src/benchflow/job.py +29 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/models.py +20 -7
- benchflow-0.3.4/src/benchflow/rewards/README.md +125 -0
- benchflow-0.3.4/src/benchflow/rewards/__init__.py +34 -0
- benchflow-0.3.4/src/benchflow/rewards/builtins.py +471 -0
- benchflow-0.3.4/src/benchflow/rewards/events.py +26 -0
- benchflow-0.3.4/src/benchflow/rewards/file_readers.py +142 -0
- benchflow-0.3.4/src/benchflow/rewards/llm.py +186 -0
- benchflow-0.3.4/src/benchflow/rewards/protocol.py +33 -0
- benchflow-0.3.4/src/benchflow/rewards/rubric.py +76 -0
- benchflow-0.3.4/src/benchflow/rewards/rubric_config.py +127 -0
- benchflow-0.3.3/src/benchflow/trial.py → benchflow-0.3.4/src/benchflow/rollout.py +429 -104
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/runtime.py +25 -23
- benchflow-0.3.4/src/benchflow/sandbox/__init__.py +9 -0
- benchflow-0.3.4/src/benchflow/sandbox/daytona.py +74 -0
- benchflow-0.3.4/src/benchflow/sandbox/docker.py +74 -0
- benchflow-0.3.4/src/benchflow/sandbox/protocol.py +74 -0
- benchflow-0.3.4/src/benchflow/sdk.py +193 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/tasks.py +1 -1
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/trajectories/__init__.py +0 -5
- benchflow-0.3.4/src/benchflow/trial.py +39 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/trial_yaml.py +2 -1
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/conformance/README.md +3 -3
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/examples/test_claude.sh +8 -8
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/examples/test_codex.sh +9 -9
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/examples/test_codex_custom_provider.sh +7 -7
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/examples/test_gemini.sh +7 -7
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/examples/test_openclaw.sh +9 -9
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/fixtures/mock_openai_responses_server.py +2 -2
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/integration/run.sh +6 -6
- benchflow-0.3.4/tests/test_adapters.py +218 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_agent_setup.py +48 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_connect_as_env.py +8 -8
- benchflow-0.3.4/tests/test_eng50_capabilities.py +248 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_internet_policy.py +10 -8
- benchflow-0.3.4/tests/test_llm_judge.py +502 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_oracle_chokepoint.py +15 -40
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_pi_acp_launcher.py +1 -1
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_reexport.py +1 -1
- benchflow-0.3.4/tests/test_rewards.py +338 -0
- benchflow-0.3.4/tests/test_rubric_config.py +175 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_runtime.py +5 -5
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_sandbox_hardening.py +6 -6
- benchflow-0.3.4/tests/test_sandbox_protocol.py +250 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_scene.py +4 -8
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_scene_outbox_trial.py +2 -2
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_sdk_internals.py +1 -1
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_self_gen_cli.py +1 -1
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_skill_eval_dryrun.py +1 -1
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_smoke.py +2 -1
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_trial_bedrock_proxy.py +8 -8
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_trial_install_agent_timeout.py +17 -17
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_verify.py +14 -14
- benchflow-0.3.3/src/benchflow/agents/user_agent.py +0 -62
- benchflow-0.3.3/src/benchflow/cli/eval.py +0 -376
- benchflow-0.3.3/src/benchflow/sdk.py +0 -582
- benchflow-0.3.3/src/benchflow/trajectories/atif.py +0 -112
- benchflow-0.3.3/src/benchflow/trajectories/claude_code.py +0 -249
- benchflow-0.3.3/tests/test_eval_cli.py +0 -118
- {benchflow-0.3.3 → benchflow-0.3.4}/.gitignore +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/CHANGELOG.md +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/LICENSE +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/_acp_run.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/_credentials.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/_daytona_patches.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/_env_setup.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/_provider_runtime.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/_sandbox.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/_scoring.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/_trajectory.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/acp/__init__.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/acp/client.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/acp/container_transport.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/acp/session.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/acp/transport.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/acp/types.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/agents/harvey_lab_acp_shim.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/agents/openclaw_acp_shim.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/agents/registry.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/cli/__init__.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/demo_task/environment/Dockerfile +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/demo_task/instruction.md +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/demo_task/task.toml +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/demo_task/tests/test.sh +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/environments.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/mcp/__init__.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/mcp/hooks.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/mcp/reviewer_server.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/metrics.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/process.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/providers/__init__.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/providers/bedrock_proxy.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/providers/bedrock_runtime.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/py.typed +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/self_gen.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/skill_eval.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/skills.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/task_download.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/templates/__init__.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/templates/judge.py.tmpl +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/templates/test.sh.tmpl +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/trajectories/otel.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/trajectories/proxy.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/trajectories/types.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/user.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/viewer.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/__init__.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/conformance/acp_smoke/environment/Dockerfile +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/conformance/acp_smoke/instruction.md +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/conformance/acp_smoke/solution/solve.sh +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/conformance/acp_smoke/task.toml +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/conformance/acp_smoke/tests/test.sh +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/conformance/conformance-results.json +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/conformance/proof_multi_agent.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/conformance/proof_snapshot.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/conformance/run_conformance.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/conftest.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/examples/hello-world-task/environment/Dockerfile +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/examples/hello-world-task/instruction.md +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/examples/hello-world-task/solution/solve.sh +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/examples/hello-world-task/task.toml +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/examples/hello-world-task/tests/test.sh +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/fixtures/mock_acp_agent.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/fixtures/mock_acp_agent_interleaved.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/fixtures/mock_acp_agent_multi_turn.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/integration/check_results.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/integration/configs/claude-agent-acp.yaml +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/integration/configs/codex-acp.yaml +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/integration/configs/gemini.yaml +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/integration/configs/harvey-lab-harness.yaml +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/integration/configs/openclaw.yaml +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/integration/configs/opencode.yaml +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/integration/configs/openhands.yaml +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/integration/configs/pi-acp.yaml +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_acp.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_agent_model_decouple.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_agent_registry.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_agent_spec.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_atif_trajectory.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_bedrock_proxy.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_bedrock_runtime.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_capture_trajectory.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_env_setup.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_exclude_tasks.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_job.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_metrics.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_mock_openai_responses_server.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_notification_order_real.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_oracle.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_process.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_provider_runtime.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_providers.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_registry_invariants.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_resolve_env_helpers.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_rewards_jsonl.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_sandbox.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_sandbox_setup.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_sandbox_verifier_workspace.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_scoring.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_sdk_lockdown.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_self_gen_orchestration.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_skill_eval.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_skill_eval_integration.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_skills.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_subscription_auth.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_task_download.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_tasks.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_trajectory_integration.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_trial_agent_timeout_verify.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_user.py +0 -0
- {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_yaml_config.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: benchflow
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: Multi-turn agent benchmarking with ACP — run any agent, any model, any provider.
|
|
5
5
|
Project-URL: Homepage, https://github.com/benchflow-ai/benchflow
|
|
6
6
|
Project-URL: Repository, https://github.com/benchflow-ai/benchflow
|
|
@@ -52,7 +52,7 @@ BenchFlow runs AI agents against benchmark tasks in sandboxed environments. Sing
|
|
|
52
52
|
|
|
53
53
|
- **Any ACP agent** — Gemini CLI, Claude Code, Codex, OpenCode, OpenHands, OpenClaw, Pi, or your own
|
|
54
54
|
- **Single + multi + progressive** — single-agent / multi-agent (coder + reviewer, simulated user) / multi-round with a Python `BaseUser` callback
|
|
55
|
-
- **
|
|
55
|
+
- **Sandboxes** — Docker locally, Daytona for parallel cloud runs, Modal for serverless/GPU-backed task environments
|
|
56
56
|
- **Hardened verifier** — defaults block BenchJack/Meerkat-style reward-hacking; tasks opt out per-feature
|
|
57
57
|
|
|
58
58
|
## Install
|
|
@@ -70,7 +70,7 @@ Start with [Getting started](./docs/getting-started.md), then [Concepts](./docs/
|
|
|
70
70
|
| If you want to… | Read |
|
|
71
71
|
|------------------|------|
|
|
72
72
|
| Run an eval on an existing task | [Getting started](./docs/getting-started.md) |
|
|
73
|
-
| Understand
|
|
73
|
+
| Understand Rollout / Scene / Role / Verifier | [Concepts](./docs/concepts.md) |
|
|
74
74
|
| Author a new task | [Task authoring](./docs/task-authoring.md) |
|
|
75
75
|
| Multi-agent: coder + reviewer, simulated user, BYOS, stateful envs | [Use cases](./docs/use-cases.md) |
|
|
76
76
|
| Multi-round single-agent (progressive disclosure, oracle access) | [Progressive disclosure](./docs/progressive-disclosure.md) |
|
|
@@ -99,12 +99,12 @@ Run any benchmark via the CLI:
|
|
|
99
99
|
|
|
100
100
|
```bash
|
|
101
101
|
# From a YAML config
|
|
102
|
-
bench eval create
|
|
102
|
+
bench eval create --config benchmarks/skillsbench-claude-glm51.yaml
|
|
103
103
|
|
|
104
104
|
# Inline — mirrors the YAML source fields
|
|
105
105
|
bench eval create \
|
|
106
106
|
--source-repo benchflow-ai/skillsbench --source-path tasks \
|
|
107
|
-
|
|
107
|
+
--agent gemini --model gemini-3.1-flash-lite-preview --sandbox daytona --concurrency 64
|
|
108
108
|
```
|
|
109
109
|
|
|
110
110
|
Repos are cloned and cached locally under `.cache/datasets/` on first use.
|
|
@@ -116,7 +116,7 @@ SkillsBench when you need its lockfile to point at the newest BenchFlow commit.
|
|
|
116
116
|
|
|
117
117
|
## Featured
|
|
118
118
|
|
|
119
|
-
- **Progressive disclosure on SWE-bench Pro** — the `BaseUser` abstraction drives a multi-round
|
|
119
|
+
- **Progressive disclosure on SWE-bench Pro** — the `BaseUser` abstraction drives a multi-round rollout: terse round-0 prompt → failing-test hints → full spec. 5/5 oracle on Daytona, runnable demo at [`docs/examples/swebench_pro_progressive_disclosure.ipynb`](./docs/examples/swebench_pro_progressive_disclosure.ipynb). Also benchflow's [Harbor #1316](https://github.com/harbor-ai/harbor/issues/1316) parity answer for the no-second-LLM case. See [Progressive disclosure](./docs/progressive-disclosure.md).
|
|
120
120
|
|
|
121
121
|
## Research artifacts
|
|
122
122
|
|
|
@@ -15,7 +15,7 @@ BenchFlow runs AI agents against benchmark tasks in sandboxed environments. Sing
|
|
|
15
15
|
|
|
16
16
|
- **Any ACP agent** — Gemini CLI, Claude Code, Codex, OpenCode, OpenHands, OpenClaw, Pi, or your own
|
|
17
17
|
- **Single + multi + progressive** — single-agent / multi-agent (coder + reviewer, simulated user) / multi-round with a Python `BaseUser` callback
|
|
18
|
-
- **
|
|
18
|
+
- **Sandboxes** — Docker locally, Daytona for parallel cloud runs, Modal for serverless/GPU-backed task environments
|
|
19
19
|
- **Hardened verifier** — defaults block BenchJack/Meerkat-style reward-hacking; tasks opt out per-feature
|
|
20
20
|
|
|
21
21
|
## Install
|
|
@@ -33,7 +33,7 @@ Start with [Getting started](./docs/getting-started.md), then [Concepts](./docs/
|
|
|
33
33
|
| If you want to… | Read |
|
|
34
34
|
|------------------|------|
|
|
35
35
|
| Run an eval on an existing task | [Getting started](./docs/getting-started.md) |
|
|
36
|
-
| Understand
|
|
36
|
+
| Understand Rollout / Scene / Role / Verifier | [Concepts](./docs/concepts.md) |
|
|
37
37
|
| Author a new task | [Task authoring](./docs/task-authoring.md) |
|
|
38
38
|
| Multi-agent: coder + reviewer, simulated user, BYOS, stateful envs | [Use cases](./docs/use-cases.md) |
|
|
39
39
|
| Multi-round single-agent (progressive disclosure, oracle access) | [Progressive disclosure](./docs/progressive-disclosure.md) |
|
|
@@ -62,12 +62,12 @@ Run any benchmark via the CLI:
|
|
|
62
62
|
|
|
63
63
|
```bash
|
|
64
64
|
# From a YAML config
|
|
65
|
-
bench eval create
|
|
65
|
+
bench eval create --config benchmarks/skillsbench-claude-glm51.yaml
|
|
66
66
|
|
|
67
67
|
# Inline — mirrors the YAML source fields
|
|
68
68
|
bench eval create \
|
|
69
69
|
--source-repo benchflow-ai/skillsbench --source-path tasks \
|
|
70
|
-
|
|
70
|
+
--agent gemini --model gemini-3.1-flash-lite-preview --sandbox daytona --concurrency 64
|
|
71
71
|
```
|
|
72
72
|
|
|
73
73
|
Repos are cloned and cached locally under `.cache/datasets/` on first use.
|
|
@@ -79,7 +79,7 @@ SkillsBench when you need its lockfile to point at the newest BenchFlow commit.
|
|
|
79
79
|
|
|
80
80
|
## Featured
|
|
81
81
|
|
|
82
|
-
- **Progressive disclosure on SWE-bench Pro** — the `BaseUser` abstraction drives a multi-round
|
|
82
|
+
- **Progressive disclosure on SWE-bench Pro** — the `BaseUser` abstraction drives a multi-round rollout: terse round-0 prompt → failing-test hints → full spec. 5/5 oracle on Daytona, runnable demo at [`docs/examples/swebench_pro_progressive_disclosure.ipynb`](./docs/examples/swebench_pro_progressive_disclosure.ipynb). Also benchflow's [Harbor #1316](https://github.com/harbor-ai/harbor/issues/1316) parity answer for the no-second-LLM case. See [Progressive disclosure](./docs/progressive-disclosure.md).
|
|
83
83
|
|
|
84
84
|
## Research artifacts
|
|
85
85
|
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
Re-exports environment APIs and adds:
|
|
4
4
|
- ACP client for multi-turn agent communication
|
|
5
5
|
- Trajectory capture (HTTP proxy, OTel collector, ACP native)
|
|
6
|
-
-
|
|
7
|
-
-
|
|
6
|
+
- Rollout lifecycle for single-task execution
|
|
7
|
+
- Evaluation orchestration with retries and concurrency
|
|
8
8
|
- Metrics collection and aggregation
|
|
9
9
|
"""
|
|
10
10
|
|
|
@@ -25,10 +25,18 @@ from harbor import (
|
|
|
25
25
|
|
|
26
26
|
# benchflow's additions
|
|
27
27
|
from benchflow._env_setup import stage_dockerfile_deps
|
|
28
|
-
from benchflow._scene import MailboxTransport, Message, MessageTransport,
|
|
28
|
+
from benchflow._scene import MailboxTransport, Message, MessageTransport, SceneRole
|
|
29
|
+
from benchflow._scene import Scene as SceneRuntime
|
|
29
30
|
from benchflow._snapshot import list_snapshots, restore, snapshot
|
|
31
|
+
from benchflow._types import Role, Scene, Turn
|
|
30
32
|
from benchflow.acp.client import ACPClient
|
|
31
33
|
from benchflow.acp.session import ACPSession
|
|
34
|
+
from benchflow.adapters import (
|
|
35
|
+
InspectAdapter,
|
|
36
|
+
ORSAdapter,
|
|
37
|
+
to_inspect_task,
|
|
38
|
+
to_ors_reward,
|
|
39
|
+
)
|
|
32
40
|
from benchflow.agents.registry import (
|
|
33
41
|
AGENTS,
|
|
34
42
|
get_agent,
|
|
@@ -43,34 +51,89 @@ from benchflow.environments import (
|
|
|
43
51
|
detect_services_from_dockerfile,
|
|
44
52
|
register_service,
|
|
45
53
|
)
|
|
46
|
-
from benchflow.
|
|
54
|
+
from benchflow.evaluation import (
|
|
55
|
+
Evaluation,
|
|
56
|
+
EvaluationConfig,
|
|
57
|
+
EvaluationResult,
|
|
58
|
+
RetryConfig,
|
|
59
|
+
)
|
|
47
60
|
from benchflow.metrics import BenchmarkMetrics, collect_metrics
|
|
48
|
-
from benchflow.models import AgentInstallError, AgentTimeoutError,
|
|
61
|
+
from benchflow.models import AgentInstallError, AgentTimeoutError, RolloutResult
|
|
62
|
+
|
|
63
|
+
# Rewards protocol (v0.4 — composable Rubric + RewardFunc)
|
|
64
|
+
from benchflow.rewards import (
|
|
65
|
+
CodeExecRewardFunc,
|
|
66
|
+
Criterion,
|
|
67
|
+
JudgeConfig,
|
|
68
|
+
LLMJudgeRewardFunc,
|
|
69
|
+
RewardEvent,
|
|
70
|
+
RewardFunc,
|
|
71
|
+
Rubric,
|
|
72
|
+
RubricConfig,
|
|
73
|
+
ScoringConfig,
|
|
74
|
+
StringMatchRewardFunc,
|
|
75
|
+
TestRewardFunc,
|
|
76
|
+
VerifyResult,
|
|
77
|
+
load_rubric_toml,
|
|
78
|
+
)
|
|
79
|
+
from benchflow.rollout import Rollout, RolloutConfig
|
|
49
80
|
from benchflow.runtime import (
|
|
50
81
|
Agent,
|
|
51
82
|
Environment,
|
|
52
83
|
Runtime,
|
|
53
84
|
RuntimeConfig,
|
|
54
85
|
RuntimeResult,
|
|
55
|
-
run,
|
|
56
|
-
)
|
|
86
|
+
run,
|
|
87
|
+
) # bf.run() — supports Agent, RolloutConfig, and str calling conventions
|
|
88
|
+
|
|
89
|
+
# Sandbox protocol (v0.4 — parallel types, Harbor not yet removed)
|
|
90
|
+
from benchflow.sandbox import ExecResult as SandboxExecResult
|
|
91
|
+
from benchflow.sandbox import ImageBuilder, ImageConfig, ImageRef, Sandbox
|
|
57
92
|
from benchflow.sdk import SDK
|
|
58
93
|
from benchflow.skills import SkillInfo, discover_skills, install_skill, parse_skill
|
|
59
94
|
from benchflow.trajectories.otel import OTelCollector
|
|
60
95
|
from benchflow.trajectories.proxy import TrajectoryProxy
|
|
61
96
|
from benchflow.trajectories.types import Trajectory
|
|
62
|
-
from benchflow.trial import Role as TrialRole
|
|
63
|
-
from benchflow.trial import Scene as TrialScene
|
|
64
|
-
from benchflow.trial import Trial, TrialConfig, Turn
|
|
65
97
|
from benchflow.trial_yaml import trial_config_from_yaml
|
|
66
98
|
from benchflow.user import BaseUser, FunctionUser, PassthroughUser, RoundResult
|
|
67
99
|
|
|
100
|
+
# Backward-compat aliases
|
|
101
|
+
Trial = Rollout
|
|
102
|
+
TrialConfig = RolloutConfig
|
|
103
|
+
TrialRole = Role
|
|
104
|
+
TrialScene = Scene
|
|
105
|
+
RunResult = RolloutResult
|
|
106
|
+
Job = Evaluation
|
|
107
|
+
JobConfig = EvaluationConfig
|
|
108
|
+
JobResult = EvaluationResult
|
|
109
|
+
|
|
68
110
|
# Public API surface. Anything not in this list is implementation detail and
|
|
69
111
|
# may change without notice. Names are grouped by source module to match the
|
|
70
112
|
# imports above and to make it obvious to a future agent which module owns
|
|
71
113
|
# what.
|
|
72
114
|
__all__ = [
|
|
73
115
|
"__version__",
|
|
116
|
+
# Rewards protocol (v0.4)
|
|
117
|
+
"Rubric",
|
|
118
|
+
"RewardFunc",
|
|
119
|
+
"RewardEvent",
|
|
120
|
+
"VerifyResult",
|
|
121
|
+
"TestRewardFunc",
|
|
122
|
+
"LLMJudgeRewardFunc",
|
|
123
|
+
"StringMatchRewardFunc",
|
|
124
|
+
"CodeExecRewardFunc",
|
|
125
|
+
# Rubric config (ENG-55)
|
|
126
|
+
"Criterion",
|
|
127
|
+
"JudgeConfig",
|
|
128
|
+
"RubricConfig",
|
|
129
|
+
"ScoringConfig",
|
|
130
|
+
"load_rubric_toml",
|
|
131
|
+
# Sandbox protocol (v0.4)
|
|
132
|
+
"Sandbox",
|
|
133
|
+
"SandboxExecResult",
|
|
134
|
+
"ImageBuilder",
|
|
135
|
+
"ImageConfig",
|
|
136
|
+
"ImageRef",
|
|
74
137
|
# Harbor re-exports
|
|
75
138
|
"BaseAgent",
|
|
76
139
|
"BaseEnvironment",
|
|
@@ -89,28 +152,38 @@ __all__ = [
|
|
|
89
152
|
"is_vertex_model",
|
|
90
153
|
"list_agents",
|
|
91
154
|
"register_agent",
|
|
92
|
-
#
|
|
155
|
+
# Evaluation orchestration (new names)
|
|
156
|
+
"Evaluation",
|
|
157
|
+
"EvaluationConfig",
|
|
158
|
+
"EvaluationResult",
|
|
159
|
+
"RetryConfig",
|
|
160
|
+
# Backward-compat aliases for Job
|
|
93
161
|
"Job",
|
|
94
162
|
"JobConfig",
|
|
95
163
|
"JobResult",
|
|
96
|
-
"RetryConfig",
|
|
97
164
|
# Metrics
|
|
98
165
|
"BenchmarkMetrics",
|
|
99
166
|
"collect_metrics",
|
|
100
167
|
# Models / errors
|
|
101
168
|
"AgentInstallError",
|
|
102
169
|
"AgentTimeoutError",
|
|
170
|
+
"RolloutResult",
|
|
103
171
|
"RunResult",
|
|
104
|
-
# Runtime (0.3
|
|
172
|
+
# Runtime (0.3 compat)
|
|
105
173
|
"Agent",
|
|
106
174
|
"Environment",
|
|
107
175
|
"Runtime",
|
|
108
176
|
"RuntimeConfig",
|
|
109
177
|
"RuntimeResult",
|
|
178
|
+
# Single entry point
|
|
110
179
|
"run",
|
|
111
|
-
#
|
|
112
|
-
"Scene",
|
|
180
|
+
# Canonical declarative types (_types.py — ENG-47)
|
|
113
181
|
"Role",
|
|
182
|
+
"Scene",
|
|
183
|
+
"Turn",
|
|
184
|
+
# Multi-agent scene runtime
|
|
185
|
+
"SceneRole",
|
|
186
|
+
"SceneRuntime",
|
|
114
187
|
"Message",
|
|
115
188
|
"MessageTransport",
|
|
116
189
|
"MailboxTransport",
|
|
@@ -118,12 +191,14 @@ __all__ = [
|
|
|
118
191
|
"snapshot",
|
|
119
192
|
"restore",
|
|
120
193
|
"list_snapshots",
|
|
121
|
-
#
|
|
194
|
+
# Rollout (single execution path — ENG-46)
|
|
195
|
+
"Rollout",
|
|
196
|
+
"RolloutConfig",
|
|
197
|
+
# Backward-compat aliases for Trial
|
|
122
198
|
"Trial",
|
|
123
199
|
"TrialConfig",
|
|
124
200
|
"TrialRole",
|
|
125
201
|
"TrialScene",
|
|
126
|
-
"Turn",
|
|
127
202
|
"trial_config_from_yaml",
|
|
128
203
|
# User abstraction (progressive disclosure)
|
|
129
204
|
"BaseUser",
|
|
@@ -147,11 +222,25 @@ __all__ = [
|
|
|
147
222
|
"OTelCollector",
|
|
148
223
|
"TrajectoryProxy",
|
|
149
224
|
"Trajectory",
|
|
225
|
+
# External adapters (ENG-51)
|
|
226
|
+
"InspectAdapter",
|
|
227
|
+
"ORSAdapter",
|
|
228
|
+
"to_inspect_task",
|
|
229
|
+
"to_ors_reward",
|
|
150
230
|
]
|
|
151
231
|
|
|
152
232
|
|
|
153
233
|
def __getattr__(name: str):
|
|
154
234
|
"""Fall through to harbor for names not explicitly re-exported."""
|
|
235
|
+
# Let Python's normal submodule resolution handle subpackages first.
|
|
236
|
+
import importlib
|
|
237
|
+
|
|
238
|
+
try:
|
|
239
|
+
return importlib.import_module(f"benchflow.{name}")
|
|
240
|
+
except ModuleNotFoundError as e:
|
|
241
|
+
if e.name != f"benchflow.{name}":
|
|
242
|
+
raise
|
|
243
|
+
|
|
155
244
|
import harbor
|
|
156
245
|
|
|
157
246
|
if hasattr(harbor, name):
|
|
@@ -163,7 +163,7 @@ def inject_vertex_credentials(agent_env: dict[str, str], model: str) -> None:
|
|
|
163
163
|
if "GOOGLE_CLOUD_PROJECT" not in agent_env:
|
|
164
164
|
raise ValueError(
|
|
165
165
|
f"GOOGLE_CLOUD_PROJECT required for Vertex AI model {model!r}. "
|
|
166
|
-
f"Export it or pass via --
|
|
166
|
+
f"Export it or pass via --agent-env GOOGLE_CLOUD_PROJECT=<project>"
|
|
167
167
|
)
|
|
168
168
|
|
|
169
169
|
|
|
@@ -243,6 +243,7 @@ async def deploy_skills(
|
|
|
243
243
|
logger.warning(f"Skills dir not found: {skills_path}")
|
|
244
244
|
else:
|
|
245
245
|
logger.info("Skills already injected via Dockerfile")
|
|
246
|
+
effective_skills = "/skills"
|
|
246
247
|
|
|
247
248
|
# Distribute to agent-specific discovery paths
|
|
248
249
|
if agent_cfg is not None:
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""Single entry point: ``bf.run(RolloutConfig) → RolloutResult``."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
|
+
|
|
7
|
+
from benchflow.models import RolloutResult
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from benchflow.rollout import RolloutConfig
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
async def run(config: RolloutConfig | None = None, **kwargs: Any) -> RolloutResult:
|
|
14
|
+
"""Execute a single rollout and return the result.
|
|
15
|
+
|
|
16
|
+
This is the canonical entry point for running an agent on a task::
|
|
17
|
+
|
|
18
|
+
import benchflow as bf
|
|
19
|
+
result = await bf.run(RolloutConfig(task_path=..., agent=...))
|
|
20
|
+
|
|
21
|
+
If *config* is None, a :class:`RolloutConfig` is built from *kwargs*
|
|
22
|
+
via :meth:`RolloutConfig.from_legacy`.
|
|
23
|
+
"""
|
|
24
|
+
from benchflow.rollout import Rollout
|
|
25
|
+
from benchflow.rollout import RolloutConfig as _RolloutConfig
|
|
26
|
+
|
|
27
|
+
if config is None:
|
|
28
|
+
config = _RolloutConfig.from_legacy(**kwargs)
|
|
29
|
+
if config.skill_mode == "self-gen":
|
|
30
|
+
from benchflow.self_gen import run_self_gen
|
|
31
|
+
|
|
32
|
+
return await run_self_gen(config)
|
|
33
|
+
rollout = await Rollout.create(config)
|
|
34
|
+
return await rollout.run()
|
|
@@ -14,12 +14,27 @@ reads the outbox after each agent exits, routes through the transport,
|
|
|
14
14
|
and injects into the next agent's prompt.
|
|
15
15
|
|
|
16
16
|
0.3 scope: exactly 2 roles, sequential execution, mailbox transport only.
|
|
17
|
+
|
|
18
|
+
.. rubric:: Capability boundary (ENG-50)
|
|
19
|
+
|
|
20
|
+
BenchFlow's scene scheduler handles **turn sequencing** and **message
|
|
21
|
+
routing** only. It does *not* manage:
|
|
22
|
+
|
|
23
|
+
* Agent-internal loops — the agent decides when/how to iterate.
|
|
24
|
+
* Tool protocols — tool-use is a per-agent capability.
|
|
25
|
+
* Agent-as-tool invocation — if agent A wants to call agent B as a
|
|
26
|
+
tool, agent A is responsible for reaching B's endpoint (all roles
|
|
27
|
+
share the sandbox, so localhost networking is available).
|
|
28
|
+
|
|
29
|
+
The scheduler ensures each role runs in order and that outbox messages
|
|
30
|
+
are delivered. Everything beyond that is the agent's responsibility.
|
|
17
31
|
"""
|
|
18
32
|
|
|
19
33
|
from __future__ import annotations
|
|
20
34
|
|
|
21
35
|
import json
|
|
22
36
|
import logging
|
|
37
|
+
import shlex
|
|
23
38
|
import uuid
|
|
24
39
|
from abc import ABC, abstractmethod
|
|
25
40
|
from collections.abc import Callable
|
|
@@ -37,7 +52,13 @@ logger = logging.getLogger(__name__)
|
|
|
37
52
|
|
|
38
53
|
|
|
39
54
|
@dataclass
|
|
40
|
-
class
|
|
55
|
+
class SceneRole:
|
|
56
|
+
"""Runtime role for multi-agent scenes (internal to _scene.py).
|
|
57
|
+
|
|
58
|
+
Unlike the canonical ``Role`` in ``_types.py``, this carries the
|
|
59
|
+
instruction text and tool list needed by the scene scheduler.
|
|
60
|
+
"""
|
|
61
|
+
|
|
41
62
|
name: str
|
|
42
63
|
agent: str
|
|
43
64
|
model: str
|
|
@@ -106,7 +127,7 @@ class Scene:
|
|
|
106
127
|
|
|
107
128
|
def __init__(
|
|
108
129
|
self,
|
|
109
|
-
roles: dict[str,
|
|
130
|
+
roles: dict[str, SceneRole],
|
|
110
131
|
transport: MessageTransport | None = None,
|
|
111
132
|
max_rounds: int = 10,
|
|
112
133
|
) -> None:
|
|
@@ -158,7 +179,7 @@ class Scene:
|
|
|
158
179
|
def is_done(self) -> bool:
|
|
159
180
|
return self._done or self._round >= self.max_rounds
|
|
160
181
|
|
|
161
|
-
def build_prompt_for_role(self, role:
|
|
182
|
+
def build_prompt_for_role(self, role: SceneRole, inbox: list[Message]) -> str:
|
|
162
183
|
"""Build the prompt for a role, injecting any pending messages."""
|
|
163
184
|
parts = [role.instruction]
|
|
164
185
|
if inbox:
|
|
@@ -190,7 +211,8 @@ class Scene:
|
|
|
190
211
|
]
|
|
191
212
|
messages = []
|
|
192
213
|
for fpath in files:
|
|
193
|
-
|
|
214
|
+
qpath = shlex.quote(fpath)
|
|
215
|
+
cat_result = await env.exec(f"cat {qpath}")
|
|
194
216
|
try:
|
|
195
217
|
data = json.loads(cat_result.stdout or "{}")
|
|
196
218
|
recipient = data.get("to", "")
|
|
@@ -212,7 +234,7 @@ class Scene:
|
|
|
212
234
|
messages.append(msg)
|
|
213
235
|
except json.JSONDecodeError:
|
|
214
236
|
logger.warning(f"[Scene] invalid JSON in outbox file: {fpath}")
|
|
215
|
-
await env.exec(f"rm -f {
|
|
237
|
+
await env.exec(f"rm -f {qpath}")
|
|
216
238
|
return messages
|
|
217
239
|
|
|
218
240
|
# ------------------------------------------------------------------
|
|
@@ -287,3 +309,7 @@ class Scene:
|
|
|
287
309
|
]
|
|
288
310
|
path.write_text("\n".join(lines) + "\n" if lines else "")
|
|
289
311
|
logger.info(f"Scene trajectory saved: {len(self.trajectory)} messages → {path}")
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
# Backward-compat alias — existing code imports ``Role`` from this module.
|
|
315
|
+
Role = SceneRole
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"""Sandbox snapshot/restore — filesystem-level checkpointing.
|
|
2
2
|
|
|
3
3
|
Provides snapshot(name) -> ref and restore(ref) for any environment that
|
|
4
|
-
supports env.exec(). Works on both Docker and Daytona
|
|
4
|
+
supports env.exec(). Works on both Docker and Daytona sandboxes.
|
|
5
5
|
|
|
6
6
|
Implementation: tar the workspace directory into /tmp/.benchflow_snapshots/.
|
|
7
|
-
In-place restore by clearing and untarring.
|
|
7
|
+
In-place restore by clearing and untarring. Sandbox-agnostic — no Docker
|
|
8
8
|
daemon or Daytona snapshot API required.
|
|
9
9
|
|
|
10
10
|
For 0.3+, Daytona's _experimental_create_snapshot can be swapped in as an
|
|
@@ -13,6 +13,8 @@ filesystem approach covers the rewind use case and is provable now.
|
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
15
|
import logging
|
|
16
|
+
import re as _re
|
|
17
|
+
import shlex
|
|
16
18
|
from pathlib import PurePosixPath
|
|
17
19
|
|
|
18
20
|
logger = logging.getLogger(__name__)
|
|
@@ -26,16 +28,14 @@ async def snapshot(env, name: str, workspace: str = "/app") -> str:
|
|
|
26
28
|
Returns a reference string suitable for restore() and for recording
|
|
27
29
|
in trial metadata / rewards.jsonl.
|
|
28
30
|
"""
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if not re.match(r"^[a-zA-Z0-9_-]+$", name):
|
|
31
|
+
if not _re.match(r"^[a-zA-Z0-9_-]+$", name):
|
|
32
32
|
raise ValueError(
|
|
33
33
|
f"Snapshot name must be alphanumeric/dash/underscore, got: {name!r}"
|
|
34
34
|
)
|
|
35
35
|
await env.exec(f"mkdir -p {_SNAP_DIR}")
|
|
36
36
|
snap_path = f"{_SNAP_DIR}/{name}.tar.gz"
|
|
37
37
|
result = await env.exec(
|
|
38
|
-
f"tar czf {snap_path} -C {workspace} .",
|
|
38
|
+
f"tar czf {shlex.quote(snap_path)} -C {shlex.quote(workspace)} .",
|
|
39
39
|
timeout_sec=120,
|
|
40
40
|
)
|
|
41
41
|
if result.return_code != 0:
|
|
@@ -54,12 +54,19 @@ async def restore(env, ref: str, workspace: str = "/app") -> None:
|
|
|
54
54
|
if len(parts) != 3 or parts[0] != "fs":
|
|
55
55
|
raise ValueError(f"invalid snapshot ref: {ref}")
|
|
56
56
|
snap_path = parts[2]
|
|
57
|
-
|
|
57
|
+
# Validate snap_path: must be under _SNAP_DIR and a .tar.gz file
|
|
58
|
+
if not snap_path.startswith(_SNAP_DIR + "/") or not snap_path.endswith(".tar.gz"):
|
|
59
|
+
raise ValueError(f"invalid snapshot ref: path must be under {_SNAP_DIR}")
|
|
60
|
+
if ".." in snap_path.split("/"):
|
|
61
|
+
raise ValueError("invalid snapshot ref: path traversal not allowed")
|
|
62
|
+
check = await env.exec(
|
|
63
|
+
f"test -f {shlex.quote(snap_path)} && echo ok || echo missing"
|
|
64
|
+
)
|
|
58
65
|
if "missing" in (check.stdout or ""):
|
|
59
66
|
raise FileNotFoundError(f"snapshot not found: {snap_path}")
|
|
60
67
|
result = await env.exec(
|
|
61
|
-
f"rm -rf {workspace}/* {workspace}/.[!.]* 2>/dev/null; "
|
|
62
|
-
f"tar xzf {snap_path} -C {workspace}",
|
|
68
|
+
f"rm -rf {shlex.quote(workspace)}/* {shlex.quote(workspace)}/.[!.]* 2>/dev/null; "
|
|
69
|
+
f"tar xzf {shlex.quote(snap_path)} -C {shlex.quote(workspace)}",
|
|
63
70
|
timeout_sec=120,
|
|
64
71
|
)
|
|
65
72
|
if result.return_code != 0:
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""Canonical Scene / Role / Turn data types for benchflow trials.
|
|
2
|
+
|
|
3
|
+
These are the *declarative* types — they describe what a trial *will* do.
|
|
4
|
+
Runtime classes (e.g. ``_scene.Scene``) consume these but are not defined
|
|
5
|
+
here.
|
|
6
|
+
|
|
7
|
+
Merged from the duplicate definitions that lived in ``trial.py`` and
|
|
8
|
+
``_scene.py`` prior to ENG-47.
|
|
9
|
+
|
|
10
|
+
.. rubric:: Capability boundary (ENG-50)
|
|
11
|
+
|
|
12
|
+
BenchFlow provides **sandbox + instruction + observation** infrastructure.
|
|
13
|
+
It does **not** orchestrate agent-internal loops, tool protocols, or
|
|
14
|
+
agent-as-tool invocations. Those are per-agent capabilities declared via
|
|
15
|
+
:pyattr:`Role.capabilities` and implemented by the agent itself.
|
|
16
|
+
|
|
17
|
+
Concretely:
|
|
18
|
+
|
|
19
|
+
* BenchFlow sets up the sandbox (Docker / Daytona / Modal), injects
|
|
20
|
+
environment variables and skills, and wires up the ACP transport.
|
|
21
|
+
* BenchFlow ensures sandbox networking allows inter-agent communication
|
|
22
|
+
(all roles in a scene share a sandbox, so localhost is reachable).
|
|
23
|
+
* The ``capabilities`` field on :class:`Role` is a **declaration** — it
|
|
24
|
+
tells downstream tooling / dashboards what the agent supports, but
|
|
25
|
+
BenchFlow itself does not act on it.
|
|
26
|
+
* Agent-as-tool is a per-agent capability. BenchFlow does not invoke
|
|
27
|
+
agents on behalf of other agents; the calling agent uses its own
|
|
28
|
+
native tool-use to reach a companion agent's endpoint.
|
|
29
|
+
* Loop management is entirely the agent's responsibility. BenchFlow
|
|
30
|
+
scenes define *turns* (prompts), not iteration.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
from __future__ import annotations
|
|
34
|
+
|
|
35
|
+
from dataclasses import dataclass, field
|
|
36
|
+
from pathlib import Path
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass
|
|
40
|
+
class Role:
|
|
41
|
+
"""One agent participant in a scene.
|
|
42
|
+
|
|
43
|
+
The ``capabilities`` field is a declarative list of strings describing
|
|
44
|
+
what the agent natively supports (e.g. ``["tool-use", "loop",
|
|
45
|
+
"agent-as-tool"]``). BenchFlow records these in trial metadata but
|
|
46
|
+
does **not** act on them — the agent itself is responsible for
|
|
47
|
+
implementing whatever capabilities it advertises.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
name: str
|
|
51
|
+
agent: str
|
|
52
|
+
model: str | None = None
|
|
53
|
+
env: dict[str, str] = field(default_factory=dict)
|
|
54
|
+
timeout_sec: int | None = None # None = inherit from task.toml
|
|
55
|
+
idle_timeout_sec: int | None = None
|
|
56
|
+
skills_dir: str | Path | None = None
|
|
57
|
+
capabilities: list[str] | None = None # e.g. ["tool-use", "agent-as-tool", "loop"]
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dataclass
|
|
61
|
+
class Turn:
|
|
62
|
+
"""One prompt in a scene. *role* selects which Role acts."""
|
|
63
|
+
|
|
64
|
+
role: str
|
|
65
|
+
prompt: str | None = None # None = expand from instruction.md
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@dataclass
|
|
69
|
+
class Scene:
|
|
70
|
+
"""One interaction region — roles take turns executing prompts."""
|
|
71
|
+
|
|
72
|
+
name: str = "default"
|
|
73
|
+
roles: list[Role] = field(default_factory=list)
|
|
74
|
+
turns: list[Turn] = field(default_factory=list)
|
|
75
|
+
skills_dir: str | Path | None = None
|
|
76
|
+
parallel_group: str | None = None # scenes with same group execute concurrently
|
|
77
|
+
|
|
78
|
+
@classmethod
|
|
79
|
+
def single(
|
|
80
|
+
cls,
|
|
81
|
+
*,
|
|
82
|
+
agent: str,
|
|
83
|
+
model: str | None = None,
|
|
84
|
+
prompts: list[str | None] | None = None,
|
|
85
|
+
role_name: str = "agent",
|
|
86
|
+
skills_dir: str | Path | None = None,
|
|
87
|
+
) -> Scene:
|
|
88
|
+
"""Shortcut for single-agent, single-role scene."""
|
|
89
|
+
prompts = prompts or [None]
|
|
90
|
+
return cls(
|
|
91
|
+
roles=[Role(name=role_name, agent=agent, model=model)],
|
|
92
|
+
turns=[Turn(role=role_name, prompt=p) for p in prompts],
|
|
93
|
+
skills_dir=skills_dir,
|
|
94
|
+
)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""External framework adapters for BenchFlow types.
|
|
2
|
+
|
|
3
|
+
These are pure format converters — they translate BenchFlow's ``Scene``,
|
|
4
|
+
``Rubric``, ``VerifyResult``, and ``RewardEvent`` types into dicts that
|
|
5
|
+
follow the conventions of external eval frameworks. No external SDK is
|
|
6
|
+
required.
|
|
7
|
+
|
|
8
|
+
Supported targets:
|
|
9
|
+
|
|
10
|
+
* **Inspect AI** — ``InspectAdapter`` / ``to_inspect_task``
|
|
11
|
+
* **ORS (OpenReward)** — ``ORSAdapter`` / ``to_ors_reward``
|
|
12
|
+
|
|
13
|
+
To add a new adapter, create a module under ``benchflow.adapters`` and
|
|
14
|
+
re-export its public symbols here.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from benchflow.adapters.inspect_ai import InspectAdapter, to_inspect_task
|
|
18
|
+
from benchflow.adapters.ors import ORSAdapter, to_ors_reward
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"InspectAdapter",
|
|
22
|
+
"ORSAdapter",
|
|
23
|
+
"to_inspect_task",
|
|
24
|
+
"to_ors_reward",
|
|
25
|
+
]
|