@tangle-network/agent-bench 0.7.1 → 0.8.0
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 +8 -0
- package/HARNESS.md +1 -1
- package/dist/adapters.js +2 -2
- package/dist/benchmarks/appworld.d.ts +61 -2
- package/dist/benchmarks/appworld.js +111 -40
- package/dist/benchmarks/appworld.js.map +1 -1
- package/dist/benchmarks/cadbench.js +1 -1
- package/dist/benchmarks/cadgenbench.js +1 -1
- package/dist/benchmarks/finresearchbench.js +16 -22
- package/dist/benchmarks/finresearchbench.js.map +1 -1
- package/dist/benchmarks/finsearchcomp.js +16 -22
- package/dist/benchmarks/finsearchcomp.js.map +1 -1
- package/dist/benchmarks/frames.js +18 -20
- package/dist/benchmarks/frames.js.map +1 -1
- package/dist/benchmarks/simpleqa.js +15 -19
- package/dist/benchmarks/simpleqa.js.map +1 -1
- package/dist/benchmarks/trata-hedge.js +20 -23
- package/dist/benchmarks/trata-hedge.js.map +1 -1
- package/dist/{cadbench-BrpwOU6A.js → cadbench-BLSyxR1N.js} +21 -20
- package/dist/cadbench-BLSyxR1N.js.map +1 -0
- package/dist/{cadgenbench-DF7hYHdl.js → cadgenbench-x2OFkf8y.js} +2 -2
- package/dist/cadgenbench-x2OFkf8y.js.map +1 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +10 -9
- package/dist/index.js.map +1 -1
- package/dist/router-turn-C2wMiDoo.js +31 -0
- package/dist/router-turn-C2wMiDoo.js.map +1 -0
- package/package.json +7 -7
- package/scripts/appworld_driver.py +1 -170
- package/scripts/run-package-tests.mjs +56 -36
- package/scripts/run-package-tests.test.mjs +33 -0
- package/scripts/trata-hedge/README.md +3 -3
- package/scripts/trata-hedge/run.sh +6 -2
- package/src/aec-gate.mts +30 -9
- package/src/agent-graphs-gen2.mts +523 -0
- package/src/agent-graphs-gen3.mts +660 -0
- package/src/agent-graphs-improve/offline-seams.mts +128 -0
- package/src/agent-graphs-improve.mts +747 -0
- package/src/atom-humaneval.mts +76 -55
- package/src/atom-mcp-e2e.mts +27 -13
- package/src/benchmarks/appworld.test.mts +119 -1
- package/src/benchmarks/appworld.ts +156 -33
- package/src/benchmarks/cadbench.ts +21 -8
- package/src/benchmarks/finresearchbench.ts +20 -16
- package/src/benchmarks/finsearchcomp.ts +20 -20
- package/src/benchmarks/frames.ts +19 -18
- package/src/benchmarks/simpleqa.ts +19 -17
- package/src/benchmarks/trata-hedge.ts +26 -18
- package/src/browser/ui-reviewer.ts +1 -1
- package/src/clbench-context-gate.mts +45 -9
- package/src/cloud-loop.mts +24 -6
- package/src/commit0-env-run.mts +6 -3
- package/src/commit0-env.ts +2 -3
- package/src/commit0-gate.mts +53 -24
- package/src/corpus.ts +3 -3
- package/src/david-attribution.mts +28 -9
- package/src/david-goliath.mts +31 -12
- package/src/egress-probe.mts +3 -3
- package/src/examples/lean-proof-gate.mts +10 -3
- package/src/examples/math-demo.mts +10 -3
- package/src/examples/strategy-demo.mts +10 -4
- package/src/fleet.mts +30 -8
- package/src/gate-cli.mts +9 -4
- package/src/gate.test.mts +7 -2
- package/src/gate.ts +23 -75
- package/src/generate-eval/certify.ts +13 -5
- package/src/hev-eval.mts +39 -10
- package/src/hev-improve.mts +28 -16
- package/src/hev-structural.mts +28 -20
- package/src/humaneval-gate.mts +24 -8
- package/src/humaneval-object-ablation.mts +52 -14
- package/src/humaneval-repair-gate.mts +53 -14
- package/src/mbpp-structural.mts +28 -20
- package/src/mcp-mount-probe.mts +23 -11
- package/src/official-optimizer-config.mts +61 -12
- package/src/official-optimizer-config.test.mts +72 -3
- package/src/quant-arena/profiles/quant-researcher.profile.json +6 -1
- package/src/quant-arena/quant-loop.mts +88 -61
- package/src/research-shot.ts +21 -11
- package/src/resolve-client.ts +6 -8
- package/src/rollout-ledger/backfill-swe-arena.test.mts +7 -2
- package/src/router-executor.ts +1 -1
- package/src/router-turn.ts +135 -0
- package/src/run-benchmarks.ts +7 -2
- package/src/sandbox-run.ts +23 -41
- package/src/search-bench/bridge.ts +66 -8
- package/src/search-bench/parametric-check.mts +19 -4
- package/src/search-bench/profiles.ts +24 -9
- package/src/search-bench/run.mts +3 -4
- package/src/smoke-structural-rollout.mts +21 -5
- package/src/supervisor-arena.mts +28 -20
- package/src/swe-arena/arms.ts +73 -15
- package/src/swe-arena/capacity.ts +44 -41
- package/src/swe-arena/diagnosis-ensemble.ts +51 -48
- package/src/swe-arena/execution.test.mts +12 -12
- package/src/swe-arena/gepa-seat.mts +12 -5
- package/src/swe-arena/gepa-seat.test.mts +45 -33
- package/src/swe-arena/outer-loop.mts +94 -70
- package/src/swe-arena/premeasured-from-cells.mts +16 -1
- package/src/swe-arena/premeasured-from-cells.test.mts +21 -1
- package/src/swe-arena/profiles/deepseek-author.profile.json +12 -0
- package/src/swe-arena/profiles/default-author.profile.json +9 -1
- package/src/swe-arena/proposer-fanout.mts +52 -99
- package/src/swe-arena/proposer-fanout.test.mts +92 -57
- package/src/swe-arena/proposer-provenance.mts +1 -1
- package/src/swe-code-improve.mts +30 -52
- package/src/swe-emit-patch.mts +26 -5
- package/src/swe-improve.mts +27 -8
- package/src/swe-jail.test.ts +128 -0
- package/src/swe-jail.ts +106 -22
- package/src/swe-local-proof.mts +27 -5
- package/src/swe-repro-calibrate.mts +5 -0
- package/src/swe-self-improve.mts +45 -10
- package/src/swe-stream.mts +68 -6
- package/src/swe-structural.mts +46 -7
- package/src/tb-container-executor.mts +38 -6
- package/src/tb-container-executor.test.mts +11 -3
- package/src/tb-supervisor-sidecar.mts +11 -27
- package/src/terminal-compare.ts +38 -18
- package/src/trata-gate.mts +26 -26
- package/src/trata-gepa.mts +25 -16
- package/src/trata-hedge-solve.mts +80 -0
- package/src/worker-blender.ts +21 -11
- package/src/worker-browser.ts +18 -5
- package/src/worker-build123d.ts +17 -8
- package/src/worker-cad.ts +31 -12
- package/src/worker.ts +1 -1
- package/tb_agents/opencode_router_agent.py +84 -42
- package/dist/cadbench-BrpwOU6A.js.map +0 -1
- package/dist/cadgenbench-DF7hYHdl.js.map +0 -1
- package/scripts/trata-hedge/solve.py +0 -83
package/src/commit0-gate.mts
CHANGED
|
@@ -53,10 +53,13 @@ import { mkdtemp, rm } from 'node:fs/promises'
|
|
|
53
53
|
import { tmpdir } from 'node:os'
|
|
54
54
|
import { join } from 'node:path'
|
|
55
55
|
import {
|
|
56
|
+
collectAgentTurn,
|
|
57
|
+
createExecutor,
|
|
56
58
|
type AgentRunSpec,
|
|
57
59
|
type Deliverable,
|
|
58
60
|
openSandboxRun,
|
|
59
61
|
type SandboxRun,
|
|
62
|
+
streamAgentTurn,
|
|
60
63
|
} from '@tangle-network/agent-runtime/kernel'
|
|
61
64
|
import { Sandbox } from '@tangle-network/sandbox'
|
|
62
65
|
import { createCommit0Adapter } from './benchmarks/commit0'
|
|
@@ -153,8 +156,17 @@ interface ShotCfg {
|
|
|
153
156
|
* registered model names (e.g. gpt-4.1). Override via WORKER_PROVIDER. */
|
|
154
157
|
provider: string
|
|
155
158
|
timeoutMs: number
|
|
156
|
-
/**
|
|
157
|
-
|
|
159
|
+
/** Local Runtime bridge transport. The profile, not this transport, selects the harness/model. */
|
|
160
|
+
bridgeUrl?: string
|
|
161
|
+
bridgeBearer?: string
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function workerProfile(cfg: ShotCfg, name: string) {
|
|
165
|
+
return {
|
|
166
|
+
name,
|
|
167
|
+
harness: 'opencode' as const,
|
|
168
|
+
model: { provider: cfg.provider, default: cfg.model },
|
|
169
|
+
}
|
|
158
170
|
}
|
|
159
171
|
|
|
160
172
|
/** The diff the in-box agent produces, read back off the box FS (+ any stream error). */
|
|
@@ -189,21 +201,15 @@ async function runShot(task: BenchTask, attempt: number, cfg: ShotCfg, steer?: s
|
|
|
189
201
|
// other rollout (the powered-run crash).
|
|
190
202
|
const controller = new AbortController()
|
|
191
203
|
const timer = cfg.timeoutMs > 0 ? setTimeout(() => controller.abort(), cfg.timeoutMs) : undefined
|
|
192
|
-
//
|
|
193
|
-
//
|
|
194
|
-
// profile + backend override is the same generic AgentRunSpec the runLoop kernel
|
|
195
|
-
// boots. Never inject an external key — the egress proxy 403s foreign credentials.
|
|
204
|
+
// The exact profile owns harness/provider/model. Sandbox overrides contain only box
|
|
205
|
+
// infrastructure; Runtime derives the backend from the profile and refuses conflicts.
|
|
196
206
|
const agentRun: AgentRunSpec<string> = {
|
|
197
|
-
profile:
|
|
207
|
+
profile: workerProfile(cfg, 'commit0-worker'),
|
|
198
208
|
name: 'commit0-worker',
|
|
199
209
|
taskToPrompt: () => '', // unused — the prompt is streamed directly by openSandboxRun
|
|
200
210
|
sandboxOverrides: {
|
|
201
211
|
name: `commit0-${task.id}-${attempt}-${randomSuffix()}`.replace(/[^a-zA-Z0-9_.-]/g, '_').slice(0, 60),
|
|
202
212
|
environment: 'universal',
|
|
203
|
-
backend: {
|
|
204
|
-
type: 'opencode',
|
|
205
|
-
model: { provider: cfg.provider, model: cfg.model, baseUrl: cfg.routerBaseUrl },
|
|
206
|
-
},
|
|
207
213
|
},
|
|
208
214
|
}
|
|
209
215
|
let run: SandboxRun<RolloutDeliverable> | undefined
|
|
@@ -310,15 +316,29 @@ async function runShotLocal(task: BenchTask, attempt: number, cfg: ShotCfg, stee
|
|
|
310
316
|
if (co.code !== 0) {
|
|
311
317
|
return { task, attempt, diff: '', ok: false, events: 0, wallMs: Date.now() - startedAt, detail: `git checkout ${meta.baseCommit} failed: ${co.out.trim().slice(-180)}` }
|
|
312
318
|
}
|
|
313
|
-
// openai/* → route through the router (OPENAI_* env); anything else → opencode's
|
|
314
|
-
// OWN configured auth (kimi-for-coding / zai coding-plan subscriptions).
|
|
315
|
-
const env = cfg.model.startsWith('openai/')
|
|
316
|
-
? { ...process.env, OPENAI_API_KEY: cfg.routerKey, OPENAI_BASE_URL: cfg.routerBaseUrl }
|
|
317
|
-
: process.env
|
|
318
319
|
const prompt = steer ? steeredPrompt(localRolloutPrompt(meta), steer) : localRolloutPrompt(meta)
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
320
|
+
if (!cfg.bridgeUrl || !cfg.bridgeBearer) {
|
|
321
|
+
throw new Error('local rollout requires CLI_BRIDGE_URL/BRIDGE_URL and CLI_BRIDGE_BEARER/BRIDGE_BEARER')
|
|
322
|
+
}
|
|
323
|
+
const factory = createExecutor({
|
|
324
|
+
backend: 'bridge',
|
|
325
|
+
bridgeUrl: cfg.bridgeUrl,
|
|
326
|
+
bridgeBearer: cfg.bridgeBearer,
|
|
327
|
+
cwd: dir,
|
|
328
|
+
...(cfg.timeoutMs > 0 ? { timeoutMs: cfg.timeoutMs } : {}),
|
|
329
|
+
})
|
|
330
|
+
const turn = await collectAgentTurn(
|
|
331
|
+
streamAgentTurn(
|
|
332
|
+
{ kind: 'executor', factory, profile: workerProfile(cfg, `commit0-local-${attempt}`) },
|
|
333
|
+
prompt,
|
|
334
|
+
cfg.timeoutMs > 0 ? { timeoutMs: cfg.timeoutMs } : {},
|
|
335
|
+
),
|
|
336
|
+
)
|
|
337
|
+
if (turn.status !== 'completed') {
|
|
338
|
+
throw new Error(turn.error?.message ?? `Runtime bridge ended with ${turn.status}`)
|
|
339
|
+
}
|
|
340
|
+
const traceEvents = turn.events.map((event) => JSON.stringify(event))
|
|
341
|
+
const events = turn.events.length
|
|
322
342
|
// Read the diff straight from git, scoped to src_dir (excludes the .venv the agent made).
|
|
323
343
|
const diffRes = await sh('bash', ['-c', `cd ${JSON.stringify(dir)} && git add -- ${JSON.stringify(meta.srcDir)} && git diff --cached -- ${JSON.stringify(meta.srcDir)}`], { timeoutMs: 60_000 })
|
|
324
344
|
const diff = diffRes.out
|
|
@@ -329,10 +349,10 @@ async function runShotLocal(task: BenchTask, attempt: number, cfg: ShotCfg, stee
|
|
|
329
349
|
diff,
|
|
330
350
|
ok,
|
|
331
351
|
events,
|
|
332
|
-
traceEvents:
|
|
352
|
+
traceEvents: traceEvents.slice(-TRACE_EVENTS_TAIL),
|
|
333
353
|
...(steer ? { steer } : {}),
|
|
334
354
|
wallMs: Date.now() - startedAt,
|
|
335
|
-
...(ok ? {} : { detail: `no diff (
|
|
355
|
+
...(ok ? {} : { detail: `no diff (Runtime bridge completed): ${turn.finalText.trim().slice(-160)}` }),
|
|
336
356
|
}
|
|
337
357
|
} catch (err) {
|
|
338
358
|
return { task, attempt, diff: '', ok: false, events: 0, ...(steer ? { steer } : {}), wallMs: Date.now() - startedAt, detail: `local rollout error: ${(err instanceof Error ? err.message : String(err)).slice(0, 180)}` }
|
|
@@ -349,7 +369,7 @@ async function main(): Promise<void> {
|
|
|
349
369
|
const backend = process.env.COMMIT0_BACKEND === 'local' ? 'local' : 'sandbox'
|
|
350
370
|
const n = Number(process.env.N ?? 8)
|
|
351
371
|
const k = Number(process.env.K ?? 4)
|
|
352
|
-
const model = process.env.WORKER_MODEL ?? (backend === 'local' ? 'kimi-for-coding/kimi-k2-thinking' : '
|
|
372
|
+
const model = process.env.WORKER_MODEL ?? (backend === 'local' ? 'kimi-for-coding/kimi-k2-thinking' : 'deepseek-v4-flash')
|
|
353
373
|
const routerBaseUrl = process.env.ROUTER_BASE ?? 'https://router.tangle.tools/v1'
|
|
354
374
|
// The arms under test. `random` = K independent blind shots (the equal-compute
|
|
355
375
|
// control); `refineAudit` = blind shot 0, then trace-only-analyst-steered shots.
|
|
@@ -363,7 +383,6 @@ async function main(): Promise<void> {
|
|
|
363
383
|
const needsRouterKey = backend === 'sandbox' || model.startsWith('openai/') || armNames.includes('refineAudit')
|
|
364
384
|
const routerKey = needsRouterKey ? must('TANGLE_API_KEY') : (process.env.TANGLE_API_KEY ?? '')
|
|
365
385
|
const sandboxBaseUrl = process.env.SANDBOX_BASE_URL ?? 'https://sandbox.tangle.tools'
|
|
366
|
-
const opencodeBin = process.env.OPENCODE_BIN ?? join(process.env.HOME ?? '', '.local/bin/opencode')
|
|
367
386
|
// openai-compat = generic passthrough so cheap router models resolve in-box;
|
|
368
387
|
// `openai` rejects non-registered model names. Override via WORKER_PROVIDER.
|
|
369
388
|
const provider = process.env.WORKER_PROVIDER ?? 'openai-compat'
|
|
@@ -384,7 +403,17 @@ async function main(): Promise<void> {
|
|
|
384
403
|
|
|
385
404
|
// Phase 1 — rollouts, concurrent. sandbox = remote box; local = cli-bridge (opencode
|
|
386
405
|
// in a tmpdir, diff read from git). Both fault-isolated → a failure is a NO-DIFF, never a throw.
|
|
387
|
-
const cfg: ShotCfg = {
|
|
406
|
+
const cfg: ShotCfg = {
|
|
407
|
+
sandboxBaseUrl,
|
|
408
|
+
sandboxKey: routerKey,
|
|
409
|
+
routerBaseUrl,
|
|
410
|
+
routerKey,
|
|
411
|
+
model,
|
|
412
|
+
provider,
|
|
413
|
+
timeoutMs,
|
|
414
|
+
bridgeUrl: process.env.CLI_BRIDGE_URL ?? process.env.BRIDGE_URL,
|
|
415
|
+
bridgeBearer: process.env.CLI_BRIDGE_BEARER ?? process.env.BRIDGE_BEARER,
|
|
416
|
+
}
|
|
388
417
|
const runRollout = backend === 'local' ? runShotLocal : runShot
|
|
389
418
|
const analyze: AnalystFn = llmAnalyst({ routerBaseUrl, routerKey, model: analystModel })
|
|
390
419
|
const logShot = (armName: string, s: Shot) =>
|
package/src/corpus.ts
CHANGED
|
@@ -23,7 +23,7 @@ import type { BenchRuntimeDecisionPoint, BenchRuntimeHookEvent } from './runtime
|
|
|
23
23
|
* verdict, the measured economics, and a bounded trace summary.
|
|
24
24
|
*
|
|
25
25
|
* `costUsd`/`tokensIn`/`tokensOut`/`wallMs` are OPTIONAL on purpose: they are
|
|
26
|
-
* present only when the worker actually reported them (the `
|
|
26
|
+
* present only when the worker actually reported them (the `runAgentRounds`/kernel
|
|
27
27
|
* path). A worker that reports no usage (e.g. a raw opencode-stdout shot)
|
|
28
28
|
* OMITS them — it never writes a fabricated `0`. Absence means "unmeasured",
|
|
29
29
|
* which is honest and lets the canonical bridge below refuse to forge a
|
|
@@ -106,7 +106,7 @@ function summarizeAttempt<Task, Output>(iter: Iteration<Task, Output>): AttemptR
|
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
/** Build a RunRecord from a
|
|
109
|
+
/** Build a RunRecord from a runAgentRounds result. `now` injected for determinism in tests. */
|
|
110
110
|
export function buildRunRecord<Task, Output>(args: {
|
|
111
111
|
benchmark: string
|
|
112
112
|
instanceId: string
|
|
@@ -247,7 +247,7 @@ export interface CorpusProjection {
|
|
|
247
247
|
* (`costUsd`/`tokensIn`/`tokensOut`/`wallMs`/`output`) is reported in
|
|
248
248
|
* `unmappable` rather than backfilled with phantom zeros. This is WHY the local
|
|
249
249
|
* raw-stdout path (which omits economics) cannot feed the gate — only the
|
|
250
|
-
* measured `
|
|
250
|
+
* measured `runAgentRounds`/sandbox path can, which is the correct, honest constraint.
|
|
251
251
|
*/
|
|
252
252
|
export async function benchRecordToCorpusRecords(
|
|
253
253
|
rec: RunRecord,
|
|
@@ -20,24 +20,43 @@ import { mkdtempSync, writeFileSync, rmSync } from 'node:fs'
|
|
|
20
20
|
import { tmpdir } from 'node:os'
|
|
21
21
|
import { join } from 'node:path'
|
|
22
22
|
import { loadHumanEval, extractCode, type HumanEvalTask } from './benchmarks/humaneval'
|
|
23
|
+
import { benchRouterProfile, runBenchRouterTurn } from './router-turn'
|
|
23
24
|
|
|
24
|
-
|
|
25
|
+
function requiredEnv(name: string): string {
|
|
26
|
+
const value = process.env[name]
|
|
27
|
+
if (!value) throw new Error(`${name} required`)
|
|
28
|
+
return value
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const KEY = requiredEnv('TANGLE_API_KEY')
|
|
25
32
|
const ROUTER = process.env.ROUTER_BASE ?? 'https://router.tangle.tools/v1'
|
|
26
33
|
const DAVID = process.env.DAVID ?? 'groq/llama-3.1-8b-instant'
|
|
27
34
|
const N = Number(process.env.N ?? 8)
|
|
28
35
|
const T = Number(process.env.T ?? 5)
|
|
29
36
|
const NTASKS = Number(process.env.NTASKS ?? 60)
|
|
30
37
|
const CONC = Number(process.env.CONCURRENCY ?? 6)
|
|
38
|
+
const MAX_TOKENS = Number(process.env.MAX_TOKENS ?? 1000)
|
|
39
|
+
const LLM_TIMEOUT_MS = Number(process.env.LLM_TIMEOUT_MS ?? 60_000)
|
|
31
40
|
|
|
32
|
-
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms))
|
|
33
41
|
async function chat(messages: { role: string; content: string }[], temp: number): Promise<string> {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
try {
|
|
43
|
+
const system = messages.find((message) => message.role === 'system')?.content
|
|
44
|
+
const turn = await runBenchRouterTurn(
|
|
45
|
+
{
|
|
46
|
+
routerBaseUrl: ROUTER,
|
|
47
|
+
routerKey: KEY,
|
|
48
|
+
profile: benchRouterProfile('david-attribution-worker', DAVID, {
|
|
49
|
+
...(system ? { systemPrompt: system } : {}),
|
|
50
|
+
temperature: temp,
|
|
51
|
+
maxTokens: MAX_TOKENS,
|
|
52
|
+
}),
|
|
53
|
+
timeoutMs: LLM_TIMEOUT_MS,
|
|
54
|
+
},
|
|
55
|
+
{ messages: messages.filter((message) => message.role !== 'system') },
|
|
56
|
+
)
|
|
57
|
+
return turn.finalText
|
|
58
|
+
} catch {
|
|
59
|
+
return ''
|
|
41
60
|
}
|
|
42
61
|
}
|
|
43
62
|
const exec = (f: string, a: string[], o: object) => new Promise<number>((res) => execFile(f, a, { ...o, maxBuffer: 8e6 }, (e) => res((e as { code?: number } | null)?.code ?? (e ? 1 : 0))))
|
package/src/david-goliath.mts
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* generator punch above its solo weight — the standing "verification is live" claim
|
|
18
18
|
* at its most dramatic. Paired McNemar on per-task discordant pairs for significance.
|
|
19
19
|
*
|
|
20
|
-
* Run from cwd=bench: env DAVID=
|
|
20
|
+
* Run from cwd=bench: env DAVID=glm-5.2 GOLIATH=deepseek-v4-flash \
|
|
21
21
|
* N=8 T=5 NTASKS=164 REPS=2 node_modules/.bin/tsx src/david-goliath.mts
|
|
22
22
|
*/
|
|
23
23
|
import { execFile } from 'node:child_process'
|
|
@@ -25,18 +25,22 @@ import { mkdtempSync, writeFileSync, rmSync } from 'node:fs'
|
|
|
25
25
|
import { tmpdir } from 'node:os'
|
|
26
26
|
import { join } from 'node:path'
|
|
27
27
|
import { loadHumanEval, extractCode, type HumanEvalTask } from './benchmarks/humaneval'
|
|
28
|
+
import { runBenchRouterTurn } from './router-turn'
|
|
28
29
|
|
|
29
30
|
const KEY = process.env.TANGLE_API_KEY
|
|
30
31
|
if (!KEY) throw new Error('TANGLE_API_KEY required')
|
|
32
|
+
const ROUTER_KEY = KEY
|
|
31
33
|
const ROUTER = process.env.ROUTER_BASE ?? 'https://router.tangle.tools/v1'
|
|
32
34
|
const DAVID = process.env.DAVID ?? 'groq/llama-3.1-8b-instant'
|
|
33
|
-
const GOLIATH = process.env.GOLIATH ?? '
|
|
35
|
+
const GOLIATH = process.env.GOLIATH ?? 'deepseek-v4-flash'
|
|
34
36
|
const N = Number(process.env.N ?? 8) // David candidate solutions
|
|
35
37
|
const T = Number(process.env.T ?? 5) // David generated tests
|
|
36
38
|
const NTASKS = Number(process.env.NTASKS ?? 164)
|
|
37
39
|
const REPS = Number(process.env.REPS ?? 2)
|
|
38
40
|
const CONC = Number(process.env.CONCURRENCY ?? 6)
|
|
39
41
|
const EXEC_TIMEOUT = Number(process.env.EXEC_TIMEOUT_MS ?? 6000)
|
|
42
|
+
const LLM_TIMEOUT = Number(process.env.LLM_TIMEOUT_MS ?? 60_000)
|
|
43
|
+
const MAX_TOKENS = Number(process.env.MAX_TOKENS ?? 1000)
|
|
40
44
|
|
|
41
45
|
// Approx $/1M tokens (in,out) for cost accounting — the router does not price
|
|
42
46
|
// every model inline, so use public rates; a cheap/frontier gap of ~20-30x is the
|
|
@@ -56,18 +60,33 @@ const addU = (a: Usage, b: Usage) => { a.in += b.in; a.out += b.out }
|
|
|
56
60
|
const usd = (m: string, u: Usage) => { const [pi, po] = priceOf(m); return (u.in * pi + u.out * po) / 1e6 }
|
|
57
61
|
|
|
58
62
|
async function chat(model: string, messages: { role: string; content: string }[], temperature: number, usage: Usage): Promise<string> {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
63
|
+
try {
|
|
64
|
+
const system = messages.find((message) => message.role === 'system')?.content
|
|
65
|
+
const result = await runBenchRouterTurn(
|
|
66
|
+
{
|
|
67
|
+
routerBaseUrl: ROUTER,
|
|
68
|
+
routerKey: ROUTER_KEY,
|
|
69
|
+
profile: {
|
|
70
|
+
name: 'david-goliath-worker',
|
|
71
|
+
harness: 'cli-base',
|
|
72
|
+
model: {
|
|
73
|
+
provider: 'tangle-router',
|
|
74
|
+
default: model,
|
|
75
|
+
metadata: { temperature, maxTokens: MAX_TOKENS },
|
|
76
|
+
},
|
|
77
|
+
...(system ? { prompt: { systemPrompt: system } } : {}),
|
|
78
|
+
},
|
|
79
|
+
timeoutMs: LLM_TIMEOUT,
|
|
80
|
+
},
|
|
81
|
+
{ messages: messages.filter((message) => message.role !== 'system') },
|
|
82
|
+
)
|
|
83
|
+
if (result.usage.tokensKnown === false) throw new Error('provider omitted token usage')
|
|
84
|
+
addU(usage, { in: result.usage.input, out: result.usage.output })
|
|
85
|
+
return result.finalText
|
|
86
|
+
} catch {
|
|
87
|
+
return ''
|
|
68
88
|
}
|
|
69
89
|
}
|
|
70
|
-
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms))
|
|
71
90
|
const exec = (file: string, args: string[], o: object) => new Promise<{ code: number; stdout: string }>((res) => execFile(file, args, { ...o, maxBuffer: 8 * 1024 * 1024 }, (e, stdout) => res({ code: (e as { code?: number } | null)?.code ?? (e ? 1 : 0), stdout: String(stdout) })))
|
|
72
91
|
async function runPy(program: string): Promise<{ ok: boolean }> {
|
|
73
92
|
const d = mkdtempSync(join(tmpdir(), 'dg-'))
|
package/src/egress-probe.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Sandbox->router egress probe: spins a box and makes one
|
|
2
|
+
* Sandbox->router egress probe: spins a box and makes one model-free authenticated API read from
|
|
3
3
|
* https://router.tangle.tools/v1 using the BOX-PROVISIONED key (OPENCODE_MODEL_API_KEY
|
|
4
4
|
* inside the box). That is the sanctioned flow: the egress proxy validates/injects
|
|
5
5
|
* credentials at the boundary and 403s foreign keys passed in from outside — a raw
|
|
@@ -19,8 +19,8 @@ try {
|
|
|
19
19
|
const r = await box.exec(`curl -s -o /dev/null -w '%{http_code}' --max-time 15 https://${h}/ || echo FAIL`)
|
|
20
20
|
console.log(h, '→', out(r))
|
|
21
21
|
}
|
|
22
|
-
const
|
|
23
|
-
console.log('
|
|
22
|
+
const models = await box.exec(`curl -s --max-time 30 https://router.tangle.tools/v1/models -H "Authorization: Bearer $OPENCODE_MODEL_API_KEY" -o /tmp/b -w '%{http_code}'; echo ' |'; head -c 250 /tmp/b`)
|
|
23
|
+
console.log('models API (box-provisioned key):', out(models) || 'EMPTY — egress broken')
|
|
24
24
|
} finally {
|
|
25
25
|
await box.delete()
|
|
26
26
|
}
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
sampleThenRefine,
|
|
25
25
|
} from '@tangle-network/agent-runtime/kernel'
|
|
26
26
|
import { ensureLeanImage, leanCheck } from './lean-verify.js'
|
|
27
|
+
import { benchRouterProfile } from '../router-turn'
|
|
27
28
|
|
|
28
29
|
// Real, mathlib-free Lean 4 theorems (compile in core Lean). `header` is everything up to `:=`;
|
|
29
30
|
// the prover supplies the proof term / tactic block. `reference` is a known-good proof used only
|
|
@@ -145,9 +146,15 @@ async function main(): Promise<void> {
|
|
|
145
146
|
worker: {
|
|
146
147
|
routerBaseUrl: process.env.ROUTER_BASE ?? 'https://router.tangle.tools/v1',
|
|
147
148
|
routerKey,
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
149
|
+
workerProfile: benchRouterProfile(
|
|
150
|
+
'lean-proof-worker',
|
|
151
|
+
process.env.WORKER_MODEL ?? 'deepseek-v4-flash',
|
|
152
|
+
{
|
|
153
|
+
systemPrompt: 'Construct and verify Lean proofs with the available checker before answering.',
|
|
154
|
+
maxTurns: 8,
|
|
155
|
+
temperature: 0.4,
|
|
156
|
+
},
|
|
157
|
+
),
|
|
151
158
|
},
|
|
152
159
|
strategies: [sample, refine, sampleThenRefine],
|
|
153
160
|
budget: Number(process.env.BUDGET ?? 3),
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
sample,
|
|
21
21
|
sampleThenRefine,
|
|
22
22
|
} from '@tangle-network/agent-runtime/kernel'
|
|
23
|
+
import { benchRouterProfile } from '../router-turn'
|
|
23
24
|
|
|
24
25
|
// GSM8K-style problems; meta.answer is the ground truth the deployable check compares to.
|
|
25
26
|
const problems: Array<{ q: string; answer: number }> = [
|
|
@@ -95,9 +96,15 @@ async function main(): Promise<void> {
|
|
|
95
96
|
worker: {
|
|
96
97
|
routerBaseUrl: process.env.ROUTER_BASE ?? 'https://router.tangle.tools/v1',
|
|
97
98
|
routerKey,
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
workerProfile: benchRouterProfile(
|
|
100
|
+
'math-worker',
|
|
101
|
+
process.env.WORKER_MODEL ?? 'deepseek-v4-flash',
|
|
102
|
+
{
|
|
103
|
+
systemPrompt: 'Solve the requested math task with the available tools and verify the result.',
|
|
104
|
+
maxTurns: 6,
|
|
105
|
+
temperature: 0.6,
|
|
106
|
+
},
|
|
107
|
+
),
|
|
101
108
|
},
|
|
102
109
|
strategies: [sample, refine, sampleThenRefine],
|
|
103
110
|
budget: Number(process.env.BUDGET ?? 3),
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* TANGLE_API_KEY=... WORKER_MODEL=gpt-4o-mini tsx src/examples/strategy-demo.mts
|
|
14
14
|
*/
|
|
15
15
|
import { adaptiveRefine, type AgenticTask, type ArtifactHandle, defineStrategy, type Environment, printBenchmarkReport, refine, runBenchmark, sample } from '@tangle-network/agent-runtime/kernel'
|
|
16
|
+
import { benchRouterProfile } from '../router-turn'
|
|
16
17
|
|
|
17
18
|
// ── 1. Implement an Environment (the only thing a new domain writes) ──────────────
|
|
18
19
|
// A toy: the agent must drive a counter to exactly the target using the increment tool.
|
|
@@ -59,8 +60,7 @@ const counterEnv: Environment = {
|
|
|
59
60
|
|
|
60
61
|
const task: AgenticTask = {
|
|
61
62
|
id: 'counter-to-5',
|
|
62
|
-
|
|
63
|
-
userPrompt: `Use the increment tool to bring the counter to exactly ${target}. Use read_count to verify before you finish. Reply DONE when the count equals ${target}.`,
|
|
63
|
+
userPrompt: `You operate a counter with tools. Use the increment tool to bring the counter to exactly ${target}. Use read_count to verify before you finish. Reply DONE when the count equals ${target}.`,
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
// ── 3. Author your OWN strategy in ~10 lines — the lego (no Supervisor ceremony) ──
|
|
@@ -97,8 +97,14 @@ async function main(): Promise<void> {
|
|
|
97
97
|
const worker = {
|
|
98
98
|
routerBaseUrl: process.env.ROUTER_BASE ?? 'https://router.tangle.tools/v1',
|
|
99
99
|
routerKey: process.env.TANGLE_API_KEY ?? '',
|
|
100
|
-
|
|
101
|
-
|
|
100
|
+
workerProfile: benchRouterProfile(
|
|
101
|
+
'strategy-demo-worker',
|
|
102
|
+
process.env.WORKER_MODEL ?? 'deepseek-v4-flash',
|
|
103
|
+
{
|
|
104
|
+
systemPrompt: 'Use the available tools to complete and verify the task.',
|
|
105
|
+
maxTurns: 6,
|
|
106
|
+
},
|
|
107
|
+
),
|
|
102
108
|
}
|
|
103
109
|
if (!worker.routerKey) throw new Error('set TANGLE_API_KEY (the worker calls the router)')
|
|
104
110
|
|
package/src/fleet.mts
CHANGED
|
@@ -12,8 +12,13 @@
|
|
|
12
12
|
*
|
|
13
13
|
* Run it twice: the second run injects the first run's learnings into the workers.
|
|
14
14
|
*/
|
|
15
|
-
import {
|
|
16
|
-
|
|
15
|
+
import {
|
|
16
|
+
defaultAnalystInstruction,
|
|
17
|
+
FileCorpus,
|
|
18
|
+
observe,
|
|
19
|
+
openSandboxRun,
|
|
20
|
+
renderReport,
|
|
21
|
+
} from '@tangle-network/agent-runtime/kernel'
|
|
17
22
|
import { Sandbox } from '@tangle-network/sandbox'
|
|
18
23
|
import { answerOutput, sandboxAgentRun, type WorkerBackendType } from './sandbox-run'
|
|
19
24
|
|
|
@@ -41,7 +46,7 @@ interface WorkerResult {
|
|
|
41
46
|
|
|
42
47
|
async function runWorker(
|
|
43
48
|
client: Sandbox,
|
|
44
|
-
cfg: { backendType: WorkerBackendType; model: string; routerBaseUrl: string },
|
|
49
|
+
cfg: { backendType: WorkerBackendType; model: string; provider: string; routerBaseUrl: string },
|
|
45
50
|
id: string,
|
|
46
51
|
task: string,
|
|
47
52
|
priorLearnings: string,
|
|
@@ -51,7 +56,13 @@ async function runWorker(
|
|
|
51
56
|
const controller = new AbortController()
|
|
52
57
|
const timer = setTimeout(() => controller.abort(), Number(process.env.TIMEOUT_MS ?? 240_000))
|
|
53
58
|
try {
|
|
54
|
-
const agentRun = sandboxAgentRun({
|
|
59
|
+
const agentRun = sandboxAgentRun({
|
|
60
|
+
profile: {
|
|
61
|
+
name: id,
|
|
62
|
+
harness: cfg.backendType,
|
|
63
|
+
model: { provider: cfg.provider, default: cfg.model },
|
|
64
|
+
},
|
|
65
|
+
})
|
|
55
66
|
const run = await openSandboxRun<string>(
|
|
56
67
|
client,
|
|
57
68
|
{ agentRun, signal: controller.signal },
|
|
@@ -74,13 +85,19 @@ async function main(): Promise<void> {
|
|
|
74
85
|
const routerKey = env('TANGLE_API_KEY')
|
|
75
86
|
const cfg = {
|
|
76
87
|
backendType: env('BACKEND', 'opencode') as WorkerBackendType,
|
|
77
|
-
model: env('MODEL', '
|
|
88
|
+
model: env('MODEL', 'deepseek-v4-flash'),
|
|
89
|
+
provider: env('WORKER_PROVIDER', 'openai-compat'),
|
|
78
90
|
routerBaseUrl: env('ROUTER_BASE_URL', 'https://router.tangle.tools/v1'),
|
|
79
91
|
}
|
|
80
92
|
const n = Math.min(Number(env('N', '2')), subtasks.length)
|
|
81
93
|
const corpus = new FileCorpus(env('CORPUS', '/tmp/fleet-corpus.jsonl'))
|
|
82
|
-
const observerModel = env('OBSERVER_MODEL', '
|
|
83
|
-
const
|
|
94
|
+
const observerModel = env('OBSERVER_MODEL', 'deepseek-v4-flash')
|
|
95
|
+
const observerProfile = {
|
|
96
|
+
name: 'fleet-observer',
|
|
97
|
+
harness: 'cli-base' as const,
|
|
98
|
+
model: { provider: 'tangle-router', default: observerModel },
|
|
99
|
+
prompt: { systemPrompt: defaultAnalystInstruction },
|
|
100
|
+
}
|
|
84
101
|
const client = new Sandbox({ baseUrl: env('SANDBOX_BASE_URL', 'https://sandbox.tangle.tools'), apiKey: routerKey })
|
|
85
102
|
|
|
86
103
|
// ── continuous: read what prior runs LEARNED, inject it into this run's workers
|
|
@@ -104,7 +121,12 @@ async function main(): Promise<void> {
|
|
|
104
121
|
if (w.error) continue
|
|
105
122
|
const ob = await observe(
|
|
106
123
|
{ task: w.task, output: w.output, trace: w.events, outcome: w.output ? 'passed' : 'unknown', runId: w.id },
|
|
107
|
-
{
|
|
124
|
+
{
|
|
125
|
+
profile: observerProfile,
|
|
126
|
+
executor: { backend: 'router', routerBaseUrl: cfg.routerBaseUrl, routerKey },
|
|
127
|
+
corpus,
|
|
128
|
+
tags: [cfg.backendType, 'fleet'],
|
|
129
|
+
},
|
|
108
130
|
)
|
|
109
131
|
totalLearned += ob.learned.length
|
|
110
132
|
console.error(` answer: ${w.output.slice(0, 120).replace(/\n/g, ' ')}`)
|
package/src/gate-cli.mts
CHANGED
|
@@ -46,9 +46,16 @@ async function main(): Promise<void> {
|
|
|
46
46
|
|
|
47
47
|
const profile = {
|
|
48
48
|
name: 'gate-solver',
|
|
49
|
-
|
|
49
|
+
harness: 'cli-base',
|
|
50
|
+
model: {
|
|
51
|
+
provider: 'tangle-router',
|
|
52
|
+
default: model,
|
|
53
|
+
metadata: {
|
|
54
|
+
temperature: Number(process.env.TEMPERATURE ?? 0.7),
|
|
55
|
+
},
|
|
56
|
+
},
|
|
50
57
|
prompt: { systemPrompt: 'You are an expert agent. Produce the single best deliverable the task’s grader will accept.' },
|
|
51
|
-
}
|
|
58
|
+
} satisfies AgentProfile
|
|
52
59
|
|
|
53
60
|
const report = await runGate({
|
|
54
61
|
adapter,
|
|
@@ -56,8 +63,6 @@ async function main(): Promise<void> {
|
|
|
56
63
|
strategies,
|
|
57
64
|
routerBaseUrl,
|
|
58
65
|
routerKey,
|
|
59
|
-
model,
|
|
60
|
-
...(process.env.TEMPERATURE ? { temperature: Number(process.env.TEMPERATURE) } : {}),
|
|
61
66
|
n: Number(process.env.N ?? 20),
|
|
62
67
|
...(process.env.IDS ? { ids: process.env.IDS.split(',') } : {}),
|
|
63
68
|
...(process.env.SPLIT ? { split: process.env.SPLIT } : {}),
|
package/src/gate.test.mts
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import assert from 'node:assert/strict'
|
|
14
|
+
import type { AgentProfile } from '@tangle-network/agent-interface'
|
|
14
15
|
import type {
|
|
15
16
|
AgentSpec,
|
|
16
17
|
DefaultVerdict,
|
|
@@ -80,7 +81,12 @@ function stubAdapter(n: number): BenchmarkAdapter {
|
|
|
80
81
|
}
|
|
81
82
|
}
|
|
82
83
|
|
|
83
|
-
const profile = {
|
|
84
|
+
const profile = {
|
|
85
|
+
name: 'stub-solver',
|
|
86
|
+
harness: 'pi',
|
|
87
|
+
model: { provider: 'test', default: 'stub-model' },
|
|
88
|
+
prompt: { systemPrompt: 'Solve the supplied benchmark task.' },
|
|
89
|
+
} satisfies AgentProfile
|
|
84
90
|
|
|
85
91
|
const report = await runGate({
|
|
86
92
|
adapter: stubAdapter(5),
|
|
@@ -88,7 +94,6 @@ const report = await runGate({
|
|
|
88
94
|
strategies: ['plain restate', 'use the STRONG verified approach', 'enumerate edge cases'],
|
|
89
95
|
routerBaseUrl: 'http://unused',
|
|
90
96
|
routerKey: 'unused',
|
|
91
|
-
model: 'stub-model',
|
|
92
97
|
solverRegistry: stubRegistry,
|
|
93
98
|
})
|
|
94
99
|
|