@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,387 @@
|
|
|
1
|
+
// src/benchmarks/frames.ts
|
|
2
|
+
import { execFile } from "child_process";
|
|
3
|
+
import { readFile } from "fs/promises";
|
|
4
|
+
import { join } from "path";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
6
|
+
import { promisify } from "util";
|
|
7
|
+
var execFileAsync = promisify(execFile);
|
|
8
|
+
var BENCH_ROOT = fileURLToPath(new URL("../..", import.meta.url));
|
|
9
|
+
var PY = join(BENCH_ROOT, ".venv", "bin", "python");
|
|
10
|
+
var FIXTURES = join(BENCH_ROOT, "fixtures", "frames.json");
|
|
11
|
+
var DATASET = "google/frames-benchmark";
|
|
12
|
+
var DATASET_REVISION = "main";
|
|
13
|
+
var FINAL_ANSWER_SENTINEL = "FINAL ANSWER:";
|
|
14
|
+
var WORKER_CONTRACT = [
|
|
15
|
+
"",
|
|
16
|
+
"Research the question using live web sources and answer it.",
|
|
17
|
+
"Cite the sources you used as full URLs, one per line, in a `CITATIONS:` block.",
|
|
18
|
+
`End your response with a single final line: \`${FINAL_ANSWER_SENTINEL} <answer>\``,
|
|
19
|
+
"The answer after the sentinel must be the bare value only (no explanation on that line)."
|
|
20
|
+
].join("\n");
|
|
21
|
+
async function py(script, args = []) {
|
|
22
|
+
const { stdout } = await execFileAsync(PY, ["-c", script, ...args], {
|
|
23
|
+
maxBuffer: 1024 * 1024 * 256
|
|
24
|
+
});
|
|
25
|
+
return stdout;
|
|
26
|
+
}
|
|
27
|
+
var ARTICLES = /* @__PURE__ */ new Set(["a", "an", "the"]);
|
|
28
|
+
var UNIT_WORDS = /* @__PURE__ */ new Set([
|
|
29
|
+
"years",
|
|
30
|
+
"year",
|
|
31
|
+
"months",
|
|
32
|
+
"month",
|
|
33
|
+
"days",
|
|
34
|
+
"day",
|
|
35
|
+
"people",
|
|
36
|
+
"percent",
|
|
37
|
+
"dollars",
|
|
38
|
+
"meters",
|
|
39
|
+
"metres",
|
|
40
|
+
"kilometers",
|
|
41
|
+
"kilometres",
|
|
42
|
+
"miles",
|
|
43
|
+
"km",
|
|
44
|
+
"m"
|
|
45
|
+
]);
|
|
46
|
+
var WRITTEN_NUMBERS = {
|
|
47
|
+
zero: 0,
|
|
48
|
+
one: 1,
|
|
49
|
+
two: 2,
|
|
50
|
+
three: 3,
|
|
51
|
+
four: 4,
|
|
52
|
+
five: 5,
|
|
53
|
+
six: 6,
|
|
54
|
+
seven: 7,
|
|
55
|
+
eight: 8,
|
|
56
|
+
nine: 9,
|
|
57
|
+
ten: 10,
|
|
58
|
+
eleven: 11,
|
|
59
|
+
twelve: 12,
|
|
60
|
+
thirteen: 13,
|
|
61
|
+
fourteen: 14,
|
|
62
|
+
fifteen: 15,
|
|
63
|
+
sixteen: 16,
|
|
64
|
+
seventeen: 17,
|
|
65
|
+
eighteen: 18,
|
|
66
|
+
nineteen: 19,
|
|
67
|
+
twenty: 20,
|
|
68
|
+
thirty: 30,
|
|
69
|
+
forty: 40,
|
|
70
|
+
fifty: 50,
|
|
71
|
+
sixty: 60,
|
|
72
|
+
seventy: 70,
|
|
73
|
+
eighty: 80,
|
|
74
|
+
ninety: 90,
|
|
75
|
+
hundred: 100,
|
|
76
|
+
thousand: 1e3,
|
|
77
|
+
million: 1e6,
|
|
78
|
+
billion: 1e9
|
|
79
|
+
};
|
|
80
|
+
var MONTHS = {
|
|
81
|
+
january: "01",
|
|
82
|
+
february: "02",
|
|
83
|
+
march: "03",
|
|
84
|
+
april: "04",
|
|
85
|
+
may: "05",
|
|
86
|
+
june: "06",
|
|
87
|
+
july: "07",
|
|
88
|
+
august: "08",
|
|
89
|
+
september: "09",
|
|
90
|
+
october: "10",
|
|
91
|
+
november: "11",
|
|
92
|
+
december: "12"
|
|
93
|
+
};
|
|
94
|
+
var SCALES = /* @__PURE__ */ new Set(["hundred", "thousand", "million", "billion"]);
|
|
95
|
+
function composeWrittenRun(words) {
|
|
96
|
+
let total = 0;
|
|
97
|
+
let current = 0;
|
|
98
|
+
for (const w of words) {
|
|
99
|
+
const v = WRITTEN_NUMBERS[w];
|
|
100
|
+
if (v === void 0) continue;
|
|
101
|
+
if (w === "hundred") {
|
|
102
|
+
current = (current === 0 ? 1 : current) * 100;
|
|
103
|
+
} else if (SCALES.has(w)) {
|
|
104
|
+
current = (current === 0 ? 1 : current) * v;
|
|
105
|
+
total += current;
|
|
106
|
+
current = 0;
|
|
107
|
+
} else {
|
|
108
|
+
current += v;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return total + current;
|
|
112
|
+
}
|
|
113
|
+
function canonicalizeNumbers(s) {
|
|
114
|
+
let out = s.replace(/(\d),(?=\d{3}\b)/g, "$1");
|
|
115
|
+
out = out.replace(/\b(\d+(?:\.\d+)?)\s+(hundred|thousand|million|billion)\b/g, (_m, num, scaleWord) => {
|
|
116
|
+
const scale = WRITTEN_NUMBERS[scaleWord] ?? 1;
|
|
117
|
+
return String(Number(num) * scale);
|
|
118
|
+
});
|
|
119
|
+
out = out.replace(/\b[a-z]+(?:[\s-]+[a-z]+)*\b/g, (run) => {
|
|
120
|
+
const words = run.split(/[\s-]+/);
|
|
121
|
+
if (!words.every((w) => w in WRITTEN_NUMBERS)) return run;
|
|
122
|
+
return String(composeWrittenRun(words));
|
|
123
|
+
});
|
|
124
|
+
return out;
|
|
125
|
+
}
|
|
126
|
+
function canonicalizeDates(s) {
|
|
127
|
+
let out = s.replace(
|
|
128
|
+
/\b(january|february|march|april|may|june|july|august|september|october|november|december)\s+(\d{1,2}),?\s+(\d{4})\b/g,
|
|
129
|
+
(_m, mon, day, year) => `${year}-${MONTHS[mon]}-${day.padStart(2, "0")}`
|
|
130
|
+
);
|
|
131
|
+
out = out.replace(
|
|
132
|
+
/\b(\d{1,2})\s+(january|february|march|april|may|june|july|august|september|october|november|december)\s+(\d{4})\b/g,
|
|
133
|
+
(_m, day, mon, year) => `${year}-${MONTHS[mon]}-${day.padStart(2, "0")}`
|
|
134
|
+
);
|
|
135
|
+
return out;
|
|
136
|
+
}
|
|
137
|
+
function normalizeAnswer(input) {
|
|
138
|
+
let s = input.toLowerCase();
|
|
139
|
+
s = canonicalizeDates(s);
|
|
140
|
+
s = canonicalizeNumbers(s);
|
|
141
|
+
s = s.replace(/[^\w\s-]/g, " ");
|
|
142
|
+
const tokens = s.split(/\s+/).filter((t) => t.length > 0).filter((t) => !ARTICLES.has(t)).filter((t) => !UNIT_WORDS.has(t));
|
|
143
|
+
return tokens.join(" ").trim();
|
|
144
|
+
}
|
|
145
|
+
function tier1Match(candidate, gold) {
|
|
146
|
+
const nc = normalizeAnswer(candidate);
|
|
147
|
+
const ng = normalizeAnswer(gold);
|
|
148
|
+
if (ng.length === 0) return false;
|
|
149
|
+
if (nc === ng) return true;
|
|
150
|
+
const re = new RegExp(`(^|\\s)${ng.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(\\s|$)`);
|
|
151
|
+
return re.test(nc);
|
|
152
|
+
}
|
|
153
|
+
function parseFinalAnswer(artifact) {
|
|
154
|
+
const lines = artifact.split(/\r?\n/);
|
|
155
|
+
for (let i = lines.length - 1; i >= 0; i -= 1) {
|
|
156
|
+
const line = lines[i] ?? "";
|
|
157
|
+
const idx = line.toUpperCase().indexOf(FINAL_ANSWER_SENTINEL);
|
|
158
|
+
if (idx !== -1) return line.slice(idx + FINAL_ANSWER_SENTINEL.length).trim();
|
|
159
|
+
}
|
|
160
|
+
for (let i = lines.length - 1; i >= 0; i -= 1) {
|
|
161
|
+
const t = (lines[i] ?? "").trim();
|
|
162
|
+
if (t.length > 0) return t;
|
|
163
|
+
}
|
|
164
|
+
return "";
|
|
165
|
+
}
|
|
166
|
+
function parseCitations(artifact) {
|
|
167
|
+
const urls = /* @__PURE__ */ new Set();
|
|
168
|
+
for (const m of artifact.matchAll(/https?:\/\/[^\s)<>"']+/g)) {
|
|
169
|
+
urls.add(m[0].replace(/[.,;]+$/, ""));
|
|
170
|
+
}
|
|
171
|
+
return [...urls];
|
|
172
|
+
}
|
|
173
|
+
function citationCoverage(citations, goldSources) {
|
|
174
|
+
if (goldSources.length === 0) return 1;
|
|
175
|
+
const slug = (u) => {
|
|
176
|
+
const m = u.match(/\/wiki\/([^#?]+)/);
|
|
177
|
+
return m ? decodeURIComponent(m[1] ?? "").toLowerCase() : u.toLowerCase();
|
|
178
|
+
};
|
|
179
|
+
const got = new Set(citations.map(slug));
|
|
180
|
+
let hit = 0;
|
|
181
|
+
for (const g of goldSources) if (got.has(slug(g))) hit += 1;
|
|
182
|
+
return hit / goldSources.length;
|
|
183
|
+
}
|
|
184
|
+
var JUDGE_PROMPT = (question, gold, candidate) => [
|
|
185
|
+
"You are a strict answer-equivalence checker for a factual question-answering benchmark.",
|
|
186
|
+
"Decide ONLY whether the candidate answer is semantically equivalent to the gold answer for this question.",
|
|
187
|
+
"It is CORRECT iff it contains the same factual value as the gold answer. Extra correct detail is fine.",
|
|
188
|
+
"A different value, a missing value, or a wrong value is INCORRECT.",
|
|
189
|
+
"",
|
|
190
|
+
`Question: ${question}`,
|
|
191
|
+
`Gold answer: ${gold}`,
|
|
192
|
+
`Candidate answer: ${candidate}`,
|
|
193
|
+
"",
|
|
194
|
+
"Respond with ONLY a fenced JSON block and nothing else:",
|
|
195
|
+
"```json",
|
|
196
|
+
'{"verdict": "correct" | "incorrect"}',
|
|
197
|
+
"```"
|
|
198
|
+
].join("\n");
|
|
199
|
+
function judgeRouter() {
|
|
200
|
+
const model = process.env.JUDGE_MODEL;
|
|
201
|
+
if (!model) throw new Error("JUDGE_MODEL is required for the FRAMES Tier-2 equivalence judge");
|
|
202
|
+
const key = process.env.TANGLE_API_KEY;
|
|
203
|
+
if (!key) throw new Error("TANGLE_API_KEY is required for the FRAMES Tier-2 judge");
|
|
204
|
+
const baseUrl = process.env.JUDGE_ROUTER_BASE ?? process.env.ROUTER_BASE ?? "https://router.tangle.tools/v1";
|
|
205
|
+
return { baseUrl, key, model };
|
|
206
|
+
}
|
|
207
|
+
async function tier2Judge(question, gold, candidate, router) {
|
|
208
|
+
const res = await fetch(`${router.baseUrl}/chat/completions`, {
|
|
209
|
+
method: "POST",
|
|
210
|
+
headers: { "content-type": "application/json", authorization: `Bearer ${router.key}` },
|
|
211
|
+
body: JSON.stringify({
|
|
212
|
+
model: router.model,
|
|
213
|
+
temperature: 0,
|
|
214
|
+
seed: 0,
|
|
215
|
+
messages: [{ role: "user", content: JUDGE_PROMPT(question, gold, candidate) }]
|
|
216
|
+
})
|
|
217
|
+
});
|
|
218
|
+
if (!res.ok) {
|
|
219
|
+
throw new Error(`FRAMES Tier-2 judge HTTP ${res.status}: ${(await res.text()).slice(0, 300)}`);
|
|
220
|
+
}
|
|
221
|
+
const body = await res.json();
|
|
222
|
+
const content = body.choices?.[0]?.message?.content;
|
|
223
|
+
if (typeof content !== "string") {
|
|
224
|
+
throw new Error(`FRAMES Tier-2 judge returned no message content: ${JSON.stringify(body).slice(0, 300)}`);
|
|
225
|
+
}
|
|
226
|
+
const fenced = content.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
227
|
+
const raw = (fenced ? fenced[1] : content)?.trim() ?? "";
|
|
228
|
+
let parsed;
|
|
229
|
+
try {
|
|
230
|
+
parsed = JSON.parse(raw);
|
|
231
|
+
} catch {
|
|
232
|
+
throw new Error(`FRAMES Tier-2 judge produced unparseable output (no JSON verdict): ${content.slice(0, 300)}`);
|
|
233
|
+
}
|
|
234
|
+
if (parsed.verdict === "correct") return true;
|
|
235
|
+
if (parsed.verdict === "incorrect") return false;
|
|
236
|
+
throw new Error(`FRAMES Tier-2 judge verdict not in {correct,incorrect}: ${JSON.stringify(parsed).slice(0, 200)}`);
|
|
237
|
+
}
|
|
238
|
+
function parseWikiLinks(raw) {
|
|
239
|
+
if (!raw) return [];
|
|
240
|
+
const out = [];
|
|
241
|
+
for (const m of raw.matchAll(/https?:\/\/[^\s'"\]]+/g)) out.push(m[0]);
|
|
242
|
+
return out;
|
|
243
|
+
}
|
|
244
|
+
function rowToTask(row, index) {
|
|
245
|
+
const goldSources = parseWikiLinks(row.wiki_links);
|
|
246
|
+
const meta = {
|
|
247
|
+
gold: row.Answer,
|
|
248
|
+
goldSources,
|
|
249
|
+
reasoningTypes: row.reasoning_types ?? "",
|
|
250
|
+
rawPrompt: row.Prompt
|
|
251
|
+
};
|
|
252
|
+
return {
|
|
253
|
+
id: `frames-${index}`,
|
|
254
|
+
split: "test",
|
|
255
|
+
prompt: row.Prompt + WORKER_CONTRACT,
|
|
256
|
+
metadata: meta
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
function readMeta(task) {
|
|
260
|
+
const md = task.metadata;
|
|
261
|
+
if (!md || typeof md.gold !== "string") {
|
|
262
|
+
throw new Error(`FRAMES task ${task.id} missing metadata.gold \u2014 loadTasks did not populate it`);
|
|
263
|
+
}
|
|
264
|
+
return md;
|
|
265
|
+
}
|
|
266
|
+
async function loadFixtures(opts) {
|
|
267
|
+
const rows = JSON.parse(await readFile(FIXTURES, "utf8"));
|
|
268
|
+
console.log(`[frames] FRAMES_FIXTURES=1 \u2014 loading ${rows.length} committed fixtures (no HF download)`);
|
|
269
|
+
let tasks = rows.map(rowToTask);
|
|
270
|
+
if (opts.ids) {
|
|
271
|
+
const want = new Set(opts.ids);
|
|
272
|
+
tasks = tasks.filter((t) => want.has(t.id));
|
|
273
|
+
} else if (opts.limit !== void 0) {
|
|
274
|
+
tasks = tasks.slice(0, opts.limit);
|
|
275
|
+
}
|
|
276
|
+
return tasks;
|
|
277
|
+
}
|
|
278
|
+
function createFramesAdapter() {
|
|
279
|
+
const fixturesMode = process.env.FRAMES_FIXTURES === "1";
|
|
280
|
+
return {
|
|
281
|
+
name: "frames",
|
|
282
|
+
async preflight() {
|
|
283
|
+
judgeRouter();
|
|
284
|
+
if (fixturesMode) {
|
|
285
|
+
await readFile(FIXTURES, "utf8").catch((err) => {
|
|
286
|
+
throw new Error(`FRAMES_FIXTURES=1 but ${FIXTURES} unreadable: ${err instanceof Error ? err.message : err}`);
|
|
287
|
+
});
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
try {
|
|
291
|
+
await py(
|
|
292
|
+
`from datasets import load_dataset
|
|
293
|
+
load_dataset(${JSON.stringify(DATASET)}, split='test', revision=${JSON.stringify(DATASET_REVISION)})
|
|
294
|
+
print('ok')`
|
|
295
|
+
);
|
|
296
|
+
} catch (err) {
|
|
297
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
298
|
+
throw new Error(
|
|
299
|
+
`frames preflight failed: ${msg}
|
|
300
|
+
Fix: (1) python3 -m venv bench/.venv && bench/.venv/bin/pip install datasets ; (2) ensure network access to Hugging Face for ${DATASET} ; or set FRAMES_FIXTURES=1 to run against the committed fixtures offline.`
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
},
|
|
304
|
+
async loadTasks(opts = {}) {
|
|
305
|
+
if (fixturesMode) return loadFixtures(opts);
|
|
306
|
+
const limit = opts.limit ?? 10;
|
|
307
|
+
const script = `
|
|
308
|
+
import json, sys
|
|
309
|
+
from datasets import load_dataset
|
|
310
|
+
ds = load_dataset(${JSON.stringify(DATASET)}, split='test', revision=${JSON.stringify(DATASET_REVISION)})
|
|
311
|
+
ids = set(json.loads(sys.argv[1])) if len(sys.argv) > 1 and sys.argv[1] else None
|
|
312
|
+
out = []
|
|
313
|
+
for i, r in enumerate(ds):
|
|
314
|
+
rid = f"frames-{i}"
|
|
315
|
+
if ids is not None and rid not in ids:
|
|
316
|
+
continue
|
|
317
|
+
out.append({
|
|
318
|
+
"_index": i,
|
|
319
|
+
"Prompt": r.get("Prompt", ""),
|
|
320
|
+
"Answer": r.get("Answer", ""),
|
|
321
|
+
"wiki_links": str(r.get("wiki_links", "")),
|
|
322
|
+
"reasoning_types": str(r.get("reasoning_types", "")),
|
|
323
|
+
})
|
|
324
|
+
if ids is None and len(out) >= ${limit}:
|
|
325
|
+
break
|
|
326
|
+
print(json.dumps(out))
|
|
327
|
+
`;
|
|
328
|
+
const stdout = await py(script, [opts.ids ? JSON.stringify(opts.ids) : ""]);
|
|
329
|
+
const rows = JSON.parse(stdout);
|
|
330
|
+
return rows.map((r) => rowToTask(r, r._index));
|
|
331
|
+
},
|
|
332
|
+
async goldArtifact(task) {
|
|
333
|
+
const meta = readMeta(task);
|
|
334
|
+
return `${FINAL_ANSWER_SENTINEL} ${meta.gold}`;
|
|
335
|
+
},
|
|
336
|
+
async judge(task, artifact) {
|
|
337
|
+
const meta = readMeta(task);
|
|
338
|
+
const finalAnswer = parseFinalAnswer(artifact);
|
|
339
|
+
const citations = parseCitations(artifact);
|
|
340
|
+
const coverage = citationCoverage(citations, meta.goldSources);
|
|
341
|
+
if (finalAnswer.length === 0) {
|
|
342
|
+
return {
|
|
343
|
+
resolved: false,
|
|
344
|
+
score: 0,
|
|
345
|
+
detail: JSON.stringify({
|
|
346
|
+
tier: "none",
|
|
347
|
+
reason: "no parseable answer",
|
|
348
|
+
normalizedGold: normalizeAnswer(meta.gold),
|
|
349
|
+
citationCoverage: coverage
|
|
350
|
+
})
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
if (tier1Match(finalAnswer, meta.gold)) {
|
|
354
|
+
return {
|
|
355
|
+
resolved: true,
|
|
356
|
+
score: 1,
|
|
357
|
+
detail: JSON.stringify({
|
|
358
|
+
tier: 1,
|
|
359
|
+
normalizedAnswer: normalizeAnswer(finalAnswer),
|
|
360
|
+
normalizedGold: normalizeAnswer(meta.gold),
|
|
361
|
+
citationCoverage: coverage
|
|
362
|
+
})
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
const verdict = await tier2Judge(meta.rawPrompt, meta.gold, finalAnswer, judgeRouter());
|
|
366
|
+
return {
|
|
367
|
+
resolved: verdict,
|
|
368
|
+
score: verdict ? 1 : 0,
|
|
369
|
+
detail: JSON.stringify({
|
|
370
|
+
tier: 2,
|
|
371
|
+
normalizedAnswer: normalizeAnswer(finalAnswer),
|
|
372
|
+
normalizedGold: normalizeAnswer(meta.gold),
|
|
373
|
+
judgeVerdict: verdict ? "correct" : "incorrect",
|
|
374
|
+
citationCoverage: coverage
|
|
375
|
+
})
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
export {
|
|
382
|
+
normalizeAnswer,
|
|
383
|
+
parseFinalAnswer,
|
|
384
|
+
parseCitations,
|
|
385
|
+
createFramesAdapter
|
|
386
|
+
};
|
|
387
|
+
//# sourceMappingURL=chunk-INNOYXCP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/benchmarks/frames.ts"],"sourcesContent":["/**\n * FRAMES adapter (google/frames-benchmark). Worker artifact = a single free-text\n * final answer string (optionally preceded by a citations block). FRAMES ships,\n * per item, a short gold `Answer` PLUS gold `wiki_links` (the Wikipedia URLs\n * needed to answer) — the gold-citation column is what lets the loop's critic\n * check citation coverage deterministically.\n *\n * Judge is two-tier, deterministic-first:\n * Tier 1 — normalized exact / token-boundary containment match (no model tokens).\n * Tier 2 — a constrained binary equivalence gate via a pinned LLM (JUDGE_MODEL,\n * temperature 0), fired ONLY when Tier 1 misses, to absorb the\n * paraphrase/alias equivalence FRAMES intends to allow.\n *\n * score is binary (resolved ? 1 : 0) — FRAMES has no partial credit. The judge\n * resolves strictly on the Answer value; wiki_links are a SOFT signal surfaced\n * in `detail` for the critic, never a hard pass criterion.\n *\n * Requires for a live run: the bench `.venv` with `datasets` installed and a\n * JUDGE_MODEL router key. For offline/CI verification set FRAMES_FIXTURES=1 to\n * load the committed fixtures (bench/fixtures/frames.json) — no HF download.\n */\n\nimport { execFile } from 'node:child_process'\nimport { readFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { promisify } from 'node:util'\nimport type { BenchmarkAdapter, BenchScore, BenchTask, LoadOptions } from './types'\n\nconst execFileAsync = promisify(execFile)\nconst BENCH_ROOT = fileURLToPath(new URL('../..', import.meta.url))\nconst PY = join(BENCH_ROOT, '.venv', 'bin', 'python')\nconst FIXTURES = join(BENCH_ROOT, 'fixtures', 'frames.json')\n\nconst DATASET = 'google/frames-benchmark'\n/** Pin the dataset revision: ids are derived from row order (no native id column),\n * so a reorder would break opts.ids selection + scorecard joins. */\nconst DATASET_REVISION = 'main'\nconst FINAL_ANSWER_SENTINEL = 'FINAL ANSWER:'\n\n/** Worker contract appended to every task prompt. Tier-1 parsing keys off the sentinel. */\nconst WORKER_CONTRACT = [\n '',\n 'Research the question using live web sources and answer it.',\n 'Cite the sources you used as full URLs, one per line, in a `CITATIONS:` block.',\n `End your response with a single final line: \\`${FINAL_ANSWER_SENTINEL} <answer>\\``,\n 'The answer after the sentinel must be the bare value only (no explanation on that line).',\n].join('\\n')\n\n/**\n * Typed seam for the future dynamic-topology research worker. The benchmark\n * adapter scores a plain `string` artifact (the BenchmarkAdapter contract); the\n * loop worker decodes its agent runs into a {@link ResearchAnswer} and serializes\n * `finalAnswer` (+ optional `CITATIONS:` block) into that string before judging.\n */\nexport interface ResearchTask {\n id: string\n question: string\n /** Gold short answer — judge resolves strictly against this. */\n gold: string\n /** Gold Wikipedia URLs — SOFT citation-coverage signal for the critic, never a hard gate. */\n goldSources: string[]\n /** FRAMES reasoning_types, e.g. 'numerical | temporal' — slices the scorecard by hop-type. */\n reasoningTypes: string\n}\n\nexport interface ResearchAnswer {\n finalAnswer: string\n citations: string[]\n}\n\ninterface FramesRow {\n Prompt: string\n Answer: string\n wiki_links: string\n reasoning_types: string\n}\n\ninterface FramesMeta {\n gold: string\n goldSources: string[]\n reasoningTypes: string\n rawPrompt: string\n}\n\n/** Run the bench venv python with a script on stdin; return stdout (throws on nonzero). */\nasync function py(script: string, args: string[] = []): Promise<string> {\n const { stdout } = await execFileAsync(PY, ['-c', script, ...args], {\n maxBuffer: 1024 * 1024 * 256,\n })\n return stdout\n}\n\nconst ARTICLES = new Set(['a', 'an', 'the'])\nconst UNIT_WORDS = new Set([\n 'years',\n 'year',\n 'months',\n 'month',\n 'days',\n 'day',\n 'people',\n 'percent',\n 'dollars',\n 'meters',\n 'metres',\n 'kilometers',\n 'kilometres',\n 'miles',\n 'km',\n 'm',\n])\n\nconst WRITTEN_NUMBERS: Record<string, number> = {\n zero: 0,\n one: 1,\n two: 2,\n three: 3,\n four: 4,\n five: 5,\n six: 6,\n seven: 7,\n eight: 8,\n nine: 9,\n ten: 10,\n eleven: 11,\n twelve: 12,\n thirteen: 13,\n fourteen: 14,\n fifteen: 15,\n sixteen: 16,\n seventeen: 17,\n eighteen: 18,\n nineteen: 19,\n twenty: 20,\n thirty: 30,\n forty: 40,\n fifty: 50,\n sixty: 60,\n seventy: 70,\n eighty: 80,\n ninety: 90,\n hundred: 100,\n thousand: 1000,\n million: 1_000_000,\n billion: 1_000_000_000,\n}\n\nconst MONTHS: Record<string, string> = {\n january: '01',\n february: '02',\n march: '03',\n april: '04',\n may: '05',\n june: '06',\n july: '07',\n august: '08',\n september: '09',\n october: '10',\n november: '11',\n december: '12',\n}\n\nconst SCALES = new Set(['hundred', 'thousand', 'million', 'billion'])\n\n/** Fold a run of written-number words into one integer (\"twenty eight\"→28, \"two hundred\"→200). */\nfunction composeWrittenRun(words: string[]): number {\n let total = 0\n let current = 0\n for (const w of words) {\n const v = WRITTEN_NUMBERS[w]\n if (v === undefined) continue\n if (w === 'hundred') {\n current = (current === 0 ? 1 : current) * 100\n } else if (SCALES.has(w)) {\n current = (current === 0 ? 1 : current) * v\n total += current\n current = 0\n } else {\n current += v\n }\n }\n return total + current\n}\n\n/** Canonicalize \"1,234\" / \"1.2 million\" / written numerals to a single numeric token. */\nfunction canonicalizeNumbers(s: string): string {\n // \"1,234,567\" → \"1234567\"\n let out = s.replace(/(\\d),(?=\\d{3}\\b)/g, '$1')\n // \"1.2 million\" / \"3 billion\" → expanded integer\n out = out.replace(/\\b(\\d+(?:\\.\\d+)?)\\s+(hundred|thousand|million|billion)\\b/g, (_m, num: string, scaleWord: string) => {\n const scale = WRITTEN_NUMBERS[scaleWord] ?? 1\n return String(Number(num) * scale)\n })\n // fold contiguous runs of written-number words into one integer\n out = out.replace(/\\b[a-z]+(?:[\\s-]+[a-z]+)*\\b/g, (run) => {\n const words = run.split(/[\\s-]+/)\n if (!words.every((w) => w in WRITTEN_NUMBERS)) return run\n return String(composeWrittenRun(words))\n })\n return out\n}\n\n/** \"March 3, 1879\" / \"3 March 1879\" → \"1879-03-03\"; year-only stays as the year. */\nfunction canonicalizeDates(s: string): string {\n let out = s.replace(\n /\\b(january|february|march|april|may|june|july|august|september|october|november|december)\\s+(\\d{1,2}),?\\s+(\\d{4})\\b/g,\n (_m, mon: string, day: string, year: string) => `${year}-${MONTHS[mon]}-${day.padStart(2, '0')}`,\n )\n out = out.replace(\n /\\b(\\d{1,2})\\s+(january|february|march|april|may|june|july|august|september|october|november|december)\\s+(\\d{4})\\b/g,\n (_m, day: string, mon: string, year: string) => `${year}-${MONTHS[mon]}-${day.padStart(2, '0')}`,\n )\n return out\n}\n\n/** Port of the SQuAD/GAIA normalize_answer routine, plus number/date canonicalization. */\nexport function normalizeAnswer(input: string): string {\n let s = input.toLowerCase()\n s = canonicalizeDates(s)\n s = canonicalizeNumbers(s)\n // strip punctuation (keep alphanumerics, ISO-date hyphens collapse to space-free below)\n s = s.replace(/[^\\w\\s-]/g, ' ')\n // tokenize, drop articles + trailing unit words, keep order\n const tokens = s\n .split(/\\s+/)\n .filter((t) => t.length > 0)\n .filter((t) => !ARTICLES.has(t))\n .filter((t) => !UNIT_WORDS.has(t))\n return tokens.join(' ').trim()\n}\n\n/** Tier-1 match: normalized equality OR normalized gold as a token-boundary substring. */\nfunction tier1Match(candidate: string, gold: string): boolean {\n const nc = normalizeAnswer(candidate)\n const ng = normalizeAnswer(gold)\n if (ng.length === 0) return false\n if (nc === ng) return true\n // token-boundary containment: gold appears as a whole-token run inside candidate\n const re = new RegExp(`(^|\\\\s)${ng.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}(\\\\s|$)`)\n return re.test(nc)\n}\n\n/**\n * Parse the worker artifact into the final answer string.\n * Order: the `FINAL ANSWER:` sentinel first (deterministic-extraction discipline);\n * fall back to the last non-empty line. Returns '' when nothing is parseable\n * (fail-closed — never guess), which judge() counts as resolved=false.\n */\nexport function parseFinalAnswer(artifact: string): string {\n const lines = artifact.split(/\\r?\\n/)\n for (let i = lines.length - 1; i >= 0; i -= 1) {\n const line = lines[i] ?? ''\n const idx = line.toUpperCase().indexOf(FINAL_ANSWER_SENTINEL)\n if (idx !== -1) return line.slice(idx + FINAL_ANSWER_SENTINEL.length).trim()\n }\n for (let i = lines.length - 1; i >= 0; i -= 1) {\n const t = (lines[i] ?? '').trim()\n if (t.length > 0) return t\n }\n return ''\n}\n\n/** Extract cited URLs from a `CITATIONS:` block and any bare URLs in the artifact. */\nexport function parseCitations(artifact: string): string[] {\n const urls = new Set<string>()\n for (const m of artifact.matchAll(/https?:\\/\\/[^\\s)<>\"']+/g)) {\n urls.add(m[0].replace(/[.,;]+$/, ''))\n }\n return [...urls]\n}\n\n/** Fraction of gold Wikipedia article slugs touched by the candidate's citations. SOFT signal. */\nfunction citationCoverage(citations: string[], goldSources: string[]): number {\n if (goldSources.length === 0) return 1\n const slug = (u: string) => {\n const m = u.match(/\\/wiki\\/([^#?]+)/)\n return m ? decodeURIComponent(m[1] ?? '').toLowerCase() : u.toLowerCase()\n }\n const got = new Set(citations.map(slug))\n let hit = 0\n for (const g of goldSources) if (got.has(slug(g))) hit += 1\n return hit / goldSources.length\n}\n\nconst JUDGE_PROMPT = (question: string, gold: string, candidate: string): string =>\n [\n 'You are a strict answer-equivalence checker for a factual question-answering benchmark.',\n 'Decide ONLY whether the candidate answer is semantically equivalent to the gold answer for this question.',\n 'It is CORRECT iff it contains the same factual value as the gold answer. Extra correct detail is fine.',\n 'A different value, a missing value, or a wrong value is INCORRECT.',\n '',\n `Question: ${question}`,\n `Gold answer: ${gold}`,\n `Candidate answer: ${candidate}`,\n '',\n 'Respond with ONLY a fenced JSON block and nothing else:',\n '```json',\n '{\"verdict\": \"correct\" | \"incorrect\"}',\n '```',\n ].join('\\n')\n\ninterface JudgeRouter {\n baseUrl: string\n key: string\n model: string\n}\n\nfunction judgeRouter(): JudgeRouter {\n const model = process.env.JUDGE_MODEL\n if (!model) throw new Error('JUDGE_MODEL is required for the FRAMES Tier-2 equivalence judge')\n const key = process.env.TANGLE_API_KEY\n if (!key) throw new Error('TANGLE_API_KEY is required for the FRAMES Tier-2 judge')\n const baseUrl = process.env.JUDGE_ROUTER_BASE ?? process.env.ROUTER_BASE ?? 'https://router.tangle.tools/v1'\n return { baseUrl, key, model }\n}\n\n/** Constrained binary equivalence gate. Pinned model, temperature 0; fail loud on unparseable output. */\nasync function tier2Judge(\n question: string,\n gold: string,\n candidate: string,\n router: JudgeRouter,\n): Promise<boolean> {\n const res = await fetch(`${router.baseUrl}/chat/completions`, {\n method: 'POST',\n headers: { 'content-type': 'application/json', authorization: `Bearer ${router.key}` },\n body: JSON.stringify({\n model: router.model,\n temperature: 0,\n seed: 0,\n messages: [{ role: 'user', content: JUDGE_PROMPT(question, gold, candidate) }],\n }),\n })\n if (!res.ok) {\n throw new Error(`FRAMES Tier-2 judge HTTP ${res.status}: ${(await res.text()).slice(0, 300)}`)\n }\n const body = (await res.json()) as { choices?: Array<{ message?: { content?: string } }> }\n const content = body.choices?.[0]?.message?.content\n if (typeof content !== 'string') {\n throw new Error(`FRAMES Tier-2 judge returned no message content: ${JSON.stringify(body).slice(0, 300)}`)\n }\n const fenced = content.match(/```(?:json)?\\s*([\\s\\S]*?)```/)\n const raw = (fenced ? fenced[1] : content)?.trim() ?? ''\n let parsed: { verdict?: unknown }\n try {\n parsed = JSON.parse(raw) as { verdict?: unknown }\n } catch {\n throw new Error(`FRAMES Tier-2 judge produced unparseable output (no JSON verdict): ${content.slice(0, 300)}`)\n }\n if (parsed.verdict === 'correct') return true\n if (parsed.verdict === 'incorrect') return false\n throw new Error(`FRAMES Tier-2 judge verdict not in {correct,incorrect}: ${JSON.stringify(parsed).slice(0, 200)}`)\n}\n\n/** FRAMES wiki_links is a python-repr list string, e.g. \"['https://…', 'https://…']\". */\nfunction parseWikiLinks(raw: string): string[] {\n if (!raw) return []\n const out: string[] = []\n for (const m of raw.matchAll(/https?:\\/\\/[^\\s'\"\\]]+/g)) out.push(m[0])\n return out\n}\n\nfunction rowToTask(row: FramesRow, index: number): BenchTask {\n const goldSources = parseWikiLinks(row.wiki_links)\n const meta: FramesMeta = {\n gold: row.Answer,\n goldSources,\n reasoningTypes: row.reasoning_types ?? '',\n rawPrompt: row.Prompt,\n }\n return {\n id: `frames-${index}`,\n split: 'test',\n prompt: row.Prompt + WORKER_CONTRACT,\n metadata: meta as unknown as Record<string, unknown>,\n }\n}\n\nfunction readMeta(task: BenchTask): FramesMeta {\n const md = task.metadata\n if (!md || typeof md.gold !== 'string') {\n throw new Error(`FRAMES task ${task.id} missing metadata.gold — loadTasks did not populate it`)\n }\n return md as unknown as FramesMeta\n}\n\nasync function loadFixtures(opts: LoadOptions): Promise<BenchTask[]> {\n const rows = JSON.parse(await readFile(FIXTURES, 'utf8')) as FramesRow[]\n console.log(`[frames] FRAMES_FIXTURES=1 — loading ${rows.length} committed fixtures (no HF download)`)\n let tasks = rows.map(rowToTask)\n if (opts.ids) {\n const want = new Set(opts.ids)\n tasks = tasks.filter((t) => want.has(t.id))\n } else if (opts.limit !== undefined) {\n tasks = tasks.slice(0, opts.limit)\n }\n return tasks\n}\n\nexport function createFramesAdapter(): BenchmarkAdapter {\n const fixturesMode = process.env.FRAMES_FIXTURES === '1'\n\n return {\n name: 'frames',\n\n async preflight() {\n // Tier-2 judge router config must be present in both modes — the loop's\n // citation-coverage stop gate is meaningless without the equivalence judge.\n judgeRouter()\n if (fixturesMode) {\n await readFile(FIXTURES, 'utf8').catch((err) => {\n throw new Error(`FRAMES_FIXTURES=1 but ${FIXTURES} unreadable: ${err instanceof Error ? err.message : err}`)\n })\n return\n }\n try {\n await py(\n `from datasets import load_dataset\nload_dataset(${JSON.stringify(DATASET)}, split='test', revision=${JSON.stringify(DATASET_REVISION)})\nprint('ok')`,\n )\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n throw new Error(\n `frames preflight failed: ${msg}\\n` +\n `Fix: (1) python3 -m venv bench/.venv && bench/.venv/bin/pip install datasets ; ` +\n `(2) ensure network access to Hugging Face for ${DATASET} ; ` +\n `or set FRAMES_FIXTURES=1 to run against the committed fixtures offline.`,\n )\n }\n },\n\n async loadTasks(opts: LoadOptions = {}) {\n if (fixturesMode) return loadFixtures(opts)\n const limit = opts.limit ?? 10\n const script = `\nimport json, sys\nfrom datasets import load_dataset\nds = load_dataset(${JSON.stringify(DATASET)}, split='test', revision=${JSON.stringify(DATASET_REVISION)})\nids = set(json.loads(sys.argv[1])) if len(sys.argv) > 1 and sys.argv[1] else None\nout = []\nfor i, r in enumerate(ds):\n rid = f\"frames-{i}\"\n if ids is not None and rid not in ids:\n continue\n out.append({\n \"_index\": i,\n \"Prompt\": r.get(\"Prompt\", \"\"),\n \"Answer\": r.get(\"Answer\", \"\"),\n \"wiki_links\": str(r.get(\"wiki_links\", \"\")),\n \"reasoning_types\": str(r.get(\"reasoning_types\", \"\")),\n })\n if ids is None and len(out) >= ${limit}:\n break\nprint(json.dumps(out))\n`\n const stdout = await py(script, [opts.ids ? JSON.stringify(opts.ids) : ''])\n const rows = JSON.parse(stdout) as Array<FramesRow & { _index: number }>\n return rows.map((r) => rowToTask(r, r._index))\n },\n\n async goldArtifact(task: BenchTask) {\n // Gold artifact = the worker-contract serialization of the gold Answer, so\n // verify-judge proves gold→resolved through the SAME parse path the real\n // artifact takes (Tier-1 short-circuits with no model tokens).\n const meta = readMeta(task)\n return `${FINAL_ANSWER_SENTINEL} ${meta.gold}`\n },\n\n async judge(task: BenchTask, artifact: string): Promise<BenchScore> {\n const meta = readMeta(task)\n const finalAnswer = parseFinalAnswer(artifact)\n const citations = parseCitations(artifact)\n const coverage = citationCoverage(citations, meta.goldSources)\n\n if (finalAnswer.length === 0) {\n // Fail-closed: distinguish prompt-adherence failure from a wrong answer.\n return {\n resolved: false,\n score: 0,\n detail: JSON.stringify({\n tier: 'none',\n reason: 'no parseable answer',\n normalizedGold: normalizeAnswer(meta.gold),\n citationCoverage: coverage,\n }),\n }\n }\n\n // Tier 1 — free, deterministic.\n if (tier1Match(finalAnswer, meta.gold)) {\n return {\n resolved: true,\n score: 1,\n detail: JSON.stringify({\n tier: 1,\n normalizedAnswer: normalizeAnswer(finalAnswer),\n normalizedGold: normalizeAnswer(meta.gold),\n citationCoverage: coverage,\n }),\n }\n }\n\n // Tier 2 — constrained LLM equivalence gate (fail loud on unparseable output).\n const verdict = await tier2Judge(meta.rawPrompt, meta.gold, finalAnswer, judgeRouter())\n return {\n resolved: verdict,\n score: verdict ? 1 : 0,\n detail: JSON.stringify({\n tier: 2,\n normalizedAnswer: normalizeAnswer(finalAnswer),\n normalizedGold: normalizeAnswer(meta.gold),\n judgeVerdict: verdict ? 'correct' : 'incorrect',\n citationCoverage: coverage,\n }),\n }\n },\n }\n}\n"],"mappings":";AAsBA,SAAS,gBAAgB;AACzB,SAAS,gBAAgB;AACzB,SAAS,YAAY;AACrB,SAAS,qBAAqB;AAC9B,SAAS,iBAAiB;AAG1B,IAAM,gBAAgB,UAAU,QAAQ;AACxC,IAAM,aAAa,cAAc,IAAI,IAAI,SAAS,YAAY,GAAG,CAAC;AAClE,IAAM,KAAK,KAAK,YAAY,SAAS,OAAO,QAAQ;AACpD,IAAM,WAAW,KAAK,YAAY,YAAY,aAAa;AAE3D,IAAM,UAAU;AAGhB,IAAM,mBAAmB;AACzB,IAAM,wBAAwB;AAG9B,IAAM,kBAAkB;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AAAA,EACA,iDAAiD,qBAAqB;AAAA,EACtE;AACF,EAAE,KAAK,IAAI;AAuCX,eAAe,GAAG,QAAgB,OAAiB,CAAC,GAAoB;AACtE,QAAM,EAAE,OAAO,IAAI,MAAM,cAAc,IAAI,CAAC,MAAM,QAAQ,GAAG,IAAI,GAAG;AAAA,IAClE,WAAW,OAAO,OAAO;AAAA,EAC3B,CAAC;AACD,SAAO;AACT;AAEA,IAAM,WAAW,oBAAI,IAAI,CAAC,KAAK,MAAM,KAAK,CAAC;AAC3C,IAAM,aAAa,oBAAI,IAAI;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,IAAM,kBAA0C;AAAA,EAC9C,MAAM;AAAA,EACN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,KAAK;AAAA,EACL,OAAO;AAAA,EACP,OAAO;AAAA,EACP,MAAM;AAAA,EACN,KAAK;AAAA,EACL,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,UAAU;AAAA,EACV,SAAS;AAAA,EACT,SAAS;AAAA,EACT,WAAW;AAAA,EACX,UAAU;AAAA,EACV,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,UAAU;AAAA,EACV,SAAS;AAAA,EACT,SAAS;AACX;AAEA,IAAM,SAAiC;AAAA,EACrC,SAAS;AAAA,EACT,UAAU;AAAA,EACV,OAAO;AAAA,EACP,OAAO;AAAA,EACP,KAAK;AAAA,EACL,MAAM;AAAA,EACN,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,SAAS;AAAA,EACT,UAAU;AAAA,EACV,UAAU;AACZ;AAEA,IAAM,SAAS,oBAAI,IAAI,CAAC,WAAW,YAAY,WAAW,SAAS,CAAC;AAGpE,SAAS,kBAAkB,OAAyB;AAClD,MAAI,QAAQ;AACZ,MAAI,UAAU;AACd,aAAW,KAAK,OAAO;AACrB,UAAM,IAAI,gBAAgB,CAAC;AAC3B,QAAI,MAAM,OAAW;AACrB,QAAI,MAAM,WAAW;AACnB,iBAAW,YAAY,IAAI,IAAI,WAAW;AAAA,IAC5C,WAAW,OAAO,IAAI,CAAC,GAAG;AACxB,iBAAW,YAAY,IAAI,IAAI,WAAW;AAC1C,eAAS;AACT,gBAAU;AAAA,IACZ,OAAO;AACL,iBAAW;AAAA,IACb;AAAA,EACF;AACA,SAAO,QAAQ;AACjB;AAGA,SAAS,oBAAoB,GAAmB;AAE9C,MAAI,MAAM,EAAE,QAAQ,qBAAqB,IAAI;AAE7C,QAAM,IAAI,QAAQ,6DAA6D,CAAC,IAAI,KAAa,cAAsB;AACrH,UAAM,QAAQ,gBAAgB,SAAS,KAAK;AAC5C,WAAO,OAAO,OAAO,GAAG,IAAI,KAAK;AAAA,EACnC,CAAC;AAED,QAAM,IAAI,QAAQ,gCAAgC,CAAC,QAAQ;AACzD,UAAM,QAAQ,IAAI,MAAM,QAAQ;AAChC,QAAI,CAAC,MAAM,MAAM,CAAC,MAAM,KAAK,eAAe,EAAG,QAAO;AACtD,WAAO,OAAO,kBAAkB,KAAK,CAAC;AAAA,EACxC,CAAC;AACD,SAAO;AACT;AAGA,SAAS,kBAAkB,GAAmB;AAC5C,MAAI,MAAM,EAAE;AAAA,IACV;AAAA,IACA,CAAC,IAAI,KAAa,KAAa,SAAiB,GAAG,IAAI,IAAI,OAAO,GAAG,CAAC,IAAI,IAAI,SAAS,GAAG,GAAG,CAAC;AAAA,EAChG;AACA,QAAM,IAAI;AAAA,IACR;AAAA,IACA,CAAC,IAAI,KAAa,KAAa,SAAiB,GAAG,IAAI,IAAI,OAAO,GAAG,CAAC,IAAI,IAAI,SAAS,GAAG,GAAG,CAAC;AAAA,EAChG;AACA,SAAO;AACT;AAGO,SAAS,gBAAgB,OAAuB;AACrD,MAAI,IAAI,MAAM,YAAY;AAC1B,MAAI,kBAAkB,CAAC;AACvB,MAAI,oBAAoB,CAAC;AAEzB,MAAI,EAAE,QAAQ,aAAa,GAAG;AAE9B,QAAM,SAAS,EACZ,MAAM,KAAK,EACX,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC,EAC1B,OAAO,CAAC,MAAM,CAAC,SAAS,IAAI,CAAC,CAAC,EAC9B,OAAO,CAAC,MAAM,CAAC,WAAW,IAAI,CAAC,CAAC;AACnC,SAAO,OAAO,KAAK,GAAG,EAAE,KAAK;AAC/B;AAGA,SAAS,WAAW,WAAmB,MAAuB;AAC5D,QAAM,KAAK,gBAAgB,SAAS;AACpC,QAAM,KAAK,gBAAgB,IAAI;AAC/B,MAAI,GAAG,WAAW,EAAG,QAAO;AAC5B,MAAI,OAAO,GAAI,QAAO;AAEtB,QAAM,KAAK,IAAI,OAAO,UAAU,GAAG,QAAQ,uBAAuB,MAAM,CAAC,SAAS;AAClF,SAAO,GAAG,KAAK,EAAE;AACnB;AAQO,SAAS,iBAAiB,UAA0B;AACzD,QAAM,QAAQ,SAAS,MAAM,OAAO;AACpC,WAAS,IAAI,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG;AAC7C,UAAM,OAAO,MAAM,CAAC,KAAK;AACzB,UAAM,MAAM,KAAK,YAAY,EAAE,QAAQ,qBAAqB;AAC5D,QAAI,QAAQ,GAAI,QAAO,KAAK,MAAM,MAAM,sBAAsB,MAAM,EAAE,KAAK;AAAA,EAC7E;AACA,WAAS,IAAI,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG;AAC7C,UAAM,KAAK,MAAM,CAAC,KAAK,IAAI,KAAK;AAChC,QAAI,EAAE,SAAS,EAAG,QAAO;AAAA,EAC3B;AACA,SAAO;AACT;AAGO,SAAS,eAAe,UAA4B;AACzD,QAAM,OAAO,oBAAI,IAAY;AAC7B,aAAW,KAAK,SAAS,SAAS,yBAAyB,GAAG;AAC5D,SAAK,IAAI,EAAE,CAAC,EAAE,QAAQ,WAAW,EAAE,CAAC;AAAA,EACtC;AACA,SAAO,CAAC,GAAG,IAAI;AACjB;AAGA,SAAS,iBAAiB,WAAqB,aAA+B;AAC5E,MAAI,YAAY,WAAW,EAAG,QAAO;AACrC,QAAM,OAAO,CAAC,MAAc;AAC1B,UAAM,IAAI,EAAE,MAAM,kBAAkB;AACpC,WAAO,IAAI,mBAAmB,EAAE,CAAC,KAAK,EAAE,EAAE,YAAY,IAAI,EAAE,YAAY;AAAA,EAC1E;AACA,QAAM,MAAM,IAAI,IAAI,UAAU,IAAI,IAAI,CAAC;AACvC,MAAI,MAAM;AACV,aAAW,KAAK,YAAa,KAAI,IAAI,IAAI,KAAK,CAAC,CAAC,EAAG,QAAO;AAC1D,SAAO,MAAM,YAAY;AAC3B;AAEA,IAAM,eAAe,CAAC,UAAkB,MAAc,cACpD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAAa,QAAQ;AAAA,EACrB,gBAAgB,IAAI;AAAA,EACpB,qBAAqB,SAAS;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,EAAE,KAAK,IAAI;AAQb,SAAS,cAA2B;AAClC,QAAM,QAAQ,QAAQ,IAAI;AAC1B,MAAI,CAAC,MAAO,OAAM,IAAI,MAAM,iEAAiE;AAC7F,QAAM,MAAM,QAAQ,IAAI;AACxB,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,wDAAwD;AAClF,QAAM,UAAU,QAAQ,IAAI,qBAAqB,QAAQ,IAAI,eAAe;AAC5E,SAAO,EAAE,SAAS,KAAK,MAAM;AAC/B;AAGA,eAAe,WACb,UACA,MACA,WACA,QACkB;AAClB,QAAM,MAAM,MAAM,MAAM,GAAG,OAAO,OAAO,qBAAqB;AAAA,IAC5D,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,oBAAoB,eAAe,UAAU,OAAO,GAAG,GAAG;AAAA,IACrF,MAAM,KAAK,UAAU;AAAA,MACnB,OAAO,OAAO;AAAA,MACd,aAAa;AAAA,MACb,MAAM;AAAA,MACN,UAAU,CAAC,EAAE,MAAM,QAAQ,SAAS,aAAa,UAAU,MAAM,SAAS,EAAE,CAAC;AAAA,IAC/E,CAAC;AAAA,EACH,CAAC;AACD,MAAI,CAAC,IAAI,IAAI;AACX,UAAM,IAAI,MAAM,4BAA4B,IAAI,MAAM,MAAM,MAAM,IAAI,KAAK,GAAG,MAAM,GAAG,GAAG,CAAC,EAAE;AAAA,EAC/F;AACA,QAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,QAAM,UAAU,KAAK,UAAU,CAAC,GAAG,SAAS;AAC5C,MAAI,OAAO,YAAY,UAAU;AAC/B,UAAM,IAAI,MAAM,oDAAoD,KAAK,UAAU,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC,EAAE;AAAA,EAC1G;AACA,QAAM,SAAS,QAAQ,MAAM,8BAA8B;AAC3D,QAAM,OAAO,SAAS,OAAO,CAAC,IAAI,UAAU,KAAK,KAAK;AACtD,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,GAAG;AAAA,EACzB,QAAQ;AACN,UAAM,IAAI,MAAM,sEAAsE,QAAQ,MAAM,GAAG,GAAG,CAAC,EAAE;AAAA,EAC/G;AACA,MAAI,OAAO,YAAY,UAAW,QAAO;AACzC,MAAI,OAAO,YAAY,YAAa,QAAO;AAC3C,QAAM,IAAI,MAAM,2DAA2D,KAAK,UAAU,MAAM,EAAE,MAAM,GAAG,GAAG,CAAC,EAAE;AACnH;AAGA,SAAS,eAAe,KAAuB;AAC7C,MAAI,CAAC,IAAK,QAAO,CAAC;AAClB,QAAM,MAAgB,CAAC;AACvB,aAAW,KAAK,IAAI,SAAS,wBAAwB,EAAG,KAAI,KAAK,EAAE,CAAC,CAAC;AACrE,SAAO;AACT;AAEA,SAAS,UAAU,KAAgB,OAA0B;AAC3D,QAAM,cAAc,eAAe,IAAI,UAAU;AACjD,QAAM,OAAmB;AAAA,IACvB,MAAM,IAAI;AAAA,IACV;AAAA,IACA,gBAAgB,IAAI,mBAAmB;AAAA,IACvC,WAAW,IAAI;AAAA,EACjB;AACA,SAAO;AAAA,IACL,IAAI,UAAU,KAAK;AAAA,IACnB,OAAO;AAAA,IACP,QAAQ,IAAI,SAAS;AAAA,IACrB,UAAU;AAAA,EACZ;AACF;AAEA,SAAS,SAAS,MAA6B;AAC7C,QAAM,KAAK,KAAK;AAChB,MAAI,CAAC,MAAM,OAAO,GAAG,SAAS,UAAU;AACtC,UAAM,IAAI,MAAM,eAAe,KAAK,EAAE,6DAAwD;AAAA,EAChG;AACA,SAAO;AACT;AAEA,eAAe,aAAa,MAAyC;AACnE,QAAM,OAAO,KAAK,MAAM,MAAM,SAAS,UAAU,MAAM,CAAC;AACxD,UAAQ,IAAI,6CAAwC,KAAK,MAAM,sCAAsC;AACrG,MAAI,QAAQ,KAAK,IAAI,SAAS;AAC9B,MAAI,KAAK,KAAK;AACZ,UAAM,OAAO,IAAI,IAAI,KAAK,GAAG;AAC7B,YAAQ,MAAM,OAAO,CAAC,MAAM,KAAK,IAAI,EAAE,EAAE,CAAC;AAAA,EAC5C,WAAW,KAAK,UAAU,QAAW;AACnC,YAAQ,MAAM,MAAM,GAAG,KAAK,KAAK;AAAA,EACnC;AACA,SAAO;AACT;AAEO,SAAS,sBAAwC;AACtD,QAAM,eAAe,QAAQ,IAAI,oBAAoB;AAErD,SAAO;AAAA,IACL,MAAM;AAAA,IAEN,MAAM,YAAY;AAGhB,kBAAY;AACZ,UAAI,cAAc;AAChB,cAAM,SAAS,UAAU,MAAM,EAAE,MAAM,CAAC,QAAQ;AAC9C,gBAAM,IAAI,MAAM,yBAAyB,QAAQ,gBAAgB,eAAe,QAAQ,IAAI,UAAU,GAAG,EAAE;AAAA,QAC7G,CAAC;AACD;AAAA,MACF;AACA,UAAI;AACF,cAAM;AAAA,UACJ;AAAA,eACK,KAAK,UAAU,OAAO,CAAC,4BAA4B,KAAK,UAAU,gBAAgB,CAAC;AAAA;AAAA,QAE1F;AAAA,MACF,SAAS,KAAK;AACZ,cAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,cAAM,IAAI;AAAA,UACR,4BAA4B,GAAG;AAAA,+HAEoB,OAAO;AAAA,QAE5D;AAAA,MACF;AAAA,IACF;AAAA,IAEA,MAAM,UAAU,OAAoB,CAAC,GAAG;AACtC,UAAI,aAAc,QAAO,aAAa,IAAI;AAC1C,YAAM,QAAQ,KAAK,SAAS;AAC5B,YAAM,SAAS;AAAA;AAAA;AAAA,oBAGD,KAAK,UAAU,OAAO,CAAC,4BAA4B,KAAK,UAAU,gBAAgB,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qCAclE,KAAK;AAAA;AAAA;AAAA;AAIpC,YAAM,SAAS,MAAM,GAAG,QAAQ,CAAC,KAAK,MAAM,KAAK,UAAU,KAAK,GAAG,IAAI,EAAE,CAAC;AAC1E,YAAM,OAAO,KAAK,MAAM,MAAM;AAC9B,aAAO,KAAK,IAAI,CAAC,MAAM,UAAU,GAAG,EAAE,MAAM,CAAC;AAAA,IAC/C;AAAA,IAEA,MAAM,aAAa,MAAiB;AAIlC,YAAM,OAAO,SAAS,IAAI;AAC1B,aAAO,GAAG,qBAAqB,IAAI,KAAK,IAAI;AAAA,IAC9C;AAAA,IAEA,MAAM,MAAM,MAAiB,UAAuC;AAClE,YAAM,OAAO,SAAS,IAAI;AAC1B,YAAM,cAAc,iBAAiB,QAAQ;AAC7C,YAAM,YAAY,eAAe,QAAQ;AACzC,YAAM,WAAW,iBAAiB,WAAW,KAAK,WAAW;AAE7D,UAAI,YAAY,WAAW,GAAG;AAE5B,eAAO;AAAA,UACL,UAAU;AAAA,UACV,OAAO;AAAA,UACP,QAAQ,KAAK,UAAU;AAAA,YACrB,MAAM;AAAA,YACN,QAAQ;AAAA,YACR,gBAAgB,gBAAgB,KAAK,IAAI;AAAA,YACzC,kBAAkB;AAAA,UACpB,CAAC;AAAA,QACH;AAAA,MACF;AAGA,UAAI,WAAW,aAAa,KAAK,IAAI,GAAG;AACtC,eAAO;AAAA,UACL,UAAU;AAAA,UACV,OAAO;AAAA,UACP,QAAQ,KAAK,UAAU;AAAA,YACrB,MAAM;AAAA,YACN,kBAAkB,gBAAgB,WAAW;AAAA,YAC7C,gBAAgB,gBAAgB,KAAK,IAAI;AAAA,YACzC,kBAAkB;AAAA,UACpB,CAAC;AAAA,QACH;AAAA,MACF;AAGA,YAAM,UAAU,MAAM,WAAW,KAAK,WAAW,KAAK,MAAM,aAAa,YAAY,CAAC;AACtF,aAAO;AAAA,QACL,UAAU;AAAA,QACV,OAAO,UAAU,IAAI;AAAA,QACrB,QAAQ,KAAK,UAAU;AAAA,UACrB,MAAM;AAAA,UACN,kBAAkB,gBAAgB,WAAW;AAAA,UAC7C,gBAAgB,gBAAgB,KAAK,IAAI;AAAA,UACzC,cAAc,UAAU,YAAY;AAAA,UACpC,kBAAkB;AAAA,QACpB,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import {
|
|
2
|
+
benchRoot,
|
|
3
|
+
runVenvPython,
|
|
4
|
+
runVenvScriptStdin
|
|
5
|
+
} from "./chunk-LRRD7NAG.js";
|
|
6
|
+
|
|
7
|
+
// src/benchmarks/dabstep.ts
|
|
8
|
+
import { join } from "path";
|
|
9
|
+
import { access, readFile, stat } from "fs/promises";
|
|
10
|
+
var FIXTURES = join(benchRoot, "fixtures", "dabstep.json");
|
|
11
|
+
var DEFAULT_SPLIT = "easy";
|
|
12
|
+
var dabstepDir = () => process.env.DABSTEP_DIR;
|
|
13
|
+
var gradeFile = (dir) => join(dir, "grade.py");
|
|
14
|
+
var resourceRoot = (dir) => join(dir, "files");
|
|
15
|
+
async function assertFile(path, label) {
|
|
16
|
+
try {
|
|
17
|
+
await access(path);
|
|
18
|
+
} catch (err) {
|
|
19
|
+
throw new Error(`DABStep: missing ${label} at ${path} (${err instanceof Error ? err.message : err})`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
async function assertOfficialFiles(dir, split) {
|
|
23
|
+
await assertFile(join(dir, "dataset.csv"), "released dataset.csv");
|
|
24
|
+
await assertFile(join(dir, "splits", `${split}.txt`), `${split} split file`);
|
|
25
|
+
await assertFile(gradeFile(dir), "official grade.py");
|
|
26
|
+
const files = resourceRoot(dir);
|
|
27
|
+
try {
|
|
28
|
+
const s = await stat(files);
|
|
29
|
+
if (!s.isDirectory()) throw new Error("not a directory");
|
|
30
|
+
} catch (err) {
|
|
31
|
+
throw new Error(`DABStep: missing benchmark files directory at ${files} (${err instanceof Error ? err.message : err})`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
var dabstepAnswerOutput = {
|
|
35
|
+
parse(events) {
|
|
36
|
+
let text = "";
|
|
37
|
+
for (const ev of events) {
|
|
38
|
+
const d = ev?.data;
|
|
39
|
+
const t = d?.finalText ?? d?.text ?? d?.result;
|
|
40
|
+
if (typeof t === "string" && t.length > 0) text = t;
|
|
41
|
+
}
|
|
42
|
+
const fences = [...text.matchAll(/```(?:text|answer)?\s*\n([\s\S]*?)```/g)];
|
|
43
|
+
return (fences.at(-1)?.[1] ?? text).trim();
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
function rowToTask(row, split, dir) {
|
|
47
|
+
const meta = {
|
|
48
|
+
taskId: row.task_id,
|
|
49
|
+
split,
|
|
50
|
+
golds: row.all_golds_by_task,
|
|
51
|
+
...dir ? { resourceRoot: resourceRoot(dir) } : {}
|
|
52
|
+
};
|
|
53
|
+
return {
|
|
54
|
+
id: String(row.task_id),
|
|
55
|
+
split,
|
|
56
|
+
prompt: [
|
|
57
|
+
"Solve this DABStep data-analysis task using the mounted payment files.",
|
|
58
|
+
"Use code or shell commands as needed, then return only the final answer.",
|
|
59
|
+
"",
|
|
60
|
+
row.instructions
|
|
61
|
+
].join("\n"),
|
|
62
|
+
metadata: meta
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function readMeta(task) {
|
|
66
|
+
const md = task.metadata;
|
|
67
|
+
if (!md || typeof md.taskId !== "number" || !Array.isArray(md.golds)) {
|
|
68
|
+
throw new Error(`dabstep task ${task.id} missing metadata \u2014 loadTasks did not populate it`);
|
|
69
|
+
}
|
|
70
|
+
return md;
|
|
71
|
+
}
|
|
72
|
+
function selectRows(rows, opts, split, dir) {
|
|
73
|
+
let tasks = rows.map((row) => rowToTask(row, split, dir));
|
|
74
|
+
if (opts.ids) {
|
|
75
|
+
const want = new Set(opts.ids);
|
|
76
|
+
tasks = tasks.filter((task) => want.has(task.id));
|
|
77
|
+
} else if (opts.limit !== void 0) {
|
|
78
|
+
tasks = tasks.slice(0, opts.limit);
|
|
79
|
+
}
|
|
80
|
+
if (tasks.length === 0) throw new Error(`DABStep: no tasks matched ${JSON.stringify(opts)} for split=${split}`);
|
|
81
|
+
return tasks;
|
|
82
|
+
}
|
|
83
|
+
async function loadFixtures(opts, split) {
|
|
84
|
+
const rows = JSON.parse(await readFile(FIXTURES, "utf8"));
|
|
85
|
+
console.warn(`[dabstep] DABSTEP_FIXTURES=1 \u2014 loading ${rows.length} adapter fixtures from ${FIXTURES}`);
|
|
86
|
+
return selectRows(rows, opts, split);
|
|
87
|
+
}
|
|
88
|
+
async function loadOfficialTasks(dir, opts, split) {
|
|
89
|
+
const script = `
|
|
90
|
+
import ast, csv, json, sys
|
|
91
|
+
from pathlib import Path
|
|
92
|
+
|
|
93
|
+
root = Path(sys.argv[1])
|
|
94
|
+
split = sys.argv[2]
|
|
95
|
+
limit = None if sys.argv[3] == "" else int(sys.argv[3])
|
|
96
|
+
ids = set(json.loads(sys.argv[4]))
|
|
97
|
+
dataset = root / "dataset.csv"
|
|
98
|
+
split_file = root / "splits" / f"{split}.txt"
|
|
99
|
+
if not dataset.exists():
|
|
100
|
+
raise SystemExit(f"missing official DABStep dataset.csv at {dataset}")
|
|
101
|
+
if not split_file.exists():
|
|
102
|
+
raise SystemExit(f"missing official DABStep split file at {split_file}")
|
|
103
|
+
split_ids = {int(line.strip()) for line in split_file.read_text().splitlines() if line.strip()}
|
|
104
|
+
out = []
|
|
105
|
+
with dataset.open(newline="") as f:
|
|
106
|
+
for row in csv.DictReader(f):
|
|
107
|
+
task_id = int(row["task_id"])
|
|
108
|
+
if task_id not in split_ids:
|
|
109
|
+
continue
|
|
110
|
+
if ids and str(task_id) not in ids:
|
|
111
|
+
continue
|
|
112
|
+
out.append({
|
|
113
|
+
"task_id": task_id,
|
|
114
|
+
"instructions": f"{row['question']}\\n{row['guidelines']}",
|
|
115
|
+
"all_golds_by_task": ast.literal_eval(str(row["all_golds_by_task"])),
|
|
116
|
+
})
|
|
117
|
+
if limit is not None and len(out) >= limit:
|
|
118
|
+
break
|
|
119
|
+
if not out:
|
|
120
|
+
raise SystemExit(f"no DABStep rows matched split={split} ids={sorted(ids)} limit={limit}")
|
|
121
|
+
print(json.dumps(out))
|
|
122
|
+
`;
|
|
123
|
+
const stdout = await runVenvPython(script, [dir, split, opts.limit === void 0 ? "" : String(opts.limit), JSON.stringify(opts.ids ?? [])]);
|
|
124
|
+
return selectRows(JSON.parse(stdout), opts, split, dir);
|
|
125
|
+
}
|
|
126
|
+
function createDabstepAdapter() {
|
|
127
|
+
const fixturesMode = process.env.DABSTEP_FIXTURES === "1";
|
|
128
|
+
return {
|
|
129
|
+
name: "dabstep",
|
|
130
|
+
output: dabstepAnswerOutput,
|
|
131
|
+
async preflight() {
|
|
132
|
+
if (fixturesMode) return;
|
|
133
|
+
const dir = dabstepDir();
|
|
134
|
+
if (!dir) {
|
|
135
|
+
throw new Error(
|
|
136
|
+
"DABSTEP_DIR is required. Fix: clone https://github.com/EnvCommons/DABStep, add the released dataset.csv under that checkout, then set DABSTEP_DIR=/path/to/DABStep."
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
await assertOfficialFiles(dir, DEFAULT_SPLIT);
|
|
140
|
+
await loadOfficialTasks(dir, { limit: 1 }, DEFAULT_SPLIT);
|
|
141
|
+
},
|
|
142
|
+
async loadTasks(opts = {}) {
|
|
143
|
+
const split = opts.split ?? DEFAULT_SPLIT;
|
|
144
|
+
if (fixturesMode) return loadFixtures(opts, split);
|
|
145
|
+
const dir = dabstepDir();
|
|
146
|
+
if (!dir) throw new Error("DABSTEP_DIR is required to load official DABStep tasks");
|
|
147
|
+
return loadOfficialTasks(dir, opts, split);
|
|
148
|
+
},
|
|
149
|
+
async goldArtifact(task) {
|
|
150
|
+
const meta = readMeta(task);
|
|
151
|
+
const first = meta.golds[0];
|
|
152
|
+
if (!first) return void 0;
|
|
153
|
+
const value = first.value;
|
|
154
|
+
return value === void 0 ? void 0 : String(value);
|
|
155
|
+
},
|
|
156
|
+
async judge(task, artifact) {
|
|
157
|
+
const meta = readMeta(task);
|
|
158
|
+
const dir = dabstepDir();
|
|
159
|
+
if (!dir) throw new Error("DABSTEP_DIR is required to judge DABStep tasks with the official grade.py");
|
|
160
|
+
const stdout = await runVenvScriptStdin(
|
|
161
|
+
join(benchRoot, "scripts", "dabstep_judge.py"),
|
|
162
|
+
["--grade-file", gradeFile(dir)],
|
|
163
|
+
JSON.stringify({ prediction: artifact, golds: meta.golds }),
|
|
164
|
+
{ cwd: benchRoot }
|
|
165
|
+
);
|
|
166
|
+
const report = JSON.parse(stdout.trim().split("\n").at(-1) ?? "{}");
|
|
167
|
+
if (report.error) throw new Error(`DABStep judge error for ${task.id}: ${report.error}`);
|
|
168
|
+
const score = typeof report.score === "number" ? report.score : 0;
|
|
169
|
+
return {
|
|
170
|
+
resolved: report.correct === true,
|
|
171
|
+
score,
|
|
172
|
+
detail: JSON.stringify({ taskId: meta.taskId, split: meta.split, correct: report.correct })
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export {
|
|
179
|
+
dabstepAnswerOutput,
|
|
180
|
+
createDabstepAdapter
|
|
181
|
+
};
|
|
182
|
+
//# sourceMappingURL=chunk-J3KDJNX2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/benchmarks/dabstep.ts"],"sourcesContent":["/**\n * DABStep adapter (EnvCommons/DABStep) — data-analysis questions over synthetic\n * payment files. Worker artifact = final answer text. Judge = the official\n * DABStep `grade.py` normalization/matching function. No LLM judge.\n *\n * Live tasks require `DABSTEP_DIR` pointing at an official DABStep checkout that\n * includes `dataset.csv`, `splits/*.txt`, `files/*`, and `grade.py`. The adapter\n * exposes `metadata.resourceRoot` so runners can mount the benchmark files into\n * AgentProfile.resources.files; it does not paste the dataset into prompt text.\n */\n\nimport { join } from 'node:path'\nimport { access, readFile, stat } from 'node:fs/promises'\nimport type { OutputAdapter } from '@tangle-network/agent-runtime/loops'\nimport { benchRoot, runVenvPython, runVenvScriptStdin } from './_harness'\nimport type { BenchmarkAdapter, BenchScore, BenchTask, LoadOptions } from './types'\n\nconst FIXTURES = join(benchRoot, 'fixtures', 'dabstep.json')\nconst DEFAULT_SPLIT = 'easy'\n\ninterface DabstepFixtureRow {\n task_id: number\n instructions: string\n all_golds_by_task: Array<Record<string, unknown>>\n}\n\ninterface DabstepMeta {\n taskId: number\n split: string\n golds: Array<Record<string, unknown>>\n resourceRoot?: string\n}\n\nconst dabstepDir = (): string | undefined => process.env.DABSTEP_DIR\nconst gradeFile = (dir: string): string => join(dir, 'grade.py')\nconst resourceRoot = (dir: string): string => join(dir, 'files')\n\nasync function assertFile(path: string, label: string): Promise<void> {\n try {\n await access(path)\n } catch (err) {\n throw new Error(`DABStep: missing ${label} at ${path} (${err instanceof Error ? err.message : err})`)\n }\n}\n\nasync function assertOfficialFiles(dir: string, split: string): Promise<void> {\n await assertFile(join(dir, 'dataset.csv'), 'released dataset.csv')\n await assertFile(join(dir, 'splits', `${split}.txt`), `${split} split file`)\n await assertFile(gradeFile(dir), 'official grade.py')\n const files = resourceRoot(dir)\n try {\n const s = await stat(files)\n if (!s.isDirectory()) throw new Error('not a directory')\n } catch (err) {\n throw new Error(`DABStep: missing benchmark files directory at ${files} (${err instanceof Error ? err.message : err})`)\n }\n}\n\nexport const dabstepAnswerOutput: OutputAdapter<string> = {\n parse(events) {\n let text = ''\n for (const ev of events) {\n const d = (ev as { data?: Record<string, unknown> })?.data\n const t = d?.finalText ?? d?.text ?? d?.result\n if (typeof t === 'string' && t.length > 0) text = t\n }\n const fences = [...text.matchAll(/```(?:text|answer)?\\s*\\n([\\s\\S]*?)```/g)]\n return (fences.at(-1)?.[1] ?? text).trim()\n },\n}\n\nfunction rowToTask(row: DabstepFixtureRow, split: string, dir?: string): BenchTask {\n const meta: DabstepMeta = {\n taskId: row.task_id,\n split,\n golds: row.all_golds_by_task,\n ...(dir ? { resourceRoot: resourceRoot(dir) } : {}),\n }\n return {\n id: String(row.task_id),\n split,\n prompt: [\n 'Solve this DABStep data-analysis task using the mounted payment files.',\n 'Use code or shell commands as needed, then return only the final answer.',\n '',\n row.instructions,\n ].join('\\n'),\n metadata: meta as unknown as Record<string, unknown>,\n }\n}\n\nfunction readMeta(task: BenchTask): DabstepMeta {\n const md = task.metadata\n if (!md || typeof md.taskId !== 'number' || !Array.isArray(md.golds)) {\n throw new Error(`dabstep task ${task.id} missing metadata — loadTasks did not populate it`)\n }\n return md as unknown as DabstepMeta\n}\n\nfunction selectRows(rows: DabstepFixtureRow[], opts: LoadOptions, split: string, dir?: string): BenchTask[] {\n let tasks = rows.map((row) => rowToTask(row, split, dir))\n if (opts.ids) {\n const want = new Set(opts.ids)\n tasks = tasks.filter((task) => want.has(task.id))\n } else if (opts.limit !== undefined) {\n tasks = tasks.slice(0, opts.limit)\n }\n if (tasks.length === 0) throw new Error(`DABStep: no tasks matched ${JSON.stringify(opts)} for split=${split}`)\n return tasks\n}\n\nasync function loadFixtures(opts: LoadOptions, split: string): Promise<BenchTask[]> {\n const rows = JSON.parse(await readFile(FIXTURES, 'utf8')) as DabstepFixtureRow[]\n console.warn(`[dabstep] DABSTEP_FIXTURES=1 — loading ${rows.length} adapter fixtures from ${FIXTURES}`)\n return selectRows(rows, opts, split)\n}\n\nasync function loadOfficialTasks(dir: string, opts: LoadOptions, split: string): Promise<BenchTask[]> {\n const script = `\nimport ast, csv, json, sys\nfrom pathlib import Path\n\nroot = Path(sys.argv[1])\nsplit = sys.argv[2]\nlimit = None if sys.argv[3] == \"\" else int(sys.argv[3])\nids = set(json.loads(sys.argv[4]))\ndataset = root / \"dataset.csv\"\nsplit_file = root / \"splits\" / f\"{split}.txt\"\nif not dataset.exists():\n raise SystemExit(f\"missing official DABStep dataset.csv at {dataset}\")\nif not split_file.exists():\n raise SystemExit(f\"missing official DABStep split file at {split_file}\")\nsplit_ids = {int(line.strip()) for line in split_file.read_text().splitlines() if line.strip()}\nout = []\nwith dataset.open(newline=\"\") as f:\n for row in csv.DictReader(f):\n task_id = int(row[\"task_id\"])\n if task_id not in split_ids:\n continue\n if ids and str(task_id) not in ids:\n continue\n out.append({\n \"task_id\": task_id,\n \"instructions\": f\"{row['question']}\\\\n{row['guidelines']}\",\n \"all_golds_by_task\": ast.literal_eval(str(row[\"all_golds_by_task\"])),\n })\n if limit is not None and len(out) >= limit:\n break\nif not out:\n raise SystemExit(f\"no DABStep rows matched split={split} ids={sorted(ids)} limit={limit}\")\nprint(json.dumps(out))\n`\n const stdout = await runVenvPython(script, [dir, split, opts.limit === undefined ? '' : String(opts.limit), JSON.stringify(opts.ids ?? [])])\n return selectRows(JSON.parse(stdout) as DabstepFixtureRow[], opts, split, dir)\n}\n\nexport function createDabstepAdapter(): BenchmarkAdapter {\n const fixturesMode = process.env.DABSTEP_FIXTURES === '1'\n\n return {\n name: 'dabstep',\n output: dabstepAnswerOutput,\n\n async preflight() {\n if (fixturesMode) return\n const dir = dabstepDir()\n if (!dir) {\n throw new Error(\n 'DABSTEP_DIR is required. Fix: clone https://github.com/EnvCommons/DABStep, add the released dataset.csv under that checkout, then set DABSTEP_DIR=/path/to/DABStep.',\n )\n }\n await assertOfficialFiles(dir, DEFAULT_SPLIT)\n await loadOfficialTasks(dir, { limit: 1 }, DEFAULT_SPLIT)\n },\n\n async loadTasks(opts: LoadOptions = {}) {\n const split = opts.split ?? DEFAULT_SPLIT\n if (fixturesMode) return loadFixtures(opts, split)\n const dir = dabstepDir()\n if (!dir) throw new Error('DABSTEP_DIR is required to load official DABStep tasks')\n return loadOfficialTasks(dir, opts, split)\n },\n\n async goldArtifact(task: BenchTask) {\n const meta = readMeta(task)\n const first = meta.golds[0]\n if (!first) return undefined\n const value = first.value\n return value === undefined ? undefined : String(value)\n },\n\n async judge(task: BenchTask, artifact: string): Promise<BenchScore> {\n const meta = readMeta(task)\n const dir = dabstepDir()\n if (!dir) throw new Error('DABSTEP_DIR is required to judge DABStep tasks with the official grade.py')\n const stdout = await runVenvScriptStdin(\n join(benchRoot, 'scripts', 'dabstep_judge.py'),\n ['--grade-file', gradeFile(dir)],\n JSON.stringify({ prediction: artifact, golds: meta.golds }),\n { cwd: benchRoot },\n )\n const report = JSON.parse(stdout.trim().split('\\n').at(-1) ?? '{}') as { correct?: boolean; score?: number; error?: string }\n if (report.error) throw new Error(`DABStep judge error for ${task.id}: ${report.error}`)\n const score = typeof report.score === 'number' ? report.score : 0\n return {\n resolved: report.correct === true,\n score,\n detail: JSON.stringify({ taskId: meta.taskId, split: meta.split, correct: report.correct }),\n }\n },\n }\n}\n"],"mappings":"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","names":[]}
|