@tangle-network/agent-bench 0.4.1 → 0.4.4

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 (195) hide show
  1. package/CHANGELOG.md +14 -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 +48 -66
  6. package/dist/benchmarks/_harness.js +329 -33
  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 +18 -15
  112. package/scripts/verify-packed-consumer.mjs +20 -17
  113. package/src/corpus.test.mts +13 -0
  114. package/src/corpus.ts +4 -0
  115. package/src/pier-trial-controller.test.mts +15 -7
  116. package/src/profile-coordinates.ts +2 -2
  117. package/src/rollout-ledger/backfill-swe-arena.mts +5 -1
  118. package/src/rollout-ledger/settle-capture.mts +7 -1
  119. package/src/search-bench/profiles.ts +1 -1
  120. package/src/skill-sandbox-smoke.mts +2 -1
  121. package/src/swe-arena/gepa-seat.mts +1 -1
  122. package/src/swe-arena/ledger-orphans.test.mts +36 -34
  123. package/dist/benchmarks/cadbench.js.map +0 -1
  124. package/dist/benchmarks/cadgenbench.js.map +0 -1
  125. package/dist/benchmarks/types.js.map +0 -1
  126. package/dist/chunk-53UPUNBZ.js +0 -325
  127. package/dist/chunk-53UPUNBZ.js.map +0 -1
  128. package/dist/chunk-5FEQDSCT.js +0 -211
  129. package/dist/chunk-5FEQDSCT.js.map +0 -1
  130. package/dist/chunk-5H5XV76F.js +0 -240
  131. package/dist/chunk-5H5XV76F.js.map +0 -1
  132. package/dist/chunk-67ACKDCX.js +0 -118
  133. package/dist/chunk-67ACKDCX.js.map +0 -1
  134. package/dist/chunk-7FKBWOQT.js +0 -147
  135. package/dist/chunk-7FKBWOQT.js.map +0 -1
  136. package/dist/chunk-BEN6IF2X.js +0 -221
  137. package/dist/chunk-BEN6IF2X.js.map +0 -1
  138. package/dist/chunk-BZY5QARD.js +0 -120
  139. package/dist/chunk-BZY5QARD.js.map +0 -1
  140. package/dist/chunk-C7T7WEK2.js +0 -103
  141. package/dist/chunk-C7T7WEK2.js.map +0 -1
  142. package/dist/chunk-CLIKAXKH.js +0 -276
  143. package/dist/chunk-CLIKAXKH.js.map +0 -1
  144. package/dist/chunk-CWIOBFSP.js +0 -197
  145. package/dist/chunk-CWIOBFSP.js.map +0 -1
  146. package/dist/chunk-CXDUTWQE.js +0 -318
  147. package/dist/chunk-CXDUTWQE.js.map +0 -1
  148. package/dist/chunk-DWALFME7.js +0 -182
  149. package/dist/chunk-DWALFME7.js.map +0 -1
  150. package/dist/chunk-EEOC6QPJ.js +0 -144
  151. package/dist/chunk-EEOC6QPJ.js.map +0 -1
  152. package/dist/chunk-EIETHPD5.js +0 -321
  153. package/dist/chunk-EIETHPD5.js.map +0 -1
  154. package/dist/chunk-GC2EPS6L.js +0 -130
  155. package/dist/chunk-GC2EPS6L.js.map +0 -1
  156. package/dist/chunk-GCHL6XPM.js +0 -169
  157. package/dist/chunk-GCHL6XPM.js.map +0 -1
  158. package/dist/chunk-HQ5HCCKF.js +0 -142
  159. package/dist/chunk-HQ5HCCKF.js.map +0 -1
  160. package/dist/chunk-HVW25KSX.js +0 -208
  161. package/dist/chunk-HVW25KSX.js.map +0 -1
  162. package/dist/chunk-INNOYXCP.js +0 -387
  163. package/dist/chunk-INNOYXCP.js.map +0 -1
  164. package/dist/chunk-J6BU3NTM.js +0 -251
  165. package/dist/chunk-J6BU3NTM.js.map +0 -1
  166. package/dist/chunk-JSQOUKXS.js +0 -149
  167. package/dist/chunk-JSQOUKXS.js.map +0 -1
  168. package/dist/chunk-JTHWEDEW.js +0 -32
  169. package/dist/chunk-JTHWEDEW.js.map +0 -1
  170. package/dist/chunk-NRMGT25X.js +0 -116
  171. package/dist/chunk-NRMGT25X.js.map +0 -1
  172. package/dist/chunk-PA2ZKHJC.js +0 -230
  173. package/dist/chunk-PA2ZKHJC.js.map +0 -1
  174. package/dist/chunk-PUIRNYI7.js +0 -189
  175. package/dist/chunk-PUIRNYI7.js.map +0 -1
  176. package/dist/chunk-QZZEAHWJ.js +0 -136
  177. package/dist/chunk-QZZEAHWJ.js.map +0 -1
  178. package/dist/chunk-SEVJPLZC.js +0 -260
  179. package/dist/chunk-SEVJPLZC.js.map +0 -1
  180. package/dist/chunk-TBKU5XQI.js +0 -228
  181. package/dist/chunk-TBKU5XQI.js.map +0 -1
  182. package/dist/chunk-UPAMRDX4.js +0 -233
  183. package/dist/chunk-UPAMRDX4.js.map +0 -1
  184. package/dist/chunk-VQRS7VUC.js +0 -342
  185. package/dist/chunk-VQRS7VUC.js.map +0 -1
  186. package/dist/chunk-WG7TM7UV.js +0 -30
  187. package/dist/chunk-WG7TM7UV.js.map +0 -1
  188. package/dist/chunk-X3BTXCJ4.js +0 -262
  189. package/dist/chunk-X3BTXCJ4.js.map +0 -1
  190. package/dist/chunk-XXFF3RRD.js +0 -162
  191. package/dist/chunk-XXFF3RRD.js.map +0 -1
  192. package/dist/chunk-ZFNOM7WR.js +0 -27
  193. package/dist/chunk-ZFNOM7WR.js.map +0 -1
  194. package/dist/chunk-ZNCCYTFG.js +0 -170
  195. package/dist/chunk-ZNCCYTFG.js.map +0 -1
@@ -1,11 +1,294 @@
1
- import {
2
- createSimpleQaAdapter,
3
- parseCitations,
4
- parseFinalAnswer
5
- } from "../chunk-SEVJPLZC.js";
6
- export {
7
- createSimpleQaAdapter,
8
- parseCitations,
9
- parseFinalAnswer
10
- };
1
+ import { readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { execFile } from "node:child_process";
4
+ import { fileURLToPath } from "node:url";
5
+ import { promisify } from "node:util";
6
+ //#region src/benchmarks/simpleqa.ts
7
+ /**
8
+ * SimpleQA adapter (OpenAI simple-evals; short-form factual QA). Worker artifact
9
+ * = a single free-text final answer string (optionally preceded by a CITATIONS:
10
+ * block). Each item ships a short gold `answer` plus metadata (topic,
11
+ * answer_type, source urls).
12
+ *
13
+ * Judge is the official SimpleQA grader — an LLM classifier that maps
14
+ * (question, gold target, predicted answer) to exactly one of:
15
+ * A = CORRECT — fully contains the gold, no contradiction
16
+ * B = INCORRECT — contradicts / contains a different factual value
17
+ * C = NOT_ATTEMPTED — hedged, non-committal, or no value given
18
+ * resolved = (grade == CORRECT). score = 1 for CORRECT else 0. The grade letter
19
+ * (with its label) rides in `detail`; NOT_ATTEMPTED is surfaced distinctly from
20
+ * INCORRECT so the scorecard can separate abstention from error.
21
+ *
22
+ * There is no deterministic tier: SimpleQA's rubric (containment + abstention)
23
+ * is the grader's job by design, so judge() always calls the pinned grader model
24
+ * (temperature 0) and fails loud on unparseable grader output. The final-answer
25
+ * extraction IS deterministic (FINAL ANSWER: sentinel, fail-closed to '').
26
+ *
27
+ * Requires for a live run: the bench `.venv` with `datasets`/`requests` not
28
+ * needed — the test set is a single public CSV fetched over HTTP — plus a
29
+ * grader key (TANGLE_API_KEY). For offline/CI verification set
30
+ * SIMPLEQA_FIXTURES=1 to load the committed fixtures
31
+ * (bench/fixtures/simpleqa.json) — no network.
32
+ */
33
+ const execFileAsync = promisify(execFile);
34
+ const BENCH_ROOT = fileURLToPath(new URL("../..", import.meta.url));
35
+ const PY = join(BENCH_ROOT, ".venv", "bin", "python");
36
+ const FIXTURES = join(BENCH_ROOT, "fixtures", "simpleqa.json");
37
+ const DATASET_URL = "https://openaipublic.blob.core.windows.net/simple-evals/simple_qa_test_set.csv";
38
+ const FINAL_ANSWER_SENTINEL = "FINAL ANSWER:";
39
+ /** Worker contract appended to every task prompt. Answer extraction keys off the sentinel. */
40
+ const WORKER_CONTRACT = [
41
+ "",
42
+ "Research the question using live web sources and answer it with a short, specific factual value.",
43
+ "Cite the sources you used as full URLs, one per line, in a `CITATIONS:` block.",
44
+ `End your response with a single final line: \`${FINAL_ANSWER_SENTINEL} <answer>\``,
45
+ "The answer after the sentinel must be the bare value only (no explanation on that line).",
46
+ "If you do not know the answer, state that you do not know rather than guessing."
47
+ ].join("\n");
48
+ /** Run the bench venv python with a script; return stdout (throws on nonzero). */
49
+ async function py(script, args = []) {
50
+ const { stdout } = await execFileAsync(PY, [
51
+ "-c",
52
+ script,
53
+ ...args
54
+ ], { maxBuffer: 1024 * 1024 * 256 });
55
+ return stdout;
56
+ }
57
+ /**
58
+ * Parse the worker artifact into the final answer string.
59
+ * Order: the `FINAL ANSWER:` sentinel first (deterministic-extraction discipline);
60
+ * fall back to the last non-empty line. Returns '' when nothing is parseable
61
+ * (fail-closed — never guess); judge() grades '' as NOT_ATTEMPTED.
62
+ */
63
+ function parseFinalAnswer(artifact) {
64
+ const lines = artifact.split(/\r?\n/);
65
+ for (let i = lines.length - 1; i >= 0; i -= 1) {
66
+ const line = lines[i] ?? "";
67
+ const idx = line.toUpperCase().indexOf(FINAL_ANSWER_SENTINEL);
68
+ if (idx !== -1) return line.slice(idx + 13).trim();
69
+ }
70
+ for (let i = lines.length - 1; i >= 0; i -= 1) {
71
+ const t = (lines[i] ?? "").trim();
72
+ if (t.length > 0) return t;
73
+ }
74
+ return "";
75
+ }
76
+ /** Extract every bare URL in the artifact (CITATIONS: block + inline). SOFT provenance signal. */
77
+ function parseCitations(artifact) {
78
+ const urls = /* @__PURE__ */ new Set();
79
+ for (const m of artifact.matchAll(/https?:\/\/[^\s)<>"']+/g)) urls.add(m[0].replace(/[.,;]+$/, ""));
80
+ return [...urls];
81
+ }
82
+ /**
83
+ * The official SimpleQA grader prompt (faithful paraphrase of OpenAI's
84
+ * simple-evals GRADER_TEMPLATE). Classifies the predicted answer against the
85
+ * gold target into A=CORRECT / B=INCORRECT / C=NOT_ATTEMPTED. The predicted
86
+ * answer must fully contain the gold value with no contradicting statement;
87
+ * hedged or non-committal answers are NOT_ATTEMPTED.
88
+ */
89
+ const GRADER_PROMPT = (question, gold, predicted) => [
90
+ "Your job is to grade a predicted answer to a factual question against the single gold target answer.",
91
+ "Grade the prediction into exactly one of three categories: CORRECT, INCORRECT, or NOT_ATTEMPTED.",
92
+ "First, follow these grading rules carefully.",
93
+ "",
94
+ "CORRECT:",
95
+ " The predicted answer fully contains the gold target without contradicting it.",
96
+ " Predictions with more detail than the gold are still CORRECT, as long as no part contradicts the gold.",
97
+ " Hedging that still commits to the gold value (e.g. \"I think it is X\" where X is the gold) is CORRECT.",
98
+ " Minor formatting, capitalization, ordering, or obvious-typo differences do not matter.",
99
+ " For numeric/date targets, the prediction is CORRECT only if it matches the gold to the precision the gold specifies.",
100
+ "",
101
+ "INCORRECT:",
102
+ " The predicted answer contradicts the gold target in any way, even if the contradiction is hedged.",
103
+ " A prediction that states a different factual value than the gold is INCORRECT.",
104
+ "",
105
+ "NOT_ATTEMPTED:",
106
+ " The gold target is neither fully given nor contradicted: the prediction declines to answer, says it does not know,",
107
+ " asks for clarification, or gives a non-committal, vague, or empty response that commits to no specific value.",
108
+ "",
109
+ "Also note the following rules:",
110
+ " Do not punish a prediction for omitting information that would not change whether the gold target is contained.",
111
+ " Grade ONLY whether the gold value is present and uncontradicted — not the overall quality of the response.",
112
+ "",
113
+ `Question: ${question}`,
114
+ `Gold target: ${gold}`,
115
+ `Predicted answer: ${predicted}`,
116
+ "",
117
+ "Respond with ONLY a fenced JSON block and nothing else:",
118
+ "```json",
119
+ "{\"grade\": \"CORRECT\" | \"INCORRECT\" | \"NOT_ATTEMPTED\"}",
120
+ "```"
121
+ ].join("\n");
122
+ function graderRouter() {
123
+ const key = process.env.TANGLE_API_KEY;
124
+ if (!key) throw new Error("TANGLE_API_KEY is required for the SimpleQA grader (set the Tangle API key)");
125
+ const model = process.env.JUDGE_MODEL ?? "deepseek-v4-flash";
126
+ return {
127
+ baseUrl: process.env.ROUTER_BASE ?? "https://router.tangle.tools/v1",
128
+ key,
129
+ model
130
+ };
131
+ }
132
+ /** Call the official grader. Pinned model, temperature 0; fail loud on unparseable output. */
133
+ async function gradeAnswer(question, gold, predicted, router) {
134
+ const res = await fetch(`${router.baseUrl}/chat/completions`, {
135
+ method: "POST",
136
+ headers: {
137
+ "content-type": "application/json",
138
+ authorization: `Bearer ${router.key}`
139
+ },
140
+ body: JSON.stringify({
141
+ model: router.model,
142
+ temperature: 0,
143
+ messages: [{
144
+ role: "user",
145
+ content: GRADER_PROMPT(question, gold, predicted)
146
+ }]
147
+ })
148
+ });
149
+ if (!res.ok) throw new Error(`SimpleQA grader HTTP ${res.status}: ${(await res.text()).slice(0, 300)}`);
150
+ const body = await res.json();
151
+ const content = body.choices?.[0]?.message?.content;
152
+ if (typeof content !== "string") throw new Error(`SimpleQA grader returned no message content: ${JSON.stringify(body).slice(0, 300)}`);
153
+ const fenced = content.match(/```(?:json)?\s*([\s\S]*?)```/);
154
+ const raw = (fenced ? fenced[1] : content)?.trim() ?? "";
155
+ let parsed;
156
+ try {
157
+ parsed = JSON.parse(raw);
158
+ } catch {
159
+ throw new Error(`SimpleQA grader produced unparseable output (no JSON grade): ${content.slice(0, 300)}`);
160
+ }
161
+ if (parsed.grade === "CORRECT" || parsed.grade === "INCORRECT" || parsed.grade === "NOT_ATTEMPTED") return parsed.grade;
162
+ throw new Error(`SimpleQA grader grade not in {CORRECT,INCORRECT,NOT_ATTEMPTED}: ${JSON.stringify(parsed).slice(0, 200)}`);
163
+ }
164
+ function rowToTask(row, index) {
165
+ const meta = {
166
+ gold: row.answer,
167
+ goldSources: row.urls,
168
+ topic: row.topic,
169
+ answerType: row.answer_type,
170
+ question: row.problem
171
+ };
172
+ return {
173
+ id: `simpleqa-${index}`,
174
+ split: "test",
175
+ prompt: row.problem + WORKER_CONTRACT,
176
+ metadata: meta
177
+ };
178
+ }
179
+ function readMeta(task) {
180
+ const md = task.metadata;
181
+ if (!md || typeof md.gold !== "string" || typeof md.question !== "string") throw new Error(`SimpleQA task ${task.id} missing metadata.gold/question — loadTasks did not populate it`);
182
+ return md;
183
+ }
184
+ function selectTasks(tasks, opts) {
185
+ if (opts.ids) {
186
+ const want = new Set(opts.ids);
187
+ return tasks.filter((t) => want.has(t.id));
188
+ }
189
+ if (opts.limit !== void 0) return tasks.slice(0, opts.limit);
190
+ return tasks;
191
+ }
192
+ async function loadFixtures(opts) {
193
+ const rows = JSON.parse(await readFile(FIXTURES, "utf8"));
194
+ console.warn(`[simpleqa] SIMPLEQA_FIXTURES=1 — loading ${rows.length} committed fixtures (no network)`);
195
+ return selectTasks(rows.map(rowToTask), opts);
196
+ }
197
+ /**
198
+ * Load the live test set: fetch the public CSV via the bench venv python, parse
199
+ * with `csv` (handles quoted/embedded-comma fields) and `ast.literal_eval` for
200
+ * the python-repr `metadata` dict. Emits the same normalized SimpleQaRow shape
201
+ * the fixtures use, so loadTasks/judge share one parse path.
202
+ */
203
+ async function loadLive(opts) {
204
+ const stdout = await py(`
205
+ import csv, ast, json, io, sys
206
+ from urllib.request import urlopen
207
+ with urlopen(${JSON.stringify(DATASET_URL)}, timeout=120) as resp:
208
+ text = resp.read().decode('utf-8')
209
+ rows = list(csv.DictReader(io.StringIO(text)))
210
+ out = []
211
+ for r in rows:
212
+ meta = ast.literal_eval(r['metadata']) if r.get('metadata') else {}
213
+ out.append({
214
+ 'problem': r.get('problem', ''),
215
+ 'answer': r.get('answer', ''),
216
+ 'topic': str(meta.get('topic', '')),
217
+ 'answer_type': str(meta.get('answer_type', '')),
218
+ 'urls': list(meta.get('urls', [])),
219
+ })
220
+ print(json.dumps(out))
221
+ `);
222
+ return selectTasks(JSON.parse(stdout).map(rowToTask), opts);
223
+ }
224
+ function createSimpleQaAdapter() {
225
+ const fixturesMode = process.env.SIMPLEQA_FIXTURES === "1";
226
+ return {
227
+ name: "simpleqa",
228
+ async preflight() {
229
+ graderRouter();
230
+ if (fixturesMode) {
231
+ await readFile(FIXTURES, "utf8").catch((err) => {
232
+ throw new Error(`SIMPLEQA_FIXTURES=1 but ${FIXTURES} unreadable: ${err instanceof Error ? err.message : err}`);
233
+ });
234
+ return;
235
+ }
236
+ try {
237
+ await py(`from urllib.request import urlopen
238
+ with urlopen(${JSON.stringify(DATASET_URL)}, timeout=120) as resp:
239
+ head = resp.read(64).decode('utf-8', 'replace')
240
+ assert head.startswith('metadata,problem,answer'), 'unexpected CSV header: ' + head[:40]
241
+ print('ok')`);
242
+ } catch (err) {
243
+ const msg = err instanceof Error ? err.message : String(err);
244
+ throw new Error(`simpleqa preflight failed: ${msg}\nFix: (1) ensure the bench venv exists (python3 -m venv bench/.venv) ; (2) ensure network access to ${DATASET_URL} ; or set SIMPLEQA_FIXTURES=1 to run against the committed fixtures offline.`);
245
+ }
246
+ },
247
+ async loadTasks(opts = {}) {
248
+ if (fixturesMode) return loadFixtures(opts);
249
+ return loadLive(opts);
250
+ },
251
+ async goldArtifact(task) {
252
+ const meta = readMeta(task);
253
+ return `${FINAL_ANSWER_SENTINEL} ${meta.gold}`;
254
+ },
255
+ async judge(task, artifact) {
256
+ const meta = readMeta(task);
257
+ const finalAnswer = parseFinalAnswer(artifact);
258
+ const citations = parseCitations(artifact);
259
+ if (finalAnswer.length === 0) return {
260
+ resolved: false,
261
+ score: 0,
262
+ detail: JSON.stringify({
263
+ grade: "NOT_ATTEMPTED",
264
+ gradeLetter: "C",
265
+ reason: "no parseable answer",
266
+ gold: meta.gold,
267
+ topic: meta.topic,
268
+ answerType: meta.answerType,
269
+ citationCount: citations.length
270
+ })
271
+ };
272
+ const grade = await gradeAnswer(meta.question, meta.gold, finalAnswer, graderRouter());
273
+ const gradeLetter = grade === "CORRECT" ? "A" : grade === "INCORRECT" ? "B" : "C";
274
+ const resolved = grade === "CORRECT";
275
+ return {
276
+ resolved,
277
+ score: resolved ? 1 : 0,
278
+ detail: JSON.stringify({
279
+ grade,
280
+ gradeLetter,
281
+ gold: meta.gold,
282
+ predicted: finalAnswer,
283
+ topic: meta.topic,
284
+ answerType: meta.answerType,
285
+ citationCount: citations.length
286
+ })
287
+ };
288
+ }
289
+ };
290
+ }
291
+ //#endregion
292
+ export { createSimpleQaAdapter, parseCitations, parseFinalAnswer };
293
+
11
294
  //# sourceMappingURL=simpleqa.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
1
+ {"version":3,"file":"simpleqa.js","names":[],"sources":["../../src/benchmarks/simpleqa.ts"],"sourcesContent":["/**\n * SimpleQA adapter (OpenAI simple-evals; short-form factual QA). Worker artifact\n * = a single free-text final answer string (optionally preceded by a CITATIONS:\n * block). Each item ships a short gold `answer` plus metadata (topic,\n * answer_type, source urls).\n *\n * Judge is the official SimpleQA grader — an LLM classifier that maps\n * (question, gold target, predicted answer) to exactly one of:\n * A = CORRECT — fully contains the gold, no contradiction\n * B = INCORRECT — contradicts / contains a different factual value\n * C = NOT_ATTEMPTED — hedged, non-committal, or no value given\n * resolved = (grade == CORRECT). score = 1 for CORRECT else 0. The grade letter\n * (with its label) rides in `detail`; NOT_ATTEMPTED is surfaced distinctly from\n * INCORRECT so the scorecard can separate abstention from error.\n *\n * There is no deterministic tier: SimpleQA's rubric (containment + abstention)\n * is the grader's job by design, so judge() always calls the pinned grader model\n * (temperature 0) and fails loud on unparseable grader output. The final-answer\n * extraction IS deterministic (FINAL ANSWER: sentinel, fail-closed to '').\n *\n * Requires for a live run: the bench `.venv` with `datasets`/`requests` not\n * needed — the test set is a single public CSV fetched over HTTP — plus a\n * grader key (TANGLE_API_KEY). For offline/CI verification set\n * SIMPLEQA_FIXTURES=1 to load the committed fixtures\n * (bench/fixtures/simpleqa.json) — no network.\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', 'simpleqa.json')\n\nconst DATASET_URL =\n 'https://openaipublic.blob.core.windows.net/simple-evals/simple_qa_test_set.csv'\nconst FINAL_ANSWER_SENTINEL = 'FINAL ANSWER:'\n\n/** Worker contract appended to every task prompt. Answer extraction keys off the sentinel. */\nconst WORKER_CONTRACT = [\n '',\n 'Research the question using live web sources and answer it with a short, specific factual value.',\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 'If you do not know the answer, state that you do not know rather than guessing.',\n].join('\\n')\n\n/**\n * Typed seam for the research worker. The benchmark adapter scores a plain\n * `string` artifact (the BenchmarkAdapter contract); the loop worker decodes its\n * agent runs into a {@link ResearchAnswer} and serializes `finalAnswer` (+ an\n * optional `CITATIONS:` block) into that string before judging.\n */\nexport interface ResearchTask {\n id: string\n question: string\n /** Gold short answer — the grader's target. */\n gold: string\n /** Source URLs cited by the dataset for this item — SOFT provenance signal, never a hard gate. */\n goldSources: string[]\n /** SimpleQA topic, e.g. 'Politics' — slices the scorecard by domain. */\n topic: string\n /** SimpleQA answer_type, e.g. 'Person' | 'Date' | 'Number' | 'Place'. */\n answerType: string\n}\n\nexport interface ResearchAnswer {\n finalAnswer: string\n citations: string[]\n}\n\n/** Normalized fixture/loader row — the single shape both the CSV loader and fixtures emit. */\ninterface SimpleQaRow {\n problem: string\n answer: string\n topic: string\n answer_type: string\n urls: string[]\n}\n\ninterface SimpleQaMeta {\n gold: string\n goldSources: string[]\n topic: string\n answerType: string\n question: string\n}\n\n/** The three official SimpleQA grades. */\ntype Grade = 'CORRECT' | 'INCORRECT' | 'NOT_ATTEMPTED'\n\n/** Run the bench venv python with a script; 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\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); judge() grades '' as NOT_ATTEMPTED.\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 every bare URL in the artifact (CITATIONS: block + inline). SOFT provenance signal. */\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/**\n * The official SimpleQA grader prompt (faithful paraphrase of OpenAI's\n * simple-evals GRADER_TEMPLATE). Classifies the predicted answer against the\n * gold target into A=CORRECT / B=INCORRECT / C=NOT_ATTEMPTED. The predicted\n * answer must fully contain the gold value with no contradicting statement;\n * hedged or non-committal answers are NOT_ATTEMPTED.\n */\nconst GRADER_PROMPT = (question: string, gold: string, predicted: string): string =>\n [\n 'Your job is to grade a predicted answer to a factual question against the single gold target answer.',\n 'Grade the prediction into exactly one of three categories: CORRECT, INCORRECT, or NOT_ATTEMPTED.',\n 'First, follow these grading rules carefully.',\n '',\n 'CORRECT:',\n ' The predicted answer fully contains the gold target without contradicting it.',\n ' Predictions with more detail than the gold are still CORRECT, as long as no part contradicts the gold.',\n ' Hedging that still commits to the gold value (e.g. \"I think it is X\" where X is the gold) is CORRECT.',\n ' Minor formatting, capitalization, ordering, or obvious-typo differences do not matter.',\n ' For numeric/date targets, the prediction is CORRECT only if it matches the gold to the precision the gold specifies.',\n '',\n 'INCORRECT:',\n ' The predicted answer contradicts the gold target in any way, even if the contradiction is hedged.',\n ' A prediction that states a different factual value than the gold is INCORRECT.',\n '',\n 'NOT_ATTEMPTED:',\n ' The gold target is neither fully given nor contradicted: the prediction declines to answer, says it does not know,',\n ' asks for clarification, or gives a non-committal, vague, or empty response that commits to no specific value.',\n '',\n 'Also note the following rules:',\n ' Do not punish a prediction for omitting information that would not change whether the gold target is contained.',\n ' Grade ONLY whether the gold value is present and uncontradicted — not the overall quality of the response.',\n '',\n `Question: ${question}`,\n `Gold target: ${gold}`,\n `Predicted answer: ${predicted}`,\n '',\n 'Respond with ONLY a fenced JSON block and nothing else:',\n '```json',\n '{\"grade\": \"CORRECT\" | \"INCORRECT\" | \"NOT_ATTEMPTED\"}',\n '```',\n ].join('\\n')\n\ninterface GraderRouter {\n baseUrl: string\n key: string\n model: string\n}\n\nfunction graderRouter(): GraderRouter {\n const key = process.env.TANGLE_API_KEY\n if (!key) throw new Error('TANGLE_API_KEY is required for the SimpleQA grader (set the Tangle API key)')\n const model = process.env.JUDGE_MODEL ?? 'deepseek-v4-flash'\n const baseUrl = process.env.ROUTER_BASE ?? 'https://router.tangle.tools/v1'\n return { baseUrl, key, model }\n}\n\n/** Call the official grader. Pinned model, temperature 0; fail loud on unparseable output. */\nasync function gradeAnswer(\n question: string,\n gold: string,\n predicted: string,\n router: GraderRouter,\n): Promise<Grade> {\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 messages: [{ role: 'user', content: GRADER_PROMPT(question, gold, predicted) }],\n }),\n })\n if (!res.ok) {\n throw new Error(`SimpleQA grader 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(`SimpleQA grader 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: { grade?: unknown }\n try {\n parsed = JSON.parse(raw) as { grade?: unknown }\n } catch {\n throw new Error(`SimpleQA grader produced unparseable output (no JSON grade): ${content.slice(0, 300)}`)\n }\n if (parsed.grade === 'CORRECT' || parsed.grade === 'INCORRECT' || parsed.grade === 'NOT_ATTEMPTED') {\n return parsed.grade\n }\n throw new Error(\n `SimpleQA grader grade not in {CORRECT,INCORRECT,NOT_ATTEMPTED}: ${JSON.stringify(parsed).slice(0, 200)}`,\n )\n}\n\nfunction rowToTask(row: SimpleQaRow, index: number): BenchTask {\n const meta: SimpleQaMeta = {\n gold: row.answer,\n goldSources: row.urls,\n topic: row.topic,\n answerType: row.answer_type,\n question: row.problem,\n }\n return {\n id: `simpleqa-${index}`,\n split: 'test',\n prompt: row.problem + WORKER_CONTRACT,\n metadata: meta as unknown as Record<string, unknown>,\n }\n}\n\nfunction readMeta(task: BenchTask): SimpleQaMeta {\n const md = task.metadata\n if (!md || typeof md.gold !== 'string' || typeof md.question !== 'string') {\n throw new Error(`SimpleQA task ${task.id} missing metadata.gold/question — loadTasks did not populate it`)\n }\n return md as unknown as SimpleQaMeta\n}\n\nfunction selectTasks(tasks: BenchTask[], opts: LoadOptions): BenchTask[] {\n if (opts.ids) {\n const want = new Set(opts.ids)\n return tasks.filter((t) => want.has(t.id))\n }\n if (opts.limit !== undefined) return tasks.slice(0, opts.limit)\n return tasks\n}\n\nasync function loadFixtures(opts: LoadOptions): Promise<BenchTask[]> {\n const rows = JSON.parse(await readFile(FIXTURES, 'utf8')) as SimpleQaRow[]\n console.warn(`[simpleqa] SIMPLEQA_FIXTURES=1 — loading ${rows.length} committed fixtures (no network)`)\n return selectTasks(rows.map(rowToTask), opts)\n}\n\n/**\n * Load the live test set: fetch the public CSV via the bench venv python, parse\n * with `csv` (handles quoted/embedded-comma fields) and `ast.literal_eval` for\n * the python-repr `metadata` dict. Emits the same normalized SimpleQaRow shape\n * the fixtures use, so loadTasks/judge share one parse path.\n */\nasync function loadLive(opts: LoadOptions): Promise<BenchTask[]> {\n const script = `\nimport csv, ast, json, io, sys\nfrom urllib.request import urlopen\nwith urlopen(${JSON.stringify(DATASET_URL)}, timeout=120) as resp:\n text = resp.read().decode('utf-8')\nrows = list(csv.DictReader(io.StringIO(text)))\nout = []\nfor r in rows:\n meta = ast.literal_eval(r['metadata']) if r.get('metadata') else {}\n out.append({\n 'problem': r.get('problem', ''),\n 'answer': r.get('answer', ''),\n 'topic': str(meta.get('topic', '')),\n 'answer_type': str(meta.get('answer_type', '')),\n 'urls': list(meta.get('urls', [])),\n })\nprint(json.dumps(out))\n`\n const stdout = await py(script)\n const rows = JSON.parse(stdout) as SimpleQaRow[]\n return selectTasks(rows.map(rowToTask), opts)\n}\n\nexport function createSimpleQaAdapter(): BenchmarkAdapter {\n const fixturesMode = process.env.SIMPLEQA_FIXTURES === '1'\n\n return {\n name: 'simpleqa',\n\n async preflight() {\n // The grader router must be configured in both modes — SimpleQA's score is\n // defined by the grader, so a run without it is meaningless.\n graderRouter()\n if (fixturesMode) {\n await readFile(FIXTURES, 'utf8').catch((err) => {\n throw new Error(\n `SIMPLEQA_FIXTURES=1 but ${FIXTURES} unreadable: ${err instanceof Error ? err.message : err}`,\n )\n })\n return\n }\n try {\n await py(\n `from urllib.request import urlopen\nwith urlopen(${JSON.stringify(DATASET_URL)}, timeout=120) as resp:\n head = resp.read(64).decode('utf-8', 'replace')\nassert head.startswith('metadata,problem,answer'), 'unexpected CSV header: ' + head[:40]\nprint('ok')`,\n )\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n throw new Error(\n `simpleqa preflight failed: ${msg}\\n` +\n `Fix: (1) ensure the bench venv exists (python3 -m venv bench/.venv) ; ` +\n `(2) ensure network access to ${DATASET_URL} ; ` +\n `or set SIMPLEQA_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 return loadLive(opts)\n },\n\n async goldArtifact(task: BenchTask) {\n // Gold artifact = the worker-contract serialization of the gold answer, so\n // verify-judge proves gold→CORRECT through the SAME parse path the real\n // artifact takes.\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\n if (finalAnswer.length === 0) {\n // No parseable value committed — NOT_ATTEMPTED by construction, no grader call.\n return {\n resolved: false,\n score: 0,\n detail: JSON.stringify({\n grade: 'NOT_ATTEMPTED',\n gradeLetter: 'C',\n reason: 'no parseable answer',\n gold: meta.gold,\n topic: meta.topic,\n answerType: meta.answerType,\n citationCount: citations.length,\n }),\n }\n }\n\n const grade = await gradeAnswer(meta.question, meta.gold, finalAnswer, graderRouter())\n const gradeLetter = grade === 'CORRECT' ? 'A' : grade === 'INCORRECT' ? 'B' : 'C'\n const resolved = grade === 'CORRECT'\n return {\n resolved,\n score: resolved ? 1 : 0,\n detail: JSON.stringify({\n grade,\n gradeLetter,\n gold: meta.gold,\n predicted: finalAnswer,\n topic: meta.topic,\n answerType: meta.answerType,\n citationCount: citations.length,\n }),\n }\n },\n }\n}\n"],"mappings":"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"}
@@ -1,21 +1,7 @@
1
- import { OutputAdapter } from '@tangle-network/agent-runtime/loops';
2
- import { StagedRunCaptureSpec } from './_harness.js';
3
- import { BenchmarkAdapter, BenchScore } from './types.js';
4
-
5
- /**
6
- * SWE-bench Verified adapter. Worker artifact = a unified-diff patch. Judge =
7
- * the official `swebench` harness: apply the patch in the instance's Docker
8
- * image, run FAIL_TO_PASS + PASS_TO_PASS, report `resolved`. Fully deterministic
9
- * — no LLM judge.
10
- *
11
- * Requires: the bench `.venv` with `swebench` installed + a running Docker
12
- * daemon (per-instance images are pulled/built on first run).
13
- *
14
- * Process/Docker/report plumbing is shared via ./_harness; this file owns the
15
- * SWE-specific pieces: the patch OutputAdapter, the dataset dump, and the
16
- * predictions-file → run_evaluation argv → report-shape mapping.
17
- */
18
-
1
+ import { BenchScore, BenchmarkAdapter } from "./types.js";
2
+ import { StagedRunCaptureSpec } from "./_harness.js";
3
+ import { OutputAdapter } from "@tangle-network/agent-runtime/loops";
4
+ //#region src/benchmarks/swe-bench.d.ts
19
5
  /**
20
6
  * The SWE deliverable's FALLBACK parser, from the agent's event STREAM.
21
7
  *
@@ -28,29 +14,30 @@ import { BenchmarkAdapter, BenchScore } from './types.js';
28
14
  declare const swePatchOutput: OutputAdapter<string>;
29
15
  type SweBenchCacheLevel = 'none' | 'base' | 'env' | 'instance';
30
16
  interface SweBenchArtifactCaptureContext {
31
- readonly taskId: string;
32
- readonly runId: string;
33
- /** One-based sequence unique within this adapter instance. */
34
- readonly attemptSequence: number;
17
+ readonly taskId: string;
18
+ readonly runId: string;
19
+ /** One-based sequence unique within this adapter instance. */
20
+ readonly attemptSequence: number;
35
21
  }
36
22
  interface SweBenchAdapterOptions {
37
- readonly timeoutMs?: number;
38
- readonly cacheLevel?: SweBenchCacheLevel;
39
- /**
40
- * Return a unique destination for any attempt whose complete official
41
- * evaluator directory and process logs should be retained.
42
- */
43
- readonly captureEvaluatorArtifacts?: (context: SweBenchArtifactCaptureContext) => StagedRunCaptureSpec | undefined;
23
+ readonly timeoutMs?: number;
24
+ readonly cacheLevel?: SweBenchCacheLevel;
25
+ /**
26
+ * Return a unique destination for any attempt whose complete official
27
+ * evaluator directory and process logs should be retained.
28
+ */
29
+ readonly captureEvaluatorArtifacts?: (context: SweBenchArtifactCaptureContext) => StagedRunCaptureSpec | undefined;
44
30
  }
45
31
  /** Convert one official report into a score without turning evaluator failures into agent failures. */
46
32
  declare function scoreSweReport(taskId: string, value: unknown): BenchScore;
47
33
  declare function sweEvaluationArgv(args: {
48
- readonly predictionsPath: string;
49
- readonly runId: string;
50
- readonly instanceId: string;
51
- readonly cacheLevel: SweBenchCacheLevel;
52
- readonly namespace?: 'swebench' | 'none';
34
+ readonly predictionsPath: string;
35
+ readonly runId: string;
36
+ readonly instanceId: string;
37
+ readonly cacheLevel: SweBenchCacheLevel;
38
+ readonly namespace?: 'swebench' | 'none';
53
39
  }): string[];
54
40
  declare function createSweBenchAdapter(options?: SweBenchAdapterOptions): BenchmarkAdapter;
55
-
56
- export { type SweBenchAdapterOptions, type SweBenchArtifactCaptureContext, type SweBenchCacheLevel, createSweBenchAdapter, scoreSweReport, sweEvaluationArgv, swePatchOutput };
41
+ //#endregion
42
+ export { SweBenchAdapterOptions, SweBenchArtifactCaptureContext, SweBenchCacheLevel, createSweBenchAdapter, scoreSweReport, sweEvaluationArgv, swePatchOutput };
43
+ //# sourceMappingURL=swe-bench.d.ts.map