@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,662 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MBPP STRUCTURAL lever — the transfer test for the HumanEval result in
|
|
3
|
+
* `hev-structural.mts`: best-of-k selection + self-repair grounded ONLY on
|
|
4
|
+
* VISIBLE checks, graded on HIDDEN tests the harness never shows the model.
|
|
5
|
+
*
|
|
6
|
+
* MBPP (sanitized, 427 tasks) has no docstring examples; the standard protocol
|
|
7
|
+
* shows the model test_list[0] (it pins the function name/signature). So:
|
|
8
|
+
* VISIBLE = test_list[0] (+ TESTGEN model-written asserts, generated from the
|
|
9
|
+
* description BEFORE any candidate exists)
|
|
10
|
+
* HIDDEN = test_list[1:] (with test_imports) — the grading suite
|
|
11
|
+
* A task with <2 asserts cannot split visible/hidden and is dropped at load.
|
|
12
|
+
*
|
|
13
|
+
* Architecture is hev-structural.mts verbatim (kept self-contained on purpose —
|
|
14
|
+
* the HumanEval rig is frozen post-verification): Phase A (harness: sample →
|
|
15
|
+
* visible-check select → visible-grounded repair, all decisions locked) then
|
|
16
|
+
* Phase B (hidden grading); per-call NONCE sentinels on both judges; global
|
|
17
|
+
* docker semaphore; in-container timeout + exit reaper; incremental OUT jsonl;
|
|
18
|
+
* per-task error rows with >15% abort; paired bootstrap + exact sign test.
|
|
19
|
+
*
|
|
20
|
+
* CALIBRATE=1: reference solutions through both judges (hidden self-check must
|
|
21
|
+
* be ~100%; failures listed — some MBPP references are known-defective).
|
|
22
|
+
*
|
|
23
|
+
* TANGLE_API_KEY=… WORKER_MODEL=meta-llama/Meta-Llama-3-8B-Instruct-Lite \
|
|
24
|
+
* ROUTER_BASE=https://api.together.xyz/v1 MBPP_JSON=/abs/sanitized-mbpp.json \
|
|
25
|
+
* N=427 K=5 REPAIRS=2 TESTGEN=6 TEMPERATURE=0.8 OUT=/abs/rows.jsonl \
|
|
26
|
+
* tsx src/mbpp-structural.mts
|
|
27
|
+
*/
|
|
28
|
+
import { execFile, execFileSync } from 'node:child_process'
|
|
29
|
+
import { randomBytes } from 'node:crypto'
|
|
30
|
+
import { appendFileSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
|
31
|
+
import { tmpdir } from 'node:os'
|
|
32
|
+
import { join } from 'node:path'
|
|
33
|
+
import { extractCode } from './benchmarks/humaneval'
|
|
34
|
+
import { type PairedLift, pairedLift, pool } from './stats.mts'
|
|
35
|
+
|
|
36
|
+
const dockerImage = 'python:3.12-slim'
|
|
37
|
+
const dockerTimeoutMs = Number(process.env.DOCKER_TIMEOUT_MS ?? 20000)
|
|
38
|
+
|
|
39
|
+
function must(name: string): string {
|
|
40
|
+
const v = process.env[name]
|
|
41
|
+
if (!v) throw new Error(`env ${name} is required`)
|
|
42
|
+
return v
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ---------- dataset ----------
|
|
46
|
+
|
|
47
|
+
export interface MbppTask {
|
|
48
|
+
taskId: number
|
|
49
|
+
description: string
|
|
50
|
+
/** test_list[0] — shown to the model; pins the function name/signature */
|
|
51
|
+
shownAssert: string
|
|
52
|
+
/** test_list[1:] — the hidden grading suite */
|
|
53
|
+
hiddenAsserts: string[]
|
|
54
|
+
testImports: string[]
|
|
55
|
+
entryPoint: string
|
|
56
|
+
/** reference solution — judge self-check only, never shown to the model */
|
|
57
|
+
referenceCode: string
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
interface RawMbpp {
|
|
61
|
+
task_id: number
|
|
62
|
+
prompt: string
|
|
63
|
+
code: string
|
|
64
|
+
test_imports?: string[]
|
|
65
|
+
test_list: string[]
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Load sanitized MBPP, sorted by task_id. Drops (and counts) tasks that cannot
|
|
69
|
+
* split visible/hidden (<2 asserts) or whose entry point cannot be resolved.
|
|
70
|
+
* Entry point = the FIRST called name in test_list[0] that also has a
|
|
71
|
+
* `def <name>` in the reference code — `assert set(f(...)) == …` must resolve
|
|
72
|
+
* to f, not set. */
|
|
73
|
+
export function loadMbpp(limit: number, offset = 0): { tasks: MbppTask[]; droppedShort: number[]; droppedEntry: number[] } {
|
|
74
|
+
const path = process.env.MBPP_JSON
|
|
75
|
+
if (!path) throw new Error('env MBPP_JSON is required (path to sanitized-mbpp.json)')
|
|
76
|
+
const raw = JSON.parse(readFileSync(path, 'utf8')) as RawMbpp[]
|
|
77
|
+
raw.sort((a, b) => a.task_id - b.task_id)
|
|
78
|
+
const tasks: MbppTask[] = []
|
|
79
|
+
const droppedShort: number[] = []
|
|
80
|
+
const droppedEntry: number[] = []
|
|
81
|
+
for (const d of raw) {
|
|
82
|
+
if (!d.test_list || d.test_list.length < 2) {
|
|
83
|
+
droppedShort.push(d.task_id)
|
|
84
|
+
continue
|
|
85
|
+
}
|
|
86
|
+
const shown = d.test_list[0] as string
|
|
87
|
+
const calls = [...shown.matchAll(/(\w+)\s*\(/g)].map((m) => m[1] as string)
|
|
88
|
+
const entry = calls.find((c) => new RegExp(`def\\s+${c}\\s*\\(`).test(d.code))
|
|
89
|
+
if (!entry) {
|
|
90
|
+
droppedEntry.push(d.task_id)
|
|
91
|
+
continue
|
|
92
|
+
}
|
|
93
|
+
tasks.push({
|
|
94
|
+
taskId: d.task_id,
|
|
95
|
+
description: d.prompt,
|
|
96
|
+
shownAssert: shown,
|
|
97
|
+
hiddenAsserts: d.test_list.slice(1),
|
|
98
|
+
testImports: d.test_imports ?? [],
|
|
99
|
+
entryPoint: entry,
|
|
100
|
+
referenceCode: d.code,
|
|
101
|
+
})
|
|
102
|
+
}
|
|
103
|
+
if (offset >= tasks.length) throw new Error(`OFFSET ${offset} >= usable dataset size ${tasks.length}`)
|
|
104
|
+
return { tasks: tasks.slice(offset, offset + limit), droppedShort, droppedEntry }
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const solveInstruction =
|
|
108
|
+
'Write a Python function for the following task. Output the COMPLETE function definition (plus any imports it needs) inside a single ```python code block. Do not write tests or example calls.'
|
|
109
|
+
|
|
110
|
+
function basePrompt(task: MbppTask): string {
|
|
111
|
+
return `${solveInstruction}\n\nTask: ${task.description}\nYour function must satisfy this example test:\n\`\`\`python\n${task.shownAssert}\n\`\`\``
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ---------- docker semaphore + jailed runner (mirrors hev-structural) ----------
|
|
115
|
+
|
|
116
|
+
let dockerSlots = 6
|
|
117
|
+
let dockerInFlight = 0
|
|
118
|
+
const dockerWaiters: Array<() => void> = []
|
|
119
|
+
async function withDockerSlot<T>(fn: () => Promise<T>): Promise<T> {
|
|
120
|
+
if (dockerInFlight >= dockerSlots) await new Promise<void>((r) => dockerWaiters.push(r))
|
|
121
|
+
dockerInFlight += 1
|
|
122
|
+
try {
|
|
123
|
+
return await fn()
|
|
124
|
+
} finally {
|
|
125
|
+
dockerInFlight -= 1
|
|
126
|
+
dockerWaiters.shift()?.()
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const containerPrefix = `mbpps-${process.pid}`
|
|
131
|
+
let containerSeq = 0
|
|
132
|
+
|
|
133
|
+
function reapContainers(): void {
|
|
134
|
+
try {
|
|
135
|
+
const ids = execFileSync('docker', ['ps', '-aq', '--filter', `name=${containerPrefix}`], { timeout: 10000 }).toString().trim()
|
|
136
|
+
if (ids) execFileSync('docker', ['rm', '-f', ...ids.split('\n')], { timeout: 15000 })
|
|
137
|
+
} catch {
|
|
138
|
+
/* reaper is best-effort by design */
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
process.on('SIGINT', () => {
|
|
142
|
+
reapContainers()
|
|
143
|
+
process.exit(130)
|
|
144
|
+
})
|
|
145
|
+
process.on('SIGTERM', () => {
|
|
146
|
+
reapContainers()
|
|
147
|
+
process.exit(143)
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
interface JailResult {
|
|
151
|
+
exitCode: number
|
|
152
|
+
stdout: string
|
|
153
|
+
stderr: string
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function runJailed(program: string): Promise<JailResult> {
|
|
157
|
+
return withDockerSlot(
|
|
158
|
+
() =>
|
|
159
|
+
new Promise<JailResult>((resolvePromise, reject) => {
|
|
160
|
+
const dir = mkdtempSync(join(tmpdir(), 'mbpps-'))
|
|
161
|
+
writeFileSync(join(dir, 'p.py'), program)
|
|
162
|
+
const name = `${containerPrefix}-${containerSeq++}`
|
|
163
|
+
let settled = false
|
|
164
|
+
const cleanup = () => {
|
|
165
|
+
rmSync(dir, { recursive: true, force: true })
|
|
166
|
+
execFile('docker', ['rm', '-f', name], () => {})
|
|
167
|
+
}
|
|
168
|
+
const finish = (res: JailResult) => {
|
|
169
|
+
if (settled) return
|
|
170
|
+
settled = true
|
|
171
|
+
clearTimeout(backstop)
|
|
172
|
+
cleanup()
|
|
173
|
+
resolvePromise(res)
|
|
174
|
+
}
|
|
175
|
+
const fail = (e: Error) => {
|
|
176
|
+
if (settled) return
|
|
177
|
+
settled = true
|
|
178
|
+
clearTimeout(backstop)
|
|
179
|
+
cleanup()
|
|
180
|
+
reject(e)
|
|
181
|
+
}
|
|
182
|
+
const backstop = setTimeout(() => finish({ exitCode: 124, stdout: '', stderr: 'backstop timeout (no output)' }), dockerTimeoutMs + 5000)
|
|
183
|
+
const inContainerSecs = Math.ceil(dockerTimeoutMs / 1000) + 2
|
|
184
|
+
execFile(
|
|
185
|
+
'docker',
|
|
186
|
+
[
|
|
187
|
+
'run', '--rm', '--name', name, '--network=none', '--cpus=1', '--memory=512m',
|
|
188
|
+
'-v', `${dir}:/w:ro`, '-w', '/w', dockerImage,
|
|
189
|
+
'timeout', '-s', 'KILL', String(inContainerSecs), 'python', '/w/p.py',
|
|
190
|
+
],
|
|
191
|
+
{ timeout: dockerTimeoutMs + 3000, killSignal: 'SIGKILL', maxBuffer: 4 * 1024 * 1024 },
|
|
192
|
+
(err, stdout, stderr) => {
|
|
193
|
+
if (err) {
|
|
194
|
+
const e = err as NodeJS.ErrnoException & { code?: number | string }
|
|
195
|
+
if (e.code === 'ENOENT') return fail(new Error('docker binary not found on PATH'))
|
|
196
|
+
const se = stderr ?? ''
|
|
197
|
+
if (/cannot connect to the docker daemon|is the docker daemon running|permission denied while trying to connect/i.test(se)) {
|
|
198
|
+
return fail(new Error(`docker daemon unreachable: ${se.slice(0, 200)}`))
|
|
199
|
+
}
|
|
200
|
+
if (/(unable to find image|pull access denied|manifest unknown|error response from daemon).*(pull|repository|registry)/i.test(se)) {
|
|
201
|
+
return fail(new Error(`docker image ${dockerImage} unavailable: ${se.slice(0, 200)}`))
|
|
202
|
+
}
|
|
203
|
+
const code = typeof e.code === 'number' ? e.code : 1
|
|
204
|
+
return finish({ exitCode: code, stdout: stdout ?? '', stderr: se })
|
|
205
|
+
}
|
|
206
|
+
finish({ exitCode: 0, stdout: stdout ?? '', stderr: stderr ?? '' })
|
|
207
|
+
},
|
|
208
|
+
)
|
|
209
|
+
}),
|
|
210
|
+
)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// ---------- the visible-check judge (Phase A's ONLY signal) ----------
|
|
214
|
+
|
|
215
|
+
export interface VisibleResult {
|
|
216
|
+
/** total visible checks run: shown assert + generated asserts (-1 = crashed) */
|
|
217
|
+
attempted: number
|
|
218
|
+
failed: number
|
|
219
|
+
failureOutput: string
|
|
220
|
+
pass: boolean
|
|
221
|
+
sAttempted?: number
|
|
222
|
+
sFailed?: number
|
|
223
|
+
gAttempted?: number
|
|
224
|
+
gFailed?: number
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/** Each visible assert runs INDIVIDUALLY in try/except so one malformed line
|
|
228
|
+
* cannot zero the rest; per-assert failure text is the repair feedback. The
|
|
229
|
+
* hidden asserts (test_list[1:]) never appear here. */
|
|
230
|
+
function buildVisibleProgram(task: MbppTask, candidate: string, nonce: string, genTests: string[]): string {
|
|
231
|
+
const shownB64 = Buffer.from(JSON.stringify([task.shownAssert]), 'utf8').toString('base64')
|
|
232
|
+
const genB64 = Buffer.from(JSON.stringify(genTests), 'utf8').toString('base64')
|
|
233
|
+
return `${task.testImports.join('\n')}\n${candidate}\n
|
|
234
|
+
import base64 as _b64, json as _json, sys as _sys
|
|
235
|
+
_shown = _json.loads(_b64.b64decode("${shownB64}").decode("utf8"))
|
|
236
|
+
_gen = _json.loads(_b64.b64decode("${genB64}").decode("utf8"))
|
|
237
|
+
_out = []
|
|
238
|
+
def _run(_tests):
|
|
239
|
+
_att, _fail = 0, 0
|
|
240
|
+
for _t in _tests:
|
|
241
|
+
_att += 1
|
|
242
|
+
try:
|
|
243
|
+
exec(_t, dict(globals()))
|
|
244
|
+
except Exception as _e:
|
|
245
|
+
_fail += 1
|
|
246
|
+
_out.append("CHECK FAILED: %s -> %s: %s" % (_t.strip()[:200], type(_e).__name__, str(_e)[:200]))
|
|
247
|
+
return _att, _fail
|
|
248
|
+
_s_att, _s_fail = _run(_shown)
|
|
249
|
+
_g_att, _g_fail = _run(_gen)
|
|
250
|
+
_att, _fail = _s_att + _g_att, _s_fail + _g_fail
|
|
251
|
+
print("VISIBLE-${nonce} attempted=%d failed=%d satt=%d sfail=%d gatt=%d gfail=%d" % (_att, _fail, _s_att, _s_fail, _g_att, _g_fail))
|
|
252
|
+
_sys.stdout.write("\\n".join(_out)[-1500:])
|
|
253
|
+
_sys.exit(0 if _att > 0 and _fail == 0 else 1)
|
|
254
|
+
`
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export async function runVisibleJudge(task: MbppTask, candidate: string, genTests: string[]): Promise<VisibleResult> {
|
|
258
|
+
const nonce = randomBytes(8).toString('hex')
|
|
259
|
+
const r = await runJailed(buildVisibleProgram(task, candidate, nonce, genTests))
|
|
260
|
+
const summary = new RegExp(`VISIBLE-${nonce} attempted=(\\d+) failed=(\\d+) satt=(\\d+) sfail=(\\d+) gatt=(\\d+) gfail=(\\d+)`).exec(r.stdout)
|
|
261
|
+
if (!summary) {
|
|
262
|
+
const detail = (r.stderr || r.stdout).slice(-1500) || 'timed out (no output)'
|
|
263
|
+
return { attempted: -1, failed: -1, failureOutput: detail, pass: false }
|
|
264
|
+
}
|
|
265
|
+
const attempted = Number(summary[1])
|
|
266
|
+
const failed = Number(summary[2])
|
|
267
|
+
const failureOutput = r.stdout.replace(summary[0], '').slice(-1500)
|
|
268
|
+
return {
|
|
269
|
+
attempted,
|
|
270
|
+
failed,
|
|
271
|
+
failureOutput,
|
|
272
|
+
pass: attempted > 0 && failed === 0,
|
|
273
|
+
sAttempted: Number(summary[3]),
|
|
274
|
+
sFailed: Number(summary[4]),
|
|
275
|
+
gAttempted: Number(summary[5]),
|
|
276
|
+
gFailed: Number(summary[6]),
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function visibleScore(h: VisibleResult): number {
|
|
281
|
+
if (h.attempted <= 0) return h.attempted === 0 ? 0 : -1
|
|
282
|
+
// The shown assert is OFFICIAL (printed in the model's prompt); generated asserts
|
|
283
|
+
// are the model's own guesses and run ~70% wrong on MBPP's one-sentence specs
|
|
284
|
+
// (measured on the pilot: 71/102 failed on officially-passing code). Rank by the
|
|
285
|
+
// official signal first; guesses only break ties — otherwise 6 noisy guesses
|
|
286
|
+
// outvote the one reliable check and selection goes NEGATIVE.
|
|
287
|
+
const sA = h.sAttempted ?? 0
|
|
288
|
+
const gA = h.gAttempted ?? 0
|
|
289
|
+
const sFrac = sA > 0 ? (sA - (h.sFailed ?? 0)) / sA : 0
|
|
290
|
+
const gFrac = gA > 0 ? (gA - (h.gFailed ?? 0)) / gA : 0
|
|
291
|
+
return sFrac + 0.001 * gFrac
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// ---------- the hidden judge (Phase B / calibration ONLY) ----------
|
|
295
|
+
|
|
296
|
+
function buildHiddenProgram(task: MbppTask, candidate: string, nonce: string): string {
|
|
297
|
+
return `${task.testImports.join('\n')}\n${candidate}\n\n${task.hiddenAsserts.join('\n')}\nprint("HIDDEN-${nonce} PASS")\n`
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
async function runHiddenJudge(task: MbppTask, candidate: string): Promise<{ pass: number; detail?: string }> {
|
|
301
|
+
const nonce = randomBytes(8).toString('hex')
|
|
302
|
+
const r = await runJailed(buildHiddenProgram(task, candidate, nonce))
|
|
303
|
+
if (r.exitCode === 0 && r.stdout.includes(`HIDDEN-${nonce} PASS`)) return { pass: 1 }
|
|
304
|
+
return { pass: 0, detail: (r.stderr || r.stdout).slice(-600) || 'timed out (no output)' }
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// ---------- model client (mirrors hev-structural) ----------
|
|
308
|
+
|
|
309
|
+
interface ClientCfg {
|
|
310
|
+
base: string
|
|
311
|
+
key: string
|
|
312
|
+
model: string
|
|
313
|
+
maxTokens: number
|
|
314
|
+
temperature: number
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
interface Completion {
|
|
318
|
+
content: string
|
|
319
|
+
attempts: number
|
|
320
|
+
tokensIn: number
|
|
321
|
+
tokensOut: number
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
async function complete(cfg: ClientCfg, messages: Array<{ role: string; content: string }>): Promise<Completion> {
|
|
325
|
+
let lastErr = ''
|
|
326
|
+
for (let attempt = 1; attempt <= 4; attempt += 1) {
|
|
327
|
+
if (attempt > 1) await new Promise((r) => setTimeout(r, 2000 * 2 ** attempt))
|
|
328
|
+
const ctl = new AbortController()
|
|
329
|
+
const timer = setTimeout(() => ctl.abort(), 240_000)
|
|
330
|
+
try {
|
|
331
|
+
const res = await fetch(`${cfg.base}/chat/completions`, {
|
|
332
|
+
method: 'POST',
|
|
333
|
+
headers: { Authorization: `Bearer ${cfg.key}`, 'Content-Type': 'application/json' },
|
|
334
|
+
body: JSON.stringify({ model: cfg.model, max_tokens: cfg.maxTokens, temperature: cfg.temperature, messages }),
|
|
335
|
+
signal: ctl.signal,
|
|
336
|
+
})
|
|
337
|
+
if (!res.ok) {
|
|
338
|
+
lastErr = `HTTP ${res.status}: ${(await res.text()).slice(0, 200)}`
|
|
339
|
+
continue
|
|
340
|
+
}
|
|
341
|
+
const d = (await res.json()) as {
|
|
342
|
+
choices?: Array<{ message?: { content?: string } }>
|
|
343
|
+
usage?: { prompt_tokens?: number; completion_tokens?: number }
|
|
344
|
+
}
|
|
345
|
+
const content = d.choices?.[0]?.message?.content ?? ''
|
|
346
|
+
if (content.trim() === '') {
|
|
347
|
+
lastErr = 'empty content'
|
|
348
|
+
continue
|
|
349
|
+
}
|
|
350
|
+
return { content, attempts: attempt, tokensIn: d.usage?.prompt_tokens ?? 0, tokensOut: d.usage?.completion_tokens ?? 0 }
|
|
351
|
+
} catch (e) {
|
|
352
|
+
lastErr = e instanceof Error ? e.message : String(e)
|
|
353
|
+
} finally {
|
|
354
|
+
clearTimeout(timer)
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
throw new Error(`completion failed after retries: ${lastErr}`)
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function extractRepairCode(reply: string): string {
|
|
361
|
+
const fences = [...reply.matchAll(/```(?:python|py)?\s*\n([\s\S]*?)```/gi)].map((m) => (m[1] ?? '').trim())
|
|
362
|
+
for (let i = fences.length - 1; i >= 0; i -= 1) {
|
|
363
|
+
if (/(^|\n)\s*def\s+\w+/.test(fences[i] as string)) return fences[i] as string
|
|
364
|
+
}
|
|
365
|
+
return extractCode(reply)
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// ---------- TESTGEN (mirrors hev-structural; description + shown assert only) ----------
|
|
369
|
+
|
|
370
|
+
const testGenInstruction = (count: number, entry: string) =>
|
|
371
|
+
`Read the following task description and example test. Write exactly ${count} single-line assert statements that test the function \`${entry}\`, based ONLY on the described behavior. Match the EXACT output type and format the example test shows (if it expects a string, expect a string; if a tuple, a tuple). Each assert must be one physical line of the form \`assert ${entry}(...) == expected\` (or a True/False check). Do NOT implement the function. Do NOT repeat the example test verbatim if you can test other cases too. Output ONLY the assert lines inside a single \`\`\`python code block.`
|
|
372
|
+
|
|
373
|
+
async function generateTests(cfg: ClientCfg, task: MbppTask, count: number): Promise<{ tests: string[]; completion: Completion }> {
|
|
374
|
+
const c = await complete(cfg, [
|
|
375
|
+
{ role: 'user', content: `${testGenInstruction(count, task.entryPoint)}\n\nTask: ${task.description}\nExample test:\n\`\`\`python\n${task.shownAssert}\n\`\`\`` },
|
|
376
|
+
])
|
|
377
|
+
const block = extractCode(c.content)
|
|
378
|
+
const balanced = (s: string) => {
|
|
379
|
+
let d = 0
|
|
380
|
+
for (const ch of s) {
|
|
381
|
+
if (ch === '(' || ch === '[' || ch === '{') d += 1
|
|
382
|
+
else if (ch === ')' || ch === ']' || ch === '}') d -= 1
|
|
383
|
+
if (d < 0) return false
|
|
384
|
+
}
|
|
385
|
+
return d === 0
|
|
386
|
+
}
|
|
387
|
+
const tests = block
|
|
388
|
+
.split('\n')
|
|
389
|
+
.map((l) => l.trim())
|
|
390
|
+
.filter((l) => l.startsWith('assert ') && l.includes(task.entryPoint) && balanced(l))
|
|
391
|
+
.slice(0, count)
|
|
392
|
+
return { tests, completion: c }
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// ---------- Phase A: the harness (sees ONLY visible information) ----------
|
|
396
|
+
|
|
397
|
+
interface HarnessOutcome {
|
|
398
|
+
taskId: number
|
|
399
|
+
samples: string[]
|
|
400
|
+
visible: VisibleResult[]
|
|
401
|
+
selectedIdx: number
|
|
402
|
+
repairs: Array<{ code: string; visible: VisibleResult }>
|
|
403
|
+
finalCode: string
|
|
404
|
+
repairStop: string
|
|
405
|
+
genTests: string[]
|
|
406
|
+
llmCalls: number
|
|
407
|
+
llmAttempts: number
|
|
408
|
+
tokensIn: number
|
|
409
|
+
tokensOut: number
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
async function runHarnessForTask(cfg: ClientCfg, task: MbppTask, k: number, maxRepairs: number, testGen: number): Promise<HarnessOutcome> {
|
|
413
|
+
let llmCalls = 0
|
|
414
|
+
let llmAttempts = 0
|
|
415
|
+
let tokensIn = 0
|
|
416
|
+
let tokensOut = 0
|
|
417
|
+
const track = (c: Completion) => {
|
|
418
|
+
llmCalls += 1
|
|
419
|
+
llmAttempts += c.attempts
|
|
420
|
+
tokensIn += c.tokensIn
|
|
421
|
+
tokensOut += c.tokensOut
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
let genTests: string[] = []
|
|
425
|
+
if (testGen > 0) {
|
|
426
|
+
const g = await generateTests(cfg, task, testGen)
|
|
427
|
+
track(g.completion)
|
|
428
|
+
genTests = g.tests
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
const samples: string[] = []
|
|
432
|
+
for (let i = 0; i < k; i += 1) {
|
|
433
|
+
const c = await complete(cfg, [{ role: 'user', content: basePrompt(task) }])
|
|
434
|
+
track(c)
|
|
435
|
+
samples.push(extractCode(c.content))
|
|
436
|
+
}
|
|
437
|
+
const visible: VisibleResult[] = []
|
|
438
|
+
for (const s of samples) visible.push(await runVisibleJudge(task, s, genTests))
|
|
439
|
+
|
|
440
|
+
let selectedIdx = 0
|
|
441
|
+
for (let i = 1; i < k; i += 1) {
|
|
442
|
+
if (visibleScore(visible[i] as VisibleResult) > visibleScore(visible[selectedIdx] as VisibleResult)) selectedIdx = i
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const selVisible = visible[selectedIdx] as VisibleResult
|
|
446
|
+
let best = { code: samples[selectedIdx] as string, visible: selVisible }
|
|
447
|
+
const repairs: HarnessOutcome['repairs'] = []
|
|
448
|
+
let repairStop = 'already-passing'
|
|
449
|
+
if (!selVisible.pass) {
|
|
450
|
+
if (selVisible.attempted === 0) {
|
|
451
|
+
repairStop = 'no-signal'
|
|
452
|
+
} else {
|
|
453
|
+
repairStop = 'rounds-exhausted'
|
|
454
|
+
let current = best
|
|
455
|
+
for (let r = 0; r < maxRepairs; r += 1) {
|
|
456
|
+
const repairPrompt = [
|
|
457
|
+
'Your Python function failed some of its checks.',
|
|
458
|
+
'The task:',
|
|
459
|
+
task.description,
|
|
460
|
+
'It must satisfy this example test:',
|
|
461
|
+
'```python',
|
|
462
|
+
task.shownAssert,
|
|
463
|
+
'```',
|
|
464
|
+
'Your current attempt:',
|
|
465
|
+
'```python',
|
|
466
|
+
current.code,
|
|
467
|
+
'```',
|
|
468
|
+
'Result of running the checks against your attempt:',
|
|
469
|
+
'```',
|
|
470
|
+
current.visible.failureOutput.trim() || '(the code crashed before the checks could run)',
|
|
471
|
+
'```',
|
|
472
|
+
'Fix the function so the checks pass. Output the COMPLETE corrected function definition inside a single ```python code block. Do not write tests or example calls.',
|
|
473
|
+
].join('\n')
|
|
474
|
+
const c = await complete(cfg, [{ role: 'user', content: repairPrompt }])
|
|
475
|
+
track(c)
|
|
476
|
+
const code = extractRepairCode(c.content)
|
|
477
|
+
const h = await runVisibleJudge(task, code, genTests)
|
|
478
|
+
repairs.push({ code, visible: h })
|
|
479
|
+
if (visibleScore(h) > visibleScore(current.visible)) current = { code, visible: h }
|
|
480
|
+
if (visibleScore(current.visible) > visibleScore(best.visible)) best = current
|
|
481
|
+
if (h.pass) {
|
|
482
|
+
repairStop = 'repaired-pass'
|
|
483
|
+
break
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
return { taskId: task.taskId, samples, visible, selectedIdx, repairs, finalCode: best.code, repairStop, genTests, llmCalls, llmAttempts, tokensIn, tokensOut }
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// ---------- statistics (mirrors hev-structural) ----------
|
|
493
|
+
|
|
494
|
+
function signTestP(deltas: number[]): { pos: number; neg: number; p: number } {
|
|
495
|
+
const pos = deltas.filter((d) => d > 1e-9).length
|
|
496
|
+
const neg = deltas.filter((d) => d < -1e-9).length
|
|
497
|
+
const m = pos + neg
|
|
498
|
+
if (m === 0) return { pos, neg, p: 1 }
|
|
499
|
+
const logC: number[] = [0]
|
|
500
|
+
for (let i = 1; i <= m; i += 1) logC.push((logC[i - 1] as number) + Math.log(m - i + 1) - Math.log(i))
|
|
501
|
+
const pmf = (x: number) => Math.exp((logC[x] as number) - m * Math.LN2)
|
|
502
|
+
const extreme = Math.max(pos, neg)
|
|
503
|
+
let p = 0
|
|
504
|
+
for (let x = extreme; x <= m; x += 1) p += pmf(x)
|
|
505
|
+
p *= 2
|
|
506
|
+
if (pos === neg) p = 1
|
|
507
|
+
return { pos, neg, p: Math.min(1, p) }
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// ---------- calibration mode ----------
|
|
511
|
+
|
|
512
|
+
async function calibrate(tasks: MbppTask[]): Promise<void> {
|
|
513
|
+
console.log(`=== CALIBRATION · MBPP reference solutions vs both judges · n=${tasks.length} ===`)
|
|
514
|
+
const rows = await pool(tasks, 16, async (t) => {
|
|
515
|
+
const hidden = await runHiddenJudge(t, t.referenceCode)
|
|
516
|
+
const visible = await runVisibleJudge(t, t.referenceCode, [])
|
|
517
|
+
return { id: t.taskId, hidden: hidden.pass, hiddenDetail: hidden.detail, vAttempted: visible.attempted, vFailed: visible.failed, visiblePass: visible.pass }
|
|
518
|
+
})
|
|
519
|
+
const hiddenPass = rows.filter((r) => r.hidden === 1)
|
|
520
|
+
const visibleFail = rows.filter((r) => !r.visiblePass)
|
|
521
|
+
console.log(` hidden judge self-check: ${hiddenPass.length}/${rows.length} reference solutions pass (must be ~100%)`)
|
|
522
|
+
if (hiddenPass.length < rows.length) {
|
|
523
|
+
for (const r of rows.filter((x) => x.hidden !== 1)) console.log(` hidden FAIL ${r.id}: ${(r.hiddenDetail ?? '').replace(/\n/g, ' | ').slice(0, 160)}`)
|
|
524
|
+
}
|
|
525
|
+
console.log(` visible-check false-fail on reference: ${visibleFail.length}/${rows.length}`)
|
|
526
|
+
if (visibleFail.length > 0) console.log(` visible false-fail ids: ${visibleFail.map((r) => `${r.id}(${r.vFailed}/${r.vAttempted})`).join(', ')}`)
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// ---------- main ----------
|
|
530
|
+
|
|
531
|
+
const pct = (x: number) => `${(x * 100).toFixed(1)}%`
|
|
532
|
+
const pp = (x: number) => `${x >= 0 ? '+' : ''}${(x * 100).toFixed(1)}pp`
|
|
533
|
+
|
|
534
|
+
interface GradedRow extends HarnessOutcome {
|
|
535
|
+
hiddenSamples: number[]
|
|
536
|
+
hiddenFinal: number
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
async function main(): Promise<void> {
|
|
540
|
+
const n = Number(process.env.N ?? 427)
|
|
541
|
+
const k = Number(process.env.K ?? 5)
|
|
542
|
+
const maxRepairs = Number(process.env.REPAIRS ?? 2)
|
|
543
|
+
const offset = Number(process.env.OFFSET ?? 0)
|
|
544
|
+
const temperature = Number(process.env.TEMPERATURE ?? 0.8)
|
|
545
|
+
const model = process.env.WORKER_MODEL ?? 'meta-llama/Meta-Llama-3-8B-Instruct-Lite'
|
|
546
|
+
const base = process.env.ROUTER_BASE ?? 'https://api.together.xyz/v1'
|
|
547
|
+
const solveConc = Number(process.env.CONCURRENCY ?? 6)
|
|
548
|
+
dockerSlots = Number(process.env.DOCKER_CONCURRENCY ?? 6)
|
|
549
|
+
const testGen = Number(process.env.TESTGEN ?? 0)
|
|
550
|
+
const out = process.env.OUT
|
|
551
|
+
|
|
552
|
+
const { tasks, droppedShort, droppedEntry } = loadMbpp(n, offset)
|
|
553
|
+
console.log(`loaded ${tasks.length} MBPP task(s); dropped ${droppedShort.length} (<2 asserts: ${droppedShort.join(',') || '-'}), ${droppedEntry.length} (entry unresolved: ${droppedEntry.join(',') || '-'})`)
|
|
554
|
+
|
|
555
|
+
if (process.env.CALIBRATE === '1') {
|
|
556
|
+
await calibrate(tasks)
|
|
557
|
+
return
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
const cfg: ClientCfg = { base, key: must('TANGLE_API_KEY'), model, maxTokens: Number(process.env.MAX_TOKENS ?? 2500), temperature }
|
|
561
|
+
|
|
562
|
+
console.log(`=== MBPP STRUCTURAL lever · visible=test_list[0]+gen · hidden=test_list[1:] · n=${tasks.length} k=${k} repairs<=${maxRepairs} temp=${temperature} testgen=${testGen} ===`)
|
|
563
|
+
console.log(` model=${model} base=${base} llm-conc=${solveConc} docker-conc=${dockerSlots} (global semaphore)`)
|
|
564
|
+
|
|
565
|
+
let done = 0
|
|
566
|
+
let errCount = 0
|
|
567
|
+
const outcomes = await pool(tasks, solveConc, async (task): Promise<HarnessOutcome | { taskId: number; error: string }> => {
|
|
568
|
+
try {
|
|
569
|
+
const o = await runHarnessForTask(cfg, task, k, maxRepairs, testGen)
|
|
570
|
+
done += 1
|
|
571
|
+
if (out) appendFileSync(`${out}.phaseA`, `${JSON.stringify({ model, temperature, k, maxRepairs, ...o })}\n`)
|
|
572
|
+
process.stderr.write(
|
|
573
|
+
` [A ${done}/${tasks.length}] mbpp/${o.taskId}: sel=${o.selectedIdx} visible=${o.visible.map((h) => visibleScore(h).toFixed(2)).join('/')} repairs=${o.repairs.length} stop=${o.repairStop}\n`,
|
|
574
|
+
)
|
|
575
|
+
return o
|
|
576
|
+
} catch (e) {
|
|
577
|
+
errCount += 1
|
|
578
|
+
const error = e instanceof Error ? e.message : String(e)
|
|
579
|
+
if (out) appendFileSync(`${out}.phaseA`, `${JSON.stringify({ model, taskId: task.taskId, error })}\n`)
|
|
580
|
+
process.stderr.write(` [A ERROR] mbpp/${task.taskId}: ${error.slice(0, 160)}\n`)
|
|
581
|
+
if (errCount > Math.max(3, 0.15 * tasks.length)) throw new Error(`aborting: ${errCount} task errors — harness-level fault, not task noise (last: ${error})`)
|
|
582
|
+
return { taskId: task.taskId, error }
|
|
583
|
+
}
|
|
584
|
+
})
|
|
585
|
+
|
|
586
|
+
const okOutcomes = outcomes.filter((o): o is HarnessOutcome => !('error' in o))
|
|
587
|
+
const okTasks = okOutcomes.map((o) => tasks.find((t) => t.taskId === o.taskId) as MbppTask)
|
|
588
|
+
if (errCount > 0) console.log(` WARNING: ${errCount}/${tasks.length} task(s) errored in Phase A — excluded from stats, recorded in ${out ?? '(no OUT set)'}.phaseA`)
|
|
589
|
+
|
|
590
|
+
console.log(`\n▶ Phase B: hidden grading (${okOutcomes.length} tasks × ${k} samples + finals)`)
|
|
591
|
+
const graded: GradedRow[] = await pool(okOutcomes, 16, async (o, ti) => {
|
|
592
|
+
const task = okTasks[ti] as MbppTask
|
|
593
|
+
const hiddenSamples: number[] = []
|
|
594
|
+
for (const s of o.samples) hiddenSamples.push((await runHiddenJudge(task, s)).pass)
|
|
595
|
+
const finalIsSelected = o.finalCode === o.samples[o.selectedIdx]
|
|
596
|
+
const hiddenFinal = finalIsSelected ? (hiddenSamples[o.selectedIdx] as number) : (await runHiddenJudge(task, o.finalCode)).pass
|
|
597
|
+
const g: GradedRow = { ...o, hiddenSamples, hiddenFinal }
|
|
598
|
+
if (out) appendFileSync(out, `${JSON.stringify({ model, temperature, k, maxRepairs, ...g })}\n`)
|
|
599
|
+
return g
|
|
600
|
+
})
|
|
601
|
+
if (out) console.log(` raw rows appended to ${out} (phase-A rows incl. errors: ${out}.phaseA)`)
|
|
602
|
+
|
|
603
|
+
const blind1First = graded.map((g) => g.hiddenSamples[0] as number)
|
|
604
|
+
const blind1Mean = graded.map((g) => g.hiddenSamples.reduce((s, x) => s + x, 0) / g.hiddenSamples.length)
|
|
605
|
+
const selected = graded.map((g) => g.hiddenSamples[g.selectedIdx] as number)
|
|
606
|
+
const repaired = graded.map((g) => g.hiddenFinal)
|
|
607
|
+
const oracleK = graded.map((g) => (g.hiddenSamples.some((x) => x === 1) ? 1 : 0))
|
|
608
|
+
const covered = graded.map((g) => (g.visible.some((h) => h.attempted > 0) ? 1 : 0))
|
|
609
|
+
const rate = (xs: number[]) => xs.reduce((s, x) => s + x, 0) / xs.length
|
|
610
|
+
const llmCallsTotal = graded.reduce((s, g) => s + g.llmCalls, 0)
|
|
611
|
+
const llmAttemptsTotal = graded.reduce((s, g) => s + g.llmAttempts, 0)
|
|
612
|
+
const tokensInTotal = graded.reduce((s, g) => s + g.tokensIn, 0)
|
|
613
|
+
const tokensOutTotal = graded.reduce((s, g) => s + g.tokensOut, 0)
|
|
614
|
+
const repairFired = graded.filter((g) => g.repairs.length > 0)
|
|
615
|
+
|
|
616
|
+
console.log(`\n${'='.repeat(78)}`)
|
|
617
|
+
console.log(`RESULTS · MBPP structural lever · n=${graded.length} · k=${k} · repairs<=${maxRepairs} · temp=${temperature} · ${model}`)
|
|
618
|
+
console.log('='.repeat(78))
|
|
619
|
+
console.log(` visible-check coverage ${pct(rate(covered))} of tasks (shown assert always present)`)
|
|
620
|
+
console.log(` blind pass@1 (mean of k) ${pct(rate(blind1Mean))} [PRIMARY baseline — ${k}-rep estimator]`)
|
|
621
|
+
console.log(` blind pass@1 (first sample) ${pct(rate(blind1First))} [single-rep reference]`)
|
|
622
|
+
console.log(` selected@1 (visible argmax) ${pct(rate(selected))}`)
|
|
623
|
+
console.log(` repaired@1 (full harness) ${pct(rate(repaired))}`)
|
|
624
|
+
console.log(` oracle pass@${k} (ceiling) ${pct(rate(oracleK))}`)
|
|
625
|
+
console.log(
|
|
626
|
+
` compute: ${llmCallsTotal} llm calls (${llmAttemptsTotal} incl. retries) = ${(llmCallsTotal / graded.length).toFixed(2)}/task; tokens in/out ${tokensInTotal}/${tokensOutTotal} (blind@1 spends 1 call/task)`,
|
|
627
|
+
)
|
|
628
|
+
console.log(` repair fired on ${repairFired.length}/${graded.length} tasks (stop: ${['already-passing', 'no-signal', 'repaired-pass', 'rounds-exhausted'].map((s) => `${s}=${graded.filter((g) => g.repairStop === s).length}`).join(', ')})`)
|
|
629
|
+
|
|
630
|
+
const row = (label: string, baseline: number[], treatment: number[]) => {
|
|
631
|
+
const l = pairedLift(baseline, treatment)
|
|
632
|
+
const st = signTestP(baseline.map((b, i) => (treatment[i] as number) - b))
|
|
633
|
+
console.log(
|
|
634
|
+
` ${label.padEnd(36)} ${pp(l.point).padStart(7)} CI [${pp(l.low)}, ${pp(l.high)}] sign-test p=${st.p < 0.001 ? st.p.toExponential(1) : st.p.toFixed(3)} (+${st.pos}/−${st.neg}) (pairs ${l.pairs})`,
|
|
635
|
+
)
|
|
636
|
+
return { l, st }
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
console.log(`\n PAIRED LIFTS vs blind pass@1 (mean-of-${k}) · 95% bootstrap CI (B=10000) + exact sign test:`)
|
|
640
|
+
const sel = row('selected@1 − blind@1 (selection)', blind1Mean, selected)
|
|
641
|
+
const rep = row('repaired@1 − blind@1 (full harness)', blind1Mean, repaired)
|
|
642
|
+
row('repaired@1 − selected@1 (repair)', selected, repaired)
|
|
643
|
+
row(`oracle@${k} − repaired@1 (unrealized)`, repaired, oracleK)
|
|
644
|
+
|
|
645
|
+
const coveredIdx = graded.map((_, i) => i).filter((i) => covered[i] === 1)
|
|
646
|
+
if (coveredIdx.length > 0 && coveredIdx.length < graded.length) {
|
|
647
|
+
const pick = (xs: number[]) => coveredIdx.map((i) => xs[i] as number)
|
|
648
|
+
console.log(`\n COVERED-ONLY subgroup (n=${coveredIdx.length}):`)
|
|
649
|
+
row(' selected@1 − blind@1', pick(blind1Mean), pick(selected))
|
|
650
|
+
row(' repaired@1 − blind@1', pick(blind1Mean), pick(repaired))
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
const verdict = (name: string, r: { l: PairedLift; st: { p: number } }) =>
|
|
654
|
+
`${name}: ${pp(r.l.point)} — ${r.l.low > 0 && r.st.p < 0.05 ? 'POSITIVE (CI excludes 0 AND sign-test p<0.05)' : r.l.high < 0 && r.st.p < 0.05 ? 'NEGATIVE' : 'n.s.'}`
|
|
655
|
+
console.log(`\n VERDICT: ${verdict('full harness', rep)}; ${verdict('selection alone', sel)}`)
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
main().catch((e) => {
|
|
659
|
+
reapContainers()
|
|
660
|
+
console.error(`mbpp-structural: ${e instanceof Error ? (e.stack ?? e.message) : String(e)}`)
|
|
661
|
+
process.exit(1)
|
|
662
|
+
})
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { StagedPierCandidateExecution } from './pier-agent'
|
|
2
|
+
|
|
3
|
+
export function createStagedPierCandidateExecutionFixture(
|
|
4
|
+
executionId: string,
|
|
5
|
+
): StagedPierCandidateExecution {
|
|
6
|
+
const directory = `/tmp/agent-bench-pier-test/${encodeURIComponent(executionId)}`
|
|
7
|
+
return {
|
|
8
|
+
executionId,
|
|
9
|
+
directory,
|
|
10
|
+
taskDirectory: `${directory}/task`,
|
|
11
|
+
candidateDirectory: `${directory}/candidate`,
|
|
12
|
+
profileDirectory: `${directory}/profile`,
|
|
13
|
+
planPath: `${directory}/execution-plan.json`,
|
|
14
|
+
receiptPath: `${directory}/materialization-receipt.json`,
|
|
15
|
+
agentArgs: ['--agent', 'pier_agents.tangle_candidate:TangleCandidateAgent'],
|
|
16
|
+
evaluatorEnv: {},
|
|
17
|
+
attemptArgs: ['--n-attempts', '1', '--max-retries', '0'],
|
|
18
|
+
}
|
|
19
|
+
}
|