@tangle-network/agent-bench 0.1.0 → 0.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/HARNESS.md +302 -0
- package/README.md +39 -2
- package/dist/adapters.d.ts +15 -0
- package/dist/adapters.js +43 -0
- package/dist/adapters.js.map +1 -0
- package/dist/benchmarks/_harness.d.ts +125 -0
- package/dist/benchmarks/_harness.js +33 -0
- package/dist/benchmarks/_harness.js.map +1 -0
- package/dist/benchmarks/aec-bench.d.ts +27 -0
- package/dist/benchmarks/aec-bench.js +8 -0
- package/dist/benchmarks/aec-bench.js.map +1 -0
- package/dist/benchmarks/agentbench.d.ts +16 -0
- package/dist/benchmarks/agentbench.js +10 -0
- package/dist/benchmarks/agentbench.js.map +1 -0
- package/dist/benchmarks/appworld.d.ts +37 -0
- package/dist/benchmarks/appworld.js +14 -0
- package/dist/benchmarks/appworld.js.map +1 -0
- package/dist/benchmarks/bfcl.d.ts +18 -0
- package/dist/benchmarks/bfcl.js +10 -0
- package/dist/benchmarks/bfcl.js.map +1 -0
- package/dist/benchmarks/cad-design.d.ts +45 -0
- package/dist/benchmarks/cad-design.js +7 -0
- package/dist/benchmarks/cad-design.js.map +1 -0
- package/dist/benchmarks/cadbench.d.ts +19 -0
- package/dist/benchmarks/cadbench.js +8 -0
- package/dist/benchmarks/cadbench.js.map +1 -0
- package/dist/benchmarks/cadgenbench.d.ts +22 -0
- package/dist/benchmarks/cadgenbench.js +8 -0
- package/dist/benchmarks/cadgenbench.js.map +1 -0
- package/dist/benchmarks/commit0.d.ts +31 -0
- package/dist/benchmarks/commit0.js +10 -0
- package/dist/benchmarks/commit0.js.map +1 -0
- package/dist/benchmarks/crag.d.ts +14 -0
- package/dist/benchmarks/crag.js +9 -0
- package/dist/benchmarks/crag.js.map +1 -0
- package/dist/benchmarks/dabstep.d.ts +18 -0
- package/dist/benchmarks/dabstep.js +10 -0
- package/dist/benchmarks/dabstep.js.map +1 -0
- package/dist/benchmarks/enterpriseops-gym.d.ts +38 -0
- package/dist/benchmarks/enterpriseops-gym.js +10 -0
- package/dist/benchmarks/enterpriseops-gym.js.map +1 -0
- package/dist/benchmarks/finresearchbench.d.ts +15 -0
- package/dist/benchmarks/finresearchbench.js +8 -0
- package/dist/benchmarks/finresearchbench.js.map +1 -0
- package/dist/benchmarks/finsearchcomp.d.ts +49 -0
- package/dist/benchmarks/finsearchcomp.js +7 -0
- package/dist/benchmarks/finsearchcomp.js.map +1 -0
- package/dist/benchmarks/frames.d.ts +59 -0
- package/dist/benchmarks/frames.js +13 -0
- package/dist/benchmarks/frames.js.map +1 -0
- package/dist/benchmarks/hotpotqa.d.ts +48 -0
- package/dist/benchmarks/hotpotqa.js +15 -0
- package/dist/benchmarks/hotpotqa.js.map +1 -0
- package/dist/benchmarks/humaneval.d.ts +53 -0
- package/dist/benchmarks/humaneval.js +15 -0
- package/dist/benchmarks/humaneval.js.map +1 -0
- package/dist/benchmarks/mind2web.d.ts +41 -0
- package/dist/benchmarks/mind2web.js +9 -0
- package/dist/benchmarks/mind2web.js.map +1 -0
- package/dist/benchmarks/nomiracl.d.ts +15 -0
- package/dist/benchmarks/nomiracl.js +9 -0
- package/dist/benchmarks/nomiracl.js.map +1 -0
- package/dist/benchmarks/open-rag-bench.d.ts +14 -0
- package/dist/benchmarks/open-rag-bench.js +9 -0
- package/dist/benchmarks/open-rag-bench.js.map +1 -0
- package/dist/benchmarks/programbench.d.ts +38 -0
- package/dist/benchmarks/programbench.js +10 -0
- package/dist/benchmarks/programbench.js.map +1 -0
- package/dist/benchmarks/rag-shared.d.ts +42 -0
- package/dist/benchmarks/rag-shared.js +39 -0
- package/dist/benchmarks/rag-shared.js.map +1 -0
- package/dist/benchmarks/ragbench.d.ts +16 -0
- package/dist/benchmarks/ragbench.js +9 -0
- package/dist/benchmarks/ragbench.js.map +1 -0
- package/dist/benchmarks/simpleqa.d.ts +64 -0
- package/dist/benchmarks/simpleqa.js +11 -0
- package/dist/benchmarks/simpleqa.js.map +1 -0
- package/dist/benchmarks/swe-bench.d.ts +56 -0
- package/dist/benchmarks/swe-bench.js +14 -0
- package/dist/benchmarks/swe-bench.js.map +1 -0
- package/dist/benchmarks/t2-ragbench.d.ts +14 -0
- package/dist/benchmarks/t2-ragbench.js +9 -0
- package/dist/benchmarks/t2-ragbench.js.map +1 -0
- package/dist/benchmarks/tau-bench-shared.d.ts +26 -0
- package/dist/benchmarks/tau-bench-shared.js +10 -0
- package/dist/benchmarks/tau-bench-shared.js.map +1 -0
- package/dist/benchmarks/tau2-bench.d.ts +7 -0
- package/dist/benchmarks/tau2-bench.js +11 -0
- package/dist/benchmarks/tau2-bench.js.map +1 -0
- package/dist/benchmarks/tau3-banking.d.ts +15 -0
- package/dist/benchmarks/tau3-banking.js +9 -0
- package/dist/benchmarks/tau3-banking.js.map +1 -0
- package/dist/benchmarks/terminal-bench.d.ts +24 -0
- package/dist/benchmarks/terminal-bench.js +8 -0
- package/dist/benchmarks/terminal-bench.js.map +1 -0
- package/dist/benchmarks/toollm.d.ts +16 -0
- package/dist/benchmarks/toollm.js +10 -0
- package/dist/benchmarks/toollm.js.map +1 -0
- package/dist/benchmarks/trata-hedge.d.ts +32 -0
- package/dist/benchmarks/trata-hedge.js +7 -0
- package/dist/benchmarks/trata-hedge.js.map +1 -0
- package/dist/benchmarks/types.d.ts +107 -0
- package/dist/benchmarks/types.js +1 -0
- package/dist/benchmarks/types.js.map +1 -0
- package/dist/benchmarks/webarena-verified.d.ts +16 -0
- package/dist/benchmarks/webarena-verified.js +10 -0
- package/dist/benchmarks/webarena-verified.js.map +1 -0
- package/dist/chunk-2PVVP7GN.js +197 -0
- package/dist/chunk-2PVVP7GN.js.map +1 -0
- package/dist/chunk-2XU6OGEN.js +170 -0
- package/dist/chunk-2XU6OGEN.js.map +1 -0
- package/dist/chunk-53UPUNBZ.js +325 -0
- package/dist/chunk-53UPUNBZ.js.map +1 -0
- package/dist/chunk-5SBJCB6W.js +144 -0
- package/dist/chunk-5SBJCB6W.js.map +1 -0
- package/dist/chunk-7WSD27QQ.js +118 -0
- package/dist/chunk-7WSD27QQ.js.map +1 -0
- package/dist/chunk-C7T7WEK2.js +103 -0
- package/dist/chunk-C7T7WEK2.js.map +1 -0
- package/dist/chunk-CKUVRZ2T.js +251 -0
- package/dist/chunk-CKUVRZ2T.js.map +1 -0
- package/dist/chunk-HBSWHQNJ.js +30 -0
- package/dist/chunk-HBSWHQNJ.js.map +1 -0
- package/dist/chunk-HHXFIHXC.js +116 -0
- package/dist/chunk-HHXFIHXC.js.map +1 -0
- package/dist/chunk-IFAV6KEM.js +276 -0
- package/dist/chunk-IFAV6KEM.js.map +1 -0
- package/dist/chunk-INNOYXCP.js +387 -0
- package/dist/chunk-INNOYXCP.js.map +1 -0
- package/dist/chunk-J3KDJNX2.js +182 -0
- package/dist/chunk-J3KDJNX2.js.map +1 -0
- package/dist/chunk-JRWWGMK7.js +148 -0
- package/dist/chunk-JRWWGMK7.js.map +1 -0
- package/dist/chunk-JTHWEDEW.js +32 -0
- package/dist/chunk-JTHWEDEW.js.map +1 -0
- package/dist/chunk-KDIKRJGB.js +120 -0
- package/dist/chunk-KDIKRJGB.js.map +1 -0
- package/dist/chunk-LRRD7NAG.js +301 -0
- package/dist/chunk-LRRD7NAG.js.map +1 -0
- package/dist/chunk-ODT47UAY.js +221 -0
- package/dist/chunk-ODT47UAY.js.map +1 -0
- package/dist/chunk-PA2ZKHJC.js +230 -0
- package/dist/chunk-PA2ZKHJC.js.map +1 -0
- package/dist/chunk-PPYSEKFM.js +182 -0
- package/dist/chunk-PPYSEKFM.js.map +1 -0
- package/dist/chunk-PUIRNYI7.js +189 -0
- package/dist/chunk-PUIRNYI7.js.map +1 -0
- package/dist/chunk-R36V2VP7.js +169 -0
- package/dist/chunk-R36V2VP7.js.map +1 -0
- package/dist/chunk-R67DFVLO.js +142 -0
- package/dist/chunk-R67DFVLO.js.map +1 -0
- package/dist/chunk-SEVJPLZC.js +260 -0
- package/dist/chunk-SEVJPLZC.js.map +1 -0
- package/dist/chunk-SYDW647C.js +318 -0
- package/dist/chunk-SYDW647C.js.map +1 -0
- package/dist/chunk-TBKU5XQI.js +228 -0
- package/dist/chunk-TBKU5XQI.js.map +1 -0
- package/dist/chunk-TSWPNOYM.js +147 -0
- package/dist/chunk-TSWPNOYM.js.map +1 -0
- package/dist/chunk-UAIOHCUK.js +27 -0
- package/dist/chunk-UAIOHCUK.js.map +1 -0
- package/dist/chunk-UPAMRDX4.js +233 -0
- package/dist/chunk-UPAMRDX4.js.map +1 -0
- package/dist/chunk-VQRS7VUC.js +342 -0
- package/dist/chunk-VQRS7VUC.js.map +1 -0
- package/dist/chunk-X3BTXCJ4.js +262 -0
- package/dist/chunk-X3BTXCJ4.js.map +1 -0
- package/dist/chunk-X5YKXC6V.js +211 -0
- package/dist/chunk-X5YKXC6V.js.map +1 -0
- package/dist/chunk-Y6O2OCUO.js +130 -0
- package/dist/chunk-Y6O2OCUO.js.map +1 -0
- package/dist/chunk-YCGY7UIZ.js +208 -0
- package/dist/chunk-YCGY7UIZ.js.map +1 -0
- package/dist/chunk-Z7ML6L77.js +162 -0
- package/dist/chunk-Z7ML6L77.js.map +1 -0
- package/dist/chunk-ZEWMTR5M.js +136 -0
- package/dist/chunk-ZEWMTR5M.js.map +1 -0
- package/dist/index.d.ts +355 -0
- package/dist/index.js +1908 -0
- package/dist/index.js.map +1 -0
- package/fixtures/aec-bench.json +18 -0
- package/fixtures/agentbench-dbbench.json +22 -0
- package/fixtures/bfcl.json +45 -0
- package/fixtures/commit0.json +72 -0
- package/fixtures/crag.json +10 -0
- package/fixtures/dabstep.json +22 -0
- package/fixtures/enterpriseops-gym.json +103 -0
- package/fixtures/finresearchbench.json +21 -0
- package/fixtures/finsearchcomp.json +66 -0
- package/fixtures/frames.json +26 -0
- package/fixtures/hotpotqa.json +182 -0
- package/fixtures/nomiracl.json +26 -0
- package/fixtures/open-rag-bench.json +16 -0
- package/fixtures/pier-agent/no-model-task/environment/Dockerfile +16 -0
- package/fixtures/pier-agent/no-model-task/environment/seed/src/status.txt +1 -0
- package/fixtures/pier-agent/no-model-task/instruction.md +6 -0
- package/fixtures/pier-agent/no-model-task/pre_artifacts.sh +6 -0
- package/fixtures/pier-agent/no-model-task/task.toml +35 -0
- package/fixtures/pier-agent/no-model-task/tests/Dockerfile +17 -0
- package/fixtures/pier-agent/no-model-task/tests/seed/src/status.txt +1 -0
- package/fixtures/pier-agent/no-model-task/tests/test.sh +19 -0
- package/fixtures/programbench.json +17 -0
- package/fixtures/ragbench.json +21 -0
- package/fixtures/simpleqa.json +121 -0
- package/fixtures/t2-ragbench.json +13 -0
- package/fixtures/tau2-bench.json +16 -0
- package/fixtures/tau3-banking.json +16 -0
- package/fixtures/toollm.json +28 -0
- package/fixtures/webarena-verified.json +20 -0
- package/package.json +60 -20
- package/pier_agents/__init__.py +18 -0
- package/pier_agents/candidate_contract.py +755 -0
- package/pier_agents/process_boundary.py +321 -0
- package/pier_agents/tangle_candidate.py +907 -0
- package/pier_agents/workspace_boundary.py +368 -0
- package/scripts/appworld_driver.py +359 -0
- package/scripts/cadbench_prepare.py +22 -0
- package/scripts/cadgenbench_hard_parts.py +48 -0
- package/scripts/clbench_codebase_judge.py +73 -0
- package/scripts/commit0_judge.py +170 -0
- package/scripts/dabstep_judge.py +42 -0
- package/scripts/enterpriseops_gym_judge.py +281 -0
- package/scripts/programbench_judge.py +120 -0
- package/scripts/render-gate-chart.mjs +176 -0
- package/scripts/run-package-tests.mjs +56 -0
- package/scripts/terminate-pier-trial.mts +66 -0
- package/scripts/trata-hedge/README.md +56 -0
- package/scripts/trata-hedge/run.sh +60 -0
- package/scripts/trata-hedge/solve.py +83 -0
- package/scripts/verify-packed-consumer.mjs +235 -0
- package/scripts/verify-pier-agent.mts +715 -0
- package/scripts/verify-pier-pair.mts +74 -0
- package/scripts/verify-pier-recovery.mts +139 -0
- package/src/adapters.ts +26 -0
- package/src/benchmarks/_harness.test.mts +178 -0
- package/src/benchmarks/_harness.ts +239 -16
- package/src/benchmarks/agentbench.ts +163 -0
- package/src/benchmarks/appworld.test.mts +15 -9
- package/src/benchmarks/bfcl.ts +346 -0
- package/src/benchmarks/crag.ts +137 -0
- package/src/benchmarks/dabstep.test.mts +70 -0
- package/src/benchmarks/dabstep.ts +212 -0
- package/src/benchmarks/external-adapters.test.mts +150 -0
- package/src/benchmarks/finresearchbench.ts +269 -0
- package/src/benchmarks/humaneval.ts +20 -8
- package/src/benchmarks/nomiracl.ts +180 -0
- package/src/benchmarks/open-rag-bench.ts +153 -0
- package/src/benchmarks/rag-benchmarks.test.mts +138 -0
- package/src/benchmarks/rag-shared.ts +327 -0
- package/src/benchmarks/ragbench.ts +171 -0
- package/src/benchmarks/swe-bench.test.mts +61 -0
- package/src/benchmarks/swe-bench.ts +201 -19
- package/src/benchmarks/t2-ragbench.ts +166 -0
- package/src/benchmarks/tau-bench-shared.ts +214 -0
- package/src/benchmarks/tau2-bench.ts +30 -0
- package/src/benchmarks/tau3-banking.ts +29 -0
- package/src/benchmarks/terminal-bench.test.mts +33 -0
- package/src/benchmarks/terminal-bench.ts +23 -8
- package/src/benchmarks/toollm.ts +254 -0
- package/src/benchmarks/types.ts +42 -0
- package/src/benchmarks/webarena-verified.ts +200 -0
- package/src/commit0-prereqs.sh +0 -0
- package/src/coordination-mcp-container-reach.mts +181 -0
- package/src/decoder-live.mts +1 -1
- package/src/examples/README.md +103 -39
- package/src/examples/benchmark-matrix.mts +101 -0
- package/src/examples/lean-proof-gate.README.md +77 -0
- package/src/examples/lean-proof-gate.mts +162 -0
- package/src/examples/lean-verify.ts +95 -0
- package/src/examples/lean.Dockerfile +12 -0
- package/src/examples/math-demo.mts +9 -7
- package/src/examples/strategy-demo.mts +10 -12
- package/src/gate.ts +3 -2
- package/src/hev-eval.mts +69 -0
- package/src/hev-improve.mts +169 -0
- package/src/hev-structural.mts +688 -0
- package/src/index.ts +73 -0
- package/src/mbpp-structural.mts +662 -0
- package/src/pier-agent.test-fixtures.mts +19 -0
- package/src/pier-agent.test.mts +363 -0
- package/src/pier-agent.ts +657 -0
- package/src/pier-result-grader.mjs +30 -0
- package/src/pier-result-grader.test.mts +62 -0
- package/src/pier-result-grader.ts +108 -0
- package/src/pier-task-outcome.test.mts +117 -0
- package/src/pier-task-outcome.ts +240 -0
- package/src/pier-trial-controller.test.mts +412 -0
- package/src/pier-trial-controller.ts +858 -0
- package/src/pier-trial-supervisor.mjs +352 -0
- package/src/resolve-client.ts +25 -2
- package/src/run-benchmarks-cli.mts +72 -0
- package/src/run-benchmarks-report.ts +66 -0
- package/src/run-benchmarks.test.mts +231 -0
- package/src/run-benchmarks.ts +589 -0
- package/src/smoke-structural-rollout.mts +393 -0
- package/src/swe-bench-env.test.ts +207 -0
- package/src/swe-bench-env.ts +554 -0
- package/src/swe-jail.ts +293 -0
- package/src/swe-self-improve.mts +84 -0
- package/src/swe-structural-judge-policy.test.ts +117 -0
- package/src/swe-structural-judge-policy.ts +133 -0
- package/src/swe-structural-policy.test.ts +124 -0
- package/src/swe-structural-policy.ts +132 -0
- package/src/swe-structural-provenance.test.ts +93 -0
- package/src/swe-structural-provenance.ts +138 -0
- package/src/swe-structural.mts +1260 -0
- package/src/swe-temp.ts +14 -0
- package/src/tb-container-executor.mts +234 -0
- package/src/tb-container-executor.test.mts +99 -0
- package/src/tb-supervisor-sidecar.mts +222 -0
- package/src/trata-gepa.mts +1 -1
- package/steerers/eops-itsm-population.json +1 -0
- package/tb_agents/opencode_refine_agent.py +117 -0
- package/tb_agents/opencode_router_agent.py +406 -0
- package/tb_agents/opencode_supervisor_agent.py +239 -0
- package/tb_agents/script_agent.py +66 -0
|
@@ -21,15 +21,26 @@ import {
|
|
|
21
21
|
runVenvPython,
|
|
22
22
|
safeRunId,
|
|
23
23
|
stageFile,
|
|
24
|
+
type StagedRunCaptureSpec,
|
|
24
25
|
} from './_harness'
|
|
25
26
|
import type { BenchmarkAdapter, BenchScore, BenchTask, LoadOptions } from './types'
|
|
26
27
|
|
|
27
28
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
|
|
29
|
+
* Fixed in-box path the agent clones the instance repo into. It is the SINGLE
|
|
30
|
+
* source of truth shared by the prompt template (which tells the agent to clone
|
|
31
|
+
* here) and `boxExtract` (which runs `git diff` here after the shot) — so the
|
|
32
|
+
* harness always knows exactly where the agent's edits live, for any instance.
|
|
33
|
+
*/
|
|
34
|
+
const SWE_REPO_DIR = '/work'
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The SWE deliverable's FALLBACK parser, from the agent's event STREAM.
|
|
38
|
+
*
|
|
39
|
+
* The PRIMARY deliverable is `boxExtract` below: a `git diff` of the agent's
|
|
40
|
+
* actual edits, read from the cloned repo's STATE inside the box (standard
|
|
41
|
+
* SWE-bench practice). This event-stream parse only runs when that diff is empty
|
|
42
|
+
* — a model that edited the source correctly but never printed a fenced diff (the
|
|
43
|
+
* exact failure this replaces) still scores off its real changes, not its prose.
|
|
33
44
|
*/
|
|
34
45
|
export const swePatchOutput: OutputAdapter<string> = {
|
|
35
46
|
parse(events) {
|
|
@@ -48,17 +59,179 @@ export const swePatchOutput: OutputAdapter<string> = {
|
|
|
48
59
|
}
|
|
49
60
|
|
|
50
61
|
const DATASET = 'princeton-nlp/SWE-bench_Verified'
|
|
62
|
+
export type SweBenchCacheLevel = 'none' | 'base' | 'env' | 'instance'
|
|
63
|
+
|
|
64
|
+
export interface SweBenchArtifactCaptureContext {
|
|
65
|
+
readonly taskId: string
|
|
66
|
+
readonly runId: string
|
|
67
|
+
/** One-based sequence unique within this adapter instance. */
|
|
68
|
+
readonly attemptSequence: number
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface SweBenchAdapterOptions {
|
|
72
|
+
readonly timeoutMs?: number
|
|
73
|
+
readonly cacheLevel?: SweBenchCacheLevel
|
|
74
|
+
/**
|
|
75
|
+
* Return a unique destination for any attempt whose complete official
|
|
76
|
+
* evaluator directory and process logs should be retained.
|
|
77
|
+
*/
|
|
78
|
+
readonly captureEvaluatorArtifacts?: (
|
|
79
|
+
context: SweBenchArtifactCaptureContext,
|
|
80
|
+
) => StagedRunCaptureSpec | undefined
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const SWE_CACHE_LEVELS = new Set<SweBenchCacheLevel>(['none', 'base', 'env', 'instance'])
|
|
84
|
+
|
|
85
|
+
function scorerNamespace(): 'swebench' | 'none' {
|
|
86
|
+
const namespace = process.env.SWEBENCH_NAMESPACE ?? 'swebench'
|
|
87
|
+
if (namespace !== 'swebench' && namespace !== 'none') {
|
|
88
|
+
throw new Error(`SWEBENCH_NAMESPACE must be swebench|none, got "${namespace}"`)
|
|
89
|
+
}
|
|
90
|
+
return namespace
|
|
91
|
+
}
|
|
92
|
+
const TEST_FILE_EXCLUDES = [
|
|
93
|
+
"':(exclude,glob)**/tests/**'",
|
|
94
|
+
"':(exclude,glob)**/test/**'",
|
|
95
|
+
"':(exclude,glob)test_*.py'",
|
|
96
|
+
"':(exclude,glob)**/test_*.py'",
|
|
97
|
+
"':(exclude,glob)*_test.py'",
|
|
98
|
+
"':(exclude,glob)**/*_test.py'",
|
|
99
|
+
"':(exclude,glob)conftest.py'",
|
|
100
|
+
"':(exclude,glob)**/conftest.py'",
|
|
101
|
+
].join(' ')
|
|
51
102
|
|
|
52
103
|
interface SweReport {
|
|
53
104
|
resolved_instances?: number
|
|
54
105
|
resolved_ids?: string[]
|
|
106
|
+
unresolved_ids?: string[]
|
|
107
|
+
empty_patch_ids?: string[]
|
|
108
|
+
completed_ids?: string[]
|
|
109
|
+
incomplete_ids?: string[]
|
|
110
|
+
error_ids?: string[]
|
|
111
|
+
submitted_ids?: string[]
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function stringIds(report: Record<string, unknown>, key: keyof SweReport): string[] {
|
|
115
|
+
const value = report[key]
|
|
116
|
+
if (value === undefined) return []
|
|
117
|
+
if (!Array.isArray(value) || value.some((entry) => typeof entry !== 'string')) {
|
|
118
|
+
throw new Error(`swe-bench: malformed ${key}`)
|
|
119
|
+
}
|
|
120
|
+
return value
|
|
55
121
|
}
|
|
56
122
|
|
|
57
|
-
|
|
123
|
+
/** Convert one official report into a score without turning evaluator failures into agent failures. */
|
|
124
|
+
export function scoreSweReport(taskId: string, value: unknown): BenchScore {
|
|
125
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
126
|
+
throw new Error('swe-bench: report must be an object')
|
|
127
|
+
}
|
|
128
|
+
const report = value as Record<string, unknown>
|
|
129
|
+
const statusIds = {
|
|
130
|
+
resolved: stringIds(report, 'resolved_ids'),
|
|
131
|
+
unresolved: stringIds(report, 'unresolved_ids'),
|
|
132
|
+
emptyPatch: stringIds(report, 'empty_patch_ids'),
|
|
133
|
+
completed: stringIds(report, 'completed_ids'),
|
|
134
|
+
incomplete: stringIds(report, 'incomplete_ids'),
|
|
135
|
+
error: stringIds(report, 'error_ids'),
|
|
136
|
+
}
|
|
137
|
+
const submitted = stringIds(report, 'submitted_ids')
|
|
138
|
+
const mentioned = Object.values(statusIds).flat()
|
|
139
|
+
if (
|
|
140
|
+
mentioned.some((id) => id !== taskId)
|
|
141
|
+
|| (submitted.length > 0 && (submitted.length !== 1 || submitted[0] !== taskId))
|
|
142
|
+
) {
|
|
143
|
+
throw new Error(`swe-bench: report identity mismatch for ${taskId}`)
|
|
144
|
+
}
|
|
145
|
+
if (statusIds.error.includes(taskId) || statusIds.incomplete.includes(taskId)) {
|
|
146
|
+
throw new Error(`swe-bench: evaluator failed for ${taskId}`)
|
|
147
|
+
}
|
|
148
|
+
const outcomes = [
|
|
149
|
+
statusIds.resolved.includes(taskId),
|
|
150
|
+
statusIds.unresolved.includes(taskId),
|
|
151
|
+
statusIds.emptyPatch.includes(taskId),
|
|
152
|
+
]
|
|
153
|
+
if (outcomes.filter(Boolean).length !== 1) {
|
|
154
|
+
throw new Error(`swe-bench: report has no unique outcome for ${taskId}`)
|
|
155
|
+
}
|
|
156
|
+
if ((outcomes[0] || outcomes[1]) && !statusIds.completed.includes(taskId)) {
|
|
157
|
+
throw new Error(`swe-bench: report lacks a completed evaluation for ${taskId}`)
|
|
158
|
+
}
|
|
159
|
+
const resolved = outcomes[0]
|
|
160
|
+
return { resolved, score: resolved ? 1 : 0, detail: JSON.stringify(report) }
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function sweEvaluationArgv(args: {
|
|
164
|
+
readonly predictionsPath: string
|
|
165
|
+
readonly runId: string
|
|
166
|
+
readonly instanceId: string
|
|
167
|
+
readonly cacheLevel: SweBenchCacheLevel
|
|
168
|
+
readonly namespace?: 'swebench' | 'none'
|
|
169
|
+
}): string[] {
|
|
170
|
+
return [
|
|
171
|
+
'-m', 'swebench.harness.run_evaluation',
|
|
172
|
+
'--dataset_name', DATASET,
|
|
173
|
+
'--predictions_path', args.predictionsPath,
|
|
174
|
+
'--run_id', args.runId,
|
|
175
|
+
'--instance_ids', args.instanceId,
|
|
176
|
+
'--max_workers', '1',
|
|
177
|
+
'--namespace', args.namespace ?? scorerNamespace(),
|
|
178
|
+
'--cache_level', args.cacheLevel,
|
|
179
|
+
]
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function shellQuote(value: string): string {
|
|
183
|
+
return `'${value.replace(/'/g, `'\\''`)}'`
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function sweMetadata(task: BenchTask): { repo: string; base: string } {
|
|
187
|
+
const repo = String(task.metadata?.repo ?? '')
|
|
188
|
+
const base = String(task.metadata?.base_commit ?? '')
|
|
189
|
+
if (!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(repo)) {
|
|
190
|
+
throw new Error(`swe-bench: invalid repo metadata for ${task.id}: ${repo}`)
|
|
191
|
+
}
|
|
192
|
+
if (!/^[0-9a-f]{7,40}$/i.test(base)) {
|
|
193
|
+
throw new Error(`swe-bench: invalid base_commit metadata for ${task.id}: ${base}`)
|
|
194
|
+
}
|
|
195
|
+
return { repo, base }
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export function createSweBenchAdapter(options: SweBenchAdapterOptions = {}): BenchmarkAdapter {
|
|
199
|
+
if (
|
|
200
|
+
options.timeoutMs !== undefined
|
|
201
|
+
&& (!Number.isSafeInteger(options.timeoutMs) || options.timeoutMs <= 0)
|
|
202
|
+
) throw new Error('swe-bench: timeoutMs must be a positive integer')
|
|
203
|
+
const cacheLevel = options.cacheLevel ?? 'env'
|
|
204
|
+
if (!SWE_CACHE_LEVELS.has(cacheLevel)) throw new Error('swe-bench: invalid cacheLevel')
|
|
205
|
+
if (
|
|
206
|
+
options.captureEvaluatorArtifacts !== undefined
|
|
207
|
+
&& typeof options.captureEvaluatorArtifacts !== 'function'
|
|
208
|
+
) throw new Error('swe-bench: captureEvaluatorArtifacts must be a function')
|
|
209
|
+
let attemptSequence = 0
|
|
58
210
|
return {
|
|
59
211
|
name: 'swe-bench-verified',
|
|
60
212
|
output: swePatchOutput,
|
|
61
213
|
|
|
214
|
+
// Extract the patch from repo STATE, not printed text: stage every edit the
|
|
215
|
+
// agent made in the cloned repo and diff it against the checked-out
|
|
216
|
+
// base_commit (`HEAD`). Test files are excluded — the judge applies the gold
|
|
217
|
+
// `test_patch` itself, so an agent edit to a test would collide on apply. The
|
|
218
|
+
// paths come out `a/<repo-relative>` (cwd = repo root), matching the gold
|
|
219
|
+
// patch format the swebench judge's `git apply` expects.
|
|
220
|
+
// Pre-stage: clone the instance repo at base_commit into SWE_REPO_DIR so the
|
|
221
|
+
// agent only edits (the harness owns the checkout — a stochastic model can't be
|
|
222
|
+
// trusted to clone to an exact path). `--quiet` keeps the exec output small.
|
|
223
|
+
boxSetup(task) {
|
|
224
|
+
const { repo, base } = sweMetadata(task)
|
|
225
|
+
return {
|
|
226
|
+
command: `rm -rf ${shellQuote(SWE_REPO_DIR)} && git clone --quiet ${shellQuote(`https://github.com/${repo}`)} ${shellQuote(SWE_REPO_DIR)} && git -C ${shellQuote(SWE_REPO_DIR)} checkout --quiet ${shellQuote(base)}`,
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
boxExtract() {
|
|
230
|
+
return {
|
|
231
|
+
command: `git -C ${shellQuote(SWE_REPO_DIR)} add -A && git -C ${shellQuote(SWE_REPO_DIR)} diff --cached -- . ${TEST_FILE_EXCLUDES}`,
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
|
|
62
235
|
async preflight() {
|
|
63
236
|
await preflightVenvImports({
|
|
64
237
|
modules: ['swebench'],
|
|
@@ -101,8 +274,10 @@ print(json.dumps(out))
|
|
|
101
274
|
prompt: [
|
|
102
275
|
`Repository: ${r.repo} @ ${r.base_commit}`,
|
|
103
276
|
'',
|
|
104
|
-
|
|
105
|
-
'
|
|
277
|
+
`The repository is ALREADY cloned at ${SWE_REPO_DIR}, checked out at commit ${r.base_commit}. Work there directly (\`cd ${SWE_REPO_DIR}\`); do not re-clone.`,
|
|
278
|
+
'',
|
|
279
|
+
'Resolve this issue by editing the repository SOURCE so the failing tests pass without breaking the passing ones. Do NOT edit test files — the evaluation runs hidden tests on a fresh checkout, so editing tests does not count. Keep the change minimal and confined to the cloned repo.',
|
|
280
|
+
'Work iteratively: reproduce the issue, implement the fix in the source, and re-run the relevant tests until they pass. You do NOT need to print the diff — the harness reads your committed edits directly from the repo.',
|
|
106
281
|
'',
|
|
107
282
|
'--- Issue ---',
|
|
108
283
|
String(r.problem_statement ?? ''),
|
|
@@ -119,8 +294,18 @@ print(json.dumps(out))
|
|
|
119
294
|
|
|
120
295
|
async judge(task: BenchTask, artifact: string): Promise<BenchScore> {
|
|
121
296
|
const runId = safeRunId('bench', task.id)
|
|
297
|
+
const capture = options.captureEvaluatorArtifacts?.({
|
|
298
|
+
taskId: task.id,
|
|
299
|
+
runId,
|
|
300
|
+
attemptSequence: ++attemptSequence,
|
|
301
|
+
})
|
|
122
302
|
return runStagedJudge({
|
|
123
303
|
tmpPrefix: 'swebench-',
|
|
304
|
+
...(options.timeoutMs === undefined ? {} : { timeoutMs: options.timeoutMs }),
|
|
305
|
+
...(capture === undefined ? {} : { capture }),
|
|
306
|
+
// Debug: retain the staged dir (holds swebench's per-instance apply/run
|
|
307
|
+
// logs) for post-mortem when SWEBENCH_KEEP_TMP is set. Off by default.
|
|
308
|
+
...(process.env.SWEBENCH_KEEP_TMP ? { keepTmp: true } : {}),
|
|
124
309
|
async stage(dir) {
|
|
125
310
|
await stageFile(
|
|
126
311
|
join(dir, 'preds.json'),
|
|
@@ -131,20 +316,17 @@ print(json.dumps(out))
|
|
|
131
316
|
},
|
|
132
317
|
// The official evaluation harness. Pulls/builds the instance image, applies
|
|
133
318
|
// the patch, runs the test spec, writes a per-run report JSON in cwd.
|
|
134
|
-
argv: (dir) =>
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
'--cache_level', 'env',
|
|
142
|
-
],
|
|
319
|
+
argv: (dir) => sweEvaluationArgv({
|
|
320
|
+
predictionsPath: join(dir, 'preds.json'),
|
|
321
|
+
runId,
|
|
322
|
+
instanceId: task.id,
|
|
323
|
+
cacheLevel,
|
|
324
|
+
namespace: scorerNamespace(),
|
|
325
|
+
}),
|
|
143
326
|
async parseReport(dir) {
|
|
144
327
|
// Report file: agent-runtime-bench.<run_id>.json
|
|
145
328
|
const report = await readJsonReport<SweReport>(join(dir, `agent-runtime-bench.${runId}.json`))
|
|
146
|
-
|
|
147
|
-
return { resolved, score: resolved ? 1 : 0, detail: JSON.stringify(report) }
|
|
329
|
+
return scoreSweReport(task.id, report)
|
|
148
330
|
},
|
|
149
331
|
})
|
|
150
332
|
},
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* T2-RAGBench adapter.
|
|
3
|
+
*
|
|
4
|
+
* T2-RAGBench stresses text+table retrieval and numerical reasoning over
|
|
5
|
+
* financial documents. The judge uses the shared deterministic answer scorer
|
|
6
|
+
* with numeric tolerance enabled by default.
|
|
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
|
+
contextBlock,
|
|
18
|
+
contextsFrom,
|
|
19
|
+
firstString,
|
|
20
|
+
isObject,
|
|
21
|
+
ragAnswerOutput,
|
|
22
|
+
readJsonRows,
|
|
23
|
+
scoreAnswerArtifact,
|
|
24
|
+
selectTasks,
|
|
25
|
+
stringFrom,
|
|
26
|
+
type RagContext,
|
|
27
|
+
} from './rag-shared'
|
|
28
|
+
|
|
29
|
+
const FIXTURES = join(benchRoot, 'fixtures', 't2-ragbench.json')
|
|
30
|
+
|
|
31
|
+
interface T2RagBenchMeta {
|
|
32
|
+
benchmark: 't2-ragbench'
|
|
33
|
+
query: string
|
|
34
|
+
goldAnswers: string[]
|
|
35
|
+
contexts: RagContext[]
|
|
36
|
+
subset: string
|
|
37
|
+
documentId: string
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const dataFile = (): string | undefined => process.env.T2_RAGBENCH_DATA_FILE
|
|
41
|
+
|
|
42
|
+
function rowToTask(raw: unknown, index: number): BenchTask {
|
|
43
|
+
if (!isObject(raw)) throw new Error(`T2-RAGBench row ${index} must be an object`)
|
|
44
|
+
const query = firstString(raw, ['question', 'query', 'prompt'])
|
|
45
|
+
const goldAnswers = allStrings(raw, [
|
|
46
|
+
'program_answer',
|
|
47
|
+
'original_answer',
|
|
48
|
+
'answer',
|
|
49
|
+
'answers',
|
|
50
|
+
'reference',
|
|
51
|
+
'reference_answer',
|
|
52
|
+
'gold',
|
|
53
|
+
])
|
|
54
|
+
if (!query) throw new Error(`T2-RAGBench row ${index} missing question/query`)
|
|
55
|
+
if (goldAnswers.length === 0) throw new Error(`T2-RAGBench row ${index} missing answer`)
|
|
56
|
+
const baseContexts =
|
|
57
|
+
contextsFrom(raw.context).length > 0
|
|
58
|
+
? contextsFrom(raw.context)
|
|
59
|
+
: contextsFrom(raw.contexts).length > 0
|
|
60
|
+
? contextsFrom(raw.contexts)
|
|
61
|
+
: contextsFrom(raw.chunks).length > 0
|
|
62
|
+
? contextsFrom(raw.chunks)
|
|
63
|
+
: contextsFrom(raw.passages)
|
|
64
|
+
const table = stringFrom(raw.table) ?? stringFrom(raw.table_text)
|
|
65
|
+
const contexts = table
|
|
66
|
+
? [...baseContexts, { id: 'table', title: 'Table', text: table }]
|
|
67
|
+
: baseContexts
|
|
68
|
+
const subset = stringFrom(raw.subset) ?? stringFrom(raw.dataset) ?? 'unknown'
|
|
69
|
+
const documentId =
|
|
70
|
+
stringFrom(raw.context_id) ??
|
|
71
|
+
stringFrom(raw.document_id) ??
|
|
72
|
+
stringFrom(raw.doc_id) ??
|
|
73
|
+
stringFrom(raw.file_name) ??
|
|
74
|
+
'unknown'
|
|
75
|
+
const id = stringFrom(raw.id) ?? stringFrom(raw.qid) ?? stringFrom(raw.query_id) ?? `t2-ragbench-${index}`
|
|
76
|
+
const meta: T2RagBenchMeta = {
|
|
77
|
+
benchmark: 't2-ragbench',
|
|
78
|
+
query,
|
|
79
|
+
goldAnswers,
|
|
80
|
+
contexts,
|
|
81
|
+
subset,
|
|
82
|
+
documentId,
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
id,
|
|
86
|
+
split: stringFrom(raw.split) ?? subset,
|
|
87
|
+
prompt: [
|
|
88
|
+
'Answer this T2-RAGBench text-and-table financial question.',
|
|
89
|
+
'Do the required numerical reasoning from the supplied context before giving the final value.',
|
|
90
|
+
'End with a single final line: `FINAL ANSWER: <answer>`.',
|
|
91
|
+
'',
|
|
92
|
+
`Question: ${query}`,
|
|
93
|
+
`Document: ${documentId}`,
|
|
94
|
+
`Subset: ${subset}`,
|
|
95
|
+
contexts.length > 0 ? `\nContext:\n${contextBlock(contexts)}` : undefined,
|
|
96
|
+
]
|
|
97
|
+
.filter(Boolean)
|
|
98
|
+
.join('\n'),
|
|
99
|
+
metadata: meta as unknown as Record<string, unknown>,
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function readMeta(task: BenchTask): T2RagBenchMeta {
|
|
104
|
+
const md = task.metadata
|
|
105
|
+
if (!md || !Array.isArray(md.goldAnswers)) {
|
|
106
|
+
throw new Error(`T2-RAGBench task ${task.id} missing metadata — loadTasks did not populate it`)
|
|
107
|
+
}
|
|
108
|
+
return md as unknown as T2RagBenchMeta
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function loadRows(path: string): Promise<unknown[]> {
|
|
112
|
+
const rows = await readJsonRows(path)
|
|
113
|
+
if (rows.length === 0) throw new Error(`T2-RAGBench: no rows in ${path}`)
|
|
114
|
+
return rows
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async function loadFixtures(opts: LoadOptions): Promise<BenchTask[]> {
|
|
118
|
+
const rows = JSON.parse(await readFile(FIXTURES, 'utf8')) as unknown[]
|
|
119
|
+
console.warn(`[t2-ragbench] T2_RAGBENCH_FIXTURES=1 — loading ${rows.length} adapter fixtures`)
|
|
120
|
+
return selectTasks(rows.map(rowToTask), opts, 'T2-RAGBench')
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function createT2RagBenchAdapter(): BenchmarkAdapter {
|
|
124
|
+
const fixturesMode = process.env.T2_RAGBENCH_FIXTURES === '1'
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
name: 't2-ragbench',
|
|
128
|
+
output: ragAnswerOutput,
|
|
129
|
+
|
|
130
|
+
async preflight() {
|
|
131
|
+
if (fixturesMode) {
|
|
132
|
+
await readFile(FIXTURES, 'utf8')
|
|
133
|
+
return
|
|
134
|
+
}
|
|
135
|
+
const path = dataFile()
|
|
136
|
+
if (!path) {
|
|
137
|
+
throw new Error(
|
|
138
|
+
'T2_RAGBENCH_DATA_FILE is required. Fix: export T2-RAGBench rows to JSONL and set T2_RAGBENCH_DATA_FILE=/path/to/t2-ragbench.jsonl, or set T2_RAGBENCH_FIXTURES=1 for adapter plumbing.',
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
await loadRows(path)
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
async loadTasks(opts: LoadOptions = {}) {
|
|
145
|
+
if (fixturesMode) return loadFixtures(opts)
|
|
146
|
+
const path = dataFile()
|
|
147
|
+
if (!path) throw new Error('T2_RAGBENCH_DATA_FILE is required to load T2-RAGBench tasks')
|
|
148
|
+
return selectTasks((await loadRows(path)).map(rowToTask), opts, 'T2-RAGBench')
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
async goldArtifact(task: BenchTask) {
|
|
152
|
+
return `${FINAL_ANSWER_SENTINEL} ${readMeta(task).goldAnswers[0] ?? ''}`
|
|
153
|
+
},
|
|
154
|
+
|
|
155
|
+
async judge(task: BenchTask, artifact: string): Promise<BenchScore> {
|
|
156
|
+
const meta = readMeta(task)
|
|
157
|
+
const score = scoreAnswerArtifact(artifact, meta.goldAnswers, { numericTolerance: 0.01 })
|
|
158
|
+
return answerScoreToBenchScore(score, {
|
|
159
|
+
benchmark: meta.benchmark,
|
|
160
|
+
subset: meta.subset,
|
|
161
|
+
documentId: meta.documentId,
|
|
162
|
+
contextCount: meta.contexts.length,
|
|
163
|
+
})
|
|
164
|
+
},
|
|
165
|
+
}
|
|
166
|
+
}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared tau-bench adapter spine.
|
|
3
|
+
*
|
|
4
|
+
* tau2 and tau3 live in the same upstream repository/package namespace today:
|
|
5
|
+
* `sierra-research/tau2-bench`, Python package `tau2`. Keep one implementation
|
|
6
|
+
* for task loading and reward recomputation so the domain/version adapters only
|
|
7
|
+
* choose env names, default domain, and fixture file.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { readFile, stat } from 'node:fs/promises'
|
|
11
|
+
import { resolve } from 'node:path'
|
|
12
|
+
import type { OutputAdapter } from '@tangle-network/agent-runtime/loops'
|
|
13
|
+
import { runVenvPython } from './_harness'
|
|
14
|
+
import type { BenchmarkAdapter, BenchScore, BenchTask, LoadOptions } from './types'
|
|
15
|
+
|
|
16
|
+
export interface TauBenchConfig {
|
|
17
|
+
name: string
|
|
18
|
+
fixturePath: string
|
|
19
|
+
fixturesEnv: string
|
|
20
|
+
dirEnv: string
|
|
21
|
+
domainEnv: string
|
|
22
|
+
defaultDomain: string
|
|
23
|
+
installHint: string
|
|
24
|
+
taskIntro: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface TauRow {
|
|
28
|
+
id: string
|
|
29
|
+
domain: string
|
|
30
|
+
user_scenario?: unknown
|
|
31
|
+
description?: unknown
|
|
32
|
+
evaluation_criteria?: unknown
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface TauMeta {
|
|
36
|
+
taskId: string
|
|
37
|
+
domain: string
|
|
38
|
+
split?: string
|
|
39
|
+
userScenario?: unknown
|
|
40
|
+
description?: unknown
|
|
41
|
+
evaluationCriteria?: unknown
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const tauResultsOutput: OutputAdapter<string> = {
|
|
45
|
+
parse(events) {
|
|
46
|
+
let text = ''
|
|
47
|
+
for (const ev of events) {
|
|
48
|
+
const d = (ev as { data?: Record<string, unknown> })?.data
|
|
49
|
+
const t = d?.finalText ?? d?.text ?? d?.result
|
|
50
|
+
if (typeof t === 'string' && t.length > 0) text = t
|
|
51
|
+
}
|
|
52
|
+
const fences = [...text.matchAll(/```(?:text|path|json)?\s*\n([\s\S]*?)```/g)]
|
|
53
|
+
return (fences.at(-1)?.[1] ?? text).trim()
|
|
54
|
+
},
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function assertPath(path: string, label: string, benchName: string): Promise<void> {
|
|
58
|
+
try {
|
|
59
|
+
await stat(path)
|
|
60
|
+
} catch (err) {
|
|
61
|
+
throw new Error(`${benchName}: missing ${label} at ${path} (${err instanceof Error ? err.message : err})`)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function benchDir(config: TauBenchConfig): string | undefined {
|
|
66
|
+
return process.env[config.dirEnv]
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function benchDomain(config: TauBenchConfig): string {
|
|
70
|
+
return process.env[config.domainEnv] ?? config.defaultDomain
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function rowToTask(row: TauRow, config: TauBenchConfig, split?: string): BenchTask {
|
|
74
|
+
const meta: TauMeta = {
|
|
75
|
+
taskId: row.id,
|
|
76
|
+
domain: row.domain,
|
|
77
|
+
split,
|
|
78
|
+
userScenario: row.user_scenario,
|
|
79
|
+
description: row.description,
|
|
80
|
+
evaluationCriteria: row.evaluation_criteria,
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
id: row.id,
|
|
84
|
+
split,
|
|
85
|
+
prompt: [
|
|
86
|
+
config.taskIntro,
|
|
87
|
+
`Run this task in the official ${row.domain} domain.`,
|
|
88
|
+
'The benchmark is a simulated multi-turn user/tool conversation.',
|
|
89
|
+
'',
|
|
90
|
+
typeof row.user_scenario === 'string' ? row.user_scenario : JSON.stringify(row.user_scenario ?? {}, null, 2),
|
|
91
|
+
'',
|
|
92
|
+
'Return the path to the official tau results.json or trajectory file containing this task run.',
|
|
93
|
+
].join('\n'),
|
|
94
|
+
metadata: meta as unknown as Record<string, unknown>,
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function readMeta(task: BenchTask, benchName: string): TauMeta {
|
|
99
|
+
const md = task.metadata
|
|
100
|
+
if (!md || typeof md.taskId !== 'string' || typeof md.domain !== 'string') {
|
|
101
|
+
throw new Error(`${benchName} task ${task.id} missing metadata — loadTasks did not populate it`)
|
|
102
|
+
}
|
|
103
|
+
return md as unknown as TauMeta
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function selectRows(rows: TauRow[], opts: LoadOptions, config: TauBenchConfig, split?: string): BenchTask[] {
|
|
107
|
+
let tasks = rows.map((row) => rowToTask(row, config, split))
|
|
108
|
+
if (opts.ids) {
|
|
109
|
+
const want = new Set(opts.ids)
|
|
110
|
+
tasks = tasks.filter((task) => want.has(task.id))
|
|
111
|
+
} else if (opts.limit !== undefined) {
|
|
112
|
+
tasks = tasks.slice(0, opts.limit)
|
|
113
|
+
}
|
|
114
|
+
if (tasks.length === 0) throw new Error(`${config.name}: no tasks matched ${JSON.stringify(opts)}`)
|
|
115
|
+
return tasks
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function loadFixtures(config: TauBenchConfig, opts: LoadOptions): Promise<BenchTask[]> {
|
|
119
|
+
const rows = JSON.parse(await readFile(config.fixturePath, 'utf8')) as TauRow[]
|
|
120
|
+
console.warn(`[${config.name}] ${config.fixturesEnv}=1 — loading ${rows.length} adapter fixtures`)
|
|
121
|
+
return selectRows(rows, opts, config, opts.split)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function loadOfficialTasks(config: TauBenchConfig, root: string, opts: LoadOptions): Promise<BenchTask[]> {
|
|
125
|
+
const domain = benchDomain(config)
|
|
126
|
+
const script = `
|
|
127
|
+
import json, sys
|
|
128
|
+
from pathlib import Path
|
|
129
|
+
root = Path(sys.argv[1])
|
|
130
|
+
domain = sys.argv[2]
|
|
131
|
+
split = sys.argv[3] or None
|
|
132
|
+
sys.path.insert(0, str(root / "src"))
|
|
133
|
+
from tau2.registry import registry
|
|
134
|
+
loader = registry.get_tasks_loader(domain)
|
|
135
|
+
tasks = loader(split)
|
|
136
|
+
rows = []
|
|
137
|
+
for task in tasks:
|
|
138
|
+
row = task.model_dump(mode="json")
|
|
139
|
+
row["domain"] = domain
|
|
140
|
+
rows.append(row)
|
|
141
|
+
print(json.dumps(rows))
|
|
142
|
+
`
|
|
143
|
+
const stdout = await runVenvPython(script, [root, domain, opts.split ?? ''])
|
|
144
|
+
return selectRows(JSON.parse(stdout) as TauRow[], opts, config, opts.split)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async function scoreOfficialTrajectory(root: string, meta: TauMeta, artifactPath: string): Promise<Record<string, unknown>> {
|
|
148
|
+
const script = `
|
|
149
|
+
import json, sys
|
|
150
|
+
from pathlib import Path
|
|
151
|
+
root = Path(sys.argv[1])
|
|
152
|
+
task_id = sys.argv[2]
|
|
153
|
+
artifact = Path(sys.argv[3])
|
|
154
|
+
sys.path.insert(0, str(root / "src"))
|
|
155
|
+
from tau2.data_model.simulation import Results
|
|
156
|
+
from tau2.scripts.evaluate_trajectories import compute_simulation_rewards
|
|
157
|
+
results = Results.load(artifact)
|
|
158
|
+
updated = compute_simulation_rewards(results)
|
|
159
|
+
scores = []
|
|
160
|
+
for sim in updated.simulations:
|
|
161
|
+
if sim.task_id == task_id and sim.reward_info is not None:
|
|
162
|
+
scores.append(float(sim.reward_info.reward))
|
|
163
|
+
if not scores:
|
|
164
|
+
raise SystemExit(f"no scored simulations for task_id={task_id} in {artifact}")
|
|
165
|
+
print(json.dumps({"count": len(scores), "score": sum(scores) / len(scores), "scores": scores}))
|
|
166
|
+
`
|
|
167
|
+
const stdout = await runVenvPython(script, [root, meta.taskId, artifactPath], 0)
|
|
168
|
+
return JSON.parse(stdout.trim().split('\n').at(-1) ?? '{}') as Record<string, unknown>
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function createTauBenchAdapter(config: TauBenchConfig): BenchmarkAdapter {
|
|
172
|
+
const fixturesMode = process.env[config.fixturesEnv] === '1'
|
|
173
|
+
|
|
174
|
+
return {
|
|
175
|
+
name: config.name,
|
|
176
|
+
output: tauResultsOutput,
|
|
177
|
+
|
|
178
|
+
async preflight() {
|
|
179
|
+
if (fixturesMode) return
|
|
180
|
+
const dir = benchDir(config)
|
|
181
|
+
if (!dir) {
|
|
182
|
+
throw new Error(`${config.dirEnv} is required. Fix: ${config.installHint}`)
|
|
183
|
+
}
|
|
184
|
+
await assertPath(`${dir}/src/tau2/registry.py`, 'tau registry', config.name)
|
|
185
|
+
await loadOfficialTasks(config, dir, { limit: 1 })
|
|
186
|
+
},
|
|
187
|
+
|
|
188
|
+
async loadTasks(opts: LoadOptions = {}) {
|
|
189
|
+
if (fixturesMode) return loadFixtures(config, opts)
|
|
190
|
+
const dir = benchDir(config)
|
|
191
|
+
if (!dir) throw new Error(`${config.dirEnv} is required to load official ${config.name} tasks`)
|
|
192
|
+
return loadOfficialTasks(config, dir, opts)
|
|
193
|
+
},
|
|
194
|
+
|
|
195
|
+
async goldArtifact() {
|
|
196
|
+
return undefined
|
|
197
|
+
},
|
|
198
|
+
|
|
199
|
+
async judge(task: BenchTask, artifact: string): Promise<BenchScore> {
|
|
200
|
+
const dir = benchDir(config)
|
|
201
|
+
if (!dir) throw new Error(`${config.dirEnv} is required to judge ${config.name} trajectory artifacts`)
|
|
202
|
+
const meta = readMeta(task, config.name)
|
|
203
|
+
const artifactPath = resolve(artifact.trim())
|
|
204
|
+
await assertPath(artifactPath, 'tau results/trajectory artifact', config.name)
|
|
205
|
+
const report = await scoreOfficialTrajectory(dir, meta, artifactPath)
|
|
206
|
+
const score = typeof report.score === 'number' ? report.score : 0
|
|
207
|
+
return {
|
|
208
|
+
resolved: score === 1,
|
|
209
|
+
score,
|
|
210
|
+
detail: JSON.stringify({ taskId: meta.taskId, domain: meta.domain, count: report.count }),
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
}
|
|
214
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tau2-bench adapter (Sierra tau2/tau-bench successor).
|
|
3
|
+
*
|
|
4
|
+
* Worker artifact = a tau2 `results.json`/trajectory file. Judge = tau2's own
|
|
5
|
+
* reward recomputation over that trajectory. A normal final-answer transcript is
|
|
6
|
+
* not a valid artifact for this benchmark.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { join } from 'node:path'
|
|
10
|
+
import { benchRoot } from './_harness'
|
|
11
|
+
import { createTauBenchAdapter, tauResultsOutput } from './tau-bench-shared'
|
|
12
|
+
import type { BenchmarkAdapter } from './types'
|
|
13
|
+
|
|
14
|
+
const FIXTURES = join(benchRoot, 'fixtures', 'tau2-bench.json')
|
|
15
|
+
|
|
16
|
+
export const tau2ResultsOutput = tauResultsOutput
|
|
17
|
+
|
|
18
|
+
export function createTau2BenchAdapter(): BenchmarkAdapter {
|
|
19
|
+
return createTauBenchAdapter({
|
|
20
|
+
name: 'tau2-bench',
|
|
21
|
+
fixturePath: FIXTURES,
|
|
22
|
+
fixturesEnv: 'TAU2_FIXTURES',
|
|
23
|
+
dirEnv: 'TAU2_BENCH_DIR',
|
|
24
|
+
domainEnv: 'TAU2_DOMAIN',
|
|
25
|
+
defaultDomain: 'retail',
|
|
26
|
+
taskIntro: 'Run this tau2 task in the official tau2 text benchmark.',
|
|
27
|
+
installHint:
|
|
28
|
+
'clone https://github.com/sierra-research/tau2-bench, install its deps in bench/.venv, and set TAU2_BENCH_DIR=/path/to/tau2-bench.',
|
|
29
|
+
})
|
|
30
|
+
}
|