@tangle-network/agent-bench 0.1.0 → 0.3.6
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.
- package/CHANGELOG.md +23 -0
- package/HARNESS.md +302 -0
- package/README.md +39 -2
- package/dist/adapters.d.ts +15 -0
- package/dist/adapters.js +43 -0
- package/dist/adapters.js.map +1 -0
- package/dist/benchmarks/_harness.d.ts +125 -0
- package/dist/benchmarks/_harness.js +33 -0
- package/dist/benchmarks/_harness.js.map +1 -0
- package/dist/benchmarks/aec-bench.d.ts +27 -0
- package/dist/benchmarks/aec-bench.js +8 -0
- package/dist/benchmarks/aec-bench.js.map +1 -0
- package/dist/benchmarks/agentbench.d.ts +16 -0
- package/dist/benchmarks/agentbench.js +10 -0
- package/dist/benchmarks/agentbench.js.map +1 -0
- package/dist/benchmarks/appworld.d.ts +37 -0
- package/dist/benchmarks/appworld.js +14 -0
- package/dist/benchmarks/appworld.js.map +1 -0
- package/dist/benchmarks/bfcl.d.ts +18 -0
- package/dist/benchmarks/bfcl.js +10 -0
- package/dist/benchmarks/bfcl.js.map +1 -0
- package/dist/benchmarks/cad-design.d.ts +45 -0
- package/dist/benchmarks/cad-design.js +7 -0
- package/dist/benchmarks/cad-design.js.map +1 -0
- package/dist/benchmarks/cadbench.d.ts +19 -0
- package/dist/benchmarks/cadbench.js +8 -0
- package/dist/benchmarks/cadbench.js.map +1 -0
- package/dist/benchmarks/cadgenbench.d.ts +22 -0
- package/dist/benchmarks/cadgenbench.js +8 -0
- package/dist/benchmarks/cadgenbench.js.map +1 -0
- package/dist/benchmarks/commit0.d.ts +31 -0
- package/dist/benchmarks/commit0.js +10 -0
- package/dist/benchmarks/commit0.js.map +1 -0
- package/dist/benchmarks/crag.d.ts +14 -0
- package/dist/benchmarks/crag.js +9 -0
- package/dist/benchmarks/crag.js.map +1 -0
- package/dist/benchmarks/dabstep.d.ts +18 -0
- package/dist/benchmarks/dabstep.js +10 -0
- package/dist/benchmarks/dabstep.js.map +1 -0
- package/dist/benchmarks/enterpriseops-gym.d.ts +38 -0
- package/dist/benchmarks/enterpriseops-gym.js +10 -0
- package/dist/benchmarks/enterpriseops-gym.js.map +1 -0
- package/dist/benchmarks/finresearchbench.d.ts +15 -0
- package/dist/benchmarks/finresearchbench.js +8 -0
- package/dist/benchmarks/finresearchbench.js.map +1 -0
- package/dist/benchmarks/finsearchcomp.d.ts +49 -0
- package/dist/benchmarks/finsearchcomp.js +7 -0
- package/dist/benchmarks/finsearchcomp.js.map +1 -0
- package/dist/benchmarks/frames.d.ts +59 -0
- package/dist/benchmarks/frames.js +13 -0
- package/dist/benchmarks/frames.js.map +1 -0
- package/dist/benchmarks/hotpotqa.d.ts +48 -0
- package/dist/benchmarks/hotpotqa.js +15 -0
- package/dist/benchmarks/hotpotqa.js.map +1 -0
- package/dist/benchmarks/humaneval.d.ts +53 -0
- package/dist/benchmarks/humaneval.js +15 -0
- package/dist/benchmarks/humaneval.js.map +1 -0
- package/dist/benchmarks/mind2web.d.ts +41 -0
- package/dist/benchmarks/mind2web.js +9 -0
- package/dist/benchmarks/mind2web.js.map +1 -0
- package/dist/benchmarks/nomiracl.d.ts +15 -0
- package/dist/benchmarks/nomiracl.js +9 -0
- package/dist/benchmarks/nomiracl.js.map +1 -0
- package/dist/benchmarks/open-rag-bench.d.ts +14 -0
- package/dist/benchmarks/open-rag-bench.js +9 -0
- package/dist/benchmarks/open-rag-bench.js.map +1 -0
- package/dist/benchmarks/programbench.d.ts +38 -0
- package/dist/benchmarks/programbench.js +10 -0
- package/dist/benchmarks/programbench.js.map +1 -0
- package/dist/benchmarks/rag-shared.d.ts +42 -0
- package/dist/benchmarks/rag-shared.js +39 -0
- package/dist/benchmarks/rag-shared.js.map +1 -0
- package/dist/benchmarks/ragbench.d.ts +16 -0
- package/dist/benchmarks/ragbench.js +9 -0
- package/dist/benchmarks/ragbench.js.map +1 -0
- package/dist/benchmarks/simpleqa.d.ts +64 -0
- package/dist/benchmarks/simpleqa.js +11 -0
- package/dist/benchmarks/simpleqa.js.map +1 -0
- package/dist/benchmarks/swe-bench.d.ts +56 -0
- package/dist/benchmarks/swe-bench.js +14 -0
- package/dist/benchmarks/swe-bench.js.map +1 -0
- package/dist/benchmarks/t2-ragbench.d.ts +14 -0
- package/dist/benchmarks/t2-ragbench.js +9 -0
- package/dist/benchmarks/t2-ragbench.js.map +1 -0
- package/dist/benchmarks/tau-bench-shared.d.ts +26 -0
- package/dist/benchmarks/tau-bench-shared.js +10 -0
- package/dist/benchmarks/tau-bench-shared.js.map +1 -0
- package/dist/benchmarks/tau2-bench.d.ts +7 -0
- package/dist/benchmarks/tau2-bench.js +11 -0
- package/dist/benchmarks/tau2-bench.js.map +1 -0
- package/dist/benchmarks/tau3-banking.d.ts +15 -0
- package/dist/benchmarks/tau3-banking.js +9 -0
- package/dist/benchmarks/tau3-banking.js.map +1 -0
- package/dist/benchmarks/terminal-bench.d.ts +24 -0
- package/dist/benchmarks/terminal-bench.js +8 -0
- package/dist/benchmarks/terminal-bench.js.map +1 -0
- package/dist/benchmarks/toollm.d.ts +16 -0
- package/dist/benchmarks/toollm.js +10 -0
- package/dist/benchmarks/toollm.js.map +1 -0
- package/dist/benchmarks/trata-hedge.d.ts +32 -0
- package/dist/benchmarks/trata-hedge.js +7 -0
- package/dist/benchmarks/trata-hedge.js.map +1 -0
- package/dist/benchmarks/types.d.ts +107 -0
- package/dist/benchmarks/types.js +1 -0
- package/dist/benchmarks/types.js.map +1 -0
- package/dist/benchmarks/webarena-verified.d.ts +16 -0
- package/dist/benchmarks/webarena-verified.js +10 -0
- package/dist/benchmarks/webarena-verified.js.map +1 -0
- package/dist/chunk-2PVVP7GN.js +197 -0
- package/dist/chunk-2PVVP7GN.js.map +1 -0
- package/dist/chunk-2XU6OGEN.js +170 -0
- package/dist/chunk-2XU6OGEN.js.map +1 -0
- package/dist/chunk-53UPUNBZ.js +325 -0
- package/dist/chunk-53UPUNBZ.js.map +1 -0
- package/dist/chunk-5SBJCB6W.js +144 -0
- package/dist/chunk-5SBJCB6W.js.map +1 -0
- package/dist/chunk-7WSD27QQ.js +118 -0
- package/dist/chunk-7WSD27QQ.js.map +1 -0
- package/dist/chunk-C7T7WEK2.js +103 -0
- package/dist/chunk-C7T7WEK2.js.map +1 -0
- package/dist/chunk-CKUVRZ2T.js +251 -0
- package/dist/chunk-CKUVRZ2T.js.map +1 -0
- package/dist/chunk-HBSWHQNJ.js +30 -0
- package/dist/chunk-HBSWHQNJ.js.map +1 -0
- package/dist/chunk-HHXFIHXC.js +116 -0
- package/dist/chunk-HHXFIHXC.js.map +1 -0
- package/dist/chunk-IFAV6KEM.js +276 -0
- package/dist/chunk-IFAV6KEM.js.map +1 -0
- package/dist/chunk-INNOYXCP.js +387 -0
- package/dist/chunk-INNOYXCP.js.map +1 -0
- package/dist/chunk-J3KDJNX2.js +182 -0
- package/dist/chunk-J3KDJNX2.js.map +1 -0
- package/dist/chunk-JRWWGMK7.js +148 -0
- package/dist/chunk-JRWWGMK7.js.map +1 -0
- package/dist/chunk-JTHWEDEW.js +32 -0
- package/dist/chunk-JTHWEDEW.js.map +1 -0
- package/dist/chunk-KDIKRJGB.js +120 -0
- package/dist/chunk-KDIKRJGB.js.map +1 -0
- package/dist/chunk-LRRD7NAG.js +301 -0
- package/dist/chunk-LRRD7NAG.js.map +1 -0
- package/dist/chunk-ODT47UAY.js +221 -0
- package/dist/chunk-ODT47UAY.js.map +1 -0
- package/dist/chunk-PA2ZKHJC.js +230 -0
- package/dist/chunk-PA2ZKHJC.js.map +1 -0
- package/dist/chunk-PPYSEKFM.js +182 -0
- package/dist/chunk-PPYSEKFM.js.map +1 -0
- package/dist/chunk-PUIRNYI7.js +189 -0
- package/dist/chunk-PUIRNYI7.js.map +1 -0
- package/dist/chunk-R36V2VP7.js +169 -0
- package/dist/chunk-R36V2VP7.js.map +1 -0
- package/dist/chunk-R67DFVLO.js +142 -0
- package/dist/chunk-R67DFVLO.js.map +1 -0
- package/dist/chunk-SEVJPLZC.js +260 -0
- package/dist/chunk-SEVJPLZC.js.map +1 -0
- package/dist/chunk-SYDW647C.js +318 -0
- package/dist/chunk-SYDW647C.js.map +1 -0
- package/dist/chunk-TBKU5XQI.js +228 -0
- package/dist/chunk-TBKU5XQI.js.map +1 -0
- package/dist/chunk-TSWPNOYM.js +147 -0
- package/dist/chunk-TSWPNOYM.js.map +1 -0
- package/dist/chunk-UAIOHCUK.js +27 -0
- package/dist/chunk-UAIOHCUK.js.map +1 -0
- package/dist/chunk-UPAMRDX4.js +233 -0
- package/dist/chunk-UPAMRDX4.js.map +1 -0
- package/dist/chunk-VQRS7VUC.js +342 -0
- package/dist/chunk-VQRS7VUC.js.map +1 -0
- package/dist/chunk-X3BTXCJ4.js +262 -0
- package/dist/chunk-X3BTXCJ4.js.map +1 -0
- package/dist/chunk-X5YKXC6V.js +211 -0
- package/dist/chunk-X5YKXC6V.js.map +1 -0
- package/dist/chunk-Y6O2OCUO.js +130 -0
- package/dist/chunk-Y6O2OCUO.js.map +1 -0
- package/dist/chunk-YCGY7UIZ.js +208 -0
- package/dist/chunk-YCGY7UIZ.js.map +1 -0
- package/dist/chunk-Z7ML6L77.js +162 -0
- package/dist/chunk-Z7ML6L77.js.map +1 -0
- package/dist/chunk-ZEWMTR5M.js +136 -0
- package/dist/chunk-ZEWMTR5M.js.map +1 -0
- package/dist/index.d.ts +355 -0
- package/dist/index.js +1908 -0
- package/dist/index.js.map +1 -0
- package/fixtures/aec-bench.json +18 -0
- package/fixtures/agentbench-dbbench.json +22 -0
- package/fixtures/bfcl.json +45 -0
- package/fixtures/commit0.json +72 -0
- package/fixtures/crag.json +10 -0
- package/fixtures/dabstep.json +22 -0
- package/fixtures/enterpriseops-gym.json +103 -0
- package/fixtures/finresearchbench.json +21 -0
- package/fixtures/finsearchcomp.json +66 -0
- package/fixtures/frames.json +26 -0
- package/fixtures/hotpotqa.json +182 -0
- package/fixtures/nomiracl.json +26 -0
- package/fixtures/open-rag-bench.json +16 -0
- package/fixtures/pier-agent/no-model-task/environment/Dockerfile +16 -0
- package/fixtures/pier-agent/no-model-task/environment/seed/src/status.txt +1 -0
- package/fixtures/pier-agent/no-model-task/instruction.md +6 -0
- package/fixtures/pier-agent/no-model-task/pre_artifacts.sh +6 -0
- package/fixtures/pier-agent/no-model-task/task.toml +35 -0
- package/fixtures/pier-agent/no-model-task/tests/Dockerfile +17 -0
- package/fixtures/pier-agent/no-model-task/tests/seed/src/status.txt +1 -0
- package/fixtures/pier-agent/no-model-task/tests/test.sh +19 -0
- package/fixtures/programbench.json +17 -0
- package/fixtures/ragbench.json +21 -0
- package/fixtures/simpleqa.json +121 -0
- package/fixtures/t2-ragbench.json +13 -0
- package/fixtures/tau2-bench.json +16 -0
- package/fixtures/tau3-banking.json +16 -0
- package/fixtures/toollm.json +28 -0
- package/fixtures/webarena-verified.json +20 -0
- package/package.json +60 -20
- package/pier_agents/__init__.py +18 -0
- package/pier_agents/candidate_contract.py +755 -0
- package/pier_agents/process_boundary.py +321 -0
- package/pier_agents/tangle_candidate.py +907 -0
- package/pier_agents/workspace_boundary.py +368 -0
- package/scripts/appworld_driver.py +359 -0
- package/scripts/cadbench_prepare.py +22 -0
- package/scripts/cadgenbench_hard_parts.py +48 -0
- package/scripts/clbench_codebase_judge.py +73 -0
- package/scripts/commit0_judge.py +170 -0
- package/scripts/dabstep_judge.py +42 -0
- package/scripts/enterpriseops_gym_judge.py +281 -0
- package/scripts/programbench_judge.py +120 -0
- package/scripts/render-gate-chart.mjs +176 -0
- package/scripts/run-package-tests.mjs +56 -0
- package/scripts/terminate-pier-trial.mts +66 -0
- package/scripts/trata-hedge/README.md +56 -0
- package/scripts/trata-hedge/run.sh +60 -0
- package/scripts/trata-hedge/solve.py +83 -0
- package/scripts/verify-packed-consumer.mjs +235 -0
- package/scripts/verify-pier-agent.mts +715 -0
- package/scripts/verify-pier-pair.mts +74 -0
- package/scripts/verify-pier-recovery.mts +139 -0
- package/src/adapters.ts +26 -0
- package/src/benchmarks/_harness.test.mts +178 -0
- package/src/benchmarks/_harness.ts +239 -16
- package/src/benchmarks/agentbench.ts +163 -0
- package/src/benchmarks/appworld.test.mts +15 -9
- package/src/benchmarks/bfcl.ts +346 -0
- package/src/benchmarks/crag.ts +137 -0
- package/src/benchmarks/dabstep.test.mts +70 -0
- package/src/benchmarks/dabstep.ts +212 -0
- package/src/benchmarks/external-adapters.test.mts +150 -0
- package/src/benchmarks/finresearchbench.ts +269 -0
- package/src/benchmarks/humaneval.ts +20 -8
- package/src/benchmarks/nomiracl.ts +180 -0
- package/src/benchmarks/open-rag-bench.ts +153 -0
- package/src/benchmarks/rag-benchmarks.test.mts +138 -0
- package/src/benchmarks/rag-shared.ts +327 -0
- package/src/benchmarks/ragbench.ts +171 -0
- package/src/benchmarks/swe-bench.test.mts +61 -0
- package/src/benchmarks/swe-bench.ts +201 -19
- package/src/benchmarks/t2-ragbench.ts +166 -0
- package/src/benchmarks/tau-bench-shared.ts +214 -0
- package/src/benchmarks/tau2-bench.ts +30 -0
- package/src/benchmarks/tau3-banking.ts +29 -0
- package/src/benchmarks/terminal-bench.test.mts +33 -0
- package/src/benchmarks/terminal-bench.ts +23 -8
- package/src/benchmarks/toollm.ts +254 -0
- package/src/benchmarks/types.ts +42 -0
- package/src/benchmarks/webarena-verified.ts +200 -0
- package/src/commit0-prereqs.sh +0 -0
- package/src/coordination-mcp-container-reach.mts +181 -0
- package/src/decoder-live.mts +1 -1
- package/src/examples/README.md +103 -39
- package/src/examples/benchmark-matrix.mts +101 -0
- package/src/examples/lean-proof-gate.README.md +77 -0
- package/src/examples/lean-proof-gate.mts +162 -0
- package/src/examples/lean-verify.ts +95 -0
- package/src/examples/lean.Dockerfile +12 -0
- package/src/examples/math-demo.mts +9 -7
- package/src/examples/strategy-demo.mts +10 -12
- package/src/gate.ts +3 -2
- package/src/hev-eval.mts +69 -0
- package/src/hev-improve.mts +169 -0
- package/src/hev-structural.mts +688 -0
- package/src/index.ts +73 -0
- package/src/mbpp-structural.mts +662 -0
- package/src/pier-agent.test-fixtures.mts +19 -0
- package/src/pier-agent.test.mts +363 -0
- package/src/pier-agent.ts +657 -0
- package/src/pier-result-grader.mjs +30 -0
- package/src/pier-result-grader.test.mts +62 -0
- package/src/pier-result-grader.ts +108 -0
- package/src/pier-task-outcome.test.mts +117 -0
- package/src/pier-task-outcome.ts +240 -0
- package/src/pier-trial-controller.test.mts +412 -0
- package/src/pier-trial-controller.ts +858 -0
- package/src/pier-trial-supervisor.mjs +352 -0
- package/src/resolve-client.ts +25 -2
- package/src/run-benchmarks-cli.mts +72 -0
- package/src/run-benchmarks-report.ts +66 -0
- package/src/run-benchmarks.test.mts +231 -0
- package/src/run-benchmarks.ts +589 -0
- package/src/smoke-structural-rollout.mts +393 -0
- package/src/swe-bench-env.test.ts +207 -0
- package/src/swe-bench-env.ts +554 -0
- package/src/swe-jail.ts +293 -0
- package/src/swe-self-improve.mts +84 -0
- package/src/swe-structural-judge-policy.test.ts +117 -0
- package/src/swe-structural-judge-policy.ts +133 -0
- package/src/swe-structural-policy.test.ts +124 -0
- package/src/swe-structural-policy.ts +132 -0
- package/src/swe-structural-provenance.test.ts +93 -0
- package/src/swe-structural-provenance.ts +138 -0
- package/src/swe-structural.mts +1260 -0
- package/src/swe-temp.ts +14 -0
- package/src/tb-container-executor.mts +234 -0
- package/src/tb-container-executor.test.mts +99 -0
- package/src/tb-supervisor-sidecar.mts +222 -0
- package/src/trata-gepa.mts +1 -1
- package/steerers/eops-itsm-population.json +1 -0
- package/tb_agents/opencode_refine_agent.py +117 -0
- package/tb_agents/opencode_router_agent.py +406 -0
- package/tb_agents/opencode_supervisor_agent.py +239 -0
- package/tb_agents/script_agent.py +66 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tau3-banking adapter.
|
|
3
|
+
*
|
|
4
|
+
* The current upstream tau3 release is the `sierra-research/tau2-bench` repo with
|
|
5
|
+
* package namespace `tau2` and a new `banking_knowledge` domain. This adapter
|
|
6
|
+
* reuses the shared tau reward recomputation path and only changes the default
|
|
7
|
+
* domain/env names.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { join } from 'node:path'
|
|
11
|
+
import { benchRoot } from './_harness'
|
|
12
|
+
import { createTauBenchAdapter } from './tau-bench-shared'
|
|
13
|
+
import type { BenchmarkAdapter } from './types'
|
|
14
|
+
|
|
15
|
+
const FIXTURES = join(benchRoot, 'fixtures', 'tau3-banking.json')
|
|
16
|
+
|
|
17
|
+
export function createTau3BankingAdapter(): BenchmarkAdapter {
|
|
18
|
+
return createTauBenchAdapter({
|
|
19
|
+
name: 'tau3-banking',
|
|
20
|
+
fixturePath: FIXTURES,
|
|
21
|
+
fixturesEnv: 'TAU3_FIXTURES',
|
|
22
|
+
dirEnv: 'TAU3_BENCH_DIR',
|
|
23
|
+
domainEnv: 'TAU3_DOMAIN',
|
|
24
|
+
defaultDomain: 'banking_knowledge',
|
|
25
|
+
taskIntro: 'Run this tau3 banking task in the official tau3 knowledge benchmark.',
|
|
26
|
+
installHint:
|
|
27
|
+
'clone https://github.com/sierra-research/tau2-bench, run `uv sync --extra knowledge`, install/import it from bench/.venv, and set TAU3_BENCH_DIR=/path/to/tau2-bench.',
|
|
28
|
+
})
|
|
29
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Offline Terminal-Bench adapter test. Live judging needs the official
|
|
3
|
+
* terminal-bench harness + Docker, so this asserts the offline surfaces and the
|
|
4
|
+
* fail-loud preflight path. Run: npx tsx --test src/benchmarks/terminal-bench.test.mts
|
|
5
|
+
*/
|
|
6
|
+
import assert from 'node:assert/strict'
|
|
7
|
+
import { test } from 'node:test'
|
|
8
|
+
import { createTerminalBenchAdapter } from './terminal-bench'
|
|
9
|
+
|
|
10
|
+
test('goldArtifact returns portable shell solutions and rejects missing ones', async () => {
|
|
11
|
+
const a = createTerminalBenchAdapter()
|
|
12
|
+
assert.equal(await a.goldArtifact({ id: 'with-solution', prompt: '', metadata: { solution: 'echo ok\n' } }), 'echo ok\n')
|
|
13
|
+
assert.equal(await a.goldArtifact({ id: 'without-solution', prompt: '', metadata: {} }), undefined)
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
test('preflight FAILS LOUD with isolated Terminal-Bench venv instructions when harness is absent', async () => {
|
|
17
|
+
const prev = process.env.TERMINAL_BENCH_VENV
|
|
18
|
+
process.env.TERMINAL_BENCH_VENV = '.venv-terminal-bench-does-not-exist'
|
|
19
|
+
try {
|
|
20
|
+
const a = createTerminalBenchAdapter()
|
|
21
|
+
await assert.rejects(a.preflight(), (e: Error) => {
|
|
22
|
+
assert.match(e.message, /pip install terminal-bench/)
|
|
23
|
+
assert.match(e.message, /ISOLATED venv/)
|
|
24
|
+
assert.match(e.message, /TERMINAL_BENCH_VENV/)
|
|
25
|
+
assert.match(e.message, /Docker daemon/)
|
|
26
|
+
return true
|
|
27
|
+
})
|
|
28
|
+
} finally {
|
|
29
|
+
if (prev === undefined) delete process.env.TERMINAL_BENCH_VENV
|
|
30
|
+
else process.env.TERMINAL_BENCH_VENV = prev
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
|
|
@@ -6,9 +6,10 @@
|
|
|
6
6
|
* our ScriptAgent), then the task's own verifier scores the resulting state.
|
|
7
7
|
* Fully deterministic — no LLM judge, no self-authored score.
|
|
8
8
|
*
|
|
9
|
-
* Requires:
|
|
10
|
-
* daemon (per-task images are built on first run).
|
|
11
|
-
*
|
|
9
|
+
* Requires: an isolated bench `.venv-terminal-bench` with `terminal-bench`
|
|
10
|
+
* installed + a running Docker daemon (per-task images are built on first run).
|
|
11
|
+
* Override with TERMINAL_BENCH_VENV. loadTasks caches the dataset from the
|
|
12
|
+
* Terminal-Bench registry on first run.
|
|
12
13
|
*
|
|
13
14
|
* Process/Docker/report plumbing is shared via ./_harness; this file owns the
|
|
14
15
|
* Terminal-Bench-specific pieces: the Dataset enumeration, the ScriptAgent replay
|
|
@@ -24,11 +25,16 @@ import {
|
|
|
24
25
|
runVenvPython,
|
|
25
26
|
safeRunId,
|
|
26
27
|
stageFile,
|
|
27
|
-
|
|
28
|
+
venvPythonAt,
|
|
28
29
|
} from './_harness'
|
|
29
30
|
import type { BenchmarkAdapter, BenchScore, BenchTask, LoadOptions } from './types'
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
// Terminal-Bench imports LiteLLM/Pydantic-2 APIs, while AppWorld pins Pydantic 1.
|
|
33
|
+
// Keep it out of the shared bench .venv. Resolved at call-time so tests/runs can
|
|
34
|
+
// override the env without reloading this module.
|
|
35
|
+
const terminalBenchVenvDir = (): string => process.env.TERMINAL_BENCH_VENV ?? '.venv-terminal-bench'
|
|
36
|
+
const terminalBenchPython = (): string => venvPythonAt(terminalBenchVenvDir())
|
|
37
|
+
const terminalBenchBin = (): string => join(benchRoot, terminalBenchVenvDir(), 'bin', 'tb')
|
|
32
38
|
|
|
33
39
|
// Pinned dataset: the 0.1.1 core set is patched for terminal-bench >=0.2.4 (the
|
|
34
40
|
// installed CLI) and is the published launch task set. name==version is what `tb
|
|
@@ -90,7 +96,12 @@ for task_dir in ds:
|
|
|
90
96
|
})
|
|
91
97
|
print(json.dumps(out))
|
|
92
98
|
`
|
|
93
|
-
const stdout = await runVenvPython(
|
|
99
|
+
const stdout = await runVenvPython(
|
|
100
|
+
script,
|
|
101
|
+
[ids ? JSON.stringify(ids) : '', limit !== null ? String(limit) : ''],
|
|
102
|
+
0,
|
|
103
|
+
terminalBenchPython(),
|
|
104
|
+
)
|
|
94
105
|
return JSON.parse(stdout) as TbTaskRow[]
|
|
95
106
|
}
|
|
96
107
|
|
|
@@ -102,8 +113,12 @@ export function createTerminalBenchAdapter(): BenchmarkAdapter {
|
|
|
102
113
|
await preflightVenvImports({
|
|
103
114
|
modules: ['terminal_bench'],
|
|
104
115
|
requireDocker: true,
|
|
116
|
+
python: terminalBenchPython(),
|
|
105
117
|
fix:
|
|
106
|
-
`Fix: (1) python3 -m venv bench
|
|
118
|
+
`Fix: (1) python3 -m venv bench/${terminalBenchVenvDir()} && ` +
|
|
119
|
+
`bench/${terminalBenchVenvDir()}/bin/pip install terminal-bench ` +
|
|
120
|
+
`(an ISOLATED venv — Terminal-Bench/LiteLLM require Pydantic 2 while AppWorld pins Pydantic 1; ` +
|
|
121
|
+
`override the dir with TERMINAL_BENCH_VENV) ; ` +
|
|
107
122
|
`(2) ensure the Docker daemon is running (the judge builds per-task images on first run). ` +
|
|
108
123
|
`The ${DATASET_REF} dataset is cached from the Terminal-Bench registry on first loadTasks.`,
|
|
109
124
|
})
|
|
@@ -146,7 +161,7 @@ export function createTerminalBenchAdapter(): BenchmarkAdapter {
|
|
|
146
161
|
const runId = safeRunId('bench', `${task.id}-${Date.now()}`)
|
|
147
162
|
return runStagedJudge({
|
|
148
163
|
tmpPrefix: 'tbench-',
|
|
149
|
-
bin:
|
|
164
|
+
bin: terminalBenchBin(),
|
|
150
165
|
cwd: () => benchRoot,
|
|
151
166
|
async stage(dir) {
|
|
152
167
|
await stageFile(join(dir, 'attempt.sh'), artifact)
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ToolLLM/ToolBench adapter.
|
|
3
|
+
*
|
|
4
|
+
* ToolBench task loading is useful for breadth, but the official ToolEval pass
|
|
5
|
+
* rate evaluator is LLM-based and stochastic. This adapter therefore scores
|
|
6
|
+
* only ToolBench's deterministic API-selection labels (`relevant APIs`). It
|
|
7
|
+
* never records a full ToolEval pass-rate score.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { readFile } from 'node:fs/promises'
|
|
11
|
+
import { join } from 'node:path'
|
|
12
|
+
import type { OutputAdapter } from '@tangle-network/agent-runtime/loops'
|
|
13
|
+
import { benchRoot } from './_harness'
|
|
14
|
+
import type { BenchmarkAdapter, BenchScore, BenchTask, LoadOptions } from './types'
|
|
15
|
+
|
|
16
|
+
const FIXTURES = join(benchRoot, 'fixtures', 'toollm.json')
|
|
17
|
+
const DEFAULT_QUERY_REL = join('data_example', 'instruction', 'G1_query.json')
|
|
18
|
+
|
|
19
|
+
interface ToolApi {
|
|
20
|
+
category_name?: string
|
|
21
|
+
tool_name: string
|
|
22
|
+
api_name: string
|
|
23
|
+
api_description?: string
|
|
24
|
+
required_parameters?: unknown[]
|
|
25
|
+
optional_parameters?: unknown[]
|
|
26
|
+
method?: string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface ToolBenchRow {
|
|
30
|
+
query_id: number | string
|
|
31
|
+
query: string
|
|
32
|
+
api_list?: ToolApi[]
|
|
33
|
+
'relevant APIs'?: Array<[string, string]>
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface ToolBenchMeta {
|
|
37
|
+
queryId: string
|
|
38
|
+
apiList: ToolApi[]
|
|
39
|
+
relevantApis: Array<[string, string]>
|
|
40
|
+
deterministicJudge: 'api-selection'
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const toolbenchDir = (): string | undefined => process.env.TOOLBENCH_DIR
|
|
44
|
+
const queryFile = (dir: string): string => process.env.TOOLLM_QUERY_FILE ?? join(dir, DEFAULT_QUERY_REL)
|
|
45
|
+
|
|
46
|
+
export const toollmOutput: OutputAdapter<string> = {
|
|
47
|
+
parse(events) {
|
|
48
|
+
let text = ''
|
|
49
|
+
for (const ev of events) {
|
|
50
|
+
const d = (ev as { data?: Record<string, unknown> })?.data
|
|
51
|
+
const t = d?.finalText ?? d?.text ?? d?.result
|
|
52
|
+
if (typeof t === 'string' && t.length > 0) text = t
|
|
53
|
+
}
|
|
54
|
+
return text.trim()
|
|
55
|
+
},
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function rowToTask(row: ToolBenchRow): BenchTask {
|
|
59
|
+
const relevantApis = normalizeApiPairs(row['relevant APIs'] ?? [])
|
|
60
|
+
const meta: ToolBenchMeta = {
|
|
61
|
+
queryId: String(row.query_id),
|
|
62
|
+
apiList: row.api_list ?? [],
|
|
63
|
+
relevantApis,
|
|
64
|
+
deterministicJudge: 'api-selection',
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
id: String(row.query_id),
|
|
68
|
+
prompt: [
|
|
69
|
+
'Solve this ToolLLM/ToolBench API-use task.',
|
|
70
|
+
'Use only the listed APIs/tools and return the completed tool-use trace plus final answer.',
|
|
71
|
+
'',
|
|
72
|
+
`Query: ${row.query}`,
|
|
73
|
+
'',
|
|
74
|
+
`Available APIs: ${JSON.stringify(row.api_list ?? [], null, 2)}`,
|
|
75
|
+
'',
|
|
76
|
+
'Return the APIs you used as JSON: {"api_calls":[{"tool_name":"...","api_name":"..."}]}.',
|
|
77
|
+
].join('\n'),
|
|
78
|
+
metadata: meta as unknown as Record<string, unknown>,
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function normalizeApiPart(value: string): string {
|
|
83
|
+
return value.toLowerCase().replace(/[^a-z0-9]+/g, '')
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function apiKey(pair: readonly [string, string]): string {
|
|
87
|
+
return `${normalizeApiPart(pair[0])}.${normalizeApiPart(pair[1])}`
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function normalizeApiPairs(value: unknown): Array<[string, string]> {
|
|
91
|
+
if (!Array.isArray(value)) return []
|
|
92
|
+
const out: Array<[string, string]> = []
|
|
93
|
+
for (const item of value) {
|
|
94
|
+
if (Array.isArray(item) && typeof item[0] === 'string' && typeof item[1] === 'string') {
|
|
95
|
+
out.push([item[0], item[1]])
|
|
96
|
+
} else if (
|
|
97
|
+
item && typeof item === 'object'
|
|
98
|
+
&& typeof (item as { tool_name?: unknown }).tool_name === 'string'
|
|
99
|
+
&& typeof (item as { api_name?: unknown }).api_name === 'string'
|
|
100
|
+
) {
|
|
101
|
+
out.push([(item as { tool_name: string }).tool_name, (item as { api_name: string }).api_name])
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return out
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function readMeta(task: BenchTask): ToolBenchMeta {
|
|
108
|
+
const md = task.metadata
|
|
109
|
+
if (!md || !Array.isArray(md.relevantApis)) {
|
|
110
|
+
throw new Error(`ToolLLM task ${task.id} missing metadata — loadTasks did not populate deterministic API-selection labels`)
|
|
111
|
+
}
|
|
112
|
+
return md as unknown as ToolBenchMeta
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function selectRows(rows: ToolBenchRow[], opts: LoadOptions): BenchTask[] {
|
|
116
|
+
let tasks = rows.map(rowToTask)
|
|
117
|
+
if (opts.ids) {
|
|
118
|
+
const want = new Set(opts.ids)
|
|
119
|
+
tasks = tasks.filter((task) => want.has(task.id))
|
|
120
|
+
} else if (opts.limit !== undefined) {
|
|
121
|
+
tasks = tasks.slice(0, opts.limit)
|
|
122
|
+
}
|
|
123
|
+
if (tasks.length === 0) throw new Error(`ToolLLM: no tasks matched ${JSON.stringify(opts)}`)
|
|
124
|
+
return tasks
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function assertDeterministicSubset(tasks: readonly BenchTask[], source: string): void {
|
|
128
|
+
const missing = tasks.filter((task) => readMeta(task).relevantApis.length === 0).map((task) => task.id)
|
|
129
|
+
if (missing.length > 0) {
|
|
130
|
+
throw new Error(
|
|
131
|
+
`ToolLLM deterministic API-selection labels missing for ${missing.length}/${tasks.length} task(s) from ${source}: ${missing.slice(0, 5).join(', ')}. ` +
|
|
132
|
+
'Use a ToolBench query file that includes "relevant APIs" labels, or do not score ToolLLM in agent-bench.',
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async function loadFixtures(opts: LoadOptions): Promise<BenchTask[]> {
|
|
138
|
+
const rows = JSON.parse(await readFile(FIXTURES, 'utf8')) as ToolBenchRow[]
|
|
139
|
+
console.warn(`[toollm] TOOLLM_FIXTURES=1 — loading ${rows.length} adapter fixtures`)
|
|
140
|
+
const tasks = selectRows(rows, opts)
|
|
141
|
+
assertDeterministicSubset(tasks, FIXTURES)
|
|
142
|
+
return tasks
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async function loadOfficialTasks(dir: string, opts: LoadOptions): Promise<BenchTask[]> {
|
|
146
|
+
const source = queryFile(dir)
|
|
147
|
+
const tasks = selectRows(JSON.parse(await readFile(source, 'utf8')) as ToolBenchRow[], opts)
|
|
148
|
+
assertDeterministicSubset(tasks, source)
|
|
149
|
+
return tasks
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function extractJsonBlock(text: string): unknown {
|
|
153
|
+
const fences = [...text.matchAll(/```(?:json)?\s*\n([\s\S]*?)```/g)]
|
|
154
|
+
const raw = (fences.at(-1)?.[1] ?? text).trim()
|
|
155
|
+
try {
|
|
156
|
+
return JSON.parse(raw)
|
|
157
|
+
} catch {
|
|
158
|
+
return undefined
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
interface ExtractedApis {
|
|
163
|
+
pairs: Array<[string, string]>
|
|
164
|
+
source: 'structured-json' | 'text-mention'
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function extractCalledApis(text: string, expected: readonly [string, string][]): ExtractedApis {
|
|
168
|
+
const parsed = extractJsonBlock(text)
|
|
169
|
+
if (parsed && typeof parsed === 'object') {
|
|
170
|
+
const raw = parsed as Record<string, unknown>
|
|
171
|
+
const fromApiCalls = normalizeApiPairs(raw.api_calls)
|
|
172
|
+
if (fromApiCalls.length > 0) return { pairs: fromApiCalls, source: 'structured-json' }
|
|
173
|
+
const fromCalls = normalizeApiPairs(raw.calls)
|
|
174
|
+
if (fromCalls.length > 0) return { pairs: fromCalls, source: 'structured-json' }
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const lower = text.toLowerCase()
|
|
178
|
+
return {
|
|
179
|
+
pairs: expected.filter(([tool, api]) => {
|
|
180
|
+
const toolNeedle = normalizeApiPart(tool)
|
|
181
|
+
const apiNeedle = normalizeApiPart(api)
|
|
182
|
+
const compactText = lower.replace(/[^a-z0-9]+/g, '')
|
|
183
|
+
return compactText.includes(`${toolNeedle}${apiNeedle}`) || (lower.includes(tool.toLowerCase()) && lower.includes(api.toLowerCase()))
|
|
184
|
+
}),
|
|
185
|
+
source: 'text-mention',
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function scoreApiSelection(task: BenchTask, artifact: string): BenchScore {
|
|
190
|
+
const meta = readMeta(task)
|
|
191
|
+
if (meta.relevantApis.length === 0) {
|
|
192
|
+
throw new Error(`ToolLLM task ${task.id} has no deterministic API-selection labels; refusing to score`)
|
|
193
|
+
}
|
|
194
|
+
const expected = new Set(meta.relevantApis.map(apiKey))
|
|
195
|
+
const extracted = extractCalledApis(artifact, meta.relevantApis)
|
|
196
|
+
const calledPairs = extracted.pairs
|
|
197
|
+
const called = new Set(calledPairs.map(apiKey))
|
|
198
|
+
const truePositives = [...called].filter((key) => expected.has(key)).length
|
|
199
|
+
const precision = called.size === 0 ? 0 : truePositives / called.size
|
|
200
|
+
const recall = truePositives / expected.size
|
|
201
|
+
const score = expected.size === 0 ? 0 : recall
|
|
202
|
+
const resolved = extracted.source === 'structured-json' && recall === 1 && precision === 1
|
|
203
|
+
return {
|
|
204
|
+
resolved,
|
|
205
|
+
score,
|
|
206
|
+
detail: JSON.stringify({
|
|
207
|
+
scoring: 'api-selection-only',
|
|
208
|
+
extractionSource: extracted.source,
|
|
209
|
+
queryId: meta.queryId,
|
|
210
|
+
expected: meta.relevantApis,
|
|
211
|
+
called: calledPairs,
|
|
212
|
+
precision,
|
|
213
|
+
recall,
|
|
214
|
+
fullToolEvalScore: null,
|
|
215
|
+
}),
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export function createToolLlmAdapter(): BenchmarkAdapter {
|
|
220
|
+
const fixturesMode = process.env.TOOLLM_FIXTURES === '1'
|
|
221
|
+
|
|
222
|
+
return {
|
|
223
|
+
name: 'toollm',
|
|
224
|
+
output: toollmOutput,
|
|
225
|
+
|
|
226
|
+
async preflight() {
|
|
227
|
+
if (fixturesMode) return
|
|
228
|
+
const dir = toolbenchDir()
|
|
229
|
+
if (!dir) {
|
|
230
|
+
throw new Error('TOOLBENCH_DIR is required. Fix: clone https://github.com/OpenBMB/ToolBench and set TOOLBENCH_DIR=/path/to/ToolBench.')
|
|
231
|
+
}
|
|
232
|
+
await loadOfficialTasks(dir, { limit: 1 })
|
|
233
|
+
},
|
|
234
|
+
|
|
235
|
+
async loadTasks(opts: LoadOptions = {}) {
|
|
236
|
+
if (fixturesMode) return loadFixtures(opts)
|
|
237
|
+
const dir = toolbenchDir()
|
|
238
|
+
if (!dir) throw new Error('TOOLBENCH_DIR is required to load ToolLLM tasks')
|
|
239
|
+
return loadOfficialTasks(dir, opts)
|
|
240
|
+
},
|
|
241
|
+
|
|
242
|
+
async goldArtifact(task: BenchTask) {
|
|
243
|
+
const meta = readMeta(task)
|
|
244
|
+
if (meta.relevantApis.length === 0) return undefined
|
|
245
|
+
return JSON.stringify({
|
|
246
|
+
api_calls: meta.relevantApis.map(([tool_name, api_name]) => ({ tool_name, api_name })),
|
|
247
|
+
}, null, 2)
|
|
248
|
+
},
|
|
249
|
+
|
|
250
|
+
async judge(task: BenchTask, artifact: string): Promise<BenchScore> {
|
|
251
|
+
return scoreApiSelection(task, artifact)
|
|
252
|
+
},
|
|
253
|
+
}
|
|
254
|
+
}
|
package/src/benchmarks/types.ts
CHANGED
|
@@ -20,12 +20,40 @@ export interface BenchTask {
|
|
|
20
20
|
metadata?: Record<string, unknown>
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
export interface JudgeArtifactFileReceipt {
|
|
24
|
+
/** POSIX path relative to the capture directory. */
|
|
25
|
+
path: string
|
|
26
|
+
/** Exact byte length of the retained file or symbolic-link target. */
|
|
27
|
+
byteLength: number
|
|
28
|
+
/** SHA-256 over the retained file bytes or UTF-8 symbolic-link target. */
|
|
29
|
+
sha256: `sha256:${string}`
|
|
30
|
+
kind: 'file' | 'symlink'
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Durable evidence written before a staged evaluator's temporary directory is removed. */
|
|
34
|
+
export interface JudgeArtifactReceipt {
|
|
35
|
+
schema: 'agent-bench/judge-artifacts/v1'
|
|
36
|
+
/** Absolute directory containing `evaluator/`, `process/`, and `receipt.json`. */
|
|
37
|
+
directory: string
|
|
38
|
+
/** Exact copy of the evaluator working directory. */
|
|
39
|
+
evaluatorDirectory: string
|
|
40
|
+
manifestPath: string
|
|
41
|
+
evaluatorSucceeded: boolean
|
|
42
|
+
files: JudgeArtifactFileReceipt[]
|
|
43
|
+
fileCount: number
|
|
44
|
+
byteLength: number
|
|
45
|
+
/** SHA-256 over every sorted path, kind, byte length, and content hash. */
|
|
46
|
+
treeSha256: `sha256:${string}`
|
|
47
|
+
}
|
|
48
|
+
|
|
23
49
|
export interface BenchScore {
|
|
24
50
|
/** Did the deterministic judge pass (tests resolved / state correct)? */
|
|
25
51
|
resolved: boolean
|
|
26
52
|
/** 0..1 — 1 = fully resolved; partial credit where the harness supports it. */
|
|
27
53
|
score: number
|
|
28
54
|
detail?: string
|
|
55
|
+
/** Present only when the caller explicitly requested durable judge evidence. */
|
|
56
|
+
judgeArtifacts?: JudgeArtifactReceipt
|
|
29
57
|
}
|
|
30
58
|
|
|
31
59
|
export interface LoadOptions {
|
|
@@ -49,6 +77,20 @@ export interface BenchmarkAdapter {
|
|
|
49
77
|
* so the gate runner (`runGate` / `runBenchmark`) needs no
|
|
50
78
|
* per-benchmark branching. */
|
|
51
79
|
output?: OutputAdapter<string>
|
|
80
|
+
/** Post-shot deliverable extraction from the box FILESYSTEM, not the event stream.
|
|
81
|
+
* When set, the shot runner execs `command` in the STILL-ALIVE box after the agent
|
|
82
|
+
* turn drains and uses its stdout as the judged artifact — the durable way to capture
|
|
83
|
+
* a git diff of the agent's in-box edits (standard SWE-bench practice: SWE-agent /
|
|
84
|
+
* OpenHands read the diff from repo STATE), instead of hoping the model printed a
|
|
85
|
+
* fenced diff in its reply. Empty stdout ⇒ the runner falls back to `output` (the
|
|
86
|
+
* event-stream parse). `cwd` defaults to the box root. */
|
|
87
|
+
boxExtract?(task: BenchTask): { command: string; cwd?: string }
|
|
88
|
+
/** Optional workspace pre-stage run in the box BEFORE the agent shot (same
|
|
89
|
+
* session as `boxExtract`). For repo-state benchmarks (SWE-bench) this clones
|
|
90
|
+
* the instance repo at `base_commit` into a fixed path so the agent only edits
|
|
91
|
+
* — the harness owns the checkout, not the (stochastic) model. A non-zero exit
|
|
92
|
+
* fails the shot loud rather than letting the agent run against an empty box. */
|
|
93
|
+
boxSetup?(task: BenchTask): { command: string; cwd?: string }
|
|
52
94
|
/** Benchmark-owned worker leaf. Set when the benchmark's native protocol IS the
|
|
53
95
|
* worker (e.g. AppWorld's interactive ReAct episode runs inside the engine,
|
|
54
96
|
* not as a chat completion) — the experiment uses this instead of the
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebArena-Verified adapter (ServiceNow/webarena-verified).
|
|
3
|
+
*
|
|
4
|
+
* Worker artifact = a WebArena-Verified run output directory, not final chat text.
|
|
5
|
+
* Judge = the official `webarena_verified eval-tasks` evaluator over that output
|
|
6
|
+
* directory. The adapter refuses to score a plain answer so we do not turn a DOM
|
|
7
|
+
* benchmark into a fake text benchmark.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { access, readFile, stat } from 'node:fs/promises'
|
|
11
|
+
import { join, resolve } from 'node:path'
|
|
12
|
+
import type { OutputAdapter } from '@tangle-network/agent-runtime/loops'
|
|
13
|
+
import { benchRoot, runVenvPython } from './_harness'
|
|
14
|
+
import type { BenchmarkAdapter, BenchScore, BenchTask, LoadOptions } from './types'
|
|
15
|
+
|
|
16
|
+
const FIXTURES = join(benchRoot, 'fixtures', 'webarena-verified.json')
|
|
17
|
+
const DATASET_REL = join('assets', 'dataset', 'webarena-verified.json')
|
|
18
|
+
|
|
19
|
+
interface WebArenaRow {
|
|
20
|
+
task_id: number
|
|
21
|
+
intent: string
|
|
22
|
+
intent_template_id?: number
|
|
23
|
+
sites?: string[]
|
|
24
|
+
start_urls?: string[]
|
|
25
|
+
eval?: unknown[]
|
|
26
|
+
revision?: number
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface WebArenaMeta {
|
|
30
|
+
taskId: number
|
|
31
|
+
intentTemplateId?: number
|
|
32
|
+
sites: string[]
|
|
33
|
+
startUrls: string[]
|
|
34
|
+
revision?: number
|
|
35
|
+
eval?: unknown[]
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const webarenaDir = (): string | undefined => process.env.WEBARENA_VERIFIED_DIR
|
|
39
|
+
|
|
40
|
+
export const webarenaOutputDirOutput: OutputAdapter<string> = {
|
|
41
|
+
parse(events) {
|
|
42
|
+
let text = ''
|
|
43
|
+
for (const ev of events) {
|
|
44
|
+
const d = (ev as { data?: Record<string, unknown> })?.data
|
|
45
|
+
const t = d?.finalText ?? d?.text ?? d?.result
|
|
46
|
+
if (typeof t === 'string' && t.length > 0) text = t
|
|
47
|
+
}
|
|
48
|
+
const fences = [...text.matchAll(/```(?:text|path)?\s*\n([\s\S]*?)```/g)]
|
|
49
|
+
return (fences.at(-1)?.[1] ?? text).trim()
|
|
50
|
+
},
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function assertFile(path: string, label: string): Promise<void> {
|
|
54
|
+
try {
|
|
55
|
+
await access(path)
|
|
56
|
+
} catch (err) {
|
|
57
|
+
throw new Error(`WebArena-Verified: missing ${label} at ${path} (${err instanceof Error ? err.message : err})`)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function assertDir(path: string, label: string): Promise<void> {
|
|
62
|
+
try {
|
|
63
|
+
const s = await stat(path)
|
|
64
|
+
if (!s.isDirectory()) throw new Error('not a directory')
|
|
65
|
+
} catch (err) {
|
|
66
|
+
throw new Error(`WebArena-Verified: missing ${label} at ${path} (${err instanceof Error ? err.message : err})`)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function rowToTask(row: WebArenaRow): BenchTask {
|
|
71
|
+
const meta: WebArenaMeta = {
|
|
72
|
+
taskId: row.task_id,
|
|
73
|
+
intentTemplateId: row.intent_template_id,
|
|
74
|
+
sites: row.sites ?? [],
|
|
75
|
+
startUrls: row.start_urls ?? [],
|
|
76
|
+
revision: row.revision,
|
|
77
|
+
eval: row.eval,
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
id: String(row.task_id),
|
|
81
|
+
prompt: [
|
|
82
|
+
'Run this WebArena-Verified browser task in the official environment.',
|
|
83
|
+
`Goal: ${row.intent}`,
|
|
84
|
+
row.start_urls?.length ? `Start URL templates: ${row.start_urls.join(', ')}` : undefined,
|
|
85
|
+
row.sites?.length ? `Sites: ${row.sites.join(', ')}` : undefined,
|
|
86
|
+
'',
|
|
87
|
+
'Return the path to the official WebArena-Verified run output directory for this task.',
|
|
88
|
+
'The judge expects that directory to contain the task response and network trace files.',
|
|
89
|
+
]
|
|
90
|
+
.filter(Boolean)
|
|
91
|
+
.join('\n'),
|
|
92
|
+
metadata: meta as unknown as Record<string, unknown>,
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function readMeta(task: BenchTask): WebArenaMeta {
|
|
97
|
+
const md = task.metadata
|
|
98
|
+
if (!md || typeof md.taskId !== 'number') {
|
|
99
|
+
throw new Error(`webarena-verified task ${task.id} missing metadata — loadTasks did not populate it`)
|
|
100
|
+
}
|
|
101
|
+
return md as unknown as WebArenaMeta
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function selectRows(rows: WebArenaRow[], opts: LoadOptions): BenchTask[] {
|
|
105
|
+
let tasks = rows.map(rowToTask)
|
|
106
|
+
if (opts.ids) {
|
|
107
|
+
const want = new Set(opts.ids)
|
|
108
|
+
tasks = tasks.filter((task) => want.has(task.id))
|
|
109
|
+
} else if (opts.limit !== undefined) {
|
|
110
|
+
tasks = tasks.slice(0, opts.limit)
|
|
111
|
+
}
|
|
112
|
+
if (tasks.length === 0) throw new Error(`WebArena-Verified: no tasks matched ${JSON.stringify(opts)}`)
|
|
113
|
+
return tasks
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async function loadFixtures(opts: LoadOptions): Promise<BenchTask[]> {
|
|
117
|
+
const rows = JSON.parse(await readFile(FIXTURES, 'utf8')) as WebArenaRow[]
|
|
118
|
+
console.warn(`[webarena-verified] WEBARENA_VERIFIED_FIXTURES=1 — loading ${rows.length} adapter fixtures`)
|
|
119
|
+
return selectRows(rows, opts)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function loadOfficialRows(dir: string, opts: LoadOptions): Promise<BenchTask[]> {
|
|
123
|
+
const rows = JSON.parse(await readFile(join(dir, DATASET_REL), 'utf8')) as WebArenaRow[]
|
|
124
|
+
return selectRows(rows, opts)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async function runOfficialEval(root: string, taskId: number, outputDir: string): Promise<Record<string, unknown>> {
|
|
128
|
+
const script = `
|
|
129
|
+
import json, os, subprocess, sys
|
|
130
|
+
from pathlib import Path
|
|
131
|
+
|
|
132
|
+
root = Path(sys.argv[1])
|
|
133
|
+
task_id = sys.argv[2]
|
|
134
|
+
output_dir = Path(sys.argv[3])
|
|
135
|
+
env = os.environ.copy()
|
|
136
|
+
env["PYTHONPATH"] = str(root / "src") + os.pathsep + env.get("PYTHONPATH", "")
|
|
137
|
+
cmd = [sys.executable, "-m", "webarena_verified", "eval-tasks", "--task-ids", task_id, "--output-dir", str(output_dir)]
|
|
138
|
+
proc = subprocess.run(cmd, cwd=root, env=env, text=True, capture_output=True)
|
|
139
|
+
if proc.returncode != 0:
|
|
140
|
+
raise SystemExit((proc.stderr or proc.stdout or f"exit {proc.returncode}")[:2000])
|
|
141
|
+
result_path = output_dir / task_id / "eval_result.json"
|
|
142
|
+
if not result_path.exists():
|
|
143
|
+
raise SystemExit(f"official evaluator did not write {result_path}")
|
|
144
|
+
print(json.dumps(json.loads(result_path.read_text())))
|
|
145
|
+
`
|
|
146
|
+
const stdout = await runVenvPython(script, [root, String(taskId), outputDir], 0)
|
|
147
|
+
return JSON.parse(stdout.trim().split('\n').at(-1) ?? '{}') as Record<string, unknown>
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function createWebArenaVerifiedAdapter(): BenchmarkAdapter {
|
|
151
|
+
const fixturesMode = process.env.WEBARENA_VERIFIED_FIXTURES === '1'
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
name: 'webarena-verified',
|
|
155
|
+
output: webarenaOutputDirOutput,
|
|
156
|
+
|
|
157
|
+
async preflight() {
|
|
158
|
+
if (fixturesMode) return
|
|
159
|
+
const dir = webarenaDir()
|
|
160
|
+
if (!dir) {
|
|
161
|
+
throw new Error(
|
|
162
|
+
'WEBARENA_VERIFIED_DIR is required. Fix: clone https://github.com/ServiceNow/webarena-verified, install its deps in bench/.venv, and set WEBARENA_VERIFIED_DIR=/path/to/webarena-verified.',
|
|
163
|
+
)
|
|
164
|
+
}
|
|
165
|
+
await assertFile(join(dir, DATASET_REL), 'official dataset')
|
|
166
|
+
await assertFile(join(dir, 'src', 'webarena_verified', '__main__.py'), 'official CLI module')
|
|
167
|
+
await runVenvPython(
|
|
168
|
+
'import sys; sys.path.insert(0, sys.argv[1]); import webarena_verified; print("ok")',
|
|
169
|
+
[join(dir, 'src')],
|
|
170
|
+
)
|
|
171
|
+
},
|
|
172
|
+
|
|
173
|
+
async loadTasks(opts: LoadOptions = {}) {
|
|
174
|
+
if (fixturesMode) return loadFixtures(opts)
|
|
175
|
+
const dir = webarenaDir()
|
|
176
|
+
if (!dir) throw new Error('WEBARENA_VERIFIED_DIR is required to load official WebArena-Verified tasks')
|
|
177
|
+
return loadOfficialRows(dir, opts)
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
async goldArtifact() {
|
|
181
|
+
return undefined
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
async judge(task: BenchTask, artifact: string): Promise<BenchScore> {
|
|
185
|
+
const dir = webarenaDir()
|
|
186
|
+
if (!dir) throw new Error('WEBARENA_VERIFIED_DIR is required to judge WebArena-Verified artifacts')
|
|
187
|
+
const outputDir = resolve(artifact.trim())
|
|
188
|
+
await assertDir(outputDir, 'run output directory')
|
|
189
|
+
const meta = readMeta(task)
|
|
190
|
+
const report = await runOfficialEval(dir, meta.taskId, outputDir)
|
|
191
|
+
const score = typeof report.score === 'number' ? report.score : 0
|
|
192
|
+
const status = typeof report.status === 'string' ? report.status : 'unknown'
|
|
193
|
+
return {
|
|
194
|
+
resolved: score === 1,
|
|
195
|
+
score,
|
|
196
|
+
detail: JSON.stringify({ taskId: meta.taskId, status, outputDir }),
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
}
|
|
200
|
+
}
|
package/src/commit0-prereqs.sh
CHANGED
|
File without changes
|