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.
Files changed (189) hide show
  1. {benchflow-0.3.3 → benchflow-0.3.4}/PKG-INFO +6 -6
  2. {benchflow-0.3.3 → benchflow-0.3.4}/README.md +5 -5
  3. {benchflow-0.3.3 → benchflow-0.3.4}/pyproject.toml +1 -1
  4. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/__init__.py +106 -17
  5. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/_agent_env.py +1 -1
  6. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/_agent_setup.py +1 -0
  7. benchflow-0.3.4/src/benchflow/_run.py +34 -0
  8. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/_scene.py +31 -5
  9. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/_snapshot.py +16 -9
  10. benchflow-0.3.4/src/benchflow/_types.py +94 -0
  11. benchflow-0.3.4/src/benchflow/adapters/__init__.py +25 -0
  12. benchflow-0.3.4/src/benchflow/adapters/inspect_ai.py +63 -0
  13. benchflow-0.3.4/src/benchflow/adapters/ors.py +68 -0
  14. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/agents/__init__.py +0 -4
  15. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/agents/pi_acp_launcher.py +2 -2
  16. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/agents/providers.py +1 -1
  17. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/cli/main.py +76 -187
  18. benchflow-0.3.3/src/benchflow/job.py → benchflow-0.3.4/src/benchflow/evaluation.py +57 -95
  19. benchflow-0.3.4/src/benchflow/job.py +29 -0
  20. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/models.py +20 -7
  21. benchflow-0.3.4/src/benchflow/rewards/README.md +125 -0
  22. benchflow-0.3.4/src/benchflow/rewards/__init__.py +34 -0
  23. benchflow-0.3.4/src/benchflow/rewards/builtins.py +471 -0
  24. benchflow-0.3.4/src/benchflow/rewards/events.py +26 -0
  25. benchflow-0.3.4/src/benchflow/rewards/file_readers.py +142 -0
  26. benchflow-0.3.4/src/benchflow/rewards/llm.py +186 -0
  27. benchflow-0.3.4/src/benchflow/rewards/protocol.py +33 -0
  28. benchflow-0.3.4/src/benchflow/rewards/rubric.py +76 -0
  29. benchflow-0.3.4/src/benchflow/rewards/rubric_config.py +127 -0
  30. benchflow-0.3.3/src/benchflow/trial.py → benchflow-0.3.4/src/benchflow/rollout.py +429 -104
  31. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/runtime.py +25 -23
  32. benchflow-0.3.4/src/benchflow/sandbox/__init__.py +9 -0
  33. benchflow-0.3.4/src/benchflow/sandbox/daytona.py +74 -0
  34. benchflow-0.3.4/src/benchflow/sandbox/docker.py +74 -0
  35. benchflow-0.3.4/src/benchflow/sandbox/protocol.py +74 -0
  36. benchflow-0.3.4/src/benchflow/sdk.py +193 -0
  37. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/tasks.py +1 -1
  38. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/trajectories/__init__.py +0 -5
  39. benchflow-0.3.4/src/benchflow/trial.py +39 -0
  40. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/trial_yaml.py +2 -1
  41. {benchflow-0.3.3 → benchflow-0.3.4}/tests/conformance/README.md +3 -3
  42. {benchflow-0.3.3 → benchflow-0.3.4}/tests/examples/test_claude.sh +8 -8
  43. {benchflow-0.3.3 → benchflow-0.3.4}/tests/examples/test_codex.sh +9 -9
  44. {benchflow-0.3.3 → benchflow-0.3.4}/tests/examples/test_codex_custom_provider.sh +7 -7
  45. {benchflow-0.3.3 → benchflow-0.3.4}/tests/examples/test_gemini.sh +7 -7
  46. {benchflow-0.3.3 → benchflow-0.3.4}/tests/examples/test_openclaw.sh +9 -9
  47. {benchflow-0.3.3 → benchflow-0.3.4}/tests/fixtures/mock_openai_responses_server.py +2 -2
  48. {benchflow-0.3.3 → benchflow-0.3.4}/tests/integration/run.sh +6 -6
  49. benchflow-0.3.4/tests/test_adapters.py +218 -0
  50. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_agent_setup.py +48 -0
  51. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_connect_as_env.py +8 -8
  52. benchflow-0.3.4/tests/test_eng50_capabilities.py +248 -0
  53. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_internet_policy.py +10 -8
  54. benchflow-0.3.4/tests/test_llm_judge.py +502 -0
  55. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_oracle_chokepoint.py +15 -40
  56. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_pi_acp_launcher.py +1 -1
  57. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_reexport.py +1 -1
  58. benchflow-0.3.4/tests/test_rewards.py +338 -0
  59. benchflow-0.3.4/tests/test_rubric_config.py +175 -0
  60. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_runtime.py +5 -5
  61. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_sandbox_hardening.py +6 -6
  62. benchflow-0.3.4/tests/test_sandbox_protocol.py +250 -0
  63. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_scene.py +4 -8
  64. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_scene_outbox_trial.py +2 -2
  65. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_sdk_internals.py +1 -1
  66. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_self_gen_cli.py +1 -1
  67. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_skill_eval_dryrun.py +1 -1
  68. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_smoke.py +2 -1
  69. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_trial_bedrock_proxy.py +8 -8
  70. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_trial_install_agent_timeout.py +17 -17
  71. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_verify.py +14 -14
  72. benchflow-0.3.3/src/benchflow/agents/user_agent.py +0 -62
  73. benchflow-0.3.3/src/benchflow/cli/eval.py +0 -376
  74. benchflow-0.3.3/src/benchflow/sdk.py +0 -582
  75. benchflow-0.3.3/src/benchflow/trajectories/atif.py +0 -112
  76. benchflow-0.3.3/src/benchflow/trajectories/claude_code.py +0 -249
  77. benchflow-0.3.3/tests/test_eval_cli.py +0 -118
  78. {benchflow-0.3.3 → benchflow-0.3.4}/.gitignore +0 -0
  79. {benchflow-0.3.3 → benchflow-0.3.4}/CHANGELOG.md +0 -0
  80. {benchflow-0.3.3 → benchflow-0.3.4}/LICENSE +0 -0
  81. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/_acp_run.py +0 -0
  82. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/_credentials.py +0 -0
  83. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/_daytona_patches.py +0 -0
  84. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/_env_setup.py +0 -0
  85. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/_provider_runtime.py +0 -0
  86. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/_sandbox.py +0 -0
  87. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/_scoring.py +0 -0
  88. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/_trajectory.py +0 -0
  89. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/acp/__init__.py +0 -0
  90. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/acp/client.py +0 -0
  91. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/acp/container_transport.py +0 -0
  92. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/acp/session.py +0 -0
  93. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/acp/transport.py +0 -0
  94. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/acp/types.py +0 -0
  95. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/agents/harvey_lab_acp_shim.py +0 -0
  96. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/agents/openclaw_acp_shim.py +0 -0
  97. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/agents/registry.py +0 -0
  98. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/cli/__init__.py +0 -0
  99. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/demo_task/environment/Dockerfile +0 -0
  100. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/demo_task/instruction.md +0 -0
  101. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/demo_task/task.toml +0 -0
  102. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/demo_task/tests/test.sh +0 -0
  103. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/environments.py +0 -0
  104. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/mcp/__init__.py +0 -0
  105. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/mcp/hooks.py +0 -0
  106. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/mcp/reviewer_server.py +0 -0
  107. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/metrics.py +0 -0
  108. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/process.py +0 -0
  109. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/providers/__init__.py +0 -0
  110. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/providers/bedrock_proxy.py +0 -0
  111. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/providers/bedrock_runtime.py +0 -0
  112. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/py.typed +0 -0
  113. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/self_gen.py +0 -0
  114. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/skill_eval.py +0 -0
  115. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/skills.py +0 -0
  116. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/task_download.py +0 -0
  117. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/templates/__init__.py +0 -0
  118. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/templates/judge.py.tmpl +0 -0
  119. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/templates/test.sh.tmpl +0 -0
  120. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/trajectories/otel.py +0 -0
  121. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/trajectories/proxy.py +0 -0
  122. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/trajectories/types.py +0 -0
  123. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/user.py +0 -0
  124. {benchflow-0.3.3 → benchflow-0.3.4}/src/benchflow/viewer.py +0 -0
  125. {benchflow-0.3.3 → benchflow-0.3.4}/tests/__init__.py +0 -0
  126. {benchflow-0.3.3 → benchflow-0.3.4}/tests/conformance/acp_smoke/environment/Dockerfile +0 -0
  127. {benchflow-0.3.3 → benchflow-0.3.4}/tests/conformance/acp_smoke/instruction.md +0 -0
  128. {benchflow-0.3.3 → benchflow-0.3.4}/tests/conformance/acp_smoke/solution/solve.sh +0 -0
  129. {benchflow-0.3.3 → benchflow-0.3.4}/tests/conformance/acp_smoke/task.toml +0 -0
  130. {benchflow-0.3.3 → benchflow-0.3.4}/tests/conformance/acp_smoke/tests/test.sh +0 -0
  131. {benchflow-0.3.3 → benchflow-0.3.4}/tests/conformance/conformance-results.json +0 -0
  132. {benchflow-0.3.3 → benchflow-0.3.4}/tests/conformance/proof_multi_agent.py +0 -0
  133. {benchflow-0.3.3 → benchflow-0.3.4}/tests/conformance/proof_snapshot.py +0 -0
  134. {benchflow-0.3.3 → benchflow-0.3.4}/tests/conformance/run_conformance.py +0 -0
  135. {benchflow-0.3.3 → benchflow-0.3.4}/tests/conftest.py +0 -0
  136. {benchflow-0.3.3 → benchflow-0.3.4}/tests/examples/hello-world-task/environment/Dockerfile +0 -0
  137. {benchflow-0.3.3 → benchflow-0.3.4}/tests/examples/hello-world-task/instruction.md +0 -0
  138. {benchflow-0.3.3 → benchflow-0.3.4}/tests/examples/hello-world-task/solution/solve.sh +0 -0
  139. {benchflow-0.3.3 → benchflow-0.3.4}/tests/examples/hello-world-task/task.toml +0 -0
  140. {benchflow-0.3.3 → benchflow-0.3.4}/tests/examples/hello-world-task/tests/test.sh +0 -0
  141. {benchflow-0.3.3 → benchflow-0.3.4}/tests/fixtures/mock_acp_agent.py +0 -0
  142. {benchflow-0.3.3 → benchflow-0.3.4}/tests/fixtures/mock_acp_agent_interleaved.py +0 -0
  143. {benchflow-0.3.3 → benchflow-0.3.4}/tests/fixtures/mock_acp_agent_multi_turn.py +0 -0
  144. {benchflow-0.3.3 → benchflow-0.3.4}/tests/integration/check_results.py +0 -0
  145. {benchflow-0.3.3 → benchflow-0.3.4}/tests/integration/configs/claude-agent-acp.yaml +0 -0
  146. {benchflow-0.3.3 → benchflow-0.3.4}/tests/integration/configs/codex-acp.yaml +0 -0
  147. {benchflow-0.3.3 → benchflow-0.3.4}/tests/integration/configs/gemini.yaml +0 -0
  148. {benchflow-0.3.3 → benchflow-0.3.4}/tests/integration/configs/harvey-lab-harness.yaml +0 -0
  149. {benchflow-0.3.3 → benchflow-0.3.4}/tests/integration/configs/openclaw.yaml +0 -0
  150. {benchflow-0.3.3 → benchflow-0.3.4}/tests/integration/configs/opencode.yaml +0 -0
  151. {benchflow-0.3.3 → benchflow-0.3.4}/tests/integration/configs/openhands.yaml +0 -0
  152. {benchflow-0.3.3 → benchflow-0.3.4}/tests/integration/configs/pi-acp.yaml +0 -0
  153. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_acp.py +0 -0
  154. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_agent_model_decouple.py +0 -0
  155. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_agent_registry.py +0 -0
  156. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_agent_spec.py +0 -0
  157. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_atif_trajectory.py +0 -0
  158. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_bedrock_proxy.py +0 -0
  159. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_bedrock_runtime.py +0 -0
  160. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_capture_trajectory.py +0 -0
  161. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_env_setup.py +0 -0
  162. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_exclude_tasks.py +0 -0
  163. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_job.py +0 -0
  164. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_metrics.py +0 -0
  165. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_mock_openai_responses_server.py +0 -0
  166. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_notification_order_real.py +0 -0
  167. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_oracle.py +0 -0
  168. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_process.py +0 -0
  169. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_provider_runtime.py +0 -0
  170. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_providers.py +0 -0
  171. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_registry_invariants.py +0 -0
  172. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_resolve_env_helpers.py +0 -0
  173. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_rewards_jsonl.py +0 -0
  174. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_sandbox.py +0 -0
  175. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_sandbox_setup.py +0 -0
  176. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_sandbox_verifier_workspace.py +0 -0
  177. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_scoring.py +0 -0
  178. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_sdk_lockdown.py +0 -0
  179. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_self_gen_orchestration.py +0 -0
  180. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_skill_eval.py +0 -0
  181. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_skill_eval_integration.py +0 -0
  182. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_skills.py +0 -0
  183. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_subscription_auth.py +0 -0
  184. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_task_download.py +0 -0
  185. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_tasks.py +0 -0
  186. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_trajectory_integration.py +0 -0
  187. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_trial_agent_timeout_verify.py +0 -0
  188. {benchflow-0.3.3 → benchflow-0.3.4}/tests/test_user.py +0 -0
  189. {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
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
- - **Sandbox backends** — Docker locally, Daytona for parallel cloud runs, Modal for serverless/GPU-backed task environments
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 Trial / Scene / Role / Verifier | [Concepts](./docs/concepts.md) |
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 -f benchmarks/skillsbench-claude-glm51.yaml
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
- -a gemini -m gemini-3.1-flash-lite-preview -e daytona -c 64
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 trial: 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).
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
- - **Sandbox backends** — Docker locally, Daytona for parallel cloud runs, Modal for serverless/GPU-backed task environments
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 Trial / Scene / Role / Verifier | [Concepts](./docs/concepts.md) |
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 -f benchmarks/skillsbench-claude-glm51.yaml
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
- -a gemini -m gemini-3.1-flash-lite-preview -e daytona -c 64
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 trial: 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).
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
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "benchflow"
3
- version = "0.3.3"
3
+ version = "0.3.4"
4
4
  description = "Multi-turn agent benchmarking with ACP — run any agent, any model, any provider."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -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
- - SDK for programmatic usage
7
- - Job orchestration with retries and concurrency
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, Role, Scene
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.job import Job, JobConfig, JobResult, RetryConfig
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, RunResult
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, # bf.run(agent, env) — the primary 0.3 API
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
- # Job orchestration
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 primary API)
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
- # Multi-agent scene
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
- # Trial (decomposed lifecycle)
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 --ae GOOGLE_CLOUD_PROJECT=<project>"
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 Role:
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, Role],
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: Role, inbox: list[Message]) -> str:
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
- cat_result = await env.exec(f"cat {fpath}")
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 {fpath}")
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 backends.
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. Backend-agnostic — no Docker
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
- import re
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
- check = await env.exec(f"test -f {snap_path} && echo ok || echo missing")
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
+ ]