@tangle-network/agent-bench 0.4.0 → 0.4.3

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.
Files changed (200) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/adapters.d.ts +4 -11
  3. package/dist/adapters.js +78 -41
  4. package/dist/adapters.js.map +1 -1
  5. package/dist/benchmarks/_harness.d.ts +51 -66
  6. package/dist/benchmarks/_harness.js +329 -31
  7. package/dist/benchmarks/_harness.js.map +1 -1
  8. package/dist/benchmarks/aec-bench.d.ts +4 -25
  9. package/dist/benchmarks/aec-bench.js +242 -7
  10. package/dist/benchmarks/aec-bench.js.map +1 -1
  11. package/dist/benchmarks/agentbench.d.ts +5 -13
  12. package/dist/benchmarks/agentbench.js +114 -9
  13. package/dist/benchmarks/agentbench.js.map +1 -1
  14. package/dist/benchmarks/appworld.d.ts +9 -29
  15. package/dist/benchmarks/appworld.js +317 -12
  16. package/dist/benchmarks/appworld.js.map +1 -1
  17. package/dist/benchmarks/bfcl.d.ts +5 -15
  18. package/dist/benchmarks/bfcl.js +264 -9
  19. package/dist/benchmarks/bfcl.js.map +1 -1
  20. package/dist/benchmarks/cad-design.d.ts +16 -41
  21. package/dist/benchmarks/cad-design.js +512 -6
  22. package/dist/benchmarks/cad-design.js.map +1 -1
  23. package/dist/benchmarks/cadbench.d.ts +4 -17
  24. package/dist/benchmarks/cadbench.js +2 -8
  25. package/dist/benchmarks/cadgenbench.d.ts +4 -20
  26. package/dist/benchmarks/cadgenbench.js +2 -8
  27. package/dist/benchmarks/commit0.d.ts +5 -27
  28. package/dist/benchmarks/commit0.js +187 -9
  29. package/dist/benchmarks/commit0.js.map +1 -1
  30. package/dist/benchmarks/crag.d.ts +4 -12
  31. package/dist/benchmarks/crag.js +110 -8
  32. package/dist/benchmarks/crag.js.map +1 -1
  33. package/dist/benchmarks/dabstep.d.ts +5 -15
  34. package/dist/benchmarks/dabstep.js +177 -9
  35. package/dist/benchmarks/dabstep.js.map +1 -1
  36. package/dist/benchmarks/enterpriseops-gym.d.ts +5 -34
  37. package/dist/benchmarks/enterpriseops-gym.js +236 -9
  38. package/dist/benchmarks/enterpriseops-gym.js.map +1 -1
  39. package/dist/benchmarks/finresearchbench.d.ts +4 -13
  40. package/dist/benchmarks/finresearchbench.js +218 -7
  41. package/dist/benchmarks/finresearchbench.js.map +1 -1
  42. package/dist/benchmarks/finsearchcomp.d.ts +8 -39
  43. package/dist/benchmarks/finsearchcomp.js +267 -6
  44. package/dist/benchmarks/finsearchcomp.js.map +1 -1
  45. package/dist/benchmarks/frames.d.ts +15 -37
  46. package/dist/benchmarks/frames.js +408 -11
  47. package/dist/benchmarks/frames.js.map +1 -1
  48. package/dist/benchmarks/hotpotqa.d.ts +4 -24
  49. package/dist/benchmarks/hotpotqa.js +250 -14
  50. package/dist/benchmarks/hotpotqa.js.map +1 -1
  51. package/dist/benchmarks/humaneval.d.ts +19 -37
  52. package/dist/benchmarks/humaneval.js +279 -16
  53. package/dist/benchmarks/humaneval.js.map +1 -1
  54. package/dist/benchmarks/mind2web.d.ts +7 -34
  55. package/dist/benchmarks/mind2web.js +257 -8
  56. package/dist/benchmarks/mind2web.js.map +1 -1
  57. package/dist/benchmarks/nomiracl.d.ts +4 -13
  58. package/dist/benchmarks/nomiracl.js +146 -8
  59. package/dist/benchmarks/nomiracl.js.map +1 -1
  60. package/dist/benchmarks/open-rag-bench.d.ts +4 -12
  61. package/dist/benchmarks/open-rag-bench.js +110 -8
  62. package/dist/benchmarks/open-rag-bench.js.map +1 -1
  63. package/dist/benchmarks/programbench.d.ts +5 -29
  64. package/dist/benchmarks/programbench.js +161 -9
  65. package/dist/benchmarks/programbench.js.map +1 -1
  66. package/dist/benchmarks/rag-shared.d.ts +21 -20
  67. package/dist/benchmarks/rag-shared.js +245 -38
  68. package/dist/benchmarks/rag-shared.js.map +1 -1
  69. package/dist/benchmarks/ragbench.d.ts +4 -14
  70. package/dist/benchmarks/ragbench.js +127 -8
  71. package/dist/benchmarks/ragbench.js.map +1 -1
  72. package/dist/benchmarks/simpleqa.d.ts +17 -44
  73. package/dist/benchmarks/simpleqa.js +293 -10
  74. package/dist/benchmarks/simpleqa.js.map +1 -1
  75. package/dist/benchmarks/swe-bench.d.ts +23 -36
  76. package/dist/benchmarks/swe-bench.js +234 -13
  77. package/dist/benchmarks/swe-bench.js.map +1 -1
  78. package/dist/benchmarks/t2-ragbench.d.ts +4 -12
  79. package/dist/benchmarks/t2-ragbench.js +118 -8
  80. package/dist/benchmarks/t2-ragbench.js.map +1 -1
  81. package/dist/benchmarks/tau-bench-shared.d.ts +14 -22
  82. package/dist/benchmarks/tau-bench-shared.js +169 -9
  83. package/dist/benchmarks/tau-bench-shared.js.map +1 -1
  84. package/dist/benchmarks/tau2-bench.d.ts +5 -5
  85. package/dist/benchmarks/tau2-bench.js +28 -10
  86. package/dist/benchmarks/tau2-bench.js.map +1 -1
  87. package/dist/benchmarks/tau3-banking.d.ts +4 -13
  88. package/dist/benchmarks/tau3-banking.js +28 -8
  89. package/dist/benchmarks/tau3-banking.js.map +1 -1
  90. package/dist/benchmarks/terminal-bench.d.ts +4 -22
  91. package/dist/benchmarks/terminal-bench.js +140 -7
  92. package/dist/benchmarks/terminal-bench.js.map +1 -1
  93. package/dist/benchmarks/toollm.d.ts +5 -13
  94. package/dist/benchmarks/toollm.js +184 -9
  95. package/dist/benchmarks/toollm.js.map +1 -1
  96. package/dist/benchmarks/trata-hedge.d.ts +4 -30
  97. package/dist/benchmarks/trata-hedge.js +336 -6
  98. package/dist/benchmarks/trata-hedge.js.map +1 -1
  99. package/dist/benchmarks/types.d.ts +85 -94
  100. package/dist/benchmarks/types.js +1 -1
  101. package/dist/benchmarks/webarena-verified.d.ts +5 -13
  102. package/dist/benchmarks/webarena-verified.js +152 -9
  103. package/dist/benchmarks/webarena-verified.js.map +1 -1
  104. package/dist/cadbench-DpQWZHp4.js +285 -0
  105. package/dist/cadbench-DpQWZHp4.js.map +1 -0
  106. package/dist/cadgenbench-DRhczfsG.js +151 -0
  107. package/dist/cadgenbench-DRhczfsG.js.map +1 -0
  108. package/dist/index.d.ts +245 -293
  109. package/dist/index.js +1669 -1791
  110. package/dist/index.js.map +1 -1
  111. package/package.json +17 -14
  112. package/pier_agents/candidate_contract.py +238 -24
  113. package/pier_agents/tangle_candidate.py +75 -5
  114. package/scripts/verify-packed-consumer.mjs +84 -17
  115. package/scripts/verify-pier-agent.mts +6 -4
  116. package/src/benchmarks/_harness.test.mts +16 -1
  117. package/src/benchmarks/_harness.ts +9 -2
  118. package/src/benchmarks/terminal-bench.ts +2 -1
  119. package/src/corpus.test.mts +13 -0
  120. package/src/corpus.ts +4 -0
  121. package/src/profile-coordinates.ts +2 -2
  122. package/src/rollout-ledger/backfill-swe-arena.mts +5 -1
  123. package/src/rollout-ledger/settle-capture.mts +7 -1
  124. package/src/search-bench/profiles.ts +1 -1
  125. package/src/skill-sandbox-smoke.mts +2 -1
  126. package/src/swe-arena/gepa-seat.mts +1 -1
  127. package/src/swe-arena/ledger-orphans.test.mts +36 -34
  128. package/dist/benchmarks/cadbench.js.map +0 -1
  129. package/dist/benchmarks/cadgenbench.js.map +0 -1
  130. package/dist/benchmarks/types.js.map +0 -1
  131. package/dist/chunk-3U5TXJZS.js +0 -251
  132. package/dist/chunk-3U5TXJZS.js.map +0 -1
  133. package/dist/chunk-53UPUNBZ.js +0 -325
  134. package/dist/chunk-53UPUNBZ.js.map +0 -1
  135. package/dist/chunk-5H5XV76F.js +0 -240
  136. package/dist/chunk-5H5XV76F.js.map +0 -1
  137. package/dist/chunk-7GRVHU22.js +0 -208
  138. package/dist/chunk-7GRVHU22.js.map +0 -1
  139. package/dist/chunk-C7T7WEK2.js +0 -103
  140. package/dist/chunk-C7T7WEK2.js.map +0 -1
  141. package/dist/chunk-HWST3SED.js +0 -162
  142. package/dist/chunk-HWST3SED.js.map +0 -1
  143. package/dist/chunk-IA2FBTWC.js +0 -318
  144. package/dist/chunk-IA2FBTWC.js.map +0 -1
  145. package/dist/chunk-IFVINJ4B.js +0 -142
  146. package/dist/chunk-IFVINJ4B.js.map +0 -1
  147. package/dist/chunk-INNOYXCP.js +0 -387
  148. package/dist/chunk-INNOYXCP.js.map +0 -1
  149. package/dist/chunk-IZ5M6OAC.js +0 -169
  150. package/dist/chunk-IZ5M6OAC.js.map +0 -1
  151. package/dist/chunk-JTHWEDEW.js +0 -32
  152. package/dist/chunk-JTHWEDEW.js.map +0 -1
  153. package/dist/chunk-K3BQGZCT.js +0 -221
  154. package/dist/chunk-K3BQGZCT.js.map +0 -1
  155. package/dist/chunk-KP5KD6EN.js +0 -276
  156. package/dist/chunk-KP5KD6EN.js.map +0 -1
  157. package/dist/chunk-MQMRLGOG.js +0 -136
  158. package/dist/chunk-MQMRLGOG.js.map +0 -1
  159. package/dist/chunk-NQG5XDSB.js +0 -147
  160. package/dist/chunk-NQG5XDSB.js.map +0 -1
  161. package/dist/chunk-PA2ZKHJC.js +0 -230
  162. package/dist/chunk-PA2ZKHJC.js.map +0 -1
  163. package/dist/chunk-PB64GYIG.js +0 -118
  164. package/dist/chunk-PB64GYIG.js.map +0 -1
  165. package/dist/chunk-PUIRNYI7.js +0 -189
  166. package/dist/chunk-PUIRNYI7.js.map +0 -1
  167. package/dist/chunk-RCYQEFNX.js +0 -30
  168. package/dist/chunk-RCYQEFNX.js.map +0 -1
  169. package/dist/chunk-RH5F53JT.js +0 -182
  170. package/dist/chunk-RH5F53JT.js.map +0 -1
  171. package/dist/chunk-SEVJPLZC.js +0 -260
  172. package/dist/chunk-SEVJPLZC.js.map +0 -1
  173. package/dist/chunk-SFLA7OH3.js +0 -27
  174. package/dist/chunk-SFLA7OH3.js.map +0 -1
  175. package/dist/chunk-SHM6MRRF.js +0 -130
  176. package/dist/chunk-SHM6MRRF.js.map +0 -1
  177. package/dist/chunk-SHYIRB7I.js +0 -120
  178. package/dist/chunk-SHYIRB7I.js.map +0 -1
  179. package/dist/chunk-SVR2LKYI.js +0 -116
  180. package/dist/chunk-SVR2LKYI.js.map +0 -1
  181. package/dist/chunk-TBKU5XQI.js +0 -228
  182. package/dist/chunk-TBKU5XQI.js.map +0 -1
  183. package/dist/chunk-UPAMRDX4.js +0 -233
  184. package/dist/chunk-UPAMRDX4.js.map +0 -1
  185. package/dist/chunk-V7AEBY6U.js +0 -144
  186. package/dist/chunk-V7AEBY6U.js.map +0 -1
  187. package/dist/chunk-VQRS7VUC.js +0 -342
  188. package/dist/chunk-VQRS7VUC.js.map +0 -1
  189. package/dist/chunk-WSKWVEQB.js +0 -317
  190. package/dist/chunk-WSKWVEQB.js.map +0 -1
  191. package/dist/chunk-X3BTXCJ4.js +0 -262
  192. package/dist/chunk-X3BTXCJ4.js.map +0 -1
  193. package/dist/chunk-XKEFIFIC.js +0 -197
  194. package/dist/chunk-XKEFIFIC.js.map +0 -1
  195. package/dist/chunk-XYA4XSNU.js +0 -148
  196. package/dist/chunk-XYA4XSNU.js.map +0 -1
  197. package/dist/chunk-YSMEKBTD.js +0 -211
  198. package/dist/chunk-YSMEKBTD.js.map +0 -1
  199. package/dist/chunk-Z4TZ76N7.js +0 -170
  200. package/dist/chunk-Z4TZ76N7.js.map +0 -1
@@ -1,8 +1,243 @@
1
- import {
2
- createAecBenchAdapter
3
- } from "../chunk-K3BQGZCT.js";
4
- import "../chunk-WSKWVEQB.js";
5
- export {
6
- createAecBenchAdapter
7
- };
1
+ import { benchRoot, preflightVenvImports, readJsonReport, runStagedJudge, stageFile, venvPython } from "./_harness.js";
2
+ import { readFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ //#region src/benchmarks/aec-bench.ts
5
+ /**
6
+ * AEC-Bench adapter (TheodoreGalanos/aec-bench, MIT) — closed-form
7
+ * Architecture/Engineering/Construction calculation tasks. Worker artifact = a
8
+ * markdown solution ending in a fenced ```json block with the required numeric
9
+ * fields. Judge = the task's OWN `tests/verify.py`, run with python3: it
10
+ * recomputes ground truth from the embedded engineering formulas, extracts the
11
+ * last JSON block from the artifact, scores each field by math.isclose within a
12
+ * per-field rel_tol, and writes {"reward": mean} + per-field details.json.
13
+ * GRADED / partial-credit, FULLY DETERMINISTIC — no LLM judge.
14
+ *
15
+ * Distinct from the multimodal nomic-ai/aec-bench; this is the deterministic
16
+ * calculation platform. The runnable-instance verify.py only needs python3 (no
17
+ * Docker, no Harbor) for the pure-calc disciplines, so the local gate runs at
18
+ * conc<=2 without a container backend.
19
+ *
20
+ * Requires for a live run: network to raw.githubusercontent.com /
21
+ * api.github.com (the in-repo tasks tree) + a python3 interpreter (the bench
22
+ * venv) to run verify.py. For offline/CI, set AEC_FIXTURES=1 to load the
23
+ * committed fixtures (bench/fixtures/aec-bench.json) — never a silent fallback.
24
+ */
25
+ const FIXTURES = join(benchRoot, "fixtures", "aec-bench.json");
26
+ const REPO = "TheodoreGalanos/aec-bench";
27
+ const RAW = `https://raw.githubusercontent.com/${REPO}/main`;
28
+ const TREE = `https://api.github.com/repos/${REPO}/git/trees/main?recursive=1`;
29
+ /** Matches every runnable-instance task id at ANY depth under tasks/. */
30
+ const verifyPathPattern = /^tasks\/(.+)\/tests\/verify\.py$/;
31
+ /** Default cap on tasks enumerated before a `limit` is applied. */
32
+ const DEFAULT_LIMIT = 10;
33
+ function recordToTask(rec) {
34
+ const meta = {
35
+ taskId: rec.id,
36
+ discipline: rec.discipline,
37
+ taskToml: rec.task_toml,
38
+ verifyPy: rec.verify_py,
39
+ goldenPassMd: rec.golden_pass_md
40
+ };
41
+ return {
42
+ id: rec.id,
43
+ split: rec.discipline,
44
+ prompt: rec.instruction,
45
+ metadata: meta
46
+ };
47
+ }
48
+ function readMeta(task) {
49
+ const md = task.metadata;
50
+ if (!md || typeof md.verifyPy !== "string" || typeof md.taskId !== "string") throw new Error(`aec-bench task ${task.id} missing verifier metadata — loadTasks did not populate it`);
51
+ return md;
52
+ }
53
+ async function fetchText(url) {
54
+ const res = await fetch(url);
55
+ if (!res.ok) throw new Error(`aec-bench fetch ${res.status}: ${url}`);
56
+ return res.text();
57
+ }
58
+ /** Like fetchText but returns null on a 404 (absent optional file); throws on any other non-OK. */
59
+ async function fetchTextOrNull(url) {
60
+ const res = await fetch(url);
61
+ if (res.status === 404) return null;
62
+ if (!res.ok) throw new Error(`aec-bench fetch ${res.status}: ${url}`);
63
+ return res.text();
64
+ }
65
+ /**
66
+ * One recursive git-tree call enumerates EVERY runnable-instance id at any depth:
67
+ * a task is runnable iff it ships tests/verify.py. The captured group is the id
68
+ * `tasks/<id>/tests/verify.py` → `<id>` (e.g. 'electrical/pf-droop', or a deeper
69
+ * '<discipline>/<family>/<task>'). Throws loud on a non-OK tree response.
70
+ */
71
+ async function listAllInstances() {
72
+ const res = await fetch(TREE);
73
+ if (!res.ok) throw new Error(`aec-bench tree ${res.status}: ${TREE}`);
74
+ const { tree } = await res.json();
75
+ const ids = [];
76
+ for (const entry of tree) {
77
+ const m = verifyPathPattern.exec(entry.path);
78
+ if (m?.[1]) ids.push(m[1]);
79
+ }
80
+ return ids;
81
+ }
82
+ /**
83
+ * Fetch one task's instruction.md + task.toml + tests/verify.py + golden_pass.md.
84
+ * Returns null when the dir is a SEED (no runnable verify.py) so enumeration can
85
+ * skip it without faking a task. Gold is OPTIONAL — a task that ships a non-md
86
+ * ground truth (e.g. tests/ground_truth.json) yields golden_pass_md=null; the
87
+ * deterministic judge needs only verify.py.
88
+ */
89
+ async function fetchInstance(id) {
90
+ const base = `${RAW}/tasks/${id}`;
91
+ const verify = await fetch(`${base}/tests/verify.py`);
92
+ if (verify.status === 404) return null;
93
+ if (!verify.ok) throw new Error(`aec-bench fetch ${verify.status}: ${id}/tests/verify.py`);
94
+ const [instruction, task_toml, golden_pass_md] = await Promise.all([
95
+ fetchText(`${base}/instruction.md`),
96
+ fetchText(`${base}/task.toml`),
97
+ fetchTextOrNull(`${base}/tests/fixtures/golden_pass.md`)
98
+ ]);
99
+ return {
100
+ id,
101
+ discipline: id.split("/")[0] ?? "",
102
+ instruction,
103
+ task_toml,
104
+ verify_py: await verify.text(),
105
+ golden_pass_md
106
+ };
107
+ }
108
+ function selectFixtures(records, opts) {
109
+ let tasks = records.map(recordToTask);
110
+ if (opts.split) tasks = tasks.filter((t) => t.split === opts.split);
111
+ if (opts.ids) {
112
+ const want = new Set(opts.ids);
113
+ tasks = tasks.filter((t) => want.has(t.id));
114
+ } else if (opts.limit !== void 0) tasks = tasks.slice(0, opts.limit);
115
+ return tasks;
116
+ }
117
+ async function loadFixtures(opts) {
118
+ const records = JSON.parse(await readFile(FIXTURES, "utf8"));
119
+ console.warn(`[aec-bench] AEC_FIXTURES=1 — loading ${records.length} committed fixtures from ${FIXTURES} (no GitHub fetch)`);
120
+ return selectFixtures(records, opts);
121
+ }
122
+ /** Enumerate live tasks: explicit ids (each required — throws on a bad id), or a
123
+ * capped slice of the recursive tree (optionally filtered to one split). Per-task
124
+ * resilient: a single fetch failure warns + SKIPS that task, never aborting the
125
+ * batch. Skips seed dirs (no verify.py) — never fabricates. */
126
+ async function loadLive(opts) {
127
+ if (opts.ids) {
128
+ const records = [];
129
+ for (const id of opts.ids) {
130
+ const rec = await fetchInstance(id);
131
+ if (!rec) throw new Error(`aec-bench: ${id} has no tests/verify.py (seed-only or wrong id)`);
132
+ records.push(rec);
133
+ }
134
+ return records.map(recordToTask);
135
+ }
136
+ const limit = opts.limit ?? DEFAULT_LIMIT;
137
+ let ids = await listAllInstances();
138
+ if (opts.split) ids = ids.filter((id) => id.startsWith(`${opts.split}/`));
139
+ const records = [];
140
+ for (const id of ids) {
141
+ if (records.length >= limit) break;
142
+ try {
143
+ const rec = await fetchInstance(id);
144
+ if (rec) records.push(rec);
145
+ } catch (err) {
146
+ console.warn(`[aec-bench] skipping ${id}: ${err instanceof Error ? err.message : String(err)}`);
147
+ }
148
+ }
149
+ if (records.length === 0) throw new Error(`aec-bench loadTasks found no runnable instances for ${JSON.stringify(opts)} (no tests/verify.py matched the requested split). Set AEC_FIXTURES=1 to run offline.`);
150
+ return records.map(recordToTask);
151
+ }
152
+ /**
153
+ * Run the task's own verify.py with python3 over the artifact via the shared
154
+ * staged-judge spine (mkdtemp → stage → spawn → parseReport → cleanup). verify.py
155
+ * writes {"reward": mean} to --output and per-field details.json as a sibling. We
156
+ * read both: reward → graded score; details → sub-scores for the trace-analyst.
157
+ * Fail loud if the verifier never wrote a numeric reward (a crashed verifier is
158
+ * NOT a silent 0 — verify.py's own except-trap writes reward 0.0, so an absent /
159
+ * non-numeric reward.json is a real bug, surfaced by readJsonReport).
160
+ */
161
+ async function runVerifier(meta, artifact) {
162
+ return runStagedJudge({
163
+ tmpPrefix: "aecbench-",
164
+ timeoutMs: 12e4,
165
+ async stage(dir) {
166
+ await Promise.all([stageFile(join(dir, "output.md"), artifact), stageFile(join(dir, "verify.py"), meta.verifyPy)]);
167
+ },
168
+ bin: venvPython,
169
+ argv: (dir) => [
170
+ join(dir, "verify.py"),
171
+ "--input",
172
+ join(dir, "output.md"),
173
+ "--output",
174
+ join(dir, "reward.json")
175
+ ],
176
+ async parseReport(dir) {
177
+ const report = await readJsonReport(join(dir, "reward.json"));
178
+ const reward = report.reward;
179
+ if (typeof reward !== "number" || !Number.isFinite(reward)) throw new Error(`aec-bench verify.py wrote no numeric reward for ${meta.taskId}: ${JSON.stringify(report)}`);
180
+ const details = await readFile(join(dir, "details.json"), "utf8").then((s) => JSON.parse(s), () => ({}));
181
+ const score = Math.max(0, Math.min(1, reward));
182
+ return {
183
+ resolved: score >= 1,
184
+ score,
185
+ detail: JSON.stringify({
186
+ taskId: meta.taskId,
187
+ discipline: meta.discipline,
188
+ reward,
189
+ fields: details
190
+ })
191
+ };
192
+ }
193
+ });
194
+ }
195
+ function createAecBenchAdapter() {
196
+ const fixturesMode = process.env.AEC_FIXTURES === "1";
197
+ return {
198
+ name: "aec-bench",
199
+ async preflight() {
200
+ await preflightVenvImports({
201
+ modules: [
202
+ "math",
203
+ "json",
204
+ "re"
205
+ ],
206
+ requireDocker: false,
207
+ fix: "Fix: python3 -m venv bench/.venv (verify.py only needs the stdlib — no pip install)."
208
+ });
209
+ if (fixturesMode) {
210
+ await readFile(FIXTURES, "utf8").catch((err) => {
211
+ throw new Error(`AEC_FIXTURES=1 but ${FIXTURES} unreadable: ${err instanceof Error ? err.message : err}`);
212
+ });
213
+ return;
214
+ }
215
+ const res = await fetch(`${RAW}/README.md`, { method: "HEAD" }).catch((err) => {
216
+ throw new Error(`aec-bench preflight failed reaching ${RAW}: ${err instanceof Error ? err.message : err}\nFix: ensure network access to raw.githubusercontent.com, or set AEC_FIXTURES=1 to run offline.`);
217
+ });
218
+ if (!res.ok) throw new Error(`aec-bench preflight: ${REPO} README HEAD ${res.status}. Set AEC_FIXTURES=1 to run against committed fixtures.`);
219
+ },
220
+ async loadTasks(opts = {}) {
221
+ if (fixturesMode) return loadFixtures(opts);
222
+ try {
223
+ return await loadLive(opts);
224
+ } catch (err) {
225
+ if (err instanceof Error && /fetch \d|ENOTFOUND|getaddrinfo|network/i.test(err.message)) {
226
+ console.warn(`[aec-bench] live fetch failed (${err.message.slice(0, 160)}); falling back to committed fixtures at ${FIXTURES}`);
227
+ return loadFixtures(opts);
228
+ }
229
+ throw err;
230
+ }
231
+ },
232
+ async goldArtifact(task) {
233
+ return readMeta(task).goldenPassMd ?? void 0;
234
+ },
235
+ async judge(task, artifact) {
236
+ return runVerifier(readMeta(task), artifact);
237
+ }
238
+ };
239
+ }
240
+ //#endregion
241
+ export { createAecBenchAdapter };
242
+
8
243
  //# sourceMappingURL=aec-bench.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
1
+ {"version":3,"file":"aec-bench.js","names":[],"sources":["../../src/benchmarks/aec-bench.ts"],"sourcesContent":["/**\n * AEC-Bench adapter (TheodoreGalanos/aec-bench, MIT) — closed-form\n * Architecture/Engineering/Construction calculation tasks. Worker artifact = a\n * markdown solution ending in a fenced ```json block with the required numeric\n * fields. Judge = the task's OWN `tests/verify.py`, run with python3: it\n * recomputes ground truth from the embedded engineering formulas, extracts the\n * last JSON block from the artifact, scores each field by math.isclose within a\n * per-field rel_tol, and writes {\"reward\": mean} + per-field details.json.\n * GRADED / partial-credit, FULLY DETERMINISTIC — no LLM judge.\n *\n * Distinct from the multimodal nomic-ai/aec-bench; this is the deterministic\n * calculation platform. The runnable-instance verify.py only needs python3 (no\n * Docker, no Harbor) for the pure-calc disciplines, so the local gate runs at\n * conc<=2 without a container backend.\n *\n * Requires for a live run: network to raw.githubusercontent.com /\n * api.github.com (the in-repo tasks tree) + a python3 interpreter (the bench\n * venv) to run verify.py. For offline/CI, set AEC_FIXTURES=1 to load the\n * committed fixtures (bench/fixtures/aec-bench.json) — never a silent fallback.\n */\n\nimport { readFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport { benchRoot, preflightVenvImports, readJsonReport, runStagedJudge, stageFile, venvPython } from './_harness'\nimport type { BenchmarkAdapter, BenchScore, BenchTask, LoadOptions } from './types'\n\nconst FIXTURES = join(benchRoot, 'fixtures', 'aec-bench.json')\n\nconst REPO = 'TheodoreGalanos/aec-bench'\nconst RAW = `https://raw.githubusercontent.com/${REPO}/main`\nconst TREE = `https://api.github.com/repos/${REPO}/git/trees/main?recursive=1`\n\n/** Matches every runnable-instance task id at ANY depth under tasks/. */\nconst verifyPathPattern = /^tasks\\/(.+)\\/tests\\/verify\\.py$/\n\n/** Default cap on tasks enumerated before a `limit` is applied. */\nconst DEFAULT_LIMIT = 10\n\ninterface AecRecord {\n /** Canonical task id `<discipline>/<task>`, e.g. 'electrical/catenary-sag'. */\n id: string\n discipline: string\n /** instruction.md — the self-contained prompt (table + required outputs + JSON schema). */\n instruction: string\n /** task.toml — metadata/difficulty/timeouts (carried for trace context). */\n task_toml: string\n /** tests/verify.py — the deterministic verifier (recomputes GT, scores fields). */\n verify_py: string\n /** tests/fixtures/golden_pass.md — the oracle artifact that scores reward 1.0,\n * when the task ships one. Null when the task only ships a non-md ground truth\n * (e.g. tests/ground_truth.json); the judge never needs it, only goldArtifact does. */\n golden_pass_md: string | null\n}\n\ninterface AecMeta {\n taskId: string\n discipline: string\n taskToml: string\n verifyPy: string\n goldenPassMd: string | null\n}\n\nfunction recordToTask(rec: AecRecord): BenchTask {\n const meta: AecMeta = {\n taskId: rec.id,\n discipline: rec.discipline,\n taskToml: rec.task_toml,\n verifyPy: rec.verify_py,\n goldenPassMd: rec.golden_pass_md,\n }\n return {\n id: rec.id,\n split: rec.discipline,\n // instruction.md is fully self-contained and already specifies the exact JSON\n // output schema + the \"write to /workspace/output.md\" contract the verifier\n // keys off — we pass it through verbatim so the verify.py extractor matches.\n prompt: rec.instruction,\n metadata: meta as unknown as Record<string, unknown>,\n }\n}\n\nfunction readMeta(task: BenchTask): AecMeta {\n const md = task.metadata\n // Gold is optional (goldenPassMd may be null) — only the verifier + id are required.\n if (!md || typeof md.verifyPy !== 'string' || typeof md.taskId !== 'string') {\n throw new Error(`aec-bench task ${task.id} missing verifier metadata — loadTasks did not populate it`)\n }\n return md as unknown as AecMeta\n}\n\nasync function fetchText(url: string): Promise<string> {\n const res = await fetch(url)\n if (!res.ok) throw new Error(`aec-bench fetch ${res.status}: ${url}`)\n return res.text()\n}\n\n/** Like fetchText but returns null on a 404 (absent optional file); throws on any other non-OK. */\nasync function fetchTextOrNull(url: string): Promise<string | null> {\n const res = await fetch(url)\n if (res.status === 404) return null\n if (!res.ok) throw new Error(`aec-bench fetch ${res.status}: ${url}`)\n return res.text()\n}\n\ninterface GitTree {\n tree: Array<{ path: string; type: string }>\n}\n\n/**\n * One recursive git-tree call enumerates EVERY runnable-instance id at any depth:\n * a task is runnable iff it ships tests/verify.py. The captured group is the id\n * `tasks/<id>/tests/verify.py` → `<id>` (e.g. 'electrical/pf-droop', or a deeper\n * '<discipline>/<family>/<task>'). Throws loud on a non-OK tree response.\n */\nasync function listAllInstances(): Promise<string[]> {\n const res = await fetch(TREE)\n if (!res.ok) throw new Error(`aec-bench tree ${res.status}: ${TREE}`)\n const { tree } = (await res.json()) as GitTree\n const ids: string[] = []\n for (const entry of tree) {\n const m = verifyPathPattern.exec(entry.path)\n if (m?.[1]) ids.push(m[1])\n }\n return ids\n}\n\n/**\n * Fetch one task's instruction.md + task.toml + tests/verify.py + golden_pass.md.\n * Returns null when the dir is a SEED (no runnable verify.py) so enumeration can\n * skip it without faking a task. Gold is OPTIONAL — a task that ships a non-md\n * ground truth (e.g. tests/ground_truth.json) yields golden_pass_md=null; the\n * deterministic judge needs only verify.py.\n */\nasync function fetchInstance(id: string): Promise<AecRecord | null> {\n const base = `${RAW}/tasks/${id}`\n const verify = await fetch(`${base}/tests/verify.py`)\n if (verify.status === 404) return null\n if (!verify.ok) throw new Error(`aec-bench fetch ${verify.status}: ${id}/tests/verify.py`)\n const [instruction, task_toml, golden_pass_md] = await Promise.all([\n fetchText(`${base}/instruction.md`),\n fetchText(`${base}/task.toml`),\n fetchTextOrNull(`${base}/tests/fixtures/golden_pass.md`),\n ])\n return {\n id,\n discipline: id.split('/')[0] ?? '',\n instruction,\n task_toml,\n verify_py: await verify.text(),\n golden_pass_md,\n }\n}\n\nfunction selectFixtures(records: AecRecord[], opts: LoadOptions): BenchTask[] {\n let tasks = records.map(recordToTask)\n if (opts.split) tasks = tasks.filter((t) => t.split === opts.split)\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\nasync function loadFixtures(opts: LoadOptions): Promise<BenchTask[]> {\n const records = JSON.parse(await readFile(FIXTURES, 'utf8')) as AecRecord[]\n console.warn(\n `[aec-bench] AEC_FIXTURES=1 — loading ${records.length} committed fixtures from ${FIXTURES} (no GitHub fetch)`,\n )\n return selectFixtures(records, opts)\n}\n\n/** Enumerate live tasks: explicit ids (each required — throws on a bad id), or a\n * capped slice of the recursive tree (optionally filtered to one split). Per-task\n * resilient: a single fetch failure warns + SKIPS that task, never aborting the\n * batch. Skips seed dirs (no verify.py) — never fabricates. */\nasync function loadLive(opts: LoadOptions): Promise<BenchTask[]> {\n if (opts.ids) {\n const records: AecRecord[] = []\n for (const id of opts.ids) {\n const rec = await fetchInstance(id)\n if (!rec) throw new Error(`aec-bench: ${id} has no tests/verify.py (seed-only or wrong id)`)\n records.push(rec)\n }\n return records.map(recordToTask)\n }\n const limit = opts.limit ?? DEFAULT_LIMIT\n let ids = await listAllInstances()\n if (opts.split) ids = ids.filter((id) => id.startsWith(`${opts.split}/`))\n const records: AecRecord[] = []\n for (const id of ids) {\n if (records.length >= limit) break\n try {\n const rec = await fetchInstance(id)\n if (rec) records.push(rec)\n } catch (err) {\n // One bad task must NEVER abort the batch — warn and skip it.\n console.warn(`[aec-bench] skipping ${id}: ${err instanceof Error ? err.message : String(err)}`)\n }\n }\n if (records.length === 0) {\n throw new Error(\n `aec-bench loadTasks found no runnable instances for ${JSON.stringify(opts)} ` +\n `(no tests/verify.py matched the requested split). Set AEC_FIXTURES=1 to run offline.`,\n )\n }\n return records.map(recordToTask)\n}\n\n/**\n * Run the task's own verify.py with python3 over the artifact via the shared\n * staged-judge spine (mkdtemp → stage → spawn → parseReport → cleanup). verify.py\n * writes {\"reward\": mean} to --output and per-field details.json as a sibling. We\n * read both: reward → graded score; details → sub-scores for the trace-analyst.\n * Fail loud if the verifier never wrote a numeric reward (a crashed verifier is\n * NOT a silent 0 — verify.py's own except-trap writes reward 0.0, so an absent /\n * non-numeric reward.json is a real bug, surfaced by readJsonReport).\n */\nasync function runVerifier(meta: AecMeta, artifact: string): Promise<BenchScore> {\n return runStagedJudge({\n tmpPrefix: 'aecbench-',\n timeoutMs: 120_000,\n async stage(dir) {\n await Promise.all([\n stageFile(join(dir, 'output.md'), artifact),\n stageFile(join(dir, 'verify.py'), meta.verifyPy),\n ])\n },\n bin: venvPython,\n argv: (dir) => [join(dir, 'verify.py'), '--input', join(dir, 'output.md'), '--output', join(dir, 'reward.json')],\n async parseReport(dir) {\n const report = await readJsonReport<{ reward?: number }>(join(dir, 'reward.json'))\n const reward = report.reward\n if (typeof reward !== 'number' || !Number.isFinite(reward)) {\n throw new Error(`aec-bench verify.py wrote no numeric reward for ${meta.taskId}: ${JSON.stringify(report)}`)\n }\n const details = await readFile(join(dir, 'details.json'), 'utf8').then(\n (s) => JSON.parse(s) as Record<string, number>,\n () => ({}),\n )\n const score = Math.max(0, Math.min(1, reward))\n return {\n // resolved = full credit (all fields within tolerance), matching aec-bench's perfect_rate.\n resolved: score >= 1,\n score,\n detail: JSON.stringify({ taskId: meta.taskId, discipline: meta.discipline, reward, fields: details }),\n }\n },\n })\n}\n\nexport function createAecBenchAdapter(): BenchmarkAdapter {\n const fixturesMode = process.env.AEC_FIXTURES === '1'\n\n return {\n name: 'aec-bench',\n\n async preflight() {\n // The verifier is python3 over the stdlib (math/json/re) — no pip install.\n // Reuse the shared import-probe so the bench venv interpreter is proven to\n // exist + run before any judge spawns verify.py.\n await preflightVenvImports({\n modules: ['math', 'json', 're'],\n requireDocker: false,\n fix: 'Fix: python3 -m venv bench/.venv (verify.py only needs the stdlib — no pip install).',\n })\n if (fixturesMode) {\n await readFile(FIXTURES, 'utf8').catch((err) => {\n throw new Error(`AEC_FIXTURES=1 but ${FIXTURES} unreadable: ${err instanceof Error ? err.message : err}`)\n })\n return\n }\n const res = await fetch(`${RAW}/README.md`, { method: 'HEAD' }).catch((err) => {\n throw new Error(\n `aec-bench preflight failed reaching ${RAW}: ${err instanceof Error ? err.message : err}\\n` +\n `Fix: ensure network access to raw.githubusercontent.com, or set AEC_FIXTURES=1 to run offline.`,\n )\n })\n if (!res.ok) {\n throw new Error(\n `aec-bench preflight: ${REPO} README HEAD ${res.status}. Set AEC_FIXTURES=1 to run against committed fixtures.`,\n )\n }\n },\n\n async loadTasks(opts: LoadOptions = {}) {\n if (fixturesMode) return loadFixtures(opts)\n try {\n return await loadLive(opts)\n } catch (err) {\n // A reachability failure falls back to fixtures with an explicit warn; a\n // wrong-id / seed-only error (the loader's own throw) propagates.\n if (err instanceof Error && /fetch \\d|ENOTFOUND|getaddrinfo|network/i.test(err.message)) {\n console.warn(\n `[aec-bench] live fetch failed (${err.message.slice(0, 160)}); falling back to committed fixtures at ${FIXTURES}`,\n )\n return loadFixtures(opts)\n }\n throw err\n }\n },\n\n async goldArtifact(task: BenchTask) {\n // Gold = the task's own golden_pass.md (scores reward 1.0 through the SAME\n // verify.py the real artifact takes), proving the judge end-to-end. Tasks\n // without a golden_pass.md (non-md ground truth) have no oracle artifact.\n const meta = readMeta(task)\n return meta.goldenPassMd ?? undefined\n },\n\n async judge(task: BenchTask, artifact: string): Promise<BenchScore> {\n const meta = readMeta(task)\n // verify.py fail-closes an empty/unparseable artifact to reward 0.0 itself,\n // so we pass it straight through (no pre-judging here).\n return runVerifier(meta, artifact)\n },\n }\n}\n"],"mappings":"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"}
@@ -1,16 +1,8 @@
1
- import { OutputAdapter } from '@tangle-network/agent-runtime/loops';
2
- import { BenchmarkAdapter } from './types.js';
3
-
4
- /**
5
- * AgentBench deterministic subset adapter.
6
- *
7
- * This targets AgentBench DBBench rows only: question + table + published label.
8
- * It does not wrap AgentBench's controller protocol or the non-deterministic game
9
- * environments. Worker artifact = final answer text. Judge = exact match against
10
- * the official DBBench label list after light whitespace/case normalization.
11
- */
12
-
1
+ import { BenchmarkAdapter } from "./types.js";
2
+ import { OutputAdapter } from "@tangle-network/agent-runtime/loops";
3
+ //#region src/benchmarks/agentbench.d.ts
13
4
  declare const agentbenchAnswerOutput: OutputAdapter<string>;
14
5
  declare function createAgentBenchAdapter(): BenchmarkAdapter;
15
-
6
+ //#endregion
16
7
  export { agentbenchAnswerOutput, createAgentBenchAdapter };
8
+ //# sourceMappingURL=agentbench.d.ts.map
@@ -1,10 +1,115 @@
1
- import {
2
- agentbenchAnswerOutput,
3
- createAgentBenchAdapter
4
- } from "../chunk-PB64GYIG.js";
5
- import "../chunk-WSKWVEQB.js";
6
- export {
7
- agentbenchAnswerOutput,
8
- createAgentBenchAdapter
9
- };
1
+ import { benchRoot } from "./_harness.js";
2
+ import { readFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ //#region src/benchmarks/agentbench.ts
5
+ /**
6
+ * AgentBench deterministic subset adapter.
7
+ *
8
+ * This targets AgentBench DBBench rows only: question + table + published label.
9
+ * It does not wrap AgentBench's controller protocol or the non-deterministic game
10
+ * environments. Worker artifact = final answer text. Judge = exact match against
11
+ * the official DBBench label list after light whitespace/case normalization.
12
+ */
13
+ const FIXTURES = join(benchRoot, "fixtures", "agentbench-dbbench.json");
14
+ const DEFAULT_SPLIT = "dev";
15
+ const agentbenchDir = () => process.env.AGENTBENCH_DIR;
16
+ const agentbenchAnswerOutput = { parse(events) {
17
+ let text = "";
18
+ for (const ev of events) {
19
+ const d = ev?.data;
20
+ const t = d?.finalText ?? d?.text ?? d?.result;
21
+ if (typeof t === "string" && t.length > 0) text = t;
22
+ }
23
+ return ([...text.matchAll(/```(?:text|answer)?\s*\n([\s\S]*?)```/g)].at(-1)?.[1] ?? text).trim();
24
+ } };
25
+ function normalizeAnswer(value) {
26
+ return value.toLowerCase().replace(/\s+/g, " ").replace(/^["'`]+|["'`]+$/g, "").trim();
27
+ }
28
+ function rowToTask(row, index, split) {
29
+ const columns = row.table?.table_info?.columns?.map((c) => `${c.name}${c.type ? ` (${c.type})` : ""}`).join(", ");
30
+ const sampleRows = row.table?.table_info?.rows?.slice(0, 40);
31
+ const meta = {
32
+ labels: row.label,
33
+ split,
34
+ subset: "dbbench",
35
+ table: row.table
36
+ };
37
+ return {
38
+ id: `dbbench-${split}-${index}`,
39
+ split,
40
+ prompt: [
41
+ "Answer this AgentBench DBBench question using the table below.",
42
+ "Return only the answer value.",
43
+ "",
44
+ `Question: ${row.description}`,
45
+ row.table?.table_name ? `Table: ${row.table.table_name}` : void 0,
46
+ columns ? `Columns: ${columns}` : void 0,
47
+ sampleRows ? `Rows JSON: ${JSON.stringify(sampleRows)}` : void 0
48
+ ].filter(Boolean).join("\n"),
49
+ metadata: meta
50
+ };
51
+ }
52
+ function readMeta(task) {
53
+ const md = task.metadata;
54
+ if (!md || !Array.isArray(md.labels)) throw new Error(`agentbench task ${task.id} missing metadata — loadTasks did not populate it`);
55
+ return md;
56
+ }
57
+ function selectRows(rows, opts, split) {
58
+ let tasks = rows.map((row, index) => rowToTask(row, index, split));
59
+ if (opts.ids) {
60
+ const want = new Set(opts.ids);
61
+ tasks = tasks.filter((task) => want.has(task.id));
62
+ } else if (opts.limit !== void 0) tasks = tasks.slice(0, opts.limit);
63
+ if (tasks.length === 0) throw new Error(`AgentBench DBBench: no tasks matched ${JSON.stringify(opts)}`);
64
+ return tasks;
65
+ }
66
+ async function loadJsonl(path) {
67
+ return (await readFile(path, "utf8")).split("\n").map((line) => line.trim()).filter(Boolean).map((line) => JSON.parse(line));
68
+ }
69
+ async function loadFixtures(opts, split) {
70
+ const rows = JSON.parse(await readFile(FIXTURES, "utf8"));
71
+ console.warn(`[agentbench] AGENTBENCH_FIXTURES=1 — loading ${rows.length} DBBench adapter fixtures`);
72
+ return selectRows(rows, opts, split);
73
+ }
74
+ function createAgentBenchAdapter() {
75
+ const fixturesMode = process.env.AGENTBENCH_FIXTURES === "1";
76
+ return {
77
+ name: "agentbench",
78
+ output: agentbenchAnswerOutput,
79
+ async preflight() {
80
+ if (fixturesMode) return;
81
+ const dir = agentbenchDir();
82
+ if (!dir) throw new Error("AGENTBENCH_DIR is required. Fix: clone https://github.com/THUDM/AgentBench and set AGENTBENCH_DIR=/path/to/AgentBench.");
83
+ await loadJsonl(join(dir, "data", "dbbench", `${DEFAULT_SPLIT}.jsonl`));
84
+ },
85
+ async loadTasks(opts = {}) {
86
+ const split = opts.split ?? DEFAULT_SPLIT;
87
+ if (fixturesMode) return loadFixtures(opts, split);
88
+ const dir = agentbenchDir();
89
+ if (!dir) throw new Error("AGENTBENCH_DIR is required to load AgentBench DBBench tasks");
90
+ return selectRows(await loadJsonl(join(dir, "data", "dbbench", `${split}.jsonl`)), opts, split);
91
+ },
92
+ async goldArtifact(task) {
93
+ return readMeta(task).labels[0];
94
+ },
95
+ async judge(task, artifact) {
96
+ const meta = readMeta(task);
97
+ const got = normalizeAnswer(artifact);
98
+ const resolved = meta.labels.map(normalizeAnswer).includes(got);
99
+ return {
100
+ resolved,
101
+ score: resolved ? 1 : 0,
102
+ detail: JSON.stringify({
103
+ subset: meta.subset,
104
+ split: meta.split,
105
+ expected: meta.labels,
106
+ got: artifact
107
+ })
108
+ };
109
+ }
110
+ };
111
+ }
112
+ //#endregion
113
+ export { agentbenchAnswerOutput, createAgentBenchAdapter };
114
+
10
115
  //# sourceMappingURL=agentbench.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
1
+ {"version":3,"file":"agentbench.js","names":[],"sources":["../../src/benchmarks/agentbench.ts"],"sourcesContent":["/**\n * AgentBench deterministic subset adapter.\n *\n * This targets AgentBench DBBench rows only: question + table + published label.\n * It does not wrap AgentBench's controller protocol or the non-deterministic game\n * environments. Worker artifact = final answer text. Judge = exact match against\n * the official DBBench label list after light whitespace/case normalization.\n */\n\nimport { readFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport type { OutputAdapter } from '@tangle-network/agent-runtime/loops'\nimport { benchRoot } from './_harness'\nimport type { BenchmarkAdapter, BenchScore, BenchTask, LoadOptions } from './types'\n\nconst FIXTURES = join(benchRoot, 'fixtures', 'agentbench-dbbench.json')\nconst DEFAULT_SPLIT = 'dev'\n\ninterface AgentBenchDbRow {\n description: string\n label: string[]\n table?: {\n table_name?: string\n table_info?: {\n columns?: Array<{ name: string; type?: string }>\n rows?: unknown[][]\n }\n }\n}\n\ninterface AgentBenchMeta {\n labels: string[]\n split: string\n subset: 'dbbench'\n table?: AgentBenchDbRow['table']\n}\n\nconst agentbenchDir = (): string | undefined => process.env.AGENTBENCH_DIR\n\nexport const agentbenchAnswerOutput: 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 normalizeAnswer(value: string): string {\n return value\n .toLowerCase()\n .replace(/\\s+/g, ' ')\n .replace(/^[\"'`]+|[\"'`]+$/g, '')\n .trim()\n}\n\nfunction rowToTask(row: AgentBenchDbRow, index: number, split: string): BenchTask {\n const columns = row.table?.table_info?.columns?.map((c) => `${c.name}${c.type ? ` (${c.type})` : ''}`).join(', ')\n const sampleRows = row.table?.table_info?.rows?.slice(0, 40)\n const meta: AgentBenchMeta = {\n labels: row.label,\n split,\n subset: 'dbbench',\n table: row.table,\n }\n return {\n id: `dbbench-${split}-${index}`,\n split,\n prompt: [\n 'Answer this AgentBench DBBench question using the table below.',\n 'Return only the answer value.',\n '',\n `Question: ${row.description}`,\n row.table?.table_name ? `Table: ${row.table.table_name}` : undefined,\n columns ? `Columns: ${columns}` : undefined,\n sampleRows ? `Rows JSON: ${JSON.stringify(sampleRows)}` : undefined,\n ]\n .filter(Boolean)\n .join('\\n'),\n metadata: meta as unknown as Record<string, unknown>,\n }\n}\n\nfunction readMeta(task: BenchTask): AgentBenchMeta {\n const md = task.metadata\n if (!md || !Array.isArray(md.labels)) {\n throw new Error(`agentbench task ${task.id} missing metadata — loadTasks did not populate it`)\n }\n return md as unknown as AgentBenchMeta\n}\n\nfunction selectRows(rows: AgentBenchDbRow[], opts: LoadOptions, split: string): BenchTask[] {\n let tasks = rows.map((row, index) => rowToTask(row, index, split))\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(`AgentBench DBBench: no tasks matched ${JSON.stringify(opts)}`)\n return tasks\n}\n\nasync function loadJsonl(path: string): Promise<AgentBenchDbRow[]> {\n const raw = await readFile(path, 'utf8')\n return raw\n .split('\\n')\n .map((line) => line.trim())\n .filter(Boolean)\n .map((line) => JSON.parse(line) as AgentBenchDbRow)\n}\n\nasync function loadFixtures(opts: LoadOptions, split: string): Promise<BenchTask[]> {\n const rows = JSON.parse(await readFile(FIXTURES, 'utf8')) as AgentBenchDbRow[]\n console.warn(`[agentbench] AGENTBENCH_FIXTURES=1 — loading ${rows.length} DBBench adapter fixtures`)\n return selectRows(rows, opts, split)\n}\n\nexport function createAgentBenchAdapter(): BenchmarkAdapter {\n const fixturesMode = process.env.AGENTBENCH_FIXTURES === '1'\n\n return {\n name: 'agentbench',\n output: agentbenchAnswerOutput,\n\n async preflight() {\n if (fixturesMode) return\n const dir = agentbenchDir()\n if (!dir) {\n throw new Error('AGENTBENCH_DIR is required. Fix: clone https://github.com/THUDM/AgentBench and set AGENTBENCH_DIR=/path/to/AgentBench.')\n }\n await loadJsonl(join(dir, 'data', 'dbbench', `${DEFAULT_SPLIT}.jsonl`))\n },\n\n async loadTasks(opts: LoadOptions = {}) {\n const split = opts.split ?? DEFAULT_SPLIT\n if (fixturesMode) return loadFixtures(opts, split)\n const dir = agentbenchDir()\n if (!dir) throw new Error('AGENTBENCH_DIR is required to load AgentBench DBBench tasks')\n return selectRows(await loadJsonl(join(dir, 'data', 'dbbench', `${split}.jsonl`)), opts, split)\n },\n\n async goldArtifact(task: BenchTask) {\n return readMeta(task).labels[0]\n },\n\n async judge(task: BenchTask, artifact: string): Promise<BenchScore> {\n const meta = readMeta(task)\n const got = normalizeAnswer(artifact)\n const expected = meta.labels.map(normalizeAnswer)\n const resolved = expected.includes(got)\n return {\n resolved,\n score: resolved ? 1 : 0,\n detail: JSON.stringify({ subset: meta.subset, split: meta.split, expected: meta.labels, got: artifact }),\n }\n },\n }\n}\n"],"mappings":"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"}
@@ -1,37 +1,17 @@
1
- import { OutputAdapter } from '@tangle-network/agent-runtime/loops';
2
- import { BenchmarkAdapter } from './types.js';
3
-
4
- /**
5
- * AppWorld adapter (StonyBrookNLP/appworld). Worker artifact = the agent's
6
- * Python solution that calls the simulated apps' APIs (the same `apis.<app>.<fn>`
7
- * surface AppWorld exposes inside `world.execute(...)`), ending in
8
- * `apis.supervisor.complete_task()`. Judge = AppWorld's OWN programmatic
9
- * evaluator: a driver runs the solution in a fresh `AppWorld(task_id=...)` world,
10
- * then `world.evaluate().to_dict()` reports `success` (binary TGC), `num_tests`
11
- * (per-requirement total) and the `passes`/`failures` lists. Score =
12
- * passes / num_tests — GRADED; resolved = success. Fully deterministic — no LLM judge.
13
- *
14
- * loadTasks enumerates the real task suite via `load_task_ids(split)`
15
- * (train|dev|test_normal|test_challenge); the prompt = `world.task.instruction`.
16
- * The OutputAdapter is stream-only, so the worker emits its solution as a fenced
17
- * ```python block which the driver executes.
18
- *
19
- * Requires for a live run: the bench `.venv` with `appworld` installed + the
20
- * unpacked engine + downloaded data (`appworld install` ; `appworld download
21
- * data`). preflight + loadTasks + judge all fail loud with the exact step when the
22
- * engine/data is absent — never a fabricated score.
23
- */
24
-
1
+ import { BenchmarkAdapter } from "./types.js";
2
+ import { OutputAdapter } from "@tangle-network/agent-runtime/loops";
3
+ //#region src/benchmarks/appworld.d.ts
25
4
  /** Worker solution code = the last fenced ```python block, else the raw text. */
26
5
  declare const appworldSolutionOutput: OutputAdapter<string>;
27
6
  declare function createAppWorldAdapter(): BenchmarkAdapter;
28
7
  /** SandboxClient whose leaf is OUR routerToolLoop driving a persistent world session. */
29
8
  declare function appworldToolLoopClient(cfg: {
30
- model: string;
31
- routerBaseUrl: string;
32
- routerKey: string;
33
- maxTurns?: number;
9
+ model: string;
10
+ routerBaseUrl: string;
11
+ routerKey: string;
12
+ maxTurns?: number;
34
13
  }): unknown;
35
14
  declare function createAppWorldReactAdapter(): BenchmarkAdapter;
36
-
15
+ //#endregion
37
16
  export { appworldSolutionOutput, appworldToolLoopClient, createAppWorldAdapter, createAppWorldReactAdapter };
17
+ //# sourceMappingURL=appworld.d.ts.map