@tangle-network/agent-bench 0.3.6 → 0.3.8
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 +11 -0
- package/HARNESS.md +43 -0
- package/dist/adapters.js +24 -24
- package/dist/benchmarks/_harness.d.ts +1 -1
- package/dist/benchmarks/_harness.js +1 -1
- package/dist/benchmarks/aec-bench.js +2 -2
- package/dist/benchmarks/agentbench.js +2 -2
- package/dist/benchmarks/appworld.js +2 -2
- package/dist/benchmarks/bfcl.js +2 -2
- package/dist/benchmarks/commit0.js +2 -2
- package/dist/benchmarks/crag.js +2 -2
- package/dist/benchmarks/dabstep.js +2 -2
- package/dist/benchmarks/enterpriseops-gym.js +2 -2
- package/dist/benchmarks/finresearchbench.js +2 -2
- package/dist/benchmarks/humaneval.d.ts +10 -1
- package/dist/benchmarks/humaneval.js +5 -3
- package/dist/benchmarks/nomiracl.js +2 -2
- package/dist/benchmarks/open-rag-bench.js +2 -2
- package/dist/benchmarks/programbench.js +2 -2
- package/dist/benchmarks/ragbench.js +2 -2
- package/dist/benchmarks/swe-bench.js +2 -2
- package/dist/benchmarks/t2-ragbench.js +2 -2
- package/dist/benchmarks/tau-bench-shared.js +2 -2
- package/dist/benchmarks/tau2-bench.js +3 -3
- package/dist/benchmarks/tau3-banking.js +3 -3
- package/dist/benchmarks/terminal-bench.js +2 -2
- package/dist/benchmarks/toollm.js +2 -2
- package/dist/benchmarks/webarena-verified.js +2 -2
- package/dist/{chunk-CKUVRZ2T.js → chunk-3U5TXJZS.js} +2 -2
- package/dist/{chunk-PPYSEKFM.js → chunk-5H5XV76F.js} +73 -15
- package/dist/chunk-5H5XV76F.js.map +1 -0
- package/dist/{chunk-YCGY7UIZ.js → chunk-7GRVHU22.js} +2 -2
- package/dist/{chunk-Z7ML6L77.js → chunk-HWST3SED.js} +2 -2
- package/dist/{chunk-SYDW647C.js → chunk-IA2FBTWC.js} +2 -2
- package/dist/{chunk-R67DFVLO.js → chunk-IFVINJ4B.js} +2 -2
- package/dist/{chunk-R36V2VP7.js → chunk-IZ5M6OAC.js} +2 -2
- package/dist/{chunk-ODT47UAY.js → chunk-K3BQGZCT.js} +2 -2
- package/dist/{chunk-IFAV6KEM.js → chunk-KP5KD6EN.js} +2 -2
- package/dist/{chunk-ZEWMTR5M.js → chunk-MQMRLGOG.js} +2 -2
- package/dist/{chunk-TSWPNOYM.js → chunk-NQG5XDSB.js} +2 -2
- package/dist/{chunk-7WSD27QQ.js → chunk-PB64GYIG.js} +2 -2
- package/dist/{chunk-HBSWHQNJ.js → chunk-RCYQEFNX.js} +3 -3
- package/dist/{chunk-J3KDJNX2.js → chunk-RH5F53JT.js} +2 -2
- package/dist/{chunk-UAIOHCUK.js → chunk-SFLA7OH3.js} +3 -3
- package/dist/{chunk-Y6O2OCUO.js → chunk-SHM6MRRF.js} +2 -2
- package/dist/{chunk-KDIKRJGB.js → chunk-SHYIRB7I.js} +2 -2
- package/dist/{chunk-HHXFIHXC.js → chunk-SVR2LKYI.js} +2 -2
- package/dist/{chunk-5SBJCB6W.js → chunk-V7AEBY6U.js} +22 -22
- package/dist/{chunk-LRRD7NAG.js → chunk-WSKWVEQB.js} +18 -2
- package/dist/chunk-WSKWVEQB.js.map +1 -0
- package/dist/{chunk-2PVVP7GN.js → chunk-XKEFIFIC.js} +2 -2
- package/dist/{chunk-JRWWGMK7.js → chunk-XYA4XSNU.js} +2 -2
- package/dist/{chunk-X5YKXC6V.js → chunk-YSMEKBTD.js} +2 -2
- package/dist/{chunk-2XU6OGEN.js → chunk-Z4TZ76N7.js} +2 -2
- package/dist/index.js +24 -24
- package/package.json +6 -5
- package/scripts/run-package-tests.mjs +30 -8
- package/scripts/verify-packed-consumer.mjs +1 -1
- package/scripts/verify-pier-agent.mts +1 -0
- package/src/benchmarks/_harness.ts +20 -2
- package/src/benchmarks/humaneval.test.mts +122 -0
- package/src/benchmarks/humaneval.ts +100 -27
- package/src/david-attribution.mts +78 -0
- package/src/david-goliath.mts +149 -0
- package/src/hev-improve.mts +25 -6
- package/src/humaneval-object-ablation.mts +201 -0
- package/src/live-improve-campaign-mbpp.mts +641 -0
- package/src/live-improve-campaign.mts +500 -0
- package/src/mbpp-structural.mts +12 -7
- package/src/quant-arena/README.md +144 -0
- package/src/quant-arena/backtest.test.mts +135 -0
- package/src/quant-arena/backtest.ts +218 -0
- package/src/quant-arena/data.test.mts +44 -0
- package/src/quant-arena/data.ts +141 -0
- package/src/quant-arena/driver.test.mts +253 -0
- package/src/quant-arena/driver.ts +219 -0
- package/src/quant-arena/fixtures/data/PROVENANCE.md +26 -0
- package/src/quant-arena/fixtures/data/holdout/IDX.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S01.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S02.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S03.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S04.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S05.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S06.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S07.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S08.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S09.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S10.csv +523 -0
- package/src/quant-arena/fixtures/data/insample/IDX.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S01.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S02.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S03.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S04.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S05.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S06.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S07.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S08.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S09.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S10.csv +2087 -0
- package/src/quant-arena/fixtures/demo-campaign/cost-ledger.jsonl +16 -0
- package/src/quant-arena/fixtures/demo-campaign/notebook.jsonl +5 -0
- package/src/quant-arena/fixtures/demo-campaign/rollout-manifest.json +171 -0
- package/src/quant-arena/fixtures/demo-campaign/strategies/cand-001-default-author/strategy.ts +119 -0
- package/src/quant-arena/fixtures/demo-campaign/strategies/cand-002-default-author/strategy.ts +119 -0
- package/src/quant-arena/fixtures/demo-campaign/strategies/cand-003-quant-researcher/strategy.ts +105 -0
- package/src/quant-arena/fixtures/demo-campaign/strategies/cand-004-quant-researcher/strategy.ts +102 -0
- package/src/quant-arena/fixtures/demo-campaign-v2/cost-ledger.jsonl +4 -0
- package/src/quant-arena/fixtures/demo-campaign-v2/notebook.jsonl +2 -0
- package/src/quant-arena/fixtures/demo-campaign-v2/rollout-manifest.json +84 -0
- package/src/quant-arena/fixtures/demo-campaign-v2/strategies/cand-001-quant-researcher/strategy.ts +117 -0
- package/src/quant-arena/holdout-certify.mts +206 -0
- package/src/quant-arena/holdout-certify.test.mts +82 -0
- package/src/quant-arena/leak-audit.test.mts +79 -0
- package/src/quant-arena/leak-audit.ts +95 -0
- package/src/quant-arena/make-fixtures.mts +161 -0
- package/src/quant-arena/multiplicity.test.mts +68 -0
- package/src/quant-arena/multiplicity.ts +87 -0
- package/src/quant-arena/nautilus-certify.ts +31 -0
- package/src/quant-arena/oms.ts +90 -0
- package/src/quant-arena/profiles/quant-researcher.profile.json +7 -0
- package/src/quant-arena/python/pyproject.toml +8 -0
- package/src/quant-arena/python/uv.lock +1297 -0
- package/src/quant-arena/python/vbt-worker.py +192 -0
- package/src/quant-arena/quant-loop.mts +813 -0
- package/src/quant-arena/quant-loop.test.mts +75 -0
- package/src/quant-arena/strategies/buy-hold-index/strategy.ts +11 -0
- package/src/quant-arena/strategies/equal-weight/strategy.ts +20 -0
- package/src/quant-arena/strategies/sma-crossover/strategy.ts +42 -0
- package/src/quant-arena/types.ts +133 -0
- package/src/quant-arena/vbt-client.ts +321 -0
- package/src/quant-arena/vbt-parity.test.mts +183 -0
- package/src/quant-arena/windows.test.mts +45 -0
- package/src/quant-arena/windows.ts +54 -0
- package/src/rollout-ledger/backfill-swe-arena.mts +606 -0
- package/src/rollout-ledger/backfill-swe-arena.test.mts +338 -0
- package/src/rollout-ledger/settle-capture.mts +442 -0
- package/src/rollout-ledger/settle-capture.test.mts +270 -0
- package/src/stream-observe.py +45 -0
- package/src/stream-observe.tpl.html +247 -0
- package/src/supervisor-arena.mts +816 -0
- package/src/swe-arena/activation.mts +228 -0
- package/src/swe-arena/activation.test.mts +303 -0
- package/src/swe-arena/analyze.ts +211 -0
- package/src/swe-arena/arms.ts +804 -0
- package/src/swe-arena/bootstrap-meta.mts +188 -0
- package/src/swe-arena/bootstrap-meta.test.mts +51 -0
- package/src/swe-arena/briefing.mts +217 -0
- package/src/swe-arena/briefing.test.mts +178 -0
- package/src/swe-arena/calibrate.ts +217 -0
- package/src/swe-arena/capabilities.mts +76 -0
- package/src/swe-arena/capabilities.test.mts +57 -0
- package/src/swe-arena/capacity.ts +194 -0
- package/src/swe-arena/cell-evidence.mts +437 -0
- package/src/swe-arena/cell-evidence.test.mts +248 -0
- package/src/swe-arena/diagnosis-ensemble.test.mts +210 -0
- package/src/swe-arena/diagnosis-ensemble.ts +520 -0
- package/src/swe-arena/execution.test.mts +1170 -0
- package/src/swe-arena/factory-command-container.ts +284 -0
- package/src/swe-arena/factory-judge-child.mts +228 -0
- package/src/swe-arena/factory.test.mts +643 -0
- package/src/swe-arena/fixtures/analyze.py +80 -0
- package/src/swe-arena/fixtures/excludes.txt +8 -0
- package/src/swe-arena/fixtures/factory/agent-eval-309/calibration.md +51 -0
- package/src/swe-arena/fixtures/factory/agent-eval-309/manifest.json +29 -0
- package/src/swe-arena/fixtures/factory/agent-eval-309/spec.md +64 -0
- package/src/swe-arena/fixtures/factory/agent-runtime-232/calibration.md +48 -0
- package/src/swe-arena/fixtures/factory/agent-runtime-232/manifest.json +29 -0
- package/src/swe-arena/fixtures/factory/agent-runtime-232/spec.md +48 -0
- package/src/swe-arena/fixtures/factory/loops-28/calibration.md +47 -0
- package/src/swe-arena/fixtures/factory/loops-28/manifest.json +30 -0
- package/src/swe-arena/fixtures/factory/loops-28/spec.md +50 -0
- package/src/swe-arena/fixtures/gen1-salvage/README.md +45 -0
- package/src/swe-arena/fixtures/gen1-salvage/cand0-e6d7361.diff +116 -0
- package/src/swe-arena/fixtures/gen1-salvage/cand1-76a8590.diff +293 -0
- package/src/swe-arena/fixtures/holdout-preregister.log +12 -0
- package/src/swe-arena/fixtures/holdout.json +44 -0
- package/src/swe-arena/fixtures/instances.json +146 -0
- package/src/swe-arena/fixtures/ledger.jsonl +12 -0
- package/src/swe-arena/fixtures/patches/pallets__flask-5014.solo.patch +36 -0
- package/src/swe-arena/fixtures/patches/pydata__xarray-4687.sup.patch +33 -0
- package/src/swe-arena/fixtures/rejudge.jsonl +15 -0
- package/src/swe-arena/fixtures/rematch.jsonl +3 -0
- package/src/swe-arena/fixtures/rematch2.jsonl +3 -0
- package/src/swe-arena/fixtures/rematch3.jsonl +3 -0
- package/src/swe-arena/fixtures/run-report/README.md +43 -0
- package/src/swe-arena/fixtures/run-report/factory-agent-eval-309-FSUP0.json +173 -0
- package/src/swe-arena/fixtures/run-report/factory-agent-eval-309-FSUP0.md +100 -0
- package/src/swe-arena/fixtures/run-report/gen3-rollup.json +551 -0
- package/src/swe-arena/fixtures/run-report/gen3-rollup.md +64 -0
- package/src/swe-arena/fixtures/sup-journal-true.json +19 -0
- package/src/swe-arena/fixtures/verify/astropy__astropy-13033.sh +48 -0
- package/src/swe-arena/fixtures/verify/django__django-11532.sh +50 -0
- package/src/swe-arena/fixtures/verify/matplotlib__matplotlib-20826.sh +76 -0
- package/src/swe-arena/fixtures/verify/pydata__xarray-4687.sh +44 -0
- package/src/swe-arena/fixtures/verify/pytest-dev__pytest-6197.sh +32 -0
- package/src/swe-arena/fixtures/verify/sphinx-doc__sphinx-9658.sh +51 -0
- package/src/swe-arena/fixtures/worker-tokens.json +42 -0
- package/src/swe-arena/fixtures.ts +237 -0
- package/src/swe-arena/gepa-seat.mts +583 -0
- package/src/swe-arena/gepa-seat.test.mts +635 -0
- package/src/swe-arena/holdout-certify.mts +408 -0
- package/src/swe-arena/holdout-certify.test.mts +160 -0
- package/src/swe-arena/judge-child.mts +37 -0
- package/src/swe-arena/ledger-orphans.mts +77 -0
- package/src/swe-arena/ledger-orphans.test.mts +147 -0
- package/src/swe-arena/lineage-record.mts +164 -0
- package/src/swe-arena/lineage-record.test.mts +115 -0
- package/src/swe-arena/manifest.mts +293 -0
- package/src/swe-arena/manifest.test.mts +169 -0
- package/src/swe-arena/materialize.ts +142 -0
- package/src/swe-arena/outer-loop.mts +2795 -0
- package/src/swe-arena/outer-loop.test.mts +696 -0
- package/src/swe-arena/parity.test.mts +87 -0
- package/src/swe-arena/premeasured-from-cells.mts +281 -0
- package/src/swe-arena/premeasured-from-cells.test.mts +180 -0
- package/src/swe-arena/proc.test.mts +174 -0
- package/src/swe-arena/proc.ts +260 -0
- package/src/swe-arena/profiles/default-author.profile.json +4 -0
- package/src/swe-arena/proposer-fanout.mts +770 -0
- package/src/swe-arena/proposer-fanout.test.mts +619 -0
- package/src/swe-arena/proposer-provenance.mts +177 -0
- package/src/swe-arena/proposer-provenance.test.mts +106 -0
- package/src/swe-arena/reconcile.ts +0 -0
- package/src/swe-arena/replay.mts +183 -0
- package/src/swe-arena/replay.test.mts +300 -0
- package/src/swe-arena/run-experiment.mts +727 -0
- package/src/swe-arena/run-report.mts +75 -0
- package/src/swe-arena/run-supervisor.mjs +297 -0
- package/src/swe-arena/run-supervisor.test.mts +500 -0
- package/src/swe-arena/score-split.mts +140 -0
- package/src/swe-arena/score-split.test.mts +123 -0
- package/src/swe-arena/serialized-judge.ts +414 -0
- package/src/swe-arena/types.ts +218 -0
- package/src/swe-code-improve.mts +328 -0
- package/src/swe-emit-patch.mts +104 -0
- package/src/swe-improve.mts +232 -0
- package/src/swe-jail.ts +2 -2
- package/src/swe-local-proof.mts +169 -0
- package/src/swe-repro-calibrate.mts +446 -0
- package/src/swe-stream.mts +1497 -0
- package/src/swe-structural.mts +245 -837
- package/dist/chunk-LRRD7NAG.js.map +0 -1
- package/dist/chunk-PPYSEKFM.js.map +0 -1
- /package/dist/{chunk-CKUVRZ2T.js.map → chunk-3U5TXJZS.js.map} +0 -0
- /package/dist/{chunk-YCGY7UIZ.js.map → chunk-7GRVHU22.js.map} +0 -0
- /package/dist/{chunk-Z7ML6L77.js.map → chunk-HWST3SED.js.map} +0 -0
- /package/dist/{chunk-SYDW647C.js.map → chunk-IA2FBTWC.js.map} +0 -0
- /package/dist/{chunk-R67DFVLO.js.map → chunk-IFVINJ4B.js.map} +0 -0
- /package/dist/{chunk-R36V2VP7.js.map → chunk-IZ5M6OAC.js.map} +0 -0
- /package/dist/{chunk-ODT47UAY.js.map → chunk-K3BQGZCT.js.map} +0 -0
- /package/dist/{chunk-IFAV6KEM.js.map → chunk-KP5KD6EN.js.map} +0 -0
- /package/dist/{chunk-ZEWMTR5M.js.map → chunk-MQMRLGOG.js.map} +0 -0
- /package/dist/{chunk-TSWPNOYM.js.map → chunk-NQG5XDSB.js.map} +0 -0
- /package/dist/{chunk-7WSD27QQ.js.map → chunk-PB64GYIG.js.map} +0 -0
- /package/dist/{chunk-HBSWHQNJ.js.map → chunk-RCYQEFNX.js.map} +0 -0
- /package/dist/{chunk-J3KDJNX2.js.map → chunk-RH5F53JT.js.map} +0 -0
- /package/dist/{chunk-UAIOHCUK.js.map → chunk-SFLA7OH3.js.map} +0 -0
- /package/dist/{chunk-Y6O2OCUO.js.map → chunk-SHM6MRRF.js.map} +0 -0
- /package/dist/{chunk-KDIKRJGB.js.map → chunk-SHYIRB7I.js.map} +0 -0
- /package/dist/{chunk-HHXFIHXC.js.map → chunk-SVR2LKYI.js.map} +0 -0
- /package/dist/{chunk-5SBJCB6W.js.map → chunk-V7AEBY6U.js.map} +0 -0
- /package/dist/{chunk-2PVVP7GN.js.map → chunk-XKEFIFIC.js.map} +0 -0
- /package/dist/{chunk-JRWWGMK7.js.map → chunk-XYA4XSNU.js.map} +0 -0
- /package/dist/{chunk-X5YKXC6V.js.map → chunk-YSMEKBTD.js.map} +0 -0
- /package/dist/{chunk-2XU6OGEN.js.map → chunk-Z4TZ76N7.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,47 +1,47 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ADAPTERS,
|
|
3
3
|
resolveAdapter
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-V7AEBY6U.js";
|
|
5
|
+
import "./chunk-XKEFIFIC.js";
|
|
6
6
|
import "./chunk-VQRS7VUC.js";
|
|
7
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-HWST3SED.js";
|
|
8
8
|
import {
|
|
9
9
|
createRagBenchAdapter
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-IFVINJ4B.js";
|
|
11
11
|
import "./chunk-SEVJPLZC.js";
|
|
12
12
|
import {
|
|
13
13
|
createSweBenchAdapter,
|
|
14
14
|
scoreSweReport,
|
|
15
15
|
sweEvaluationArgv,
|
|
16
16
|
swePatchOutput
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-3U5TXJZS.js";
|
|
18
18
|
import {
|
|
19
19
|
createT2RagBenchAdapter
|
|
20
|
-
} from "./chunk-
|
|
21
|
-
import "./chunk-
|
|
22
|
-
import "./chunk-
|
|
23
|
-
import "./chunk-
|
|
24
|
-
import "./chunk-
|
|
20
|
+
} from "./chunk-SHM6MRRF.js";
|
|
21
|
+
import "./chunk-RCYQEFNX.js";
|
|
22
|
+
import "./chunk-SFLA7OH3.js";
|
|
23
|
+
import "./chunk-IZ5M6OAC.js";
|
|
24
|
+
import "./chunk-XYA4XSNU.js";
|
|
25
25
|
import "./chunk-INNOYXCP.js";
|
|
26
26
|
import "./chunk-PA2ZKHJC.js";
|
|
27
|
-
import "./chunk-
|
|
27
|
+
import "./chunk-5H5XV76F.js";
|
|
28
28
|
import "./chunk-TBKU5XQI.js";
|
|
29
29
|
import {
|
|
30
30
|
createNoMiraclAdapter
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-NQG5XDSB.js";
|
|
32
32
|
import {
|
|
33
33
|
createOpenRagBenchAdapter
|
|
34
|
-
} from "./chunk-
|
|
35
|
-
import "./chunk-
|
|
34
|
+
} from "./chunk-SHYIRB7I.js";
|
|
35
|
+
import "./chunk-MQMRLGOG.js";
|
|
36
36
|
import "./chunk-PUIRNYI7.js";
|
|
37
37
|
import "./chunk-C7T7WEK2.js";
|
|
38
38
|
import {
|
|
39
39
|
runRefineLoop
|
|
40
40
|
} from "./chunk-JTHWEDEW.js";
|
|
41
|
-
import "./chunk-
|
|
41
|
+
import "./chunk-Z4TZ76N7.js";
|
|
42
42
|
import {
|
|
43
43
|
createCragAdapter
|
|
44
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-SVR2LKYI.js";
|
|
45
45
|
import {
|
|
46
46
|
FINAL_ANSWER_SENTINEL,
|
|
47
47
|
answerScoreToBenchScore,
|
|
@@ -54,18 +54,18 @@ import {
|
|
|
54
54
|
scoreAnswerArtifact,
|
|
55
55
|
tokenF1
|
|
56
56
|
} from "./chunk-X3BTXCJ4.js";
|
|
57
|
-
import "./chunk-
|
|
58
|
-
import "./chunk-
|
|
59
|
-
import "./chunk-
|
|
57
|
+
import "./chunk-RH5F53JT.js";
|
|
58
|
+
import "./chunk-YSMEKBTD.js";
|
|
59
|
+
import "./chunk-7GRVHU22.js";
|
|
60
60
|
import "./chunk-UPAMRDX4.js";
|
|
61
|
-
import "./chunk-
|
|
62
|
-
import "./chunk-
|
|
63
|
-
import "./chunk-
|
|
64
|
-
import "./chunk-
|
|
61
|
+
import "./chunk-K3BQGZCT.js";
|
|
62
|
+
import "./chunk-PB64GYIG.js";
|
|
63
|
+
import "./chunk-IA2FBTWC.js";
|
|
64
|
+
import "./chunk-KP5KD6EN.js";
|
|
65
65
|
import {
|
|
66
66
|
StagedJudgeError,
|
|
67
67
|
runStagedJudge
|
|
68
|
-
} from "./chunk-
|
|
68
|
+
} from "./chunk-WSKWVEQB.js";
|
|
69
69
|
import "./chunk-53UPUNBZ.js";
|
|
70
70
|
|
|
71
71
|
// src/run-benchmarks.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/agent-bench",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.8",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The unified benchmark suite for agent-runtime agents: 31 adapters (commit0, enterpriseops-gym, ragbench, crag, nomiracl, open-rag-bench, t2-ragbench, tau3-banking, bfcl, finresearchbench, …) behind one resolveAdapter registry, each with a real judge or fail-loud unsupported scorer. Score any profile/skill/prompt change against them. Map: bench/HARNESS.md.",
|
|
6
6
|
"repository": {
|
|
@@ -25,10 +25,11 @@
|
|
|
25
25
|
}
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@tangle-network/agent-eval": "0.
|
|
29
|
-
"@tangle-network/agent-interface": "0.
|
|
30
|
-
"@tangle-network/
|
|
31
|
-
"@tangle-network/
|
|
28
|
+
"@tangle-network/agent-eval": "0.125.0",
|
|
29
|
+
"@tangle-network/agent-interface": "0.32.0",
|
|
30
|
+
"@tangle-network/agent-knowledge": "^4.1.0",
|
|
31
|
+
"@tangle-network/sandbox": "^0.12.0",
|
|
32
|
+
"@tangle-network/agent-runtime": "0.104.0"
|
|
32
33
|
},
|
|
33
34
|
"devDependencies": {
|
|
34
35
|
"@types/node": "^25.9.3",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { execFile } from 'node:child_process'
|
|
2
|
-
import { access, readdir } from 'node:fs/promises'
|
|
2
|
+
import { access, readdir, readFile } from 'node:fs/promises'
|
|
3
3
|
import path from 'node:path'
|
|
4
4
|
import { fileURLToPath } from 'node:url'
|
|
5
5
|
import { promisify } from 'node:util'
|
|
@@ -43,14 +43,36 @@ try {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
const tests = await collectTests(sourceDir)
|
|
46
|
-
|
|
47
|
-
if (relativeTests.length === 0) throw new Error('no package tests found under src/')
|
|
46
|
+
if (tests.length === 0) throw new Error('no package tests found under src/')
|
|
48
47
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
// Two test runtimes coexist under src/: node:test files run under `node --test`;
|
|
49
|
+
// vitest files (the swe-arena suite) crash there (`vitest` APIs need the vitest
|
|
50
|
+
// worker), so partition by the framework each file actually imports.
|
|
51
|
+
const nodeTests = []
|
|
52
|
+
const vitestTests = []
|
|
53
|
+
for (const file of tests) {
|
|
54
|
+
const body = await readFile(file, 'utf8')
|
|
55
|
+
if (/from\s+['"]vitest['"]/.test(body)) vitestTests.push(file)
|
|
56
|
+
else nodeTests.push(file)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (nodeTests.length > 0) {
|
|
60
|
+
await run(
|
|
61
|
+
process.execPath,
|
|
62
|
+
['--test', '--import', 'tsx', ...nodeTests.map((file) => path.relative(benchDir, file))],
|
|
63
|
+
{
|
|
64
|
+
...process.env,
|
|
65
|
+
TSX_TSCONFIG_PATH: 'tsconfig.public.json',
|
|
66
|
+
},
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (vitestTests.length > 0) {
|
|
71
|
+
await run('npx', ['vitest', 'run', ...vitestTests.map((file) => path.relative(benchDir, file))])
|
|
72
|
+
}
|
|
53
73
|
|
|
54
74
|
await run(python, ['-m', 'unittest', 'discover', '-s', 'pier_agents', '-p', '*_test.py'])
|
|
55
75
|
|
|
56
|
-
console.log(
|
|
76
|
+
console.log(
|
|
77
|
+
`package tests passed: ${tests.length}/${tests.length} TypeScript files (${nodeTests.length} node:test + ${vitestTests.length} vitest) + Pier bridge`,
|
|
78
|
+
)
|
|
@@ -120,7 +120,7 @@ try {
|
|
|
120
120
|
)
|
|
121
121
|
await writeFile(
|
|
122
122
|
path.join(consumerDir, 'index.mjs'),
|
|
123
|
-
"import { resolveAdapter, runBenchmarks } from '@tangle-network/agent-bench'\nimport { ADAPTERS } from '@tangle-network/agent-bench/adapters'\nimport { createCragAdapter } from '@tangle-network/agent-bench/benchmarks/crag'\n\nif (typeof resolveAdapter !== 'function' || typeof runBenchmarks !== 'function') throw new Error('root exports are not executable')\nif (typeof ADAPTERS !== 'object' || typeof createCragAdapter !== 'function') throw new Error('subpath exports are not executable')\nif (resolveAdapter('crag').name !== 'crag') throw new Error('compiled adapter registry returned the wrong adapter')\n",
|
|
123
|
+
"import { resolveAdapter, runBenchmarks } from '@tangle-network/agent-bench'\nimport { ADAPTERS } from '@tangle-network/agent-bench/adapters'\nimport { createCragAdapter } from '@tangle-network/agent-bench/benchmarks/crag'\n\nif (typeof resolveAdapter !== 'function' || typeof runBenchmarks !== 'function') throw new Error('root exports are not executable')\nif (typeof ADAPTERS !== 'object' || typeof createCragAdapter !== 'function') throw new Error('subpath exports are not executable')\nif (resolveAdapter('crag').name !== 'crag') throw new Error('compiled adapter registry returned the wrong adapter')\nprocess.env.TOOLLM_FIXTURES = '1'\nconst toolLlmTasks = await resolveAdapter('toollm').loadTasks({ limit: 1 })\nif (toolLlmTasks.length !== 1 || toolLlmTasks[0]?.id !== '1') throw new Error('packed ToolLLM fixture loading failed')\n",
|
|
124
124
|
)
|
|
125
125
|
await writeFile(
|
|
126
126
|
path.join(consumerDir, 'tsconfig.json'),
|
|
@@ -370,6 +370,7 @@ ${proofArm === 'success' ? "(task / 'src/status.txt').write_text('ready\\nowner=
|
|
|
370
370
|
},
|
|
371
371
|
instructionDelivery: { kind: 'argv-append' as const },
|
|
372
372
|
cwd: { workspace: 'task' as const, path: '.' },
|
|
373
|
+
env: { PATH: { kind: 'public' as const, value: '/usr/local/bin:/usr/bin:/bin' } },
|
|
373
374
|
environment: { kind: 'evaluator-task-container' as const },
|
|
374
375
|
workspace: candidateWorkspace,
|
|
375
376
|
isolation: {
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
import { execFile, spawn } from 'node:child_process'
|
|
20
20
|
import { createHash } from 'node:crypto'
|
|
21
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
21
22
|
import {
|
|
22
23
|
cp,
|
|
23
24
|
lstat,
|
|
@@ -42,8 +43,25 @@ import type {
|
|
|
42
43
|
|
|
43
44
|
const execFileAsync = promisify(execFile)
|
|
44
45
|
|
|
45
|
-
/**
|
|
46
|
-
|
|
46
|
+
/** Locate the package by identity because source files and compiled chunks have different depths. */
|
|
47
|
+
function resolveBenchRoot(moduleUrl: string): string {
|
|
48
|
+
let current = dirname(fileURLToPath(moduleUrl))
|
|
49
|
+
while (true) {
|
|
50
|
+
const manifestPath = join(current, 'package.json')
|
|
51
|
+
if (existsSync(manifestPath)) {
|
|
52
|
+
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8')) as { name?: unknown }
|
|
53
|
+
if (manifest.name === '@tangle-network/agent-bench') return current
|
|
54
|
+
}
|
|
55
|
+
const parent = dirname(current)
|
|
56
|
+
if (parent === current) {
|
|
57
|
+
throw new Error(`Unable to locate @tangle-network/agent-bench from ${moduleUrl}`)
|
|
58
|
+
}
|
|
59
|
+
current = parent
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Package root for agent-bench, so `.venv`, scripts, and fixtures resolve. */
|
|
64
|
+
export const benchRoot = resolveBenchRoot(import.meta.url)
|
|
47
65
|
|
|
48
66
|
/** Resolve the shared interpreter without requiring an installed package to contain a venv. */
|
|
49
67
|
export function resolveBenchPython(
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import assert from 'node:assert/strict'
|
|
2
|
+
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
|
3
|
+
import { tmpdir } from 'node:os'
|
|
4
|
+
import { join } from 'node:path'
|
|
5
|
+
import { describe, it } from 'node:test'
|
|
6
|
+
import { runPythonProgram } from './humaneval'
|
|
7
|
+
|
|
8
|
+
describe('HumanEval Python isolation', () => {
|
|
9
|
+
it('runs the exact program through the resource-capped networkless container', async () => {
|
|
10
|
+
const dir = mkdtempSync(join(tmpdir(), 'humaneval-docker-test-'))
|
|
11
|
+
const fakeDocker = join(dir, 'docker')
|
|
12
|
+
const capture = join(dir, 'capture.jsonl')
|
|
13
|
+
writeFileSync(
|
|
14
|
+
fakeDocker,
|
|
15
|
+
`#!/usr/bin/env node
|
|
16
|
+
import * as fs from 'node:fs'
|
|
17
|
+
import * as path from 'node:path'
|
|
18
|
+
const args = process.argv.slice(2)
|
|
19
|
+
if (args[0] === 'rm') process.exit(0)
|
|
20
|
+
if (process.env.FAKE_DOCKER_MISSING === '1') {
|
|
21
|
+
process.stderr.write('docker: Error response from daemon: No such image: python:3.12-slim')
|
|
22
|
+
process.exit(125)
|
|
23
|
+
}
|
|
24
|
+
const mount = args[args.indexOf('-v') + 1]
|
|
25
|
+
const hostDir = mount.slice(0, -':/w:ro'.length)
|
|
26
|
+
fs.appendFileSync(process.env.FAKE_DOCKER_CAPTURE, JSON.stringify({
|
|
27
|
+
args,
|
|
28
|
+
program: fs.readFileSync(path.join(hostDir, 'p.py'), 'utf8'),
|
|
29
|
+
}) + '\\n')
|
|
30
|
+
if (process.env.FAKE_DOCKER_NO_START === '1') {
|
|
31
|
+
process.stderr.write('docker: Error response from daemon: unable to start container process')
|
|
32
|
+
process.exit(125)
|
|
33
|
+
}
|
|
34
|
+
process.stdout.write(args[args.length - 2] + '\\n')
|
|
35
|
+
if (process.env.FAKE_DOCKER_CANDIDATE === 'daemon-text') {
|
|
36
|
+
process.stderr.write('Cannot connect to the Docker daemon')
|
|
37
|
+
process.exit(1)
|
|
38
|
+
}
|
|
39
|
+
if (process.env.FAKE_DOCKER_CANDIDATE === 'exit-125') process.exit(125)
|
|
40
|
+
process.stdout.write('CONTAINER_OK\\n')
|
|
41
|
+
`,
|
|
42
|
+
{ mode: 0o755 },
|
|
43
|
+
)
|
|
44
|
+
const originalPath = process.env.PATH
|
|
45
|
+
const originalCapture = process.env.FAKE_DOCKER_CAPTURE
|
|
46
|
+
const originalMissing = process.env.FAKE_DOCKER_MISSING
|
|
47
|
+
const originalCandidate = process.env.FAKE_DOCKER_CANDIDATE
|
|
48
|
+
const originalNoStart = process.env.FAKE_DOCKER_NO_START
|
|
49
|
+
process.env.PATH = `${dir}:${originalPath ?? ''}`
|
|
50
|
+
process.env.FAKE_DOCKER_CAPTURE = capture
|
|
51
|
+
try {
|
|
52
|
+
const program = 'print("exact bytes")\n'
|
|
53
|
+
const result = await runPythonProgram(program, 2_000)
|
|
54
|
+
assert.equal(result.exitCode, 0)
|
|
55
|
+
assert.match(result.stdout, /CONTAINER_OK/)
|
|
56
|
+
|
|
57
|
+
const firstCall = JSON.parse(readFileSync(capture, 'utf8').trim().split('\n')[0]!) as {
|
|
58
|
+
args: string[]
|
|
59
|
+
program: string
|
|
60
|
+
}
|
|
61
|
+
assert.equal(firstCall.program, program)
|
|
62
|
+
assert.deepEqual(firstCall.args.slice(0, 2), ['run', '--rm'])
|
|
63
|
+
assert.ok(firstCall.args.includes('--network=none'))
|
|
64
|
+
assert.ok(firstCall.args.includes('--cpus=1'))
|
|
65
|
+
assert.ok(firstCall.args.includes('--memory=512m'))
|
|
66
|
+
assert.ok(firstCall.args.includes('--pids-limit=64'))
|
|
67
|
+
assert.ok(firstCall.args.includes('--cap-drop=ALL'))
|
|
68
|
+
assert.ok(firstCall.args.includes('--security-opt=no-new-privileges'))
|
|
69
|
+
assert.ok(firstCall.args.includes('--pull=never'))
|
|
70
|
+
assert.ok(firstCall.args.includes('--read-only'))
|
|
71
|
+
assert.equal(
|
|
72
|
+
firstCall.args[firstCall.args.indexOf('--tmpfs') + 1],
|
|
73
|
+
'/tmp:rw,nosuid,nodev,noexec,size=64m,mode=1777',
|
|
74
|
+
)
|
|
75
|
+
assert.equal(firstCall.args[firstCall.args.indexOf('--user') + 1], '65534:65534')
|
|
76
|
+
assert.match(firstCall.args[firstCall.args.indexOf('-v') + 1] ?? '', /:\/w:ro$/)
|
|
77
|
+
const command = firstCall.args.slice(firstCall.args.indexOf('python:3.12-slim'))
|
|
78
|
+
assert.equal(command[0], 'python:3.12-slim')
|
|
79
|
+
assert.equal(command[1], 'sh')
|
|
80
|
+
assert.equal(command[2], '-c')
|
|
81
|
+
assert.match(command[3] ?? '', /command -v timeout/)
|
|
82
|
+
assert.equal(command[4], 'agent-runtime-checker')
|
|
83
|
+
assert.match(command[5] ?? '', /^__AGENT_RUNTIME_CANDIDATE_STARTED_hev-/)
|
|
84
|
+
assert.equal(command[6], '2s')
|
|
85
|
+
|
|
86
|
+
process.env.FAKE_DOCKER_MISSING = '1'
|
|
87
|
+
await assert.rejects(
|
|
88
|
+
runPythonProgram('print("must not score")\n', 2_000),
|
|
89
|
+
/docker image python:3\.12-slim unavailable/,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
delete process.env.FAKE_DOCKER_MISSING
|
|
93
|
+
process.env.FAKE_DOCKER_CANDIDATE = 'daemon-text'
|
|
94
|
+
const daemonText = await runPythonProgram('raise SystemExit(1)\n', 2_000)
|
|
95
|
+
assert.equal(daemonText.exitCode, 1)
|
|
96
|
+
assert.match(daemonText.stderr, /Cannot connect to the Docker daemon/)
|
|
97
|
+
|
|
98
|
+
process.env.FAKE_DOCKER_CANDIDATE = 'exit-125'
|
|
99
|
+
const exit125 = await runPythonProgram('raise SystemExit(125)\n', 2_000)
|
|
100
|
+
assert.equal(exit125.exitCode, 125)
|
|
101
|
+
|
|
102
|
+
delete process.env.FAKE_DOCKER_CANDIDATE
|
|
103
|
+
process.env.FAKE_DOCKER_NO_START = '1'
|
|
104
|
+
await assert.rejects(
|
|
105
|
+
runPythonProgram('print("never started")\n', 2_000),
|
|
106
|
+
/did not start the candidate/,
|
|
107
|
+
)
|
|
108
|
+
} finally {
|
|
109
|
+
if (originalPath === undefined) delete process.env.PATH
|
|
110
|
+
else process.env.PATH = originalPath
|
|
111
|
+
if (originalCapture === undefined) delete process.env.FAKE_DOCKER_CAPTURE
|
|
112
|
+
else process.env.FAKE_DOCKER_CAPTURE = originalCapture
|
|
113
|
+
if (originalMissing === undefined) delete process.env.FAKE_DOCKER_MISSING
|
|
114
|
+
else process.env.FAKE_DOCKER_MISSING = originalMissing
|
|
115
|
+
if (originalCandidate === undefined) delete process.env.FAKE_DOCKER_CANDIDATE
|
|
116
|
+
else process.env.FAKE_DOCKER_CANDIDATE = originalCandidate
|
|
117
|
+
if (originalNoStart === undefined) delete process.env.FAKE_DOCKER_NO_START
|
|
118
|
+
else process.env.FAKE_DOCKER_NO_START = originalNoStart
|
|
119
|
+
rmSync(dir, { recursive: true, force: true })
|
|
120
|
+
}
|
|
121
|
+
})
|
|
122
|
+
})
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import { execFile } from 'node:child_process'
|
|
20
|
-
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
|
20
|
+
import { chmodSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
|
21
21
|
import { tmpdir } from 'node:os'
|
|
22
22
|
import { join } from 'node:path'
|
|
23
23
|
import { gunzipSync } from 'node:zlib'
|
|
@@ -111,19 +111,36 @@ export interface CheckResult {
|
|
|
111
111
|
detail?: string
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
export interface PythonProgramResult {
|
|
115
|
+
exitCode: number
|
|
116
|
+
stdout: string
|
|
117
|
+
stderr: string
|
|
118
|
+
}
|
|
119
|
+
|
|
114
120
|
/** Run one candidate's deployable test program in an isolated container:
|
|
115
|
-
*
|
|
121
|
+
* read-only root, bounded writable `/tmp`, no network, one CPU, and 512 MiB.
|
|
116
122
|
* Exit 0 → pass. A docker invocation error (binary missing, daemon down, image
|
|
117
|
-
*
|
|
123
|
+
* unavailable) is NOT a test failure — it throws so the harness fails loud rather
|
|
118
124
|
* than scoring every candidate 0 from a broken checker. */
|
|
119
125
|
let dockerRunSeq = 0
|
|
120
126
|
|
|
121
|
-
|
|
127
|
+
/** Run arbitrary Python in the same isolated container used by the HumanEval
|
|
128
|
+
* checker. This is also the execution-tool primitive for experiments that let
|
|
129
|
+
* a model test its own snippets; model-written code must never run on the host. */
|
|
130
|
+
export function runPythonProgram(
|
|
131
|
+
program: string,
|
|
132
|
+
timeoutMs = dockerTimeoutMs,
|
|
133
|
+
): Promise<PythonProgramResult> {
|
|
122
134
|
const dir = mkdtempSync(join(tmpdir(), 'hev-'))
|
|
123
|
-
|
|
135
|
+
// Rootless Docker maps container root to a subordinate host uid that cannot
|
|
136
|
+
// traverse mkdtemp's default 0700 directory. The directory contains only the
|
|
137
|
+
// disposable candidate script and is mounted read-only into the container.
|
|
138
|
+
chmodSync(dir, 0o755)
|
|
139
|
+
writeFileSync(join(dir, 'p.py'), program, { mode: 0o644 })
|
|
124
140
|
// Unique container name so we can force-reap it regardless of the docker client's state.
|
|
125
141
|
const name = `hev-${process.pid}-${dockerRunSeq++}`
|
|
126
|
-
|
|
142
|
+
const startMarker = `__AGENT_RUNTIME_CANDIDATE_STARTED_${name}__`
|
|
143
|
+
return new Promise<PythonProgramResult>((resolvePromise, reject) => {
|
|
127
144
|
let settled = false
|
|
128
145
|
const cleanup = () => {
|
|
129
146
|
rmSync(dir, { recursive: true, force: true })
|
|
@@ -132,7 +149,7 @@ export function runChecker(task: HumanEvalTask, candidate: string): Promise<Chec
|
|
|
132
149
|
// the name is unique, so no reuse race).
|
|
133
150
|
execFile('docker', ['rm', '-f', name], () => {})
|
|
134
151
|
}
|
|
135
|
-
const finish = (res:
|
|
152
|
+
const finish = (res: PythonProgramResult) => {
|
|
136
153
|
if (settled) return
|
|
137
154
|
settled = true
|
|
138
155
|
clearTimeout(backstop)
|
|
@@ -146,56 +163,106 @@ export function runChecker(task: HumanEvalTask, candidate: string): Promise<Chec
|
|
|
146
163
|
cleanup()
|
|
147
164
|
reject(e)
|
|
148
165
|
}
|
|
149
|
-
//
|
|
150
|
-
//
|
|
151
|
-
//
|
|
152
|
-
const
|
|
166
|
+
// Candidate timeouts are enforced inside the container. If the outer Docker
|
|
167
|
+
// client or daemon misses this larger deadline, that is infrastructure failure,
|
|
168
|
+
// not a wrong answer.
|
|
169
|
+
const outerTimeoutMs = timeoutMs + 3_000
|
|
170
|
+
const backstop = setTimeout(
|
|
171
|
+
() => fail(new Error(`docker checker did not return within ${outerTimeoutMs + 3_000}ms`)),
|
|
172
|
+
outerTimeoutMs + 3_000,
|
|
173
|
+
)
|
|
174
|
+
const inContainerSeconds = Math.max(1, Math.ceil(timeoutMs / 1_000))
|
|
153
175
|
execFile(
|
|
154
176
|
'docker',
|
|
155
177
|
[
|
|
156
178
|
'run',
|
|
157
179
|
'--rm',
|
|
180
|
+
'--pull=never',
|
|
158
181
|
'--name',
|
|
159
182
|
name,
|
|
160
183
|
'--network=none',
|
|
161
184
|
'--cpus=1',
|
|
162
185
|
'--memory=512m',
|
|
186
|
+
'--pids-limit=64',
|
|
187
|
+
'--cap-drop=ALL',
|
|
188
|
+
'--security-opt=no-new-privileges',
|
|
189
|
+
'--read-only',
|
|
190
|
+
'--tmpfs',
|
|
191
|
+
'/tmp:rw,nosuid,nodev,noexec,size=64m,mode=1777',
|
|
192
|
+
'--user',
|
|
193
|
+
'65534:65534',
|
|
163
194
|
'-v',
|
|
164
195
|
`${dir}:/w:ro`,
|
|
165
196
|
'-w',
|
|
166
197
|
'/w',
|
|
167
198
|
dockerImage,
|
|
168
|
-
'
|
|
169
|
-
'
|
|
199
|
+
'sh',
|
|
200
|
+
'-c',
|
|
201
|
+
'command -v timeout >/dev/null 2>&1 && command -v python >/dev/null 2>&1 && [ -r /w/p.py ] || exit 126; printf "%s\\n" "$1"; exec timeout -s KILL "$2" python /w/p.py',
|
|
202
|
+
'agent-runtime-checker',
|
|
203
|
+
startMarker,
|
|
204
|
+
`${inContainerSeconds}s`,
|
|
170
205
|
],
|
|
171
|
-
{ timeout:
|
|
172
|
-
(err,
|
|
206
|
+
{ timeout: outerTimeoutMs, killSignal: 'SIGKILL', maxBuffer: 4 * 1024 * 1024 },
|
|
207
|
+
(err, stdout, stderr) => {
|
|
208
|
+
const markerLine = `${startMarker}\n`
|
|
209
|
+
const candidateStarted = stdout?.startsWith(markerLine) === true
|
|
210
|
+
const candidateStdout = candidateStarted ? stdout.slice(markerLine.length) : (stdout ?? '')
|
|
173
211
|
if (err) {
|
|
174
|
-
const e = err as
|
|
212
|
+
const e = err as Error & { killed?: boolean; code?: number | string }
|
|
175
213
|
if (e.code === 'ENOENT') {
|
|
176
214
|
fail(new Error('docker binary not found on PATH — cannot run the deployable checker'))
|
|
177
215
|
return
|
|
178
216
|
}
|
|
179
|
-
if (
|
|
180
|
-
fail(new Error(`docker
|
|
217
|
+
if (e.killed) {
|
|
218
|
+
fail(new Error(`docker checker exceeded its ${outerTimeoutMs}ms outer timeout`))
|
|
181
219
|
return
|
|
182
220
|
}
|
|
183
|
-
if (
|
|
184
|
-
|
|
221
|
+
if (!candidateStarted) {
|
|
222
|
+
if (
|
|
223
|
+
/cannot connect to the docker daemon|is the docker daemon running|permission denied while trying to connect/i.test(
|
|
224
|
+
stderr,
|
|
225
|
+
)
|
|
226
|
+
) {
|
|
227
|
+
fail(new Error(`docker daemon unreachable: ${stderr.slice(0, 200)}`))
|
|
228
|
+
return
|
|
229
|
+
}
|
|
230
|
+
if (/unable to find image|no such image|pull access denied|manifest unknown/i.test(stderr)) {
|
|
231
|
+
fail(new Error(`docker image ${dockerImage} unavailable: ${stderr.slice(0, 200)}`))
|
|
232
|
+
return
|
|
233
|
+
}
|
|
234
|
+
fail(
|
|
235
|
+
new Error(
|
|
236
|
+
`docker checker did not start the candidate: ${stderr.slice(0, 200) || e.message}`,
|
|
237
|
+
),
|
|
238
|
+
)
|
|
185
239
|
return
|
|
186
240
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
241
|
+
const exitCode = typeof e.code === 'number' ? e.code : 1
|
|
242
|
+
finish({
|
|
243
|
+
exitCode,
|
|
244
|
+
stdout: candidateStdout,
|
|
245
|
+
stderr: stderr ?? '',
|
|
246
|
+
})
|
|
191
247
|
return
|
|
192
248
|
}
|
|
193
|
-
|
|
249
|
+
if (!candidateStarted) {
|
|
250
|
+
fail(new Error('docker checker exited without starting the candidate'))
|
|
251
|
+
return
|
|
252
|
+
}
|
|
253
|
+
finish({ exitCode: 0, stdout: candidateStdout, stderr: stderr ?? '' })
|
|
194
254
|
},
|
|
195
255
|
)
|
|
196
256
|
})
|
|
197
257
|
}
|
|
198
258
|
|
|
259
|
+
export async function runChecker(task: HumanEvalTask, candidate: string): Promise<CheckResult> {
|
|
260
|
+
const result = await runPythonProgram(buildProgram(task, candidate))
|
|
261
|
+
return result.exitCode === 0
|
|
262
|
+
? { pass: 1 }
|
|
263
|
+
: { pass: 0, detail: result.stderr.slice(-600) || 'timed out (no output)' }
|
|
264
|
+
}
|
|
265
|
+
|
|
199
266
|
/** A HumanEval task carries its checker inputs in metadata so the deterministic
|
|
200
267
|
* judge can rebuild the deployable program from a `BenchTask` alone. */
|
|
201
268
|
interface HumanEvalMeta extends Record<string, unknown> {
|
|
@@ -229,14 +296,20 @@ export function createHumanEvalAdapter(): BenchmarkAdapter {
|
|
|
229
296
|
return {
|
|
230
297
|
name: 'humaneval',
|
|
231
298
|
async preflight() {
|
|
232
|
-
//
|
|
233
|
-
//
|
|
299
|
+
// Prove the daemon and exact local image before any model call. Scoring uses
|
|
300
|
+
// --pull=never so a registry or cold pull cannot become a candidate outcome.
|
|
234
301
|
await new Promise<void>((resolve, reject) => {
|
|
235
302
|
execFile('docker', ['version', '--format', '{{.Server.Version}}'], (err) => {
|
|
236
303
|
if (err) reject(new Error('HumanEval judge needs a running Docker daemon (python:3.12-slim, --network=none)'))
|
|
237
304
|
else resolve()
|
|
238
305
|
})
|
|
239
306
|
})
|
|
307
|
+
await new Promise<void>((resolve, reject) => {
|
|
308
|
+
execFile('docker', ['image', 'inspect', dockerImage], (err) => {
|
|
309
|
+
if (err) reject(new Error(`HumanEval judge needs the cached Docker image ${dockerImage}`))
|
|
310
|
+
else resolve()
|
|
311
|
+
})
|
|
312
|
+
})
|
|
240
313
|
},
|
|
241
314
|
async loadTasks(opts?: LoadOptions) {
|
|
242
315
|
const offset = Number(process.env.OFFSET ?? 0)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DAVID mechanism attribution — decompose the cheap-model harness's held-out
|
|
3
|
+
* accuracy into what SAMPLING buys vs what VERIFICATION-SELECTION buys, so a
|
|
4
|
+
* David-Goliath win is credited to the right lever (not just best-of-N luck).
|
|
5
|
+
*
|
|
6
|
+
* For each task, generate N candidate solutions + the model's own tests, then
|
|
7
|
+
* report four numbers on the HIDDEN test:
|
|
8
|
+
* pass@1 — first candidate (no harness).
|
|
9
|
+
* mean-cand — expected accuracy of a RANDOM candidate (sampling floor).
|
|
10
|
+
* oracle@N — a correct candidate exists among the N (ceiling of selection).
|
|
11
|
+
* verify-select — the candidate the self-tests picked (the actual David).
|
|
12
|
+
* verify-select − mean-cand = what VERIFICATION adds over blind sampling;
|
|
13
|
+
* oracle@N − verify-select = the selection gap left on the table.
|
|
14
|
+
*
|
|
15
|
+
* Run from cwd=bench: env DAVID=groq/llama-3.1-8b-instant N=8 T=5 NTASKS=60 \
|
|
16
|
+
* node_modules/.bin/tsx src/david-attribution.mts
|
|
17
|
+
*/
|
|
18
|
+
import { execFile } from 'node:child_process'
|
|
19
|
+
import { mkdtempSync, writeFileSync, rmSync } from 'node:fs'
|
|
20
|
+
import { tmpdir } from 'node:os'
|
|
21
|
+
import { join } from 'node:path'
|
|
22
|
+
import { loadHumanEval, extractCode, type HumanEvalTask } from './benchmarks/humaneval'
|
|
23
|
+
|
|
24
|
+
const KEY = process.env.TANGLE_API_KEY!
|
|
25
|
+
const ROUTER = process.env.ROUTER_BASE ?? 'https://router.tangle.tools/v1'
|
|
26
|
+
const DAVID = process.env.DAVID ?? 'groq/llama-3.1-8b-instant'
|
|
27
|
+
const N = Number(process.env.N ?? 8)
|
|
28
|
+
const T = Number(process.env.T ?? 5)
|
|
29
|
+
const NTASKS = Number(process.env.NTASKS ?? 60)
|
|
30
|
+
const CONC = Number(process.env.CONCURRENCY ?? 6)
|
|
31
|
+
|
|
32
|
+
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms))
|
|
33
|
+
async function chat(messages: { role: string; content: string }[], temp: number): Promise<string> {
|
|
34
|
+
for (let a = 0; ; a++) {
|
|
35
|
+
try {
|
|
36
|
+
const r = await fetch(`${ROUTER}/chat/completions`, { method: 'POST', headers: { 'content-type': 'application/json', authorization: `Bearer ${KEY}` }, body: JSON.stringify({ model: DAVID, messages, temperature: temp, max_tokens: 1000 }), signal: AbortSignal.timeout(60_000) })
|
|
37
|
+
if ([408, 429, 500, 502, 503, 504, 520, 522, 524].includes(r.status)) { if (a >= 5) return ''; await sleep(700 * 2 ** a); continue }
|
|
38
|
+
if (!r.ok) return ''
|
|
39
|
+
return (((await r.json()) as { choices?: { message?: { content?: string } }[] }).choices?.[0]?.message?.content) ?? ''
|
|
40
|
+
} catch { if (a >= 5) return ''; await sleep(700 * 2 ** a) }
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const exec = (f: string, a: string[], o: object) => new Promise<number>((res) => execFile(f, a, { ...o, maxBuffer: 8e6 }, (e) => res((e as { code?: number } | null)?.code ?? (e ? 1 : 0))))
|
|
44
|
+
async function runPy(p: string): Promise<boolean> { const d = mkdtempSync(join(tmpdir(), 'da-')); try { writeFileSync(join(d, 'p.py'), p); return (await exec('python3', [join(d, 'p.py')], { cwd: d, timeout: 6000 })) === 0 } finally { rmSync(d, { recursive: true, force: true }) } }
|
|
45
|
+
const SOLVE = 'Expert Python. Output the COMPLETE function in one ```python block, no prose, no tests.'
|
|
46
|
+
const genSol = async (t: HumanEvalTask, temp: number) => extractCode(await chat([{ role: 'system', content: SOLVE }, { role: 'user', content: `Complete:\n\n\`\`\`python\n${t.prompt}\`\`\`` }], temp))
|
|
47
|
+
async function genTests(t: HumanEvalTask): Promise<string[]> {
|
|
48
|
+
const b = extractCode(await chat([{ role: 'system', content: 'Write Python assert unit tests. Output ONLY a ```python block of `assert <entry>(...) == ...` lines. No function, no prose.' }, { role: 'user', content: `entry: ${t.entryPoint}\n\n\`\`\`python\n${t.prompt}\`\`\`` }], 0.4))
|
|
49
|
+
return b.split('\n').map((l) => l.trim()).filter((l) => l.startsWith('assert ') && l.includes(t.entryPoint)).slice(0, T + 3)
|
|
50
|
+
}
|
|
51
|
+
const judge = async (t: HumanEvalTask, code: string) => code.trim() ? runPy(`${code}\n\n${t.test}\n\ncheck(${t.entryPoint})\n`) : false
|
|
52
|
+
async function scoreTests(code: string, tests: string[]): Promise<number> { if (!code.trim() || !tests.length) return 0; let p = 0; for (const a of tests) if (await runPy(`${code}\n\n${a}\n`)) p++; return p }
|
|
53
|
+
async function pool<T2, R>(xs: T2[], n: number, fn: (x: T2) => Promise<R>): Promise<R[]> { const o = new Array<R>(xs.length); let i = 0; await Promise.all(Array.from({ length: n }, async () => { while (i < xs.length) { const k = i++; o[k] = await fn(xs[k]!) } })); return o }
|
|
54
|
+
|
|
55
|
+
async function main(): Promise<void> {
|
|
56
|
+
const tasks = await loadHumanEval(NTASKS, 0)
|
|
57
|
+
console.error(`=== ATTRIBUTION · ${DAVID} · N=${N} sols + ${T} tests · n=${tasks.length} ===`)
|
|
58
|
+
let done = 0
|
|
59
|
+
const rows = await pool(tasks, CONC, async (t) => {
|
|
60
|
+
const cands = (await Promise.all(Array.from({ length: N }, () => genSol(t, 0.7)))).filter((c) => c.trim())
|
|
61
|
+
if (!cands.length) return { p1: 0, mean: 0, oracle: 0, sel: 0 }
|
|
62
|
+
const tests = await genTests(t)
|
|
63
|
+
const hidden = await Promise.all(cands.map((c) => judge(t, c))) // hidden-test pass per candidate (for attribution only)
|
|
64
|
+
const selScores = tests.length ? await Promise.all(cands.map((c) => scoreTests(c, tests))) : cands.map(() => 0)
|
|
65
|
+
let bi = 0; for (let i = 1; i < cands.length; i++) if (selScores[i]! > selScores[bi]! || (selScores[i]! === selScores[bi]! && cands[i]!.length > cands[bi]!.length)) bi = i
|
|
66
|
+
if (++done % 15 === 0) console.error(` ${done}/${tasks.length}`)
|
|
67
|
+
return { p1: hidden[0] ? 1 : 0, mean: hidden.filter(Boolean).length / cands.length, oracle: hidden.some(Boolean) ? 1 : 0, sel: hidden[bi] ? 1 : 0 }
|
|
68
|
+
})
|
|
69
|
+
const n = rows.length, avg = (f: (r: typeof rows[number]) => number) => (rows.reduce((s, r) => s + f(r), 0) / n) * 100
|
|
70
|
+
console.log('\n=== ATTRIBUTION (held-out) ===')
|
|
71
|
+
console.log(` pass@1 (no harness) : ${avg((r) => r.p1).toFixed(1)}%`)
|
|
72
|
+
console.log(` mean random candidate : ${avg((r) => r.mean).toFixed(1)}% (sampling floor)`)
|
|
73
|
+
console.log(` verify-select (DAVID) : ${avg((r) => r.sel).toFixed(1)}%`)
|
|
74
|
+
console.log(` oracle@N (a correct exists): ${avg((r) => r.oracle).toFixed(1)}% (selection ceiling)`)
|
|
75
|
+
console.log(` --> verification adds over random sampling: +${(avg((r) => r.sel) - avg((r) => r.mean)).toFixed(1)}pp`)
|
|
76
|
+
console.log(` --> selection gap still on table (oracle-select): ${(avg((r) => r.oracle) - avg((r) => r.sel)).toFixed(1)}pp`)
|
|
77
|
+
}
|
|
78
|
+
main().catch((e) => { console.error('MAIN:', e instanceof Error ? e.stack : e); process.exit(1) })
|