@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
|
@@ -17,19 +17,49 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import { execFile, spawn } from 'node:child_process'
|
|
20
|
-
import {
|
|
20
|
+
import { createHash } from 'node:crypto'
|
|
21
|
+
import {
|
|
22
|
+
cp,
|
|
23
|
+
lstat,
|
|
24
|
+
mkdir,
|
|
25
|
+
mkdtemp,
|
|
26
|
+
readFile,
|
|
27
|
+
readlink,
|
|
28
|
+
readdir,
|
|
29
|
+
rename,
|
|
30
|
+
rm,
|
|
31
|
+
writeFile,
|
|
32
|
+
} from 'node:fs/promises'
|
|
21
33
|
import { tmpdir } from 'node:os'
|
|
22
|
-
import { join } from 'node:path'
|
|
34
|
+
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from 'node:path'
|
|
23
35
|
import { fileURLToPath } from 'node:url'
|
|
24
36
|
import { promisify } from 'node:util'
|
|
25
|
-
import type {
|
|
37
|
+
import type {
|
|
38
|
+
BenchScore,
|
|
39
|
+
JudgeArtifactFileReceipt,
|
|
40
|
+
JudgeArtifactReceipt,
|
|
41
|
+
} from './types'
|
|
26
42
|
|
|
27
43
|
const execFileAsync = promisify(execFile)
|
|
28
44
|
|
|
29
45
|
/** Repo root for the bench package (…/bench), so `.venv` and `fixtures` resolve. */
|
|
30
46
|
export const benchRoot = fileURLToPath(new URL('../..', import.meta.url))
|
|
31
|
-
|
|
32
|
-
|
|
47
|
+
|
|
48
|
+
/** Resolve the shared interpreter without requiring an installed package to contain a venv. */
|
|
49
|
+
export function resolveBenchPython(
|
|
50
|
+
env: Readonly<{ AGENT_BENCH_PYTHON?: string }> = process.env,
|
|
51
|
+
root: string = benchRoot,
|
|
52
|
+
): string {
|
|
53
|
+
const configured = env.AGENT_BENCH_PYTHON
|
|
54
|
+
if (configured === undefined) return join(root, '.venv', 'bin', 'python')
|
|
55
|
+
if (!isAbsolute(configured)) {
|
|
56
|
+
throw new Error('AGENT_BENCH_PYTHON must be an absolute path')
|
|
57
|
+
}
|
|
58
|
+
return configured
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** The shared interpreter every Python-backed evaluator runs through. */
|
|
62
|
+
export const venvPython = resolveBenchPython()
|
|
33
63
|
|
|
34
64
|
/** Interpreter for a NAMED isolated venv (e.g. `.venv-commit0`). Benches whose pip
|
|
35
65
|
* deps conflict with the shared `.venv` (commit0 downgrades pydantic/sqlalchemy)
|
|
@@ -149,10 +179,160 @@ export interface StagedRunSpec {
|
|
|
149
179
|
* if the expected report is absent/malformed (fail loud — no default score).
|
|
150
180
|
*/
|
|
151
181
|
parseReport(dir: string): Promise<BenchScore>
|
|
182
|
+
/**
|
|
183
|
+
* Copy the complete evaluator directory plus raw process stdout/stderr to this
|
|
184
|
+
* caller-owned directory before cleanup. The destination must not exist.
|
|
185
|
+
*/
|
|
186
|
+
capture?: StagedRunCaptureSpec
|
|
152
187
|
/** Keep the temp dir on disk (debugging). Default false → always cleaned up. */
|
|
153
188
|
keepTmp?: boolean
|
|
154
189
|
}
|
|
155
190
|
|
|
191
|
+
export interface StagedRunCaptureSpec {
|
|
192
|
+
/** Destination for `evaluator/`, `process/`, and the hashed `receipt.json`. */
|
|
193
|
+
destination: string
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** A staged run failed after any requested evidence was durably retained. */
|
|
197
|
+
export class StagedJudgeError extends Error {
|
|
198
|
+
readonly judgeArtifacts?: JudgeArtifactReceipt
|
|
199
|
+
|
|
200
|
+
constructor(message: string, judgeArtifacts?: JudgeArtifactReceipt, options?: ErrorOptions) {
|
|
201
|
+
super(message, options)
|
|
202
|
+
this.name = 'StagedJudgeError'
|
|
203
|
+
this.judgeArtifacts = judgeArtifacts
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function sha256(bytes: Uint8Array): `sha256:${string}` {
|
|
208
|
+
return `sha256:${createHash('sha256').update(bytes).digest('hex')}`
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function portablePath(path: string): string {
|
|
212
|
+
return path.split(sep).join('/')
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function compareText(left: string, right: string): number {
|
|
216
|
+
return left < right ? -1 : left > right ? 1 : 0
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async function collectArtifactFiles(
|
|
220
|
+
root: string,
|
|
221
|
+
current: string,
|
|
222
|
+
): Promise<JudgeArtifactFileReceipt[]> {
|
|
223
|
+
const absolute = join(root, current)
|
|
224
|
+
const entries = await readdir(absolute, { withFileTypes: true })
|
|
225
|
+
const files: JudgeArtifactFileReceipt[] = []
|
|
226
|
+
for (const entry of entries.sort((left, right) => compareText(left.name, right.name))) {
|
|
227
|
+
const relativePath = join(current, entry.name)
|
|
228
|
+
const path = join(root, relativePath)
|
|
229
|
+
if (entry.isDirectory()) {
|
|
230
|
+
files.push(...await collectArtifactFiles(root, relativePath))
|
|
231
|
+
continue
|
|
232
|
+
}
|
|
233
|
+
if (entry.isFile()) {
|
|
234
|
+
const bytes = await readFile(path)
|
|
235
|
+
files.push({
|
|
236
|
+
path: portablePath(relativePath),
|
|
237
|
+
byteLength: bytes.byteLength,
|
|
238
|
+
sha256: sha256(bytes),
|
|
239
|
+
kind: 'file',
|
|
240
|
+
})
|
|
241
|
+
continue
|
|
242
|
+
}
|
|
243
|
+
if (entry.isSymbolicLink()) {
|
|
244
|
+
const targetBytes = await readlink(path, { encoding: 'buffer' })
|
|
245
|
+
files.push({
|
|
246
|
+
path: portablePath(relativePath),
|
|
247
|
+
byteLength: targetBytes.byteLength,
|
|
248
|
+
sha256: sha256(targetBytes),
|
|
249
|
+
kind: 'symlink',
|
|
250
|
+
})
|
|
251
|
+
continue
|
|
252
|
+
}
|
|
253
|
+
throw new Error(`staged judge capture does not support ${relativePath}`)
|
|
254
|
+
}
|
|
255
|
+
return files
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function isWithin(parent: string, candidate: string): boolean {
|
|
259
|
+
const path = relative(parent, candidate)
|
|
260
|
+
return path === '' || (!path.startsWith(`..${sep}`) && path !== '..' && !isAbsolute(path))
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
async function assertDestinationAbsent(destination: string): Promise<void> {
|
|
264
|
+
try {
|
|
265
|
+
await lstat(destination)
|
|
266
|
+
} catch (error) {
|
|
267
|
+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') return
|
|
268
|
+
throw error
|
|
269
|
+
}
|
|
270
|
+
throw new Error(`staged judge capture destination already exists: ${destination}`)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
async function captureStagedRun(
|
|
274
|
+
sourceDirectory: string,
|
|
275
|
+
spec: StagedRunCaptureSpec,
|
|
276
|
+
processOutput: Readonly<{ stdout: Buffer; stderr: Buffer }>,
|
|
277
|
+
evaluatorSucceeded: boolean,
|
|
278
|
+
): Promise<JudgeArtifactReceipt> {
|
|
279
|
+
const source = resolve(sourceDirectory)
|
|
280
|
+
const destination = resolve(spec.destination)
|
|
281
|
+
if (isWithin(source, destination)) {
|
|
282
|
+
throw new Error('staged judge capture destination must be outside the evaluator directory')
|
|
283
|
+
}
|
|
284
|
+
await mkdir(dirname(destination), { recursive: true })
|
|
285
|
+
await assertDestinationAbsent(destination)
|
|
286
|
+
const staging = await mkdtemp(join(dirname(destination), `.${basename(destination)}-capture-`))
|
|
287
|
+
try {
|
|
288
|
+
await cp(source, join(staging, 'evaluator'), {
|
|
289
|
+
recursive: true,
|
|
290
|
+
errorOnExist: true,
|
|
291
|
+
force: false,
|
|
292
|
+
preserveTimestamps: true,
|
|
293
|
+
verbatimSymlinks: true,
|
|
294
|
+
})
|
|
295
|
+
await mkdir(join(staging, 'process'))
|
|
296
|
+
await writeFile(join(staging, 'process', 'stdout.bin'), processOutput.stdout)
|
|
297
|
+
await writeFile(join(staging, 'process', 'stderr.bin'), processOutput.stderr)
|
|
298
|
+
|
|
299
|
+
const files = [
|
|
300
|
+
...await collectArtifactFiles(staging, 'evaluator'),
|
|
301
|
+
...await collectArtifactFiles(staging, 'process'),
|
|
302
|
+
].sort((left, right) => compareText(left.path, right.path))
|
|
303
|
+
const byteLength = files.reduce((total, file) => total + file.byteLength, 0)
|
|
304
|
+
const treeBytes = Buffer.from(
|
|
305
|
+
files
|
|
306
|
+
.map((file) => `${file.path}\0${file.kind}\0${file.byteLength}\0${file.sha256}\n`)
|
|
307
|
+
.join(''),
|
|
308
|
+
'utf8',
|
|
309
|
+
)
|
|
310
|
+
const receipt: JudgeArtifactReceipt = {
|
|
311
|
+
schema: 'agent-bench/judge-artifacts/v1',
|
|
312
|
+
directory: destination,
|
|
313
|
+
evaluatorDirectory: join(destination, 'evaluator'),
|
|
314
|
+
manifestPath: join(destination, 'receipt.json'),
|
|
315
|
+
evaluatorSucceeded,
|
|
316
|
+
files,
|
|
317
|
+
fileCount: files.length,
|
|
318
|
+
byteLength,
|
|
319
|
+
treeSha256: sha256(treeBytes),
|
|
320
|
+
}
|
|
321
|
+
await writeFile(join(staging, 'receipt.json'), `${JSON.stringify(receipt, null, 2)}\n`)
|
|
322
|
+
await rename(staging, destination)
|
|
323
|
+
return receipt
|
|
324
|
+
} catch (error) {
|
|
325
|
+
await rm(staging, { recursive: true, force: true }).catch(() => {})
|
|
326
|
+
throw error
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function processBytes(value: unknown): Buffer {
|
|
331
|
+
if (Buffer.isBuffer(value)) return value
|
|
332
|
+
if (value === undefined || value === null) return Buffer.alloc(0)
|
|
333
|
+
return Buffer.from(String(value), 'utf8')
|
|
334
|
+
}
|
|
335
|
+
|
|
156
336
|
/**
|
|
157
337
|
* The shared judge body: mkdtemp → stage → spawn evaluator → parseReport →
|
|
158
338
|
* cleanup. The evaluator's stdout/stderr is surfaced on failure; the temp dir is
|
|
@@ -160,23 +340,66 @@ export interface StagedRunSpec {
|
|
|
160
340
|
*/
|
|
161
341
|
export async function runStagedJudge(spec: StagedRunSpec): Promise<BenchScore> {
|
|
162
342
|
const dir = await mkdtemp(join(tmpdir(), spec.tmpPrefix))
|
|
343
|
+
let stdout: Buffer<ArrayBufferLike> = Buffer.alloc(0)
|
|
344
|
+
let stderr: Buffer<ArrayBufferLike> = Buffer.alloc(0)
|
|
345
|
+
let evaluatorSucceeded = false
|
|
346
|
+
let score: BenchScore | undefined
|
|
347
|
+
let failure: unknown
|
|
163
348
|
try {
|
|
164
|
-
await spec.stage(dir)
|
|
165
|
-
const bin = spec.bin ?? venvPython
|
|
166
349
|
try {
|
|
167
|
-
await
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
350
|
+
await spec.stage(dir)
|
|
351
|
+
const bin = spec.bin ?? venvPython
|
|
352
|
+
const argv = spec.argv(dir)
|
|
353
|
+
try {
|
|
354
|
+
const output = await execFileAsync(bin, argv, {
|
|
355
|
+
cwd: spec.cwd ? spec.cwd(dir) : dir,
|
|
356
|
+
encoding: 'buffer',
|
|
357
|
+
maxBuffer: bigBuffer,
|
|
358
|
+
...(spec.timeoutMs ? { timeout: spec.timeoutMs } : {}),
|
|
359
|
+
})
|
|
360
|
+
stdout = processBytes(output.stdout)
|
|
361
|
+
stderr = processBytes(output.stderr)
|
|
362
|
+
evaluatorSucceeded = true
|
|
363
|
+
} catch (err) {
|
|
364
|
+
const e = err as { stderr?: unknown; stdout?: unknown; message?: string }
|
|
365
|
+
stdout = processBytes(e.stdout)
|
|
366
|
+
stderr = processBytes(e.stderr)
|
|
367
|
+
const detail = processBytes(e.stderr ?? e.stdout ?? e.message ?? String(err))
|
|
368
|
+
.toString('utf8')
|
|
369
|
+
.slice(0, 2000)
|
|
370
|
+
throw new Error(`${spec.tmpPrefix.replace(/-$/, '')} evaluator failed (${bin} ${argv.join(' ')}):\n${detail}`)
|
|
371
|
+
}
|
|
372
|
+
score = await spec.parseReport(dir)
|
|
172
373
|
} catch (err) {
|
|
173
|
-
|
|
174
|
-
const detail = (e.stderr || e.stdout || e.message || String(err)).slice(0, 2000)
|
|
175
|
-
throw new Error(`${spec.tmpPrefix.replace(/-$/, '')} evaluator failed (${bin} ${spec.argv(dir).join(' ')}):\n${detail}`)
|
|
374
|
+
failure = err
|
|
176
375
|
}
|
|
177
|
-
return await spec.parseReport(dir)
|
|
178
376
|
} finally {
|
|
377
|
+
let judgeArtifacts: JudgeArtifactReceipt | undefined
|
|
378
|
+
if (spec.capture) {
|
|
379
|
+
try {
|
|
380
|
+
judgeArtifacts = await captureStagedRun(
|
|
381
|
+
dir,
|
|
382
|
+
spec.capture,
|
|
383
|
+
{ stdout, stderr },
|
|
384
|
+
evaluatorSucceeded,
|
|
385
|
+
)
|
|
386
|
+
} catch (captureError) {
|
|
387
|
+
failure = new Error(
|
|
388
|
+
`staged judge failed to capture evaluator artifacts: ${captureError instanceof Error ? captureError.message : captureError}`,
|
|
389
|
+
{ cause: failure ?? captureError },
|
|
390
|
+
)
|
|
391
|
+
}
|
|
392
|
+
}
|
|
179
393
|
if (!spec.keepTmp) await rm(dir, { recursive: true, force: true }).catch(() => {})
|
|
394
|
+
if (failure) {
|
|
395
|
+
throw new StagedJudgeError(
|
|
396
|
+
failure instanceof Error ? failure.message : String(failure),
|
|
397
|
+
judgeArtifacts,
|
|
398
|
+
{ cause: failure },
|
|
399
|
+
)
|
|
400
|
+
}
|
|
401
|
+
if (!score) throw new StagedJudgeError('staged judge completed without a score', judgeArtifacts)
|
|
402
|
+
return judgeArtifacts ? { ...score, judgeArtifacts } : score
|
|
180
403
|
}
|
|
181
404
|
}
|
|
182
405
|
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentBench deterministic subset adapter.
|
|
3
|
+
*
|
|
4
|
+
* This targets AgentBench DBBench rows only: question + table + published label.
|
|
5
|
+
* It does not wrap AgentBench's controller protocol or the non-deterministic game
|
|
6
|
+
* environments. Worker artifact = final answer text. Judge = exact match against
|
|
7
|
+
* the official DBBench label list after light whitespace/case normalization.
|
|
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', 'agentbench-dbbench.json')
|
|
17
|
+
const DEFAULT_SPLIT = 'dev'
|
|
18
|
+
|
|
19
|
+
interface AgentBenchDbRow {
|
|
20
|
+
description: string
|
|
21
|
+
label: string[]
|
|
22
|
+
table?: {
|
|
23
|
+
table_name?: string
|
|
24
|
+
table_info?: {
|
|
25
|
+
columns?: Array<{ name: string; type?: string }>
|
|
26
|
+
rows?: unknown[][]
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface AgentBenchMeta {
|
|
32
|
+
labels: string[]
|
|
33
|
+
split: string
|
|
34
|
+
subset: 'dbbench'
|
|
35
|
+
table?: AgentBenchDbRow['table']
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const agentbenchDir = (): string | undefined => process.env.AGENTBENCH_DIR
|
|
39
|
+
|
|
40
|
+
export const agentbenchAnswerOutput: 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|answer)?\s*\n([\s\S]*?)```/g)]
|
|
49
|
+
return (fences.at(-1)?.[1] ?? text).trim()
|
|
50
|
+
},
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function normalizeAnswer(value: string): string {
|
|
54
|
+
return value
|
|
55
|
+
.toLowerCase()
|
|
56
|
+
.replace(/\s+/g, ' ')
|
|
57
|
+
.replace(/^["'`]+|["'`]+$/g, '')
|
|
58
|
+
.trim()
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function rowToTask(row: AgentBenchDbRow, index: number, split: string): BenchTask {
|
|
62
|
+
const columns = row.table?.table_info?.columns?.map((c) => `${c.name}${c.type ? ` (${c.type})` : ''}`).join(', ')
|
|
63
|
+
const sampleRows = row.table?.table_info?.rows?.slice(0, 40)
|
|
64
|
+
const meta: AgentBenchMeta = {
|
|
65
|
+
labels: row.label,
|
|
66
|
+
split,
|
|
67
|
+
subset: 'dbbench',
|
|
68
|
+
table: row.table,
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
id: `dbbench-${split}-${index}`,
|
|
72
|
+
split,
|
|
73
|
+
prompt: [
|
|
74
|
+
'Answer this AgentBench DBBench question using the table below.',
|
|
75
|
+
'Return only the answer value.',
|
|
76
|
+
'',
|
|
77
|
+
`Question: ${row.description}`,
|
|
78
|
+
row.table?.table_name ? `Table: ${row.table.table_name}` : undefined,
|
|
79
|
+
columns ? `Columns: ${columns}` : undefined,
|
|
80
|
+
sampleRows ? `Rows JSON: ${JSON.stringify(sampleRows)}` : undefined,
|
|
81
|
+
]
|
|
82
|
+
.filter(Boolean)
|
|
83
|
+
.join('\n'),
|
|
84
|
+
metadata: meta as unknown as Record<string, unknown>,
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function readMeta(task: BenchTask): AgentBenchMeta {
|
|
89
|
+
const md = task.metadata
|
|
90
|
+
if (!md || !Array.isArray(md.labels)) {
|
|
91
|
+
throw new Error(`agentbench task ${task.id} missing metadata — loadTasks did not populate it`)
|
|
92
|
+
}
|
|
93
|
+
return md as unknown as AgentBenchMeta
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function selectRows(rows: AgentBenchDbRow[], opts: LoadOptions, split: string): BenchTask[] {
|
|
97
|
+
let tasks = rows.map((row, index) => rowToTask(row, index, split))
|
|
98
|
+
if (opts.ids) {
|
|
99
|
+
const want = new Set(opts.ids)
|
|
100
|
+
tasks = tasks.filter((task) => want.has(task.id))
|
|
101
|
+
} else if (opts.limit !== undefined) {
|
|
102
|
+
tasks = tasks.slice(0, opts.limit)
|
|
103
|
+
}
|
|
104
|
+
if (tasks.length === 0) throw new Error(`AgentBench DBBench: no tasks matched ${JSON.stringify(opts)}`)
|
|
105
|
+
return tasks
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function loadJsonl(path: string): Promise<AgentBenchDbRow[]> {
|
|
109
|
+
const raw = await readFile(path, 'utf8')
|
|
110
|
+
return raw
|
|
111
|
+
.split('\n')
|
|
112
|
+
.map((line) => line.trim())
|
|
113
|
+
.filter(Boolean)
|
|
114
|
+
.map((line) => JSON.parse(line) as AgentBenchDbRow)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async function loadFixtures(opts: LoadOptions, split: string): Promise<BenchTask[]> {
|
|
118
|
+
const rows = JSON.parse(await readFile(FIXTURES, 'utf8')) as AgentBenchDbRow[]
|
|
119
|
+
console.warn(`[agentbench] AGENTBENCH_FIXTURES=1 — loading ${rows.length} DBBench adapter fixtures`)
|
|
120
|
+
return selectRows(rows, opts, split)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function createAgentBenchAdapter(): BenchmarkAdapter {
|
|
124
|
+
const fixturesMode = process.env.AGENTBENCH_FIXTURES === '1'
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
name: 'agentbench',
|
|
128
|
+
output: agentbenchAnswerOutput,
|
|
129
|
+
|
|
130
|
+
async preflight() {
|
|
131
|
+
if (fixturesMode) return
|
|
132
|
+
const dir = agentbenchDir()
|
|
133
|
+
if (!dir) {
|
|
134
|
+
throw new Error('AGENTBENCH_DIR is required. Fix: clone https://github.com/THUDM/AgentBench and set AGENTBENCH_DIR=/path/to/AgentBench.')
|
|
135
|
+
}
|
|
136
|
+
await loadJsonl(join(dir, 'data', 'dbbench', `${DEFAULT_SPLIT}.jsonl`))
|
|
137
|
+
},
|
|
138
|
+
|
|
139
|
+
async loadTasks(opts: LoadOptions = {}) {
|
|
140
|
+
const split = opts.split ?? DEFAULT_SPLIT
|
|
141
|
+
if (fixturesMode) return loadFixtures(opts, split)
|
|
142
|
+
const dir = agentbenchDir()
|
|
143
|
+
if (!dir) throw new Error('AGENTBENCH_DIR is required to load AgentBench DBBench tasks')
|
|
144
|
+
return selectRows(await loadJsonl(join(dir, 'data', 'dbbench', `${split}.jsonl`)), opts, split)
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
async goldArtifact(task: BenchTask) {
|
|
148
|
+
return readMeta(task).labels[0]
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
async judge(task: BenchTask, artifact: string): Promise<BenchScore> {
|
|
152
|
+
const meta = readMeta(task)
|
|
153
|
+
const got = normalizeAnswer(artifact)
|
|
154
|
+
const expected = meta.labels.map(normalizeAnswer)
|
|
155
|
+
const resolved = expected.includes(got)
|
|
156
|
+
return {
|
|
157
|
+
resolved,
|
|
158
|
+
score: resolved ? 1 : 0,
|
|
159
|
+
detail: JSON.stringify({ subset: meta.subset, split: meta.split, expected: meta.labels, got: artifact }),
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
}
|
|
163
|
+
}
|
|
@@ -27,19 +27,25 @@ test('goldArtifact is undefined — reference solution ships only inside the eng
|
|
|
27
27
|
assert.equal(await a.goldArtifact({ id: 't', prompt: '', metadata: { taskId: 't', split: 'dev' } }), undefined)
|
|
28
28
|
})
|
|
29
29
|
|
|
30
|
-
test('preflight FAILS LOUD with the install + download-data fix
|
|
30
|
+
test('preflight passes when installed or FAILS LOUD with the install + download-data fix', async () => {
|
|
31
31
|
const a = createAppWorldAdapter()
|
|
32
|
-
|
|
32
|
+
try {
|
|
33
|
+
await a.preflight()
|
|
34
|
+
} catch (err) {
|
|
35
|
+
const e = err as Error
|
|
33
36
|
assert.match(e.message, /pip install appworld/)
|
|
34
37
|
assert.match(e.message, /appworld download data/)
|
|
35
|
-
|
|
36
|
-
})
|
|
38
|
+
}
|
|
37
39
|
})
|
|
38
40
|
|
|
39
|
-
test('loadTasks
|
|
41
|
+
test('loadTasks either enumerates live engine rows or FAILS LOUD without fabricating tasks', async () => {
|
|
40
42
|
const a = createAppWorldAdapter()
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
try {
|
|
44
|
+
const tasks = await a.loadTasks({ limit: 1 })
|
|
45
|
+
assert.equal(tasks.length, 1)
|
|
46
|
+
assert.ok(tasks[0].id.length > 0)
|
|
47
|
+
assert.match(tasks[0].prompt, /Solve this by writing Python/)
|
|
48
|
+
} catch (err) {
|
|
49
|
+
assert.match((err as Error).message, /appworld driver failed|appworld import failed/)
|
|
50
|
+
}
|
|
45
51
|
})
|