@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.
Files changed (317) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/HARNESS.md +302 -0
  3. package/README.md +39 -2
  4. package/dist/adapters.d.ts +15 -0
  5. package/dist/adapters.js +43 -0
  6. package/dist/adapters.js.map +1 -0
  7. package/dist/benchmarks/_harness.d.ts +125 -0
  8. package/dist/benchmarks/_harness.js +33 -0
  9. package/dist/benchmarks/_harness.js.map +1 -0
  10. package/dist/benchmarks/aec-bench.d.ts +27 -0
  11. package/dist/benchmarks/aec-bench.js +8 -0
  12. package/dist/benchmarks/aec-bench.js.map +1 -0
  13. package/dist/benchmarks/agentbench.d.ts +16 -0
  14. package/dist/benchmarks/agentbench.js +10 -0
  15. package/dist/benchmarks/agentbench.js.map +1 -0
  16. package/dist/benchmarks/appworld.d.ts +37 -0
  17. package/dist/benchmarks/appworld.js +14 -0
  18. package/dist/benchmarks/appworld.js.map +1 -0
  19. package/dist/benchmarks/bfcl.d.ts +18 -0
  20. package/dist/benchmarks/bfcl.js +10 -0
  21. package/dist/benchmarks/bfcl.js.map +1 -0
  22. package/dist/benchmarks/cad-design.d.ts +45 -0
  23. package/dist/benchmarks/cad-design.js +7 -0
  24. package/dist/benchmarks/cad-design.js.map +1 -0
  25. package/dist/benchmarks/cadbench.d.ts +19 -0
  26. package/dist/benchmarks/cadbench.js +8 -0
  27. package/dist/benchmarks/cadbench.js.map +1 -0
  28. package/dist/benchmarks/cadgenbench.d.ts +22 -0
  29. package/dist/benchmarks/cadgenbench.js +8 -0
  30. package/dist/benchmarks/cadgenbench.js.map +1 -0
  31. package/dist/benchmarks/commit0.d.ts +31 -0
  32. package/dist/benchmarks/commit0.js +10 -0
  33. package/dist/benchmarks/commit0.js.map +1 -0
  34. package/dist/benchmarks/crag.d.ts +14 -0
  35. package/dist/benchmarks/crag.js +9 -0
  36. package/dist/benchmarks/crag.js.map +1 -0
  37. package/dist/benchmarks/dabstep.d.ts +18 -0
  38. package/dist/benchmarks/dabstep.js +10 -0
  39. package/dist/benchmarks/dabstep.js.map +1 -0
  40. package/dist/benchmarks/enterpriseops-gym.d.ts +38 -0
  41. package/dist/benchmarks/enterpriseops-gym.js +10 -0
  42. package/dist/benchmarks/enterpriseops-gym.js.map +1 -0
  43. package/dist/benchmarks/finresearchbench.d.ts +15 -0
  44. package/dist/benchmarks/finresearchbench.js +8 -0
  45. package/dist/benchmarks/finresearchbench.js.map +1 -0
  46. package/dist/benchmarks/finsearchcomp.d.ts +49 -0
  47. package/dist/benchmarks/finsearchcomp.js +7 -0
  48. package/dist/benchmarks/finsearchcomp.js.map +1 -0
  49. package/dist/benchmarks/frames.d.ts +59 -0
  50. package/dist/benchmarks/frames.js +13 -0
  51. package/dist/benchmarks/frames.js.map +1 -0
  52. package/dist/benchmarks/hotpotqa.d.ts +48 -0
  53. package/dist/benchmarks/hotpotqa.js +15 -0
  54. package/dist/benchmarks/hotpotqa.js.map +1 -0
  55. package/dist/benchmarks/humaneval.d.ts +53 -0
  56. package/dist/benchmarks/humaneval.js +15 -0
  57. package/dist/benchmarks/humaneval.js.map +1 -0
  58. package/dist/benchmarks/mind2web.d.ts +41 -0
  59. package/dist/benchmarks/mind2web.js +9 -0
  60. package/dist/benchmarks/mind2web.js.map +1 -0
  61. package/dist/benchmarks/nomiracl.d.ts +15 -0
  62. package/dist/benchmarks/nomiracl.js +9 -0
  63. package/dist/benchmarks/nomiracl.js.map +1 -0
  64. package/dist/benchmarks/open-rag-bench.d.ts +14 -0
  65. package/dist/benchmarks/open-rag-bench.js +9 -0
  66. package/dist/benchmarks/open-rag-bench.js.map +1 -0
  67. package/dist/benchmarks/programbench.d.ts +38 -0
  68. package/dist/benchmarks/programbench.js +10 -0
  69. package/dist/benchmarks/programbench.js.map +1 -0
  70. package/dist/benchmarks/rag-shared.d.ts +42 -0
  71. package/dist/benchmarks/rag-shared.js +39 -0
  72. package/dist/benchmarks/rag-shared.js.map +1 -0
  73. package/dist/benchmarks/ragbench.d.ts +16 -0
  74. package/dist/benchmarks/ragbench.js +9 -0
  75. package/dist/benchmarks/ragbench.js.map +1 -0
  76. package/dist/benchmarks/simpleqa.d.ts +64 -0
  77. package/dist/benchmarks/simpleqa.js +11 -0
  78. package/dist/benchmarks/simpleqa.js.map +1 -0
  79. package/dist/benchmarks/swe-bench.d.ts +56 -0
  80. package/dist/benchmarks/swe-bench.js +14 -0
  81. package/dist/benchmarks/swe-bench.js.map +1 -0
  82. package/dist/benchmarks/t2-ragbench.d.ts +14 -0
  83. package/dist/benchmarks/t2-ragbench.js +9 -0
  84. package/dist/benchmarks/t2-ragbench.js.map +1 -0
  85. package/dist/benchmarks/tau-bench-shared.d.ts +26 -0
  86. package/dist/benchmarks/tau-bench-shared.js +10 -0
  87. package/dist/benchmarks/tau-bench-shared.js.map +1 -0
  88. package/dist/benchmarks/tau2-bench.d.ts +7 -0
  89. package/dist/benchmarks/tau2-bench.js +11 -0
  90. package/dist/benchmarks/tau2-bench.js.map +1 -0
  91. package/dist/benchmarks/tau3-banking.d.ts +15 -0
  92. package/dist/benchmarks/tau3-banking.js +9 -0
  93. package/dist/benchmarks/tau3-banking.js.map +1 -0
  94. package/dist/benchmarks/terminal-bench.d.ts +24 -0
  95. package/dist/benchmarks/terminal-bench.js +8 -0
  96. package/dist/benchmarks/terminal-bench.js.map +1 -0
  97. package/dist/benchmarks/toollm.d.ts +16 -0
  98. package/dist/benchmarks/toollm.js +10 -0
  99. package/dist/benchmarks/toollm.js.map +1 -0
  100. package/dist/benchmarks/trata-hedge.d.ts +32 -0
  101. package/dist/benchmarks/trata-hedge.js +7 -0
  102. package/dist/benchmarks/trata-hedge.js.map +1 -0
  103. package/dist/benchmarks/types.d.ts +107 -0
  104. package/dist/benchmarks/types.js +1 -0
  105. package/dist/benchmarks/types.js.map +1 -0
  106. package/dist/benchmarks/webarena-verified.d.ts +16 -0
  107. package/dist/benchmarks/webarena-verified.js +10 -0
  108. package/dist/benchmarks/webarena-verified.js.map +1 -0
  109. package/dist/chunk-2PVVP7GN.js +197 -0
  110. package/dist/chunk-2PVVP7GN.js.map +1 -0
  111. package/dist/chunk-2XU6OGEN.js +170 -0
  112. package/dist/chunk-2XU6OGEN.js.map +1 -0
  113. package/dist/chunk-53UPUNBZ.js +325 -0
  114. package/dist/chunk-53UPUNBZ.js.map +1 -0
  115. package/dist/chunk-5SBJCB6W.js +144 -0
  116. package/dist/chunk-5SBJCB6W.js.map +1 -0
  117. package/dist/chunk-7WSD27QQ.js +118 -0
  118. package/dist/chunk-7WSD27QQ.js.map +1 -0
  119. package/dist/chunk-C7T7WEK2.js +103 -0
  120. package/dist/chunk-C7T7WEK2.js.map +1 -0
  121. package/dist/chunk-CKUVRZ2T.js +251 -0
  122. package/dist/chunk-CKUVRZ2T.js.map +1 -0
  123. package/dist/chunk-HBSWHQNJ.js +30 -0
  124. package/dist/chunk-HBSWHQNJ.js.map +1 -0
  125. package/dist/chunk-HHXFIHXC.js +116 -0
  126. package/dist/chunk-HHXFIHXC.js.map +1 -0
  127. package/dist/chunk-IFAV6KEM.js +276 -0
  128. package/dist/chunk-IFAV6KEM.js.map +1 -0
  129. package/dist/chunk-INNOYXCP.js +387 -0
  130. package/dist/chunk-INNOYXCP.js.map +1 -0
  131. package/dist/chunk-J3KDJNX2.js +182 -0
  132. package/dist/chunk-J3KDJNX2.js.map +1 -0
  133. package/dist/chunk-JRWWGMK7.js +148 -0
  134. package/dist/chunk-JRWWGMK7.js.map +1 -0
  135. package/dist/chunk-JTHWEDEW.js +32 -0
  136. package/dist/chunk-JTHWEDEW.js.map +1 -0
  137. package/dist/chunk-KDIKRJGB.js +120 -0
  138. package/dist/chunk-KDIKRJGB.js.map +1 -0
  139. package/dist/chunk-LRRD7NAG.js +301 -0
  140. package/dist/chunk-LRRD7NAG.js.map +1 -0
  141. package/dist/chunk-ODT47UAY.js +221 -0
  142. package/dist/chunk-ODT47UAY.js.map +1 -0
  143. package/dist/chunk-PA2ZKHJC.js +230 -0
  144. package/dist/chunk-PA2ZKHJC.js.map +1 -0
  145. package/dist/chunk-PPYSEKFM.js +182 -0
  146. package/dist/chunk-PPYSEKFM.js.map +1 -0
  147. package/dist/chunk-PUIRNYI7.js +189 -0
  148. package/dist/chunk-PUIRNYI7.js.map +1 -0
  149. package/dist/chunk-R36V2VP7.js +169 -0
  150. package/dist/chunk-R36V2VP7.js.map +1 -0
  151. package/dist/chunk-R67DFVLO.js +142 -0
  152. package/dist/chunk-R67DFVLO.js.map +1 -0
  153. package/dist/chunk-SEVJPLZC.js +260 -0
  154. package/dist/chunk-SEVJPLZC.js.map +1 -0
  155. package/dist/chunk-SYDW647C.js +318 -0
  156. package/dist/chunk-SYDW647C.js.map +1 -0
  157. package/dist/chunk-TBKU5XQI.js +228 -0
  158. package/dist/chunk-TBKU5XQI.js.map +1 -0
  159. package/dist/chunk-TSWPNOYM.js +147 -0
  160. package/dist/chunk-TSWPNOYM.js.map +1 -0
  161. package/dist/chunk-UAIOHCUK.js +27 -0
  162. package/dist/chunk-UAIOHCUK.js.map +1 -0
  163. package/dist/chunk-UPAMRDX4.js +233 -0
  164. package/dist/chunk-UPAMRDX4.js.map +1 -0
  165. package/dist/chunk-VQRS7VUC.js +342 -0
  166. package/dist/chunk-VQRS7VUC.js.map +1 -0
  167. package/dist/chunk-X3BTXCJ4.js +262 -0
  168. package/dist/chunk-X3BTXCJ4.js.map +1 -0
  169. package/dist/chunk-X5YKXC6V.js +211 -0
  170. package/dist/chunk-X5YKXC6V.js.map +1 -0
  171. package/dist/chunk-Y6O2OCUO.js +130 -0
  172. package/dist/chunk-Y6O2OCUO.js.map +1 -0
  173. package/dist/chunk-YCGY7UIZ.js +208 -0
  174. package/dist/chunk-YCGY7UIZ.js.map +1 -0
  175. package/dist/chunk-Z7ML6L77.js +162 -0
  176. package/dist/chunk-Z7ML6L77.js.map +1 -0
  177. package/dist/chunk-ZEWMTR5M.js +136 -0
  178. package/dist/chunk-ZEWMTR5M.js.map +1 -0
  179. package/dist/index.d.ts +355 -0
  180. package/dist/index.js +1908 -0
  181. package/dist/index.js.map +1 -0
  182. package/fixtures/aec-bench.json +18 -0
  183. package/fixtures/agentbench-dbbench.json +22 -0
  184. package/fixtures/bfcl.json +45 -0
  185. package/fixtures/commit0.json +72 -0
  186. package/fixtures/crag.json +10 -0
  187. package/fixtures/dabstep.json +22 -0
  188. package/fixtures/enterpriseops-gym.json +103 -0
  189. package/fixtures/finresearchbench.json +21 -0
  190. package/fixtures/finsearchcomp.json +66 -0
  191. package/fixtures/frames.json +26 -0
  192. package/fixtures/hotpotqa.json +182 -0
  193. package/fixtures/nomiracl.json +26 -0
  194. package/fixtures/open-rag-bench.json +16 -0
  195. package/fixtures/pier-agent/no-model-task/environment/Dockerfile +16 -0
  196. package/fixtures/pier-agent/no-model-task/environment/seed/src/status.txt +1 -0
  197. package/fixtures/pier-agent/no-model-task/instruction.md +6 -0
  198. package/fixtures/pier-agent/no-model-task/pre_artifacts.sh +6 -0
  199. package/fixtures/pier-agent/no-model-task/task.toml +35 -0
  200. package/fixtures/pier-agent/no-model-task/tests/Dockerfile +17 -0
  201. package/fixtures/pier-agent/no-model-task/tests/seed/src/status.txt +1 -0
  202. package/fixtures/pier-agent/no-model-task/tests/test.sh +19 -0
  203. package/fixtures/programbench.json +17 -0
  204. package/fixtures/ragbench.json +21 -0
  205. package/fixtures/simpleqa.json +121 -0
  206. package/fixtures/t2-ragbench.json +13 -0
  207. package/fixtures/tau2-bench.json +16 -0
  208. package/fixtures/tau3-banking.json +16 -0
  209. package/fixtures/toollm.json +28 -0
  210. package/fixtures/webarena-verified.json +20 -0
  211. package/package.json +60 -20
  212. package/pier_agents/__init__.py +18 -0
  213. package/pier_agents/candidate_contract.py +755 -0
  214. package/pier_agents/process_boundary.py +321 -0
  215. package/pier_agents/tangle_candidate.py +907 -0
  216. package/pier_agents/workspace_boundary.py +368 -0
  217. package/scripts/appworld_driver.py +359 -0
  218. package/scripts/cadbench_prepare.py +22 -0
  219. package/scripts/cadgenbench_hard_parts.py +48 -0
  220. package/scripts/clbench_codebase_judge.py +73 -0
  221. package/scripts/commit0_judge.py +170 -0
  222. package/scripts/dabstep_judge.py +42 -0
  223. package/scripts/enterpriseops_gym_judge.py +281 -0
  224. package/scripts/programbench_judge.py +120 -0
  225. package/scripts/render-gate-chart.mjs +176 -0
  226. package/scripts/run-package-tests.mjs +56 -0
  227. package/scripts/terminate-pier-trial.mts +66 -0
  228. package/scripts/trata-hedge/README.md +56 -0
  229. package/scripts/trata-hedge/run.sh +60 -0
  230. package/scripts/trata-hedge/solve.py +83 -0
  231. package/scripts/verify-packed-consumer.mjs +235 -0
  232. package/scripts/verify-pier-agent.mts +715 -0
  233. package/scripts/verify-pier-pair.mts +74 -0
  234. package/scripts/verify-pier-recovery.mts +139 -0
  235. package/src/adapters.ts +26 -0
  236. package/src/benchmarks/_harness.test.mts +178 -0
  237. package/src/benchmarks/_harness.ts +239 -16
  238. package/src/benchmarks/agentbench.ts +163 -0
  239. package/src/benchmarks/appworld.test.mts +15 -9
  240. package/src/benchmarks/bfcl.ts +346 -0
  241. package/src/benchmarks/crag.ts +137 -0
  242. package/src/benchmarks/dabstep.test.mts +70 -0
  243. package/src/benchmarks/dabstep.ts +212 -0
  244. package/src/benchmarks/external-adapters.test.mts +150 -0
  245. package/src/benchmarks/finresearchbench.ts +269 -0
  246. package/src/benchmarks/humaneval.ts +20 -8
  247. package/src/benchmarks/nomiracl.ts +180 -0
  248. package/src/benchmarks/open-rag-bench.ts +153 -0
  249. package/src/benchmarks/rag-benchmarks.test.mts +138 -0
  250. package/src/benchmarks/rag-shared.ts +327 -0
  251. package/src/benchmarks/ragbench.ts +171 -0
  252. package/src/benchmarks/swe-bench.test.mts +61 -0
  253. package/src/benchmarks/swe-bench.ts +201 -19
  254. package/src/benchmarks/t2-ragbench.ts +166 -0
  255. package/src/benchmarks/tau-bench-shared.ts +214 -0
  256. package/src/benchmarks/tau2-bench.ts +30 -0
  257. package/src/benchmarks/tau3-banking.ts +29 -0
  258. package/src/benchmarks/terminal-bench.test.mts +33 -0
  259. package/src/benchmarks/terminal-bench.ts +23 -8
  260. package/src/benchmarks/toollm.ts +254 -0
  261. package/src/benchmarks/types.ts +42 -0
  262. package/src/benchmarks/webarena-verified.ts +200 -0
  263. package/src/commit0-prereqs.sh +0 -0
  264. package/src/coordination-mcp-container-reach.mts +181 -0
  265. package/src/decoder-live.mts +1 -1
  266. package/src/examples/README.md +103 -39
  267. package/src/examples/benchmark-matrix.mts +101 -0
  268. package/src/examples/lean-proof-gate.README.md +77 -0
  269. package/src/examples/lean-proof-gate.mts +162 -0
  270. package/src/examples/lean-verify.ts +95 -0
  271. package/src/examples/lean.Dockerfile +12 -0
  272. package/src/examples/math-demo.mts +9 -7
  273. package/src/examples/strategy-demo.mts +10 -12
  274. package/src/gate.ts +3 -2
  275. package/src/hev-eval.mts +69 -0
  276. package/src/hev-improve.mts +169 -0
  277. package/src/hev-structural.mts +688 -0
  278. package/src/index.ts +73 -0
  279. package/src/mbpp-structural.mts +662 -0
  280. package/src/pier-agent.test-fixtures.mts +19 -0
  281. package/src/pier-agent.test.mts +363 -0
  282. package/src/pier-agent.ts +657 -0
  283. package/src/pier-result-grader.mjs +30 -0
  284. package/src/pier-result-grader.test.mts +62 -0
  285. package/src/pier-result-grader.ts +108 -0
  286. package/src/pier-task-outcome.test.mts +117 -0
  287. package/src/pier-task-outcome.ts +240 -0
  288. package/src/pier-trial-controller.test.mts +412 -0
  289. package/src/pier-trial-controller.ts +858 -0
  290. package/src/pier-trial-supervisor.mjs +352 -0
  291. package/src/resolve-client.ts +25 -2
  292. package/src/run-benchmarks-cli.mts +72 -0
  293. package/src/run-benchmarks-report.ts +66 -0
  294. package/src/run-benchmarks.test.mts +231 -0
  295. package/src/run-benchmarks.ts +589 -0
  296. package/src/smoke-structural-rollout.mts +393 -0
  297. package/src/swe-bench-env.test.ts +207 -0
  298. package/src/swe-bench-env.ts +554 -0
  299. package/src/swe-jail.ts +293 -0
  300. package/src/swe-self-improve.mts +84 -0
  301. package/src/swe-structural-judge-policy.test.ts +117 -0
  302. package/src/swe-structural-judge-policy.ts +133 -0
  303. package/src/swe-structural-policy.test.ts +124 -0
  304. package/src/swe-structural-policy.ts +132 -0
  305. package/src/swe-structural-provenance.test.ts +93 -0
  306. package/src/swe-structural-provenance.ts +138 -0
  307. package/src/swe-structural.mts +1260 -0
  308. package/src/swe-temp.ts +14 -0
  309. package/src/tb-container-executor.mts +234 -0
  310. package/src/tb-container-executor.test.mts +99 -0
  311. package/src/tb-supervisor-sidecar.mts +222 -0
  312. package/src/trata-gepa.mts +1 -1
  313. package/steerers/eops-itsm-population.json +1 -0
  314. package/tb_agents/opencode_refine_agent.py +117 -0
  315. package/tb_agents/opencode_router_agent.py +406 -0
  316. package/tb_agents/opencode_supervisor_agent.py +239 -0
  317. package/tb_agents/script_agent.py +66 -0
@@ -0,0 +1,346 @@
1
+ /**
2
+ * Berkeley Function Calling Leaderboard adapter.
3
+ *
4
+ * Scope: deterministic function-call ground-truth categories from the official
5
+ * BFCL data files. This is NOT the full live BFCL leaderboard evaluator: agentic
6
+ * web-search/memory categories and BFCL's own model-response harness remain
7
+ * upstream responsibilities. The adapter loads official JSONL rows plus their
8
+ * `possible_answer` file and scores structured function-call artifacts against
9
+ * allowed function/argument values.
10
+ */
11
+
12
+ import { readFile, stat } from 'node:fs/promises'
13
+ import { join } from 'node:path'
14
+ import type { OutputAdapter } from '@tangle-network/agent-runtime/loops'
15
+ import { benchRoot } from './_harness'
16
+ import type { BenchmarkAdapter, BenchScore, BenchTask, LoadOptions } from './types'
17
+
18
+ const FIXTURES = join(benchRoot, 'fixtures', 'bfcl.json')
19
+ const DEFAULT_CATEGORY = 'BFCL_v4_simple_python'
20
+
21
+ interface BfclRow {
22
+ id: string
23
+ question: unknown
24
+ function: unknown
25
+ }
26
+
27
+ interface BfclAnswerRow {
28
+ id: string
29
+ ground_truth: unknown
30
+ }
31
+
32
+ interface BfclAllowedCall {
33
+ name: string
34
+ arguments: Record<string, unknown[]>
35
+ }
36
+
37
+ interface BfclMeta {
38
+ rowId: string
39
+ category: string
40
+ functions: unknown
41
+ expected: BfclAllowedCall[]
42
+ scoring: 'bfcl-ground-truth-subset'
43
+ }
44
+
45
+ interface BfclActualCall {
46
+ name: string
47
+ arguments: Record<string, unknown>
48
+ }
49
+
50
+ const bfclDir = (): string | undefined => process.env.BFCL_DIR
51
+ const bfclCategory = (): string => process.env.BFCL_CATEGORY ?? DEFAULT_CATEGORY
52
+
53
+ export const bfclOutput: OutputAdapter<string> = {
54
+ parse(events) {
55
+ let text = ''
56
+ for (const ev of events) {
57
+ const d = (ev as { data?: Record<string, unknown> })?.data
58
+ const t = d?.finalText ?? d?.text ?? d?.result
59
+ if (typeof t === 'string' && t.length > 0) text = t
60
+ }
61
+ return text.trim()
62
+ },
63
+ }
64
+
65
+ async function assertPath(path: string, label: string): Promise<void> {
66
+ try {
67
+ await stat(path)
68
+ } catch (err) {
69
+ throw new Error(`BFCL: missing ${label} at ${path} (${err instanceof Error ? err.message : err})`)
70
+ }
71
+ }
72
+
73
+ function readJsonl(raw: string): unknown[] {
74
+ return raw
75
+ .split(/\r?\n/)
76
+ .map((line) => line.trim())
77
+ .filter((line) => line.length > 0)
78
+ .map((line) => JSON.parse(line) as unknown)
79
+ }
80
+
81
+ function taskFile(dir: string, category: string): string {
82
+ return process.env.BFCL_DATA_FILE ?? join(dir, 'bfcl_eval', 'data', `${category}.json`)
83
+ }
84
+
85
+ function answerFile(dir: string, category: string): string {
86
+ return process.env.BFCL_ANSWER_FILE ?? join(dir, 'bfcl_eval', 'data', 'possible_answer', `${category}.json`)
87
+ }
88
+
89
+ function questionText(question: unknown): string {
90
+ if (typeof question === 'string') return question
91
+ if (!Array.isArray(question)) return JSON.stringify(question, null, 2)
92
+ const turns: string[] = []
93
+ for (const item of question.flat(3)) {
94
+ if (item && typeof item === 'object') {
95
+ const role = typeof (item as { role?: unknown }).role === 'string' ? (item as { role: string }).role : 'user'
96
+ const content = (item as { content?: unknown }).content
97
+ if (typeof content === 'string') turns.push(`${role}: ${content}`)
98
+ }
99
+ }
100
+ return turns.length > 0 ? turns.join('\n') : JSON.stringify(question, null, 2)
101
+ }
102
+
103
+ function normalizeScalar(value: unknown): string {
104
+ if (typeof value === 'number') return Number.isInteger(value) ? String(value) : String(Number(value.toFixed(8)))
105
+ if (typeof value === 'string') return value.trim().toLowerCase()
106
+ return JSON.stringify(value)
107
+ }
108
+
109
+ function normalizeAllowed(value: unknown): unknown[] {
110
+ return Array.isArray(value) ? value : [value]
111
+ }
112
+
113
+ function groundTruthToCalls(value: unknown): BfclAllowedCall[] {
114
+ if (!Array.isArray(value)) return []
115
+ const out: BfclAllowedCall[] = []
116
+ for (const item of value) {
117
+ if (!item || typeof item !== 'object') continue
118
+ for (const [name, args] of Object.entries(item as Record<string, unknown>)) {
119
+ if (!args || typeof args !== 'object' || Array.isArray(args)) continue
120
+ const normalized: Record<string, unknown[]> = {}
121
+ for (const [argName, allowed] of Object.entries(args as Record<string, unknown>)) {
122
+ normalized[argName] = normalizeAllowed(allowed)
123
+ }
124
+ out.push({ name, arguments: normalized })
125
+ }
126
+ }
127
+ return out
128
+ }
129
+
130
+ function rowToTask(row: BfclRow, answer: BfclAnswerRow, category: string): BenchTask {
131
+ const expected = groundTruthToCalls(answer.ground_truth)
132
+ if (expected.length === 0) {
133
+ throw new Error(`BFCL ${row.id}: possible_answer has no deterministic ground_truth calls`)
134
+ }
135
+ const meta: BfclMeta = {
136
+ rowId: row.id,
137
+ category,
138
+ functions: row.function,
139
+ expected,
140
+ scoring: 'bfcl-ground-truth-subset',
141
+ }
142
+ return {
143
+ id: row.id,
144
+ split: category,
145
+ prompt: [
146
+ 'Solve this BFCL function-calling task.',
147
+ 'Return only JSON: {"function_calls":[{"name":"...","arguments":{...}}]}.',
148
+ '',
149
+ questionText(row.question),
150
+ '',
151
+ `Available functions: ${JSON.stringify(row.function, null, 2)}`,
152
+ ].join('\n'),
153
+ metadata: meta as unknown as Record<string, unknown>,
154
+ }
155
+ }
156
+
157
+ function readMeta(task: BenchTask): BfclMeta {
158
+ const md = task.metadata
159
+ if (!md || !Array.isArray(md.expected)) {
160
+ throw new Error(`BFCL task ${task.id} missing expected calls — loadTasks did not populate metadata`)
161
+ }
162
+ return md as unknown as BfclMeta
163
+ }
164
+
165
+ function selectTasks(tasks: BenchTask[], opts: LoadOptions): BenchTask[] {
166
+ let out = tasks
167
+ if (opts.split) out = out.filter((task) => task.split === opts.split)
168
+ if (opts.ids) {
169
+ const want = new Set(opts.ids)
170
+ out = out.filter((task) => want.has(task.id))
171
+ } else if (opts.limit !== undefined) {
172
+ out = out.slice(0, opts.limit)
173
+ }
174
+ if (out.length === 0) throw new Error(`BFCL: no tasks matched ${JSON.stringify(opts)}`)
175
+ return out
176
+ }
177
+
178
+ function buildTasks(rows: BfclRow[], answers: BfclAnswerRow[], category: string, opts: LoadOptions): BenchTask[] {
179
+ const byId = new Map(answers.map((answer) => [answer.id, answer]))
180
+ const tasks = rows.map((row) => {
181
+ const answer = byId.get(row.id)
182
+ if (!answer) throw new Error(`BFCL ${category}: missing possible_answer for ${row.id}`)
183
+ return rowToTask(row, answer, category)
184
+ })
185
+ return selectTasks(tasks, opts)
186
+ }
187
+
188
+ async function loadFixtures(opts: LoadOptions): Promise<BenchTask[]> {
189
+ const fixture = JSON.parse(await readFile(FIXTURES, 'utf8')) as { rows: BfclRow[]; answers: BfclAnswerRow[]; category: string }
190
+ console.warn(`[bfcl] BFCL_FIXTURES=1 — loading ${fixture.rows.length} adapter fixtures`)
191
+ return buildTasks(fixture.rows, fixture.answers, fixture.category, opts)
192
+ }
193
+
194
+ async function loadOfficialTasks(dir: string, opts: LoadOptions): Promise<BenchTask[]> {
195
+ const category = bfclCategory()
196
+ const dataPath = taskFile(dir, category)
197
+ const answersPath = answerFile(dir, category)
198
+ const [rawRows, rawAnswers] = await Promise.all([readFile(dataPath, 'utf8'), readFile(answersPath, 'utf8')])
199
+ const rows = readJsonl(rawRows) as BfclRow[]
200
+ const answers = readJsonl(rawAnswers) as BfclAnswerRow[]
201
+ return buildTasks(rows, answers, category, opts)
202
+ }
203
+
204
+ function extractJsonBlock(text: string): unknown {
205
+ const fences = [...text.matchAll(/```(?:json)?\s*([\s\S]*?)```/g)]
206
+ const raw = (fences.at(-1)?.[1] ?? text).trim()
207
+ try {
208
+ return JSON.parse(raw)
209
+ } catch {
210
+ return undefined
211
+ }
212
+ }
213
+
214
+ function normalizeArguments(value: unknown): Record<string, unknown> {
215
+ if (typeof value === 'string') {
216
+ try {
217
+ return normalizeArguments(JSON.parse(value))
218
+ } catch {
219
+ return {}
220
+ }
221
+ }
222
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return {}
223
+ return value as Record<string, unknown>
224
+ }
225
+
226
+ function callFromObject(value: unknown): BfclActualCall | undefined {
227
+ if (!value || typeof value !== 'object') return undefined
228
+ const raw = value as Record<string, unknown>
229
+ if (raw.function && typeof raw.function === 'object') {
230
+ const fn = raw.function as Record<string, unknown>
231
+ if (typeof fn.name === 'string') return { name: fn.name, arguments: normalizeArguments(fn.arguments) }
232
+ }
233
+ const name =
234
+ typeof raw.name === 'string' ? raw.name
235
+ : typeof raw.function_name === 'string' ? raw.function_name
236
+ : typeof raw.tool_name === 'string' ? raw.tool_name
237
+ : undefined
238
+ if (!name) return undefined
239
+ return { name, arguments: normalizeArguments(raw.arguments ?? raw.args ?? raw.parameters) }
240
+ }
241
+
242
+ function extractActualCalls(artifact: string): BfclActualCall[] {
243
+ const parsed = extractJsonBlock(artifact)
244
+ if (Array.isArray(parsed)) return parsed.map(callFromObject).filter((call): call is BfclActualCall => Boolean(call))
245
+ if (parsed && typeof parsed === 'object') {
246
+ const raw = parsed as Record<string, unknown>
247
+ for (const key of ['function_calls', 'tool_calls', 'calls']) {
248
+ if (Array.isArray(raw[key])) {
249
+ return raw[key].map(callFromObject).filter((call): call is BfclActualCall => Boolean(call))
250
+ }
251
+ }
252
+ const single = callFromObject(raw)
253
+ if (single) return [single]
254
+ }
255
+ return []
256
+ }
257
+
258
+ function argMatches(expectedValues: unknown[], actual: unknown): boolean {
259
+ return expectedValues.some((expected) => normalizeScalar(expected) === normalizeScalar(actual))
260
+ }
261
+
262
+ function callMatches(expected: BfclAllowedCall, actual: BfclActualCall): boolean {
263
+ if (expected.name !== actual.name) return false
264
+ for (const [argName, allowed] of Object.entries(expected.arguments)) {
265
+ if (!(argName in actual.arguments)) {
266
+ if (allowed.some((value) => value === '' || value === null || value === undefined)) continue
267
+ return false
268
+ }
269
+ if (!argMatches(allowed, actual.arguments[argName])) return false
270
+ }
271
+ return true
272
+ }
273
+
274
+ function scoreCalls(task: BenchTask, artifact: string): BenchScore {
275
+ const meta = readMeta(task)
276
+ const actual = extractActualCalls(artifact)
277
+ const used = new Set<number>()
278
+ let matched = 0
279
+ for (const expected of meta.expected) {
280
+ const index = actual.findIndex((call, i) => !used.has(i) && callMatches(expected, call))
281
+ if (index >= 0) {
282
+ used.add(index)
283
+ matched += 1
284
+ }
285
+ }
286
+ const recall = meta.expected.length === 0 ? 0 : matched / meta.expected.length
287
+ const precision = actual.length === 0 ? 0 : matched / actual.length
288
+ const score = recall
289
+ return {
290
+ resolved: recall === 1 && precision === 1,
291
+ score,
292
+ detail: JSON.stringify({
293
+ scoring: meta.scoring,
294
+ category: meta.category,
295
+ expected: meta.expected,
296
+ actual,
297
+ precision,
298
+ recall,
299
+ fullBfclLeaderboardScore: null,
300
+ }),
301
+ }
302
+ }
303
+
304
+ export function createBfclAdapter(): BenchmarkAdapter {
305
+ const fixturesMode = process.env.BFCL_FIXTURES === '1'
306
+
307
+ return {
308
+ name: 'bfcl',
309
+ output: bfclOutput,
310
+
311
+ async preflight() {
312
+ if (fixturesMode) return
313
+ const dir = bfclDir()
314
+ if (!dir) {
315
+ throw new Error(
316
+ 'BFCL_DIR is required. Fix: clone https://github.com/ShishirPatil/gorilla, set BFCL_DIR=/path/to/gorilla/berkeley-function-call-leaderboard, and optionally set BFCL_CATEGORY=BFCL_v4_simple_python.',
317
+ )
318
+ }
319
+ const category = bfclCategory()
320
+ await assertPath(taskFile(dir, category), 'BFCL task JSONL')
321
+ await assertPath(answerFile(dir, category), 'BFCL possible_answer JSONL')
322
+ await loadOfficialTasks(dir, { limit: 1 })
323
+ },
324
+
325
+ async loadTasks(opts: LoadOptions = {}) {
326
+ if (fixturesMode) return loadFixtures(opts)
327
+ const dir = bfclDir()
328
+ if (!dir) throw new Error('BFCL_DIR is required to load official BFCL rows')
329
+ return loadOfficialTasks(dir, opts)
330
+ },
331
+
332
+ async goldArtifact(task: BenchTask) {
333
+ const meta = readMeta(task)
334
+ return JSON.stringify({
335
+ function_calls: meta.expected.map((call) => ({
336
+ name: call.name,
337
+ arguments: Object.fromEntries(Object.entries(call.arguments).map(([key, values]) => [key, values[0]])),
338
+ })),
339
+ }, null, 2)
340
+ },
341
+
342
+ async judge(task: BenchTask, artifact: string): Promise<BenchScore> {
343
+ return scoreCalls(task, artifact)
344
+ },
345
+ }
346
+ }
@@ -0,0 +1,137 @@
1
+ /**
2
+ * CRAG adapter (Comprehensive RAG Benchmark).
3
+ *
4
+ * Live mode expects an official or compatible CRAG JSON/JSONL export. The
5
+ * adapter preserves CRAG domain/type/dynamism tags in metadata and scores final
6
+ * answers deterministically against the provided gold answer list.
7
+ */
8
+
9
+ import { readFile } from 'node:fs/promises'
10
+ import { join } from 'node:path'
11
+ import { benchRoot } from './_harness'
12
+ import type { BenchmarkAdapter, BenchScore, BenchTask, LoadOptions } from './types'
13
+ import {
14
+ FINAL_ANSWER_SENTINEL,
15
+ allStrings,
16
+ answerScoreToBenchScore,
17
+ firstString,
18
+ isObject,
19
+ ragAnswerOutput,
20
+ readJsonRows,
21
+ scoreAnswerArtifact,
22
+ selectTasks,
23
+ stringFrom,
24
+ } from './rag-shared'
25
+
26
+ const FIXTURES = join(benchRoot, 'fixtures', 'crag.json')
27
+
28
+ interface CragMeta {
29
+ benchmark: 'crag'
30
+ query: string
31
+ goldAnswers: string[]
32
+ domain: string
33
+ questionType: string
34
+ dynamism: string
35
+ }
36
+
37
+ const dataFile = (): string | undefined => process.env.CRAG_DATA_FILE
38
+
39
+ function rowToTask(raw: unknown, index: number): BenchTask {
40
+ if (!isObject(raw)) throw new Error(`CRAG row ${index} must be an object`)
41
+ const query = firstString(raw, ['query', 'question', 'prompt'])
42
+ const goldAnswers = allStrings(raw, ['answer', 'answers', 'gold', 'gold_answer', 'expected_answer'])
43
+ if (!query) throw new Error(`CRAG row ${index} missing query`)
44
+ if (goldAnswers.length === 0) throw new Error(`CRAG row ${index} missing gold answer`)
45
+ const domain = stringFrom(raw.domain) ?? 'unknown'
46
+ const questionType = stringFrom(raw.question_type) ?? stringFrom(raw.questionType) ?? 'unknown'
47
+ const dynamism = stringFrom(raw.static_or_dynamic) ?? stringFrom(raw.dynamism) ?? 'unknown'
48
+ const id = stringFrom(raw.id) ?? stringFrom(raw.query_id) ?? `crag-${index}`
49
+ const meta: CragMeta = {
50
+ benchmark: 'crag',
51
+ query,
52
+ goldAnswers,
53
+ domain,
54
+ questionType,
55
+ dynamism,
56
+ }
57
+ return {
58
+ id,
59
+ split: stringFrom(raw.split) ?? domain,
60
+ prompt: [
61
+ 'Answer this CRAG factual question.',
62
+ 'Return a concise answer and do not guess when the evidence is insufficient.',
63
+ 'End with a single final line: `FINAL ANSWER: <answer>`.',
64
+ '',
65
+ `Question: ${query}`,
66
+ `Domain: ${domain}`,
67
+ `Question type: ${questionType}`,
68
+ `Dynamism: ${dynamism}`,
69
+ ].join('\n'),
70
+ metadata: meta as unknown as Record<string, unknown>,
71
+ }
72
+ }
73
+
74
+ function readMeta(task: BenchTask): CragMeta {
75
+ const md = task.metadata
76
+ if (!md || !Array.isArray(md.goldAnswers)) {
77
+ throw new Error(`CRAG task ${task.id} missing metadata — loadTasks did not populate it`)
78
+ }
79
+ return md as unknown as CragMeta
80
+ }
81
+
82
+ async function loadRows(path: string): Promise<unknown[]> {
83
+ const rows = await readJsonRows(path)
84
+ if (rows.length === 0) throw new Error(`CRAG: no rows in ${path}`)
85
+ return rows
86
+ }
87
+
88
+ async function loadFixtures(opts: LoadOptions): Promise<BenchTask[]> {
89
+ const rows = JSON.parse(await readFile(FIXTURES, 'utf8')) as unknown[]
90
+ console.warn(`[crag] CRAG_FIXTURES=1 — loading ${rows.length} adapter fixtures`)
91
+ return selectTasks(rows.map(rowToTask), opts, 'CRAG')
92
+ }
93
+
94
+ export function createCragAdapter(): BenchmarkAdapter {
95
+ const fixturesMode = process.env.CRAG_FIXTURES === '1'
96
+
97
+ return {
98
+ name: 'crag',
99
+ output: ragAnswerOutput,
100
+
101
+ async preflight() {
102
+ if (fixturesMode) {
103
+ await readFile(FIXTURES, 'utf8')
104
+ return
105
+ }
106
+ const path = dataFile()
107
+ if (!path) {
108
+ throw new Error(
109
+ 'CRAG_DATA_FILE is required. Fix: export facebookresearch/CRAG rows to JSONL and set CRAG_DATA_FILE=/path/to/crag.jsonl, or set CRAG_FIXTURES=1 for adapter plumbing.',
110
+ )
111
+ }
112
+ await loadRows(path)
113
+ },
114
+
115
+ async loadTasks(opts: LoadOptions = {}) {
116
+ if (fixturesMode) return loadFixtures(opts)
117
+ const path = dataFile()
118
+ if (!path) throw new Error('CRAG_DATA_FILE is required to load CRAG tasks')
119
+ return selectTasks((await loadRows(path)).map(rowToTask), opts, 'CRAG')
120
+ },
121
+
122
+ async goldArtifact(task: BenchTask) {
123
+ return `${FINAL_ANSWER_SENTINEL} ${readMeta(task).goldAnswers[0] ?? ''}`
124
+ },
125
+
126
+ async judge(task: BenchTask, artifact: string): Promise<BenchScore> {
127
+ const meta = readMeta(task)
128
+ const score = scoreAnswerArtifact(artifact, meta.goldAnswers)
129
+ return answerScoreToBenchScore(score, {
130
+ benchmark: meta.benchmark,
131
+ domain: meta.domain,
132
+ questionType: meta.questionType,
133
+ dynamism: meta.dynamism,
134
+ })
135
+ },
136
+ }
137
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Offline DABStep adapter test. Official live tasks need a DABStep checkout with
3
+ * the released dataset.csv. Fixture mode only exercises adapter plumbing; it
4
+ * never scores benchmark rows without the official grade.py.
5
+ */
6
+ import assert from 'node:assert/strict'
7
+ import { mkdtemp, rm } from 'node:fs/promises'
8
+ import { tmpdir } from 'node:os'
9
+ import { join } from 'node:path'
10
+ import { test } from 'node:test'
11
+ import { createDabstepAdapter, dabstepAnswerOutput } from './dabstep'
12
+
13
+ process.env.DABSTEP_FIXTURES = '1'
14
+
15
+ type Events = Parameters<typeof dabstepAnswerOutput.parse>[0]
16
+ const stream = (text: string): Events => [{ data: { finalText: text } }] as unknown as Events
17
+
18
+ test('loadTasks fixtures expose DABStep prompt and resource metadata shape', async () => {
19
+ const adapter = createDabstepAdapter()
20
+ const tasks = await adapter.loadTasks({ ids: ['1'] })
21
+ assert.equal(tasks.length, 1)
22
+ assert.equal(tasks[0].id, '1')
23
+ assert.match(tasks[0].prompt, /DABStep data-analysis task/)
24
+ const meta = tasks[0].metadata as Record<string, unknown>
25
+ assert.equal(meta.taskId, 1)
26
+ assert.equal(Array.isArray(meta.golds), true)
27
+ })
28
+
29
+ test('answer OutputAdapter extracts final fenced answer when present', () => {
30
+ assert.equal(dabstepAnswerOutput.parse(stream('work\n```answer\n42\n```')), '42')
31
+ assert.equal(dabstepAnswerOutput.parse(stream('Final Answer: 42')), 'Final Answer: 42')
32
+ })
33
+
34
+ test('goldArtifact exposes the fixture oracle without scoring it as a benchmark result', async () => {
35
+ const adapter = createDabstepAdapter()
36
+ const [task] = await adapter.loadTasks({ ids: ['1'] })
37
+ const gold = await adapter.goldArtifact(task)
38
+ assert.equal(gold, '42')
39
+ })
40
+
41
+ test('judge fails loud without an official DABSTEP_DIR/grade.py', async () => {
42
+ const adapter = createDabstepAdapter()
43
+ const [task] = await adapter.loadTasks({ ids: ['1'] })
44
+ delete process.env.DABSTEP_DIR
45
+ await assert.rejects(adapter.judge(task, '42'), /DABSTEP_DIR is required/)
46
+ })
47
+
48
+ test('preflight is fixture-safe and live mode fails loud without DABSTEP_DIR', async () => {
49
+ const fixtureAdapter = createDabstepAdapter()
50
+ await fixtureAdapter.preflight()
51
+ delete process.env.DABSTEP_FIXTURES
52
+ delete process.env.DABSTEP_DIR
53
+ const liveAdapter = createDabstepAdapter()
54
+ await assert.rejects(liveAdapter.preflight(), /DABSTEP_DIR is required/)
55
+ process.env.DABSTEP_FIXTURES = '1'
56
+ })
57
+
58
+ test('live preflight fails loud when the checkout is missing released dataset.csv', async () => {
59
+ delete process.env.DABSTEP_FIXTURES
60
+ const dir = await mkdtemp(join(tmpdir(), 'dabstep-missing-dataset-'))
61
+ process.env.DABSTEP_DIR = dir
62
+ try {
63
+ const adapter = createDabstepAdapter()
64
+ await assert.rejects(adapter.preflight(), /released dataset\.csv/)
65
+ } finally {
66
+ process.env.DABSTEP_FIXTURES = '1'
67
+ delete process.env.DABSTEP_DIR
68
+ await rm(dir, { recursive: true, force: true })
69
+ }
70
+ })