@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
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manual reachability probe for the coordination MCP from inside a Docker container.
|
|
3
|
+
*
|
|
4
|
+
* Run: `npx tsx bench/src/coordination-mcp-container-reach.mts`.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { execFile } from 'node:child_process'
|
|
8
|
+
import { promisify } from 'node:util'
|
|
9
|
+
|
|
10
|
+
const execFileAsync = promisify(execFile)
|
|
11
|
+
import {
|
|
12
|
+
type Agent,
|
|
13
|
+
type AgentProfile,
|
|
14
|
+
type AgentSpec,
|
|
15
|
+
createExecutorRegistry,
|
|
16
|
+
createSupervisor,
|
|
17
|
+
type Executor,
|
|
18
|
+
type ExecutorResult,
|
|
19
|
+
InMemoryResultBlobStore,
|
|
20
|
+
InMemorySpawnJournal,
|
|
21
|
+
type Scope,
|
|
22
|
+
type UsageEvent,
|
|
23
|
+
} from '../../src/runtime/index'
|
|
24
|
+
import { serveCoordinationMcp } from '../../src/runtime/supervise/coordination-mcp'
|
|
25
|
+
|
|
26
|
+
const HOST_BIND = '0.0.0.0'
|
|
27
|
+
const DOCKER_BRIDGE_GATEWAY = process.env.DOCKER_BRIDGE_GATEWAY ?? '172.17.0.1'
|
|
28
|
+
|
|
29
|
+
function trivialWorker(name: string): Agent<unknown, unknown> {
|
|
30
|
+
const ex: Executor<unknown> = {
|
|
31
|
+
runtime: 'router',
|
|
32
|
+
execute() {
|
|
33
|
+
return (async function* () {
|
|
34
|
+
yield { kind: 'iteration' } as UsageEvent
|
|
35
|
+
})()
|
|
36
|
+
},
|
|
37
|
+
teardown: () => Promise.resolve({ destroyed: true }),
|
|
38
|
+
resultArtifact: (): ExecutorResult<unknown> => ({
|
|
39
|
+
outRef: `w:${name}`,
|
|
40
|
+
out: { ok: true },
|
|
41
|
+
verdict: { valid: true, score: 1 },
|
|
42
|
+
spent: { iterations: 1, tokens: { input: 0, output: 0 }, usd: 0, ms: 0 },
|
|
43
|
+
}),
|
|
44
|
+
}
|
|
45
|
+
const spec: AgentSpec = { profile: { name } as AgentProfile, harness: null, executor: ex }
|
|
46
|
+
return { name, act: async () => ({ ok: true }), executorSpec: spec } as Agent<unknown, unknown> & {
|
|
47
|
+
executorSpec: AgentSpec
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface ToolsListResponse {
|
|
52
|
+
result?: { tools?: Array<{ name: string; description?: string; inputSchema?: unknown }> }
|
|
53
|
+
error?: { code: number; message: string }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function dockerCurlToolsList(
|
|
57
|
+
containerUrl: string,
|
|
58
|
+
): Promise<{ raw: string; parsed: ToolsListResponse; cmd: string }> {
|
|
59
|
+
const rpc = JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'tools/list' })
|
|
60
|
+
const args = [
|
|
61
|
+
'run',
|
|
62
|
+
'--rm',
|
|
63
|
+
'--add-host',
|
|
64
|
+
'host.docker.internal:host-gateway',
|
|
65
|
+
'curlimages/curl',
|
|
66
|
+
'-s',
|
|
67
|
+
'-X',
|
|
68
|
+
'POST',
|
|
69
|
+
containerUrl,
|
|
70
|
+
'-H',
|
|
71
|
+
'content-type: application/json',
|
|
72
|
+
'-d',
|
|
73
|
+
rpc,
|
|
74
|
+
]
|
|
75
|
+
const cmd = `docker ${args.join(' ')}`
|
|
76
|
+
const { stdout } = await execFileAsync('docker', args, { encoding: 'utf8', timeout: 120_000 })
|
|
77
|
+
return { raw: stdout, parsed: JSON.parse(stdout) as ToolsListResponse, cmd }
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function opencodeConfigSnippet(containerUrl: string): string {
|
|
81
|
+
return JSON.stringify(
|
|
82
|
+
{
|
|
83
|
+
$schema: 'https://opencode.ai/config.json',
|
|
84
|
+
permission: {
|
|
85
|
+
external_directory: 'allow',
|
|
86
|
+
bash: 'allow',
|
|
87
|
+
edit: 'allow',
|
|
88
|
+
read: 'allow',
|
|
89
|
+
write: 'allow',
|
|
90
|
+
webfetch: 'allow',
|
|
91
|
+
task: 'allow',
|
|
92
|
+
plan_enter: 'allow',
|
|
93
|
+
plan_exit: 'allow',
|
|
94
|
+
question: 'allow',
|
|
95
|
+
},
|
|
96
|
+
mcp: {
|
|
97
|
+
coordination: {
|
|
98
|
+
type: 'remote',
|
|
99
|
+
url: containerUrl,
|
|
100
|
+
enabled: true,
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
null,
|
|
105
|
+
2,
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async function main(): Promise<void> {
|
|
110
|
+
const blobs = new InMemoryResultBlobStore()
|
|
111
|
+
let ok = false
|
|
112
|
+
|
|
113
|
+
const root: Agent<unknown, unknown> = {
|
|
114
|
+
name: 'coordination-mcp-container-reach',
|
|
115
|
+
async act(_t, scope: Scope<unknown>) {
|
|
116
|
+
const mcp = await serveCoordinationMcp({
|
|
117
|
+
scope,
|
|
118
|
+
blobs,
|
|
119
|
+
makeWorkerAgent: () => trivialWorker('w'),
|
|
120
|
+
perWorker: { maxIterations: 4, maxTokens: 2000 },
|
|
121
|
+
host: HOST_BIND,
|
|
122
|
+
})
|
|
123
|
+
// Docker containers reach the host through the bridge gateway, not the 0.0.0.0 bind URL.
|
|
124
|
+
const containerUrl = `http://${DOCKER_BRIDGE_GATEWAY}:${mcp.port}/mcp`
|
|
125
|
+
const hostDockerInternalUrl = `http://host.docker.internal:${mcp.port}/mcp`
|
|
126
|
+
|
|
127
|
+
console.error(`[probe] host-binding used : ${HOST_BIND}`)
|
|
128
|
+
console.error(`[probe] server url (mcp.url) : ${mcp.url}`)
|
|
129
|
+
console.error(`[probe] container-reachable url : ${containerUrl}`)
|
|
130
|
+
console.error(`[probe] alt (host.docker.internal) : ${hostDockerInternalUrl}`)
|
|
131
|
+
console.error('')
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
console.error('[probe] POSTing tools/list from inside a docker container ...')
|
|
135
|
+
const { raw, parsed, cmd } = await dockerCurlToolsList(containerUrl)
|
|
136
|
+
console.error(`[probe] container ran: ${cmd}`)
|
|
137
|
+
console.error(`[probe] container got back (${raw.length} bytes):`)
|
|
138
|
+
console.error(raw)
|
|
139
|
+
console.error('')
|
|
140
|
+
|
|
141
|
+
const toolNames = (parsed.result?.tools ?? []).map((t) => t.name)
|
|
142
|
+
const hasSpawn = toolNames.includes('spawn_agent')
|
|
143
|
+
const hasAwait = toolNames.includes('await_event')
|
|
144
|
+
console.error(`[probe] tools advertised: ${toolNames.join(', ')}`)
|
|
145
|
+
console.error(`[probe] spawn_agent present = ${hasSpawn}; await_event present = ${hasAwait}`)
|
|
146
|
+
ok = hasSpawn && hasAwait
|
|
147
|
+
|
|
148
|
+
if (ok) {
|
|
149
|
+
console.error('')
|
|
150
|
+
console.error('[probe] opencode.json snippet (arm B writes this via OPENCODE_CONFIG):')
|
|
151
|
+
console.error(opencodeConfigSnippet(containerUrl))
|
|
152
|
+
}
|
|
153
|
+
return ok ? { reachable: true } : undefined
|
|
154
|
+
} finally {
|
|
155
|
+
await mcp.close()
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
await createSupervisor<unknown, unknown>().run(root, 'reach', {
|
|
161
|
+
budget: { maxIterations: 100, maxTokens: 400_000 },
|
|
162
|
+
runId: 'coordination-mcp-container-reach',
|
|
163
|
+
journal: new InMemorySpawnJournal(),
|
|
164
|
+
blobs,
|
|
165
|
+
executors: createExecutorRegistry(),
|
|
166
|
+
maxDepth: 4,
|
|
167
|
+
now: () => Date.now(),
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
console.error(
|
|
171
|
+
ok
|
|
172
|
+
? 'CONTAINER-REACHABLE: docker tools/list returned spawn_agent and await_event.'
|
|
173
|
+
: 'NOT reachable from container; see output above.',
|
|
174
|
+
)
|
|
175
|
+
process.exit(ok ? 0 : 1)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
main().catch((e) => {
|
|
179
|
+
console.error(e instanceof Error ? (e.stack ?? e.message) : String(e))
|
|
180
|
+
process.exit(1)
|
|
181
|
+
})
|
package/src/decoder-live.mts
CHANGED
|
@@ -47,7 +47,7 @@ async function main(): Promise<number> {
|
|
|
47
47
|
},
|
|
48
48
|
profile: { name: 'decoder-live' },
|
|
49
49
|
},
|
|
50
|
-
} as never)) as Record<string, (...a: never[]) => unknown> & { id?: string }
|
|
50
|
+
} as never)) as unknown as Record<string, (...a: never[]) => unknown> & { id?: string }
|
|
51
51
|
|
|
52
52
|
try {
|
|
53
53
|
console.error('[live] box', box.id, '— waiting for running…')
|
package/src/examples/README.md
CHANGED
|
@@ -1,58 +1,122 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Bench examples — make an AI actually earn its answer
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Four runnable programs that share one idea: never trust an AI's answer on its word — put a **check**
|
|
4
|
+
in front of it and let the AI keep trying until the check passes. A check is just a function that
|
|
5
|
+
returns pass/fail (grade a number, run a test suite, compile a proof). Give the AI a check and a
|
|
6
|
+
compute budget and this toolkit finds the best way to spend that budget: try many answers and keep
|
|
7
|
+
the best, or let a critic read the failure and steer the next attempt.
|
|
8
|
+
|
|
9
|
+
All four run from the `bench/` directory (`cd bench` first). Nothing here needs a special framework —
|
|
10
|
+
each is a single file you can read top to bottom.
|
|
11
|
+
|
|
12
|
+
## Which one to run first
|
|
13
|
+
|
|
14
|
+
| run this | what you learn | needs a key? |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| **`strategy-demo.mts`** | the whole idea on a 20-line toy task | yes (one router key) |
|
|
17
|
+
| **`math-demo.mts`** | the same thing grading real answers (word problems) | yes |
|
|
18
|
+
| **`lean-proof-gate.mts`** | a check the AI literally cannot fool (a theorem prover) | no — offline mode |
|
|
19
|
+
| **`benchmark-matrix.mts`** | rank many AI agents against public benchmarks | no — offline demo |
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## `strategy-demo.mts` — the whole idea in one small file
|
|
24
|
+
|
|
25
|
+
The smallest end-to-end demo. The task is a toy: drive a counter to exactly 5 using an `increment`
|
|
26
|
+
tool. Because the task is trivial, it needs only a router key — no dataset, no cloud sandbox — so you
|
|
27
|
+
can watch the machinery instead of the problem.
|
|
28
|
+
|
|
29
|
+
It shows three ways to use the toolkit, each a few lines:
|
|
30
|
+
|
|
31
|
+
1. **Just run it.** Hand it your task and your check; it compares the built-in ways of spending the
|
|
32
|
+
budget and reports which won.
|
|
33
|
+
2. **Pick the tactics.** Choose from `sample` (make N independent attempts, keep the best-scoring
|
|
34
|
+
one), `refine` (attempt, let a critic read what went wrong, steer the next attempt, repeat), and
|
|
35
|
+
`adaptiveRefine` (refine, but abandon and restart a line that stops improving).
|
|
36
|
+
3. **Write your own.** `defineStrategy(name, body)` composes two moves — `shot()` (one worker
|
|
37
|
+
attempt) and `critique()` (a separate reviewer reads the transcript and returns a steer) — into
|
|
38
|
+
any tactic you invent. The demo authors one called `doubleCheck` in ~10 lines.
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
TANGLE_API_KEY=... WORKER_MODEL=gpt-4o-mini tsx src/examples/strategy-demo.mts
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Prints two leaderboards: the built-in tactics, then the built-ins plus your authored `doubleCheck`,
|
|
45
|
+
each scored by the counter task's own check. Default model is `deepseek-v4-flash`; override with
|
|
46
|
+
`WORKER_MODEL`.
|
|
47
|
+
|
|
48
|
+
## `math-demo.mts` — the same toolkit grading real answers
|
|
49
|
+
|
|
50
|
+
Identical machinery, but the check now grades **answers**: three grade-school word problems, scored
|
|
51
|
+
by exact numeric match against the known answer. The worker gets a `calculator` tool and must submit
|
|
52
|
+
a final number. This is the template for any domain where success means "did the final answer come
|
|
53
|
+
out right" — a tax return value, a computed total, a rubric score. To move it to your domain you swap
|
|
54
|
+
one function: the check.
|
|
6
55
|
|
|
7
56
|
```bash
|
|
8
|
-
TANGLE_API_KEY=...
|
|
57
|
+
TANGLE_API_KEY=... WORKER_MODEL=gpt-4o-mini BUDGET=3 tsx src/examples/math-demo.mts
|
|
9
58
|
```
|
|
10
59
|
|
|
11
|
-
|
|
60
|
+
Compares `sample`, `refine`, and `sampleThenRefine` (N fresh attempts, then critique-and-iterate on
|
|
61
|
+
the best) at an equal budget of `BUDGET` attempts each, so you can see whether iterating on feedback
|
|
62
|
+
actually beats blind resampling. Needs `TANGLE_API_KEY`; default model `deepseek-v4-flash`.
|
|
12
63
|
|
|
13
|
-
|
|
14
|
-
**how you spend the budget to beat the check**. You implement an `Environment` (5 hooks)
|
|
15
|
-
and get the strategies compared, scored by your own check, for free.
|
|
64
|
+
## `lean-proof-gate.mts` — a check the AI cannot fool
|
|
16
65
|
|
|
17
|
-
|
|
66
|
+
An AI writes a mathematical proof and **Lean 4** (a real theorem prover) compiles it. A wrong proof
|
|
67
|
+
is rejected with a precise error the AI reads and fixes; the loop repeats until Lean accepts a
|
|
68
|
+
genuinely correct proof. This is the most honest check in the folder: correctness is machine-verified,
|
|
69
|
+
not judged by another model. It has its own full walkthrough in
|
|
70
|
+
[`lean-proof-gate.README.md`](./lean-proof-gate.README.md).
|
|
18
71
|
|
|
19
|
-
|
|
20
|
-
|
|
72
|
+
```bash
|
|
73
|
+
# See the checker work — no API key (builds a Lean image, first run ~3 min):
|
|
74
|
+
tsx src/examples/lean-proof-gate.mts --verify-only
|
|
21
75
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
- **`refine`** — attempt → a critic reads the trace → steer the next → repeat (iterate).
|
|
26
|
-
- **`adaptiveRefine`** — refine, but abandon-and-restart a line that stops improving
|
|
27
|
-
(branch-when-stuck).
|
|
76
|
+
# Run the full write-check-retry loop — needs a model:
|
|
77
|
+
TANGLE_API_KEY=... WORKER_MODEL=gpt-4.1 BUDGET=3 tsx src/examples/lean-proof-gate.mts
|
|
78
|
+
```
|
|
28
79
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
analyst reads the trace → a steer) — with **zero** Supervisor/Scope ceremony. The demo
|
|
32
|
-
authors `doubleCheck` inline in ~10 lines. This is the unit a skill (or an agent) emits.
|
|
80
|
+
The `--verify-only` mode compiles five real proofs plus one deliberately wrong one and shows the
|
|
81
|
+
wrong one getting rejected — proof the check is real before you spend a cent on a model.
|
|
33
82
|
|
|
34
|
-
##
|
|
83
|
+
## `benchmark-matrix.mts` — rank many agents across many benchmarks
|
|
35
84
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
85
|
+
The other three grade one task domain. This one builds a **leaderboard**: it runs a set of AI agents
|
|
86
|
+
against a set of public coding/research benchmarks (HumanEval, SWE-bench, terminal-bench, and more)
|
|
87
|
+
and ranks them. Each benchmark brings its **own** grader, so the score is the benchmark's, not a
|
|
88
|
+
number we made up. An "agent" here is a **cell** — a combination of coding harness (opencode, Codex,
|
|
89
|
+
a bare router call) and model (e.g. `glm-4.6`, `gpt-5`).
|
|
41
90
|
|
|
42
91
|
```bash
|
|
43
|
-
|
|
92
|
+
# Offline demo — no keys. A stub benchmark with a deterministic grader proves the
|
|
93
|
+
# whole pipeline and prints a ranked board:
|
|
94
|
+
tsx src/examples/benchmark-matrix.mts
|
|
95
|
+
|
|
96
|
+
# Live — real benchmarks graded by their real graders:
|
|
97
|
+
TANGLE_API_KEY=... BENCHMARKS=humaneval tsx src/examples/benchmark-matrix.mts
|
|
44
98
|
```
|
|
45
99
|
|
|
46
|
-
|
|
100
|
+
Pick the benchmarks with the `BENCHMARKS` env var (comma-separated). The **cells** (which
|
|
101
|
+
harness+model agents to rank) are the `cells` array at the top of the file — edit that array to
|
|
102
|
+
change who competes. Other knobs: `N` (tasks per benchmark, default 10), `CONCURRENCY` (default 4),
|
|
103
|
+
and `SANDBOX_BASE` if your harnesses run in a remote sandbox.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Where the interesting results are
|
|
108
|
+
|
|
109
|
+
On the toy and math tasks the tactics often tie — the task is too easy for the way-you-spend-budget
|
|
110
|
+
to matter. The gap shows up on hard, stateful work: on an internal operations benchmark, `refine`
|
|
111
|
+
(iterate on a critic's feedback) beats blind `sample` by **+16.4 percentage points**. See
|
|
112
|
+
`bench/HARNESS.md` and `bench/src/agentic-run.mts` for that harder environment.
|
|
47
113
|
|
|
48
|
-
|
|
49
|
-
- **the critic / steerer** → `worker.analystInstruction` (the analyst prompt; GEPA tunes this)
|
|
50
|
-
- **the worker** → the model (`worker.model`)
|
|
51
|
-
- **the strategy** → `defineStrategy` (or drop to `runAgentic` / the Supervisor for novel topologies)
|
|
114
|
+
## The four hooks you customize
|
|
52
115
|
|
|
53
|
-
|
|
116
|
+
Everything above is built from four swappable pieces. To adapt any of these examples to your problem,
|
|
117
|
+
you change one of these and leave the rest alone:
|
|
54
118
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
119
|
+
- **the check** — your pass/fail function (grade the answer, run the tests, compile the proof).
|
|
120
|
+
- **the reviewer** — the prompt the critic uses to read a failed attempt and suggest a fix.
|
|
121
|
+
- **the worker** — the model that does the work (`WORKER_MODEL`).
|
|
122
|
+
- **the tactic** — how the budget is spent (`sample` / `refine` / your own `defineStrategy`).
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rank a set of AI agents across a set of public benchmarks, and print a leaderboard.
|
|
3
|
+
*
|
|
4
|
+
* A "cell" is one agent = a coding harness (opencode, Codex, a bare router call) + a model.
|
|
5
|
+
* Each benchmark (humaneval, swe-bench, terminal-bench, …) brings its OWN deterministic
|
|
6
|
+
* grader, so the score is the benchmark's, not one we invented. Pick the benchmarks with
|
|
7
|
+
* the BENCHMARKS env var; edit the `cells` array below to change who competes.
|
|
8
|
+
*
|
|
9
|
+
* Run from bench/:
|
|
10
|
+
* # offline demo (no creds): a stub benchmark + stub agent prove the pipeline end to end
|
|
11
|
+
* tsx src/examples/benchmark-matrix.mts
|
|
12
|
+
*
|
|
13
|
+
* # live: real benchmarks graded by their real graders
|
|
14
|
+
* TANGLE_API_KEY=... BENCHMARKS=humaneval tsx src/examples/benchmark-matrix.mts
|
|
15
|
+
*/
|
|
16
|
+
import type { BenchmarkAdapter, BenchScore, BenchTask } from '../benchmarks/types'
|
|
17
|
+
import { type BenchCell, type BenchShot, printBenchmarksReport, runBenchmarks } from '../run-benchmarks'
|
|
18
|
+
|
|
19
|
+
/** The matrix: three cells spanning harnesses, models, and transports. */
|
|
20
|
+
const cells: BenchCell[] = [
|
|
21
|
+
{ label: 'opencode/glm-4.6', harness: 'opencode', model: 'glm-4.6', backend: 'sandbox' },
|
|
22
|
+
{ label: 'codex/gpt-5', harness: 'codex', model: 'gpt-5', backend: 'sandbox' },
|
|
23
|
+
{ label: 'router/deepseek-v4-flash', model: 'deepseek-v4-flash', backend: 'router' },
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
async function live(): Promise<void> {
|
|
27
|
+
const benchmarks = (process.env.BENCHMARKS ?? 'humaneval')
|
|
28
|
+
.split(',')
|
|
29
|
+
.map((s) => s.trim())
|
|
30
|
+
.filter(Boolean)
|
|
31
|
+
const report = await runBenchmarks({
|
|
32
|
+
benchmarks,
|
|
33
|
+
cells,
|
|
34
|
+
routerBaseUrl: process.env.ROUTER_BASE ?? 'https://router.tangle.tools/v1',
|
|
35
|
+
routerKey: process.env.TANGLE_API_KEY!,
|
|
36
|
+
...(process.env.SANDBOX_BASE ? { sandboxBaseUrl: process.env.SANDBOX_BASE } : {}),
|
|
37
|
+
n: Number(process.env.N ?? 10),
|
|
38
|
+
concurrency: Number(process.env.CONCURRENCY ?? 4),
|
|
39
|
+
onResult: (r) =>
|
|
40
|
+
console.log(` [${r.ok ? (r.resolved ? 'PASS' : 'fail') : 'ERR '}] ${r.benchmark} ${r.cell} ${r.taskId}`),
|
|
41
|
+
})
|
|
42
|
+
console.log(`\n=== live: ${report.benchmarks.join(', ')} × ${report.cells.length} cells ===`)
|
|
43
|
+
console.log(printBenchmarksReport(report))
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Offline proof: a stub registry whose judge is deterministic, and a stub shot whose "ability"
|
|
47
|
+
* differs per cell — so the leaderboard ORDER reflects the cells, with no model or sandbox. */
|
|
48
|
+
async function demo(): Promise<void> {
|
|
49
|
+
const stub = (name: string, n: number): BenchmarkAdapter => ({
|
|
50
|
+
name,
|
|
51
|
+
preflight: async () => {},
|
|
52
|
+
loadTasks: async (o) =>
|
|
53
|
+
Array.from({ length: o?.limit ?? n }, (_, i) => ({
|
|
54
|
+
id: `${name}-${i}`,
|
|
55
|
+
prompt: `task ${i}`,
|
|
56
|
+
metadata: { gold: `G-${name}-${i}` },
|
|
57
|
+
})),
|
|
58
|
+
judge: async (t: BenchTask, a: string): Promise<BenchScore> => ({
|
|
59
|
+
resolved: a === t.metadata?.gold,
|
|
60
|
+
score: a === t.metadata?.gold ? 1 : 0,
|
|
61
|
+
}),
|
|
62
|
+
goldArtifact: async (t: BenchTask) => String(t.metadata?.gold),
|
|
63
|
+
})
|
|
64
|
+
const registry: Record<string, BenchmarkAdapter> = {
|
|
65
|
+
'demo-coding': stub('demo-coding', 8),
|
|
66
|
+
'demo-research': stub('demo-research', 8),
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Cell ability: opencode/glm solves the most, router the least — deterministic by task index.
|
|
70
|
+
const ability: Record<string, number> = {
|
|
71
|
+
'opencode/glm-4.6': 0.88,
|
|
72
|
+
'codex/gpt-5': 0.62,
|
|
73
|
+
'router/deepseek-v4-flash': 0.38,
|
|
74
|
+
}
|
|
75
|
+
const shot: BenchShot = async ({ task, cell }) => {
|
|
76
|
+
const idx = Number(task.id.split('-').pop())
|
|
77
|
+
const solves = idx / 8 < (ability[cell.label] ?? 0.5)
|
|
78
|
+
return { artifact: solves ? String(task.metadata?.gold) : 'WRONG', ok: true }
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const report = await runBenchmarks({
|
|
82
|
+
benchmarks: ['demo-coding', 'demo-research'],
|
|
83
|
+
cells,
|
|
84
|
+
routerBaseUrl: 'demo',
|
|
85
|
+
routerKey: 'demo',
|
|
86
|
+
runShot: shot,
|
|
87
|
+
resolveAdapter: (k) => {
|
|
88
|
+
const a = registry[k]
|
|
89
|
+
if (!a) throw new Error(`unknown: ${k}`)
|
|
90
|
+
return a
|
|
91
|
+
},
|
|
92
|
+
})
|
|
93
|
+
console.log('=== offline demo: 2 benchmarks × 3 cells (stub judge, no creds) ===')
|
|
94
|
+
console.log(printBenchmarksReport(report))
|
|
95
|
+
console.log('\nSet TANGLE_API_KEY + BENCHMARKS to run real benchmarks against these cells.')
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
void (process.env.TANGLE_API_KEY ? live() : demo()).catch((e) => {
|
|
99
|
+
console.error(e instanceof Error ? (e.stack ?? e.message) : String(e))
|
|
100
|
+
process.exit(1)
|
|
101
|
+
})
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Prove math with an AI that can't bluff
|
|
2
|
+
|
|
3
|
+
An AI writes a mathematical proof; a real theorem prover — **Lean 4** — checks it. If the proof is
|
|
4
|
+
wrong, Lean says exactly why, the AI reads that error and tries again, and it repeats until Lean
|
|
5
|
+
accepts a genuinely correct proof. Nothing is taken on the model's word: every proof that passes
|
|
6
|
+
here is machine-verified.
|
|
7
|
+
|
|
8
|
+
This is the automatable core of [pipeline-math](https://github.com/Pengbinghui/pipeline-math), which
|
|
9
|
+
used the same prover-and-checker idea to crack open mathematics problems.
|
|
10
|
+
|
|
11
|
+
## Why it matters
|
|
12
|
+
|
|
13
|
+
Ask a language model to "prove X" and it will hand you a confident, fluent, and often **wrong**
|
|
14
|
+
proof. Here it cannot get away with that. Lean compiles the proof and rejects anything that doesn't
|
|
15
|
+
actually follow — and we also reject `sorry`/`admit` (Lean's "trust me, skip this" placeholders). The
|
|
16
|
+
checker is ground truth, so looping against it turns a plausible-sounding guesser into a prover whose
|
|
17
|
+
every answer is verified.
|
|
18
|
+
|
|
19
|
+
## How it works — you don't need a special framework
|
|
20
|
+
|
|
21
|
+
A "prover-verifier loop" sounds exotic; it's two ordinary parts:
|
|
22
|
+
|
|
23
|
+
1. a **checker** — a function that returns pass/fail. Here: run the Lean compiler on the proof.
|
|
24
|
+
2. a **retry strategy** — show the model its failure and let it try again (`refine`, below).
|
|
25
|
+
|
|
26
|
+
Point the strategy at the checker and you have the loop. Swap Lean for unit tests and the same setup
|
|
27
|
+
proves *code*; swap it for a rubric and it writes *to spec*.
|
|
28
|
+
|
|
29
|
+
Two retries actually stack here: **within one attempt** the model calls a `lean_check` tool (the real
|
|
30
|
+
compiler) to fix its proof before answering; **across attempts** `refine` carries the last failure
|
|
31
|
+
forward. `sample` is the honest baseline — one blind attempt, no feedback.
|
|
32
|
+
|
|
33
|
+
## See the checker work — no API key needed
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
tsx src/examples/lean-proof-gate.mts --verify-only
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
This builds a Lean 4 image (first run ~3 min) and compiles five real proofs plus one deliberately
|
|
40
|
+
wrong one:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
PASS and-swap (⟨h.2, h.1⟩)
|
|
44
|
+
PASS or-swap (h.symm)
|
|
45
|
+
PASS add-comm (by omega)
|
|
46
|
+
PASS mul-one (by simp)
|
|
47
|
+
PASS reverse-reverse (by simp)
|
|
48
|
+
negative control (deliberately wrong): FAIL ✓ rejected
|
|
49
|
+
verifier: 5/5 reference proofs accepted, wrong proof rejected=true
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The wrong proof fails with a real Lean error — `argument h.left has type p but is expected to have
|
|
53
|
+
type q` — which is exactly the message the AI reads and fixes.
|
|
54
|
+
|
|
55
|
+
## Run the full loop — needs a model
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
TANGLE_API_KEY=… WORKER_MODEL=gpt-4.1 BUDGET=3 tsx src/examples/lean-proof-gate.mts
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
A real model proves the theorems, calling Lean to check each attempt. The report compares one blind
|
|
62
|
+
attempt (`sample`) against iterating on the checker's feedback (`refine`), so you can see whether the
|
|
63
|
+
loop actually earns its cost.
|
|
64
|
+
|
|
65
|
+
## Files
|
|
66
|
+
|
|
67
|
+
| file | what it is |
|
|
68
|
+
|---|---|
|
|
69
|
+
| `lean-proof-gate.mts` | the theorems, the checker wiring, and the run |
|
|
70
|
+
| `lean-verify.ts` | the checker: compile in Docker, reject `sorry`/`admit`, run with no network so a proof can't fetch its way to a pass |
|
|
71
|
+
| `lean.Dockerfile` | the Lean 4 toolchain image |
|
|
72
|
+
|
|
73
|
+
## Honest scope
|
|
74
|
+
|
|
75
|
+
The **checker** is real and runs on your machine today (it needs Docker). The **prover** half needs a
|
|
76
|
+
model key for the live loop. The theorems are small and use only core Lean (no `mathlib`) so they
|
|
77
|
+
compile in seconds — the point is the loop and the un-foolable check, not the difficulty of the math.
|