@tangle-network/agent-bench 0.4.1 → 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.
- package/CHANGELOG.md +9 -0
- package/dist/adapters.d.ts +4 -11
- package/dist/adapters.js +78 -41
- package/dist/adapters.js.map +1 -1
- package/dist/benchmarks/_harness.d.ts +48 -66
- package/dist/benchmarks/_harness.js +329 -33
- package/dist/benchmarks/_harness.js.map +1 -1
- package/dist/benchmarks/aec-bench.d.ts +4 -25
- package/dist/benchmarks/aec-bench.js +242 -7
- package/dist/benchmarks/aec-bench.js.map +1 -1
- package/dist/benchmarks/agentbench.d.ts +5 -13
- package/dist/benchmarks/agentbench.js +114 -9
- package/dist/benchmarks/agentbench.js.map +1 -1
- package/dist/benchmarks/appworld.d.ts +9 -29
- package/dist/benchmarks/appworld.js +317 -12
- package/dist/benchmarks/appworld.js.map +1 -1
- package/dist/benchmarks/bfcl.d.ts +5 -15
- package/dist/benchmarks/bfcl.js +264 -9
- package/dist/benchmarks/bfcl.js.map +1 -1
- package/dist/benchmarks/cad-design.d.ts +16 -41
- package/dist/benchmarks/cad-design.js +512 -6
- package/dist/benchmarks/cad-design.js.map +1 -1
- package/dist/benchmarks/cadbench.d.ts +4 -17
- package/dist/benchmarks/cadbench.js +2 -8
- package/dist/benchmarks/cadgenbench.d.ts +4 -20
- package/dist/benchmarks/cadgenbench.js +2 -8
- package/dist/benchmarks/commit0.d.ts +5 -27
- package/dist/benchmarks/commit0.js +187 -9
- package/dist/benchmarks/commit0.js.map +1 -1
- package/dist/benchmarks/crag.d.ts +4 -12
- package/dist/benchmarks/crag.js +110 -8
- package/dist/benchmarks/crag.js.map +1 -1
- package/dist/benchmarks/dabstep.d.ts +5 -15
- package/dist/benchmarks/dabstep.js +177 -9
- package/dist/benchmarks/dabstep.js.map +1 -1
- package/dist/benchmarks/enterpriseops-gym.d.ts +5 -34
- package/dist/benchmarks/enterpriseops-gym.js +236 -9
- package/dist/benchmarks/enterpriseops-gym.js.map +1 -1
- package/dist/benchmarks/finresearchbench.d.ts +4 -13
- package/dist/benchmarks/finresearchbench.js +218 -7
- package/dist/benchmarks/finresearchbench.js.map +1 -1
- package/dist/benchmarks/finsearchcomp.d.ts +8 -39
- package/dist/benchmarks/finsearchcomp.js +267 -6
- package/dist/benchmarks/finsearchcomp.js.map +1 -1
- package/dist/benchmarks/frames.d.ts +15 -37
- package/dist/benchmarks/frames.js +408 -11
- package/dist/benchmarks/frames.js.map +1 -1
- package/dist/benchmarks/hotpotqa.d.ts +4 -24
- package/dist/benchmarks/hotpotqa.js +250 -14
- package/dist/benchmarks/hotpotqa.js.map +1 -1
- package/dist/benchmarks/humaneval.d.ts +19 -37
- package/dist/benchmarks/humaneval.js +279 -16
- package/dist/benchmarks/humaneval.js.map +1 -1
- package/dist/benchmarks/mind2web.d.ts +7 -34
- package/dist/benchmarks/mind2web.js +257 -8
- package/dist/benchmarks/mind2web.js.map +1 -1
- package/dist/benchmarks/nomiracl.d.ts +4 -13
- package/dist/benchmarks/nomiracl.js +146 -8
- package/dist/benchmarks/nomiracl.js.map +1 -1
- package/dist/benchmarks/open-rag-bench.d.ts +4 -12
- package/dist/benchmarks/open-rag-bench.js +110 -8
- package/dist/benchmarks/open-rag-bench.js.map +1 -1
- package/dist/benchmarks/programbench.d.ts +5 -29
- package/dist/benchmarks/programbench.js +161 -9
- package/dist/benchmarks/programbench.js.map +1 -1
- package/dist/benchmarks/rag-shared.d.ts +21 -20
- package/dist/benchmarks/rag-shared.js +245 -38
- package/dist/benchmarks/rag-shared.js.map +1 -1
- package/dist/benchmarks/ragbench.d.ts +4 -14
- package/dist/benchmarks/ragbench.js +127 -8
- package/dist/benchmarks/ragbench.js.map +1 -1
- package/dist/benchmarks/simpleqa.d.ts +17 -44
- package/dist/benchmarks/simpleqa.js +293 -10
- package/dist/benchmarks/simpleqa.js.map +1 -1
- package/dist/benchmarks/swe-bench.d.ts +23 -36
- package/dist/benchmarks/swe-bench.js +234 -13
- package/dist/benchmarks/swe-bench.js.map +1 -1
- package/dist/benchmarks/t2-ragbench.d.ts +4 -12
- package/dist/benchmarks/t2-ragbench.js +118 -8
- package/dist/benchmarks/t2-ragbench.js.map +1 -1
- package/dist/benchmarks/tau-bench-shared.d.ts +14 -22
- package/dist/benchmarks/tau-bench-shared.js +169 -9
- package/dist/benchmarks/tau-bench-shared.js.map +1 -1
- package/dist/benchmarks/tau2-bench.d.ts +5 -5
- package/dist/benchmarks/tau2-bench.js +28 -10
- package/dist/benchmarks/tau2-bench.js.map +1 -1
- package/dist/benchmarks/tau3-banking.d.ts +4 -13
- package/dist/benchmarks/tau3-banking.js +28 -8
- package/dist/benchmarks/tau3-banking.js.map +1 -1
- package/dist/benchmarks/terminal-bench.d.ts +4 -22
- package/dist/benchmarks/terminal-bench.js +140 -7
- package/dist/benchmarks/terminal-bench.js.map +1 -1
- package/dist/benchmarks/toollm.d.ts +5 -13
- package/dist/benchmarks/toollm.js +184 -9
- package/dist/benchmarks/toollm.js.map +1 -1
- package/dist/benchmarks/trata-hedge.d.ts +4 -30
- package/dist/benchmarks/trata-hedge.js +336 -6
- package/dist/benchmarks/trata-hedge.js.map +1 -1
- package/dist/benchmarks/types.d.ts +85 -94
- package/dist/benchmarks/types.js +1 -1
- package/dist/benchmarks/webarena-verified.d.ts +5 -13
- package/dist/benchmarks/webarena-verified.js +152 -9
- package/dist/benchmarks/webarena-verified.js.map +1 -1
- package/dist/cadbench-DpQWZHp4.js +285 -0
- package/dist/cadbench-DpQWZHp4.js.map +1 -0
- package/dist/cadgenbench-DRhczfsG.js +151 -0
- package/dist/cadgenbench-DRhczfsG.js.map +1 -0
- package/dist/index.d.ts +245 -293
- package/dist/index.js +1669 -1791
- package/dist/index.js.map +1 -1
- package/package.json +17 -14
- package/scripts/verify-packed-consumer.mjs +20 -17
- package/src/corpus.test.mts +13 -0
- package/src/corpus.ts +4 -0
- package/src/profile-coordinates.ts +2 -2
- package/src/rollout-ledger/backfill-swe-arena.mts +5 -1
- package/src/rollout-ledger/settle-capture.mts +7 -1
- package/src/search-bench/profiles.ts +1 -1
- package/src/skill-sandbox-smoke.mts +2 -1
- package/src/swe-arena/gepa-seat.mts +1 -1
- package/src/swe-arena/ledger-orphans.test.mts +36 -34
- package/dist/benchmarks/cadbench.js.map +0 -1
- package/dist/benchmarks/cadgenbench.js.map +0 -1
- package/dist/benchmarks/types.js.map +0 -1
- package/dist/chunk-53UPUNBZ.js +0 -325
- package/dist/chunk-53UPUNBZ.js.map +0 -1
- package/dist/chunk-5FEQDSCT.js +0 -211
- package/dist/chunk-5FEQDSCT.js.map +0 -1
- package/dist/chunk-5H5XV76F.js +0 -240
- package/dist/chunk-5H5XV76F.js.map +0 -1
- package/dist/chunk-67ACKDCX.js +0 -118
- package/dist/chunk-67ACKDCX.js.map +0 -1
- package/dist/chunk-7FKBWOQT.js +0 -147
- package/dist/chunk-7FKBWOQT.js.map +0 -1
- package/dist/chunk-BEN6IF2X.js +0 -221
- package/dist/chunk-BEN6IF2X.js.map +0 -1
- package/dist/chunk-BZY5QARD.js +0 -120
- package/dist/chunk-BZY5QARD.js.map +0 -1
- package/dist/chunk-C7T7WEK2.js +0 -103
- package/dist/chunk-C7T7WEK2.js.map +0 -1
- package/dist/chunk-CLIKAXKH.js +0 -276
- package/dist/chunk-CLIKAXKH.js.map +0 -1
- package/dist/chunk-CWIOBFSP.js +0 -197
- package/dist/chunk-CWIOBFSP.js.map +0 -1
- package/dist/chunk-CXDUTWQE.js +0 -318
- package/dist/chunk-CXDUTWQE.js.map +0 -1
- package/dist/chunk-DWALFME7.js +0 -182
- package/dist/chunk-DWALFME7.js.map +0 -1
- package/dist/chunk-EEOC6QPJ.js +0 -144
- package/dist/chunk-EEOC6QPJ.js.map +0 -1
- package/dist/chunk-EIETHPD5.js +0 -321
- package/dist/chunk-EIETHPD5.js.map +0 -1
- package/dist/chunk-GC2EPS6L.js +0 -130
- package/dist/chunk-GC2EPS6L.js.map +0 -1
- package/dist/chunk-GCHL6XPM.js +0 -169
- package/dist/chunk-GCHL6XPM.js.map +0 -1
- package/dist/chunk-HQ5HCCKF.js +0 -142
- package/dist/chunk-HQ5HCCKF.js.map +0 -1
- package/dist/chunk-HVW25KSX.js +0 -208
- package/dist/chunk-HVW25KSX.js.map +0 -1
- package/dist/chunk-INNOYXCP.js +0 -387
- package/dist/chunk-INNOYXCP.js.map +0 -1
- package/dist/chunk-J6BU3NTM.js +0 -251
- package/dist/chunk-J6BU3NTM.js.map +0 -1
- package/dist/chunk-JSQOUKXS.js +0 -149
- package/dist/chunk-JSQOUKXS.js.map +0 -1
- package/dist/chunk-JTHWEDEW.js +0 -32
- package/dist/chunk-JTHWEDEW.js.map +0 -1
- package/dist/chunk-NRMGT25X.js +0 -116
- package/dist/chunk-NRMGT25X.js.map +0 -1
- package/dist/chunk-PA2ZKHJC.js +0 -230
- package/dist/chunk-PA2ZKHJC.js.map +0 -1
- package/dist/chunk-PUIRNYI7.js +0 -189
- package/dist/chunk-PUIRNYI7.js.map +0 -1
- package/dist/chunk-QZZEAHWJ.js +0 -136
- package/dist/chunk-QZZEAHWJ.js.map +0 -1
- package/dist/chunk-SEVJPLZC.js +0 -260
- package/dist/chunk-SEVJPLZC.js.map +0 -1
- package/dist/chunk-TBKU5XQI.js +0 -228
- package/dist/chunk-TBKU5XQI.js.map +0 -1
- package/dist/chunk-UPAMRDX4.js +0 -233
- package/dist/chunk-UPAMRDX4.js.map +0 -1
- package/dist/chunk-VQRS7VUC.js +0 -342
- package/dist/chunk-VQRS7VUC.js.map +0 -1
- package/dist/chunk-WG7TM7UV.js +0 -30
- package/dist/chunk-WG7TM7UV.js.map +0 -1
- package/dist/chunk-X3BTXCJ4.js +0 -262
- package/dist/chunk-X3BTXCJ4.js.map +0 -1
- package/dist/chunk-XXFF3RRD.js +0 -162
- package/dist/chunk-XXFF3RRD.js.map +0 -1
- package/dist/chunk-ZFNOM7WR.js +0 -27
- package/dist/chunk-ZFNOM7WR.js.map +0 -1
- package/dist/chunk-ZNCCYTFG.js +0 -170
- package/dist/chunk-ZNCCYTFG.js.map +0 -1
|
@@ -1,11 +1,294 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
} from "
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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 {
|
|
2
|
-
import { StagedRunCaptureSpec } from
|
|
3
|
-
import {
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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 {
|
|
41
|
+
//#endregion
|
|
42
|
+
export { SweBenchAdapterOptions, SweBenchArtifactCaptureContext, SweBenchCacheLevel, createSweBenchAdapter, scoreSweReport, sweEvaluationArgv, swePatchOutput };
|
|
43
|
+
//# sourceMappingURL=swe-bench.d.ts.map
|