@tangle-network/agent-bench 0.3.6 → 0.3.7
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 +7 -0
- package/dist/adapters.js +2 -2
- package/dist/benchmarks/humaneval.d.ts +10 -1
- package/dist/benchmarks/humaneval.js +5 -3
- package/dist/{chunk-PPYSEKFM.js → chunk-5H5XV76F.js} +73 -15
- package/dist/chunk-5H5XV76F.js.map +1 -0
- package/dist/{chunk-5SBJCB6W.js → chunk-PWQVGAJB.js} +2 -2
- package/dist/index.js +2 -2
- package/package.json +5 -4
- package/scripts/run-package-tests.mjs +30 -8
- package/scripts/verify-pier-agent.mts +1 -0
- package/src/benchmarks/humaneval.test.mts +122 -0
- package/src/benchmarks/humaneval.ts +100 -27
- package/src/david-attribution.mts +78 -0
- package/src/david-goliath.mts +149 -0
- package/src/hev-improve.mts +25 -6
- package/src/humaneval-object-ablation.mts +201 -0
- package/src/live-improve-campaign-mbpp.mts +641 -0
- package/src/live-improve-campaign.mts +500 -0
- package/src/mbpp-structural.mts +12 -7
- package/src/stream-observe.py +45 -0
- package/src/stream-observe.tpl.html +247 -0
- package/src/supervisor-arena.mts +816 -0
- package/src/swe-arena/analyze.ts +211 -0
- package/src/swe-arena/arms.ts +788 -0
- package/src/swe-arena/bootstrap-meta.mts +188 -0
- package/src/swe-arena/bootstrap-meta.test.mts +51 -0
- package/src/swe-arena/calibrate.ts +116 -0
- package/src/swe-arena/capabilities.mts +76 -0
- package/src/swe-arena/capabilities.test.mts +57 -0
- package/src/swe-arena/capacity.ts +194 -0
- package/src/swe-arena/cell-evidence.mts +405 -0
- package/src/swe-arena/cell-evidence.test.mts +248 -0
- package/src/swe-arena/diagnosis-ensemble.test.mts +210 -0
- package/src/swe-arena/diagnosis-ensemble.ts +520 -0
- package/src/swe-arena/execution.test.mts +1170 -0
- package/src/swe-arena/fixtures/analyze.py +80 -0
- package/src/swe-arena/fixtures/excludes.txt +8 -0
- package/src/swe-arena/fixtures/gen1-salvage/README.md +45 -0
- package/src/swe-arena/fixtures/gen1-salvage/cand0-e6d7361.diff +116 -0
- package/src/swe-arena/fixtures/gen1-salvage/cand1-76a8590.diff +293 -0
- package/src/swe-arena/fixtures/holdout-preregister.log +12 -0
- package/src/swe-arena/fixtures/holdout.json +44 -0
- package/src/swe-arena/fixtures/instances.json +146 -0
- package/src/swe-arena/fixtures/ledger.jsonl +12 -0
- package/src/swe-arena/fixtures/patches/pallets__flask-5014.solo.patch +36 -0
- package/src/swe-arena/fixtures/patches/pydata__xarray-4687.sup.patch +33 -0
- package/src/swe-arena/fixtures/rejudge.jsonl +15 -0
- package/src/swe-arena/fixtures/rematch.jsonl +3 -0
- package/src/swe-arena/fixtures/rematch2.jsonl +3 -0
- package/src/swe-arena/fixtures/rematch3.jsonl +3 -0
- package/src/swe-arena/fixtures/sup-journal-true.json +19 -0
- package/src/swe-arena/fixtures/verify/astropy__astropy-13033.sh +48 -0
- package/src/swe-arena/fixtures/verify/django__django-11532.sh +50 -0
- package/src/swe-arena/fixtures/verify/matplotlib__matplotlib-20826.sh +76 -0
- package/src/swe-arena/fixtures/verify/pydata__xarray-4687.sh +44 -0
- package/src/swe-arena/fixtures/verify/pytest-dev__pytest-6197.sh +32 -0
- package/src/swe-arena/fixtures/verify/sphinx-doc__sphinx-9658.sh +51 -0
- package/src/swe-arena/fixtures/worker-tokens.json +42 -0
- package/src/swe-arena/fixtures.ts +104 -0
- package/src/swe-arena/holdout-certify.mts +408 -0
- package/src/swe-arena/holdout-certify.test.mts +160 -0
- package/src/swe-arena/judge-child.mts +37 -0
- package/src/swe-arena/manifest.mts +293 -0
- package/src/swe-arena/manifest.test.mts +169 -0
- package/src/swe-arena/materialize.ts +142 -0
- package/src/swe-arena/outer-loop.mts +2145 -0
- package/src/swe-arena/outer-loop.test.mts +696 -0
- package/src/swe-arena/parity.test.mts +87 -0
- package/src/swe-arena/proc.test.mts +174 -0
- package/src/swe-arena/proc.ts +260 -0
- package/src/swe-arena/profiles/default-author.profile.json +4 -0
- package/src/swe-arena/proposer-fanout.mts +489 -0
- package/src/swe-arena/proposer-fanout.test.mts +372 -0
- package/src/swe-arena/reconcile.ts +0 -0
- package/src/swe-arena/replay.mts +183 -0
- package/src/swe-arena/replay.test.mts +300 -0
- package/src/swe-arena/run-experiment.mts +361 -0
- package/src/swe-arena/run-supervisor.mjs +297 -0
- package/src/swe-arena/run-supervisor.test.mts +498 -0
- package/src/swe-arena/serialized-judge.ts +414 -0
- package/src/swe-arena/types.ts +166 -0
- package/src/swe-code-improve.mts +328 -0
- package/src/swe-emit-patch.mts +104 -0
- package/src/swe-improve.mts +232 -0
- package/src/swe-jail.ts +2 -2
- package/src/swe-local-proof.mts +169 -0
- package/src/swe-repro-calibrate.mts +446 -0
- package/src/swe-stream.mts +1497 -0
- package/dist/chunk-PPYSEKFM.js.map +0 -1
- /package/dist/{chunk-5SBJCB6W.js.map → chunk-PWQVGAJB.js.map} +0 -0
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* live-improve-campaign — the FIRST LIVE self-improvement campaign on the merged
|
|
3
|
+
* machinery: `improve()` (surface 'rollout-policy') tunes the structuralRollout dials
|
|
4
|
+
* { k, repairRounds, testgen } against REAL HumanEval with a REAL worker model, and the
|
|
5
|
+
* library's own held-out gate (defaultProductionGate: paired bootstrap over held-out
|
|
6
|
+
* scenarios, ship iff CI.low > deltaThreshold 0.05) makes the ship/hold call. No human
|
|
7
|
+
* picks winners; this script only wires the real evaluator into the loop and reports.
|
|
8
|
+
*
|
|
9
|
+
* Wiring template: rollout-policy.test.ts's end-to-end improve() run, with the fake
|
|
10
|
+
* judge gradient replaced by the real evaluator (the smoke's harness):
|
|
11
|
+
* agent(surface, scenario) = runAgentic(structuralRollout(parsed policy)) over an
|
|
12
|
+
* INERT verifier surface (no hidden signal reaches selection/repair), visible checks
|
|
13
|
+
* via the shipped sandboxCheckRunner over a docker --network=none exec channel, then
|
|
14
|
+
* SCRIPT-SIDE hidden grading of the locked winner candidate by the nonce-sentinel
|
|
15
|
+
* judge (hev-structural's runHiddenJudge pattern: pass requires exit 0 AND the
|
|
16
|
+
* per-call nonce in stdout — a candidate printing a forged verdict cannot pass).
|
|
17
|
+
*
|
|
18
|
+
* Honesty split:
|
|
19
|
+
* - DEV = HumanEval index [0, DEV_N) and HELD-OUT = [DEV_N, DEV_N+HOLD_N) — fixed,
|
|
20
|
+
* disjoint slices, passed as explicit `budget.holdoutScenarios` so the library's
|
|
21
|
+
* own train/holdout split machinery enforces disjointness (it throws on overlap).
|
|
22
|
+
* - The proposer is deterministic enumeration; `analyzeGeneration: null` keeps the
|
|
23
|
+
* findings channel empty, so the improver's context is ONLY the DEV composites the
|
|
24
|
+
* loop itself accumulates. Held-out cells run after all generations, gate-side only.
|
|
25
|
+
* - The gate decision is `result.gateDecision` from the library — never recomputed here.
|
|
26
|
+
*
|
|
27
|
+
* Run (key via dotenvx; never in the shell history):
|
|
28
|
+
* cd ~/company/devops/secrets && dotenvx run -f agent-state.env -- bash -c ' \
|
|
29
|
+
* cd /home/drew/code/agent-runtime-swe && \
|
|
30
|
+
* HUMANEVAL_GZ=/abs/HumanEval.jsonl.gz npx tsx bench/src/live-improve-campaign.mts'
|
|
31
|
+
* Smoke first (cost gate): SMOKE=1 shrinks to 6 dev + 6 held-out tasks, 1 generation,
|
|
32
|
+
* population 2 — proves the full path completes before the real burn.
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
import { execFile, execFileSync } from 'node:child_process'
|
|
36
|
+
import { randomBytes } from 'node:crypto'
|
|
37
|
+
import { appendFileSync, mkdirSync } from 'node:fs'
|
|
38
|
+
import { tmpdir } from 'node:os'
|
|
39
|
+
import { join } from 'node:path'
|
|
40
|
+
import type {
|
|
41
|
+
DispatchContext,
|
|
42
|
+
JudgeConfig,
|
|
43
|
+
MutableSurface,
|
|
44
|
+
Scenario,
|
|
45
|
+
} from '@tangle-network/agent-eval/contract'
|
|
46
|
+
import type { AgentProfile } from '@tangle-network/agent-interface'
|
|
47
|
+
import { improve } from '../../src/improvement/improve'
|
|
48
|
+
import {
|
|
49
|
+
parseRolloutPolicy,
|
|
50
|
+
ROLLOUT_POLICY_EXTENSION,
|
|
51
|
+
serializeRolloutPolicy,
|
|
52
|
+
structuralRolloutPolicyFromProfile,
|
|
53
|
+
} from '../../src/improvement/rollout-policy'
|
|
54
|
+
import {
|
|
55
|
+
type AgenticRunResult,
|
|
56
|
+
type CheckExecChannel,
|
|
57
|
+
type CheckOutcome,
|
|
58
|
+
type CheckRunner,
|
|
59
|
+
createVerifierEnvironment,
|
|
60
|
+
runAgentic,
|
|
61
|
+
sandboxCheckRunner,
|
|
62
|
+
structuralRollout,
|
|
63
|
+
type StructuralRolloutResult,
|
|
64
|
+
} from '../../src/runtime/index'
|
|
65
|
+
import { basePrompt, type HumanEvalTask, loadHumanEval } from './benchmarks/humaneval'
|
|
66
|
+
|
|
67
|
+
function must(name: string): string {
|
|
68
|
+
const v = process.env[name]
|
|
69
|
+
if (!v) throw new Error(`env ${name} is required`)
|
|
70
|
+
return v
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const SMOKE = process.env.SMOKE === '1'
|
|
74
|
+
const DEV_N = Number(process.env.DEV_N ?? (SMOKE ? 6 : 60))
|
|
75
|
+
const HOLD_N = Number(process.env.HOLD_N ?? (SMOKE ? 6 : 60))
|
|
76
|
+
const GENERATIONS = Number(process.env.GENERATIONS ?? (SMOKE ? 1 : 2))
|
|
77
|
+
const POPULATION = Number(process.env.POPULATION ?? (SMOKE ? 2 : 4))
|
|
78
|
+
const CONCURRENCY = Number(process.env.CONCURRENCY ?? 8)
|
|
79
|
+
const DOCKER_CONCURRENCY = Number(process.env.DOCKER_CONCURRENCY ?? 6)
|
|
80
|
+
// Default worker: Qwen2.5-7B — the second model of the strategy's measured basis
|
|
81
|
+
// (Llama-3-8B/Qwen2.5-7B). The original smoke worker (Meta-Llama-3-8B-Instruct-Lite)
|
|
82
|
+
// and every other 8B Llama variant were retired from Together serverless
|
|
83
|
+
// (`model_not_available`, verified 2026-07); this is the closest live weak worker.
|
|
84
|
+
const MODEL = process.env.MODEL ?? 'Qwen/Qwen2.5-7B-Instruct-Turbo'
|
|
85
|
+
const BASE = process.env.ROUTER_BASE ?? 'https://api.together.xyz/v1'
|
|
86
|
+
const TEMP = Number(process.env.TEMPERATURE ?? 0.8)
|
|
87
|
+
const MAX_TOKENS = Number(process.env.MAX_TOKENS ?? 2500)
|
|
88
|
+
const DOLLARS = Number(process.env.DOLLARS ?? 15)
|
|
89
|
+
const RUN_DIR = process.env.RUN_DIR ?? join(tmpdir(), `live-improve-campaign-${Date.now()}`)
|
|
90
|
+
|
|
91
|
+
const systemPrompt = 'You are an expert Python programmer.'
|
|
92
|
+
const dockerImage = 'python:3.12-slim'
|
|
93
|
+
const dockerTimeoutMs = Number(process.env.DOCKER_TIMEOUT_MS ?? 20000)
|
|
94
|
+
|
|
95
|
+
// ── Docker: ONE semaphored --network=none exec channel for BOTH judges ───────────────
|
|
96
|
+
// Visible checks (sandboxCheckRunner) and the hidden nonce judge each pipe a python
|
|
97
|
+
// program as `printf '%s' '<b64>' | base64 -d | python3 -`; every container passes
|
|
98
|
+
// through one global semaphore so task-level concurrency cannot stampede the daemon.
|
|
99
|
+
|
|
100
|
+
let dockerInFlight = 0
|
|
101
|
+
const dockerWaiters: Array<() => void> = []
|
|
102
|
+
async function withDockerSlot<T>(fn: () => Promise<T>): Promise<T> {
|
|
103
|
+
if (dockerInFlight >= DOCKER_CONCURRENCY) await new Promise<void>((r) => dockerWaiters.push(r))
|
|
104
|
+
dockerInFlight += 1
|
|
105
|
+
try {
|
|
106
|
+
return await fn()
|
|
107
|
+
} finally {
|
|
108
|
+
dockerInFlight -= 1
|
|
109
|
+
dockerWaiters.shift()?.()
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const containerPrefix = `lic-${process.pid}`
|
|
114
|
+
let containerSeq = 0
|
|
115
|
+
|
|
116
|
+
function reapContainers(): void {
|
|
117
|
+
try {
|
|
118
|
+
const ids = execFileSync('docker', ['ps', '-aq', '--filter', `name=${containerPrefix}`], {
|
|
119
|
+
timeout: 10000,
|
|
120
|
+
})
|
|
121
|
+
.toString()
|
|
122
|
+
.trim()
|
|
123
|
+
if (ids) execFileSync('docker', ['rm', '-f', ...ids.split('\n')], { timeout: 15000 })
|
|
124
|
+
} catch {
|
|
125
|
+
/* reaper is best-effort by design */
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
process.on('SIGINT', () => {
|
|
129
|
+
reapContainers()
|
|
130
|
+
process.exit(130)
|
|
131
|
+
})
|
|
132
|
+
process.on('SIGTERM', () => {
|
|
133
|
+
reapContainers()
|
|
134
|
+
process.exit(143)
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
const dockerBox: CheckExecChannel = {
|
|
138
|
+
exec(command, options) {
|
|
139
|
+
const timeoutMs = options?.timeoutMs ?? dockerTimeoutMs
|
|
140
|
+
return withDockerSlot(
|
|
141
|
+
() =>
|
|
142
|
+
new Promise((resolve, reject) => {
|
|
143
|
+
const name = `${containerPrefix}-${containerSeq++}`
|
|
144
|
+
let settled = false
|
|
145
|
+
const reap = () => execFile('docker', ['rm', '-f', name], () => {})
|
|
146
|
+
const finish = (r: { exitCode: number; stdout: string; stderr: string }) => {
|
|
147
|
+
if (settled) return
|
|
148
|
+
settled = true
|
|
149
|
+
clearTimeout(backstop)
|
|
150
|
+
reap()
|
|
151
|
+
resolve(r)
|
|
152
|
+
}
|
|
153
|
+
const fail = (e: Error) => {
|
|
154
|
+
if (settled) return
|
|
155
|
+
settled = true
|
|
156
|
+
clearTimeout(backstop)
|
|
157
|
+
reap()
|
|
158
|
+
reject(e)
|
|
159
|
+
}
|
|
160
|
+
// execFile's timeout kills the docker CLIENT; a hung container could leave
|
|
161
|
+
// the callback unfired. The backstop guarantees resolution and the named
|
|
162
|
+
// reap kills the stray container.
|
|
163
|
+
const backstop = setTimeout(
|
|
164
|
+
() => finish({ exitCode: 124, stdout: '', stderr: 'timed out (backstop)' }),
|
|
165
|
+
timeoutMs + 3000,
|
|
166
|
+
)
|
|
167
|
+
execFile(
|
|
168
|
+
'docker',
|
|
169
|
+
['run', '--rm', '--name', name, '--network=none', '--cpus=1', '--memory=512m', dockerImage, 'sh', '-c', command],
|
|
170
|
+
{ timeout: timeoutMs, killSignal: 'SIGKILL', maxBuffer: 4 * 1024 * 1024 },
|
|
171
|
+
(err, stdout, stderr) => {
|
|
172
|
+
if (err) {
|
|
173
|
+
const e = err as NodeJS.ErrnoException & { code?: number | string }
|
|
174
|
+
if (e.code === 'ENOENT') {
|
|
175
|
+
fail(new Error('docker binary not found on PATH'))
|
|
176
|
+
return
|
|
177
|
+
}
|
|
178
|
+
if (/cannot connect to the docker daemon|is the docker daemon running|permission denied while trying to connect/i.test(stderr ?? '')) {
|
|
179
|
+
fail(new Error(`docker daemon unreachable: ${(stderr ?? '').slice(0, 200)}`))
|
|
180
|
+
return
|
|
181
|
+
}
|
|
182
|
+
finish({ exitCode: typeof e.code === 'number' ? e.code : 1, stdout: stdout ?? '', stderr: stderr ?? '' })
|
|
183
|
+
return
|
|
184
|
+
}
|
|
185
|
+
finish({ exitCode: 0, stdout: stdout ?? '', stderr: stderr ?? '' })
|
|
186
|
+
},
|
|
187
|
+
)
|
|
188
|
+
}),
|
|
189
|
+
)
|
|
190
|
+
},
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// ── The hidden nonce judge (script-side, AFTER the strategy locks its artifact) ──────
|
|
194
|
+
// Pass requires the per-call nonce sentinel that check() prints AFTER succeeding —
|
|
195
|
+
// exit-0-before-check (a candidate calling sys.exit(0)) is a fail here, where trusting
|
|
196
|
+
// the exit code alone would score it a pass. Nothing from this run reaches the strategy.
|
|
197
|
+
|
|
198
|
+
function buildHiddenProgram(task: HumanEvalTask, candidate: string, nonce: string): string {
|
|
199
|
+
return `${task.prompt}\n${candidate}\n\n${task.test}\n\ncheck(${task.entryPoint})\nprint("HIDDEN-${nonce} PASS")\n`
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async function runHiddenJudge(
|
|
203
|
+
task: HumanEvalTask,
|
|
204
|
+
candidate: string,
|
|
205
|
+
): Promise<{ pass: number; detail?: string }> {
|
|
206
|
+
const nonce = randomBytes(8).toString('hex')
|
|
207
|
+
const b64 = Buffer.from(buildHiddenProgram(task, candidate, nonce), 'utf8').toString('base64')
|
|
208
|
+
const r = await dockerBox.exec(`printf '%s' '${b64}' | base64 -d | python3 -`, {
|
|
209
|
+
timeoutMs: dockerTimeoutMs,
|
|
210
|
+
})
|
|
211
|
+
if (r.exitCode === 0 && r.stdout.includes(`HIDDEN-${nonce} PASS`)) return { pass: 1 }
|
|
212
|
+
return { pass: 0, detail: (r.stderr || r.stdout).slice(-300) || 'timed out (no output)' }
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// ── Scenarios: fixed disjoint slices of HumanEval ─────────────────────────────────────
|
|
216
|
+
|
|
217
|
+
interface HevScenario extends Scenario {
|
|
218
|
+
kind: 'humaneval'
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const taskById = new Map<string, HumanEvalTask>()
|
|
222
|
+
|
|
223
|
+
// ── The real evaluator: one cell = one structuralRollout run + hidden grade ──────────
|
|
224
|
+
|
|
225
|
+
interface CellArtifact {
|
|
226
|
+
taskId: string
|
|
227
|
+
policy: string
|
|
228
|
+
/** Hidden nonce-judge grade of the FINAL selected candidate: {0,1}. */
|
|
229
|
+
pass: number
|
|
230
|
+
detail?: string
|
|
231
|
+
repairStop: string
|
|
232
|
+
shots: number
|
|
233
|
+
completions: number
|
|
234
|
+
authoredChecks: number
|
|
235
|
+
tokens: { input: number; output: number }
|
|
236
|
+
usd: number
|
|
237
|
+
ms: number
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
interface ScoredCandidate {
|
|
241
|
+
candidate: string
|
|
242
|
+
outcome: CheckOutcome
|
|
243
|
+
}
|
|
244
|
+
function recordingRunner(inner: CheckRunner, log: ScoredCandidate[]): CheckRunner {
|
|
245
|
+
return {
|
|
246
|
+
async run(candidate, checks, ctx) {
|
|
247
|
+
const outcome = await inner.run(candidate, checks, ctx)
|
|
248
|
+
log.push({ candidate, outcome })
|
|
249
|
+
return outcome
|
|
250
|
+
},
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Global spend meter (every cell of every phase — baseline, generations, holdout).
|
|
255
|
+
const spend = { cells: 0, llmCalls: 0, tokensIn: 0, tokensOut: 0, usd: 0, hiddenPass: 0 }
|
|
256
|
+
|
|
257
|
+
async function evaluateCell(
|
|
258
|
+
surface: MutableSurface,
|
|
259
|
+
scenario: HevScenario,
|
|
260
|
+
ctx: DispatchContext,
|
|
261
|
+
): Promise<CellArtifact> {
|
|
262
|
+
const policy = parseRolloutPolicy(surface)
|
|
263
|
+
if (!policy) {
|
|
264
|
+
throw new Error(`agent: surface carries no valid rollout policy: ${String(surface).slice(0, 120)}`)
|
|
265
|
+
}
|
|
266
|
+
const task = taskById.get(scenario.id)
|
|
267
|
+
if (!task) throw new Error(`agent: unknown scenario id ${scenario.id}`)
|
|
268
|
+
|
|
269
|
+
const scored: ScoredCandidate[] = []
|
|
270
|
+
const strategy = structuralRollout({
|
|
271
|
+
policy: { ...policy, temperature: TEMP },
|
|
272
|
+
checkRunner: recordingRunner(sandboxCheckRunner({ box: dockerBox }), scored),
|
|
273
|
+
})
|
|
274
|
+
// INERT check: the strategy's harness-verified score channel carries no hidden
|
|
275
|
+
// signal — hidden grading happens below, after the rollout locks its artifact.
|
|
276
|
+
const inertSurface = createVerifierEnvironment({
|
|
277
|
+
name: 'humaneval-inert',
|
|
278
|
+
check: () => ({ passes: 0, total: 1, errored: 0 }),
|
|
279
|
+
})
|
|
280
|
+
const result = (await runAgentic({
|
|
281
|
+
surface: inertSurface,
|
|
282
|
+
task: {
|
|
283
|
+
id: task.taskId,
|
|
284
|
+
systemPrompt,
|
|
285
|
+
userPrompt: basePrompt(task),
|
|
286
|
+
meta: { entryPoint: task.entryPoint },
|
|
287
|
+
},
|
|
288
|
+
routerBaseUrl: BASE,
|
|
289
|
+
routerKey: must('TOGETHER_API_KEY'),
|
|
290
|
+
model: MODEL,
|
|
291
|
+
temperature: TEMP,
|
|
292
|
+
maxTokens: MAX_TOKENS,
|
|
293
|
+
innerTurns: 2,
|
|
294
|
+
strategy,
|
|
295
|
+
// The strategy's documented sizing: k samples + repair rounds + the check-author consult.
|
|
296
|
+
budget: policy.k + policy.repairRounds + 1,
|
|
297
|
+
})) as AgenticRunResult & StructuralRolloutResult
|
|
298
|
+
|
|
299
|
+
// Backend integrity: report REAL usage on every cell (expectUsage 'assert' upstream).
|
|
300
|
+
ctx.cost.observe(result.usd, 'together')
|
|
301
|
+
ctx.cost.observeTokens(result.tokens)
|
|
302
|
+
|
|
303
|
+
const winner = result.selection.find((r) => r.selected)
|
|
304
|
+
if (!winner) {
|
|
305
|
+
throw new Error(`${task.taskId}: no receipt marked selected (repairStop=${result.repairStop})`)
|
|
306
|
+
}
|
|
307
|
+
const rec = scored[winner.candidateIndex]
|
|
308
|
+
if (!rec) {
|
|
309
|
+
throw new Error(
|
|
310
|
+
`${task.taskId}: selected receipt #${winner.candidateIndex} has no recorded candidate (${scored.length} scored)`,
|
|
311
|
+
)
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const hidden = await runHiddenJudge(task, rec.candidate)
|
|
315
|
+
|
|
316
|
+
spend.cells += 1
|
|
317
|
+
spend.llmCalls += result.completions
|
|
318
|
+
spend.tokensIn += result.tokens.input
|
|
319
|
+
spend.tokensOut += result.tokens.output
|
|
320
|
+
spend.usd += result.usd
|
|
321
|
+
spend.hiddenPass += hidden.pass
|
|
322
|
+
|
|
323
|
+
const artifact: CellArtifact = {
|
|
324
|
+
taskId: task.taskId,
|
|
325
|
+
policy: serializeRolloutPolicy(policy),
|
|
326
|
+
pass: hidden.pass,
|
|
327
|
+
...(hidden.detail ? { detail: hidden.detail } : {}),
|
|
328
|
+
repairStop: result.repairStop,
|
|
329
|
+
shots: result.shots,
|
|
330
|
+
completions: result.completions,
|
|
331
|
+
authoredChecks: result.authoredChecks,
|
|
332
|
+
tokens: result.tokens,
|
|
333
|
+
usd: result.usd,
|
|
334
|
+
ms: result.ms,
|
|
335
|
+
}
|
|
336
|
+
appendFileSync(
|
|
337
|
+
join(RUN_DIR, 'cells.jsonl'),
|
|
338
|
+
`${JSON.stringify({ cellId: ctx.cellId, generation: ctx.generation ?? null, ...artifact, detail: undefined })}\n`,
|
|
339
|
+
)
|
|
340
|
+
console.log(
|
|
341
|
+
` [cell ${String(spend.cells).padStart(3)}] ${task.taskId.padEnd(14)} ${artifact.policy.padEnd(38)} hidden=${hidden.pass ? 'PASS' : 'fail'} ${result.repairStop} calls=${result.completions}`,
|
|
342
|
+
)
|
|
343
|
+
return artifact
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// The in-loop judge is a deterministic transcriber of the script-side hidden grade —
|
|
347
|
+
// the grading itself never runs inside the strategy or the proposer's view.
|
|
348
|
+
const hiddenJudge: JudgeConfig<CellArtifact, HevScenario> = {
|
|
349
|
+
name: 'hidden-nonce-judge',
|
|
350
|
+
dimensions: [
|
|
351
|
+
{ key: 'hidden', description: 'HumanEval hidden check() suite (docker --network=none, nonce sentinel)' },
|
|
352
|
+
],
|
|
353
|
+
score: ({ artifact }) => ({
|
|
354
|
+
dimensions: { hidden: artifact.pass },
|
|
355
|
+
composite: artifact.pass,
|
|
356
|
+
notes: artifact.pass ? 'hidden PASS' : `hidden fail: ${(artifact.detail ?? '').slice(0, 160)}`,
|
|
357
|
+
}),
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// ── Reporting helpers (read the library's own result objects; never re-decide) ───────
|
|
361
|
+
|
|
362
|
+
interface CampaignLike {
|
|
363
|
+
cells: Array<{ error?: string | null; judgeScores: Record<string, { composite: number }> }>
|
|
364
|
+
}
|
|
365
|
+
function passStats(campaign: CampaignLike): { passed: number; scored: number; errored: number; rate: number } {
|
|
366
|
+
let passed = 0
|
|
367
|
+
let scored = 0
|
|
368
|
+
let errored = 0
|
|
369
|
+
for (const cell of campaign.cells) {
|
|
370
|
+
if (cell.error) {
|
|
371
|
+
errored += 1
|
|
372
|
+
continue
|
|
373
|
+
}
|
|
374
|
+
scored += 1
|
|
375
|
+
const scores = Object.values(cell.judgeScores)
|
|
376
|
+
const composite = scores.length === 0 ? 0 : scores.reduce((s, j) => s + j.composite, 0) / scores.length
|
|
377
|
+
if (composite >= 0.999) passed += 1
|
|
378
|
+
}
|
|
379
|
+
return { passed, scored, errored, rate: scored > 0 ? passed / scored : 0 }
|
|
380
|
+
}
|
|
381
|
+
const pct = (x: number) => `${(100 * x).toFixed(1)}%`
|
|
382
|
+
|
|
383
|
+
async function main(): Promise<void> {
|
|
384
|
+
must('TOGETHER_API_KEY')
|
|
385
|
+
mkdirSync(RUN_DIR, { recursive: true })
|
|
386
|
+
const started = Date.now()
|
|
387
|
+
|
|
388
|
+
const all = await loadHumanEval(DEV_N + HOLD_N, 0)
|
|
389
|
+
if (all.length !== DEV_N + HOLD_N) {
|
|
390
|
+
throw new Error(`expected ${DEV_N + HOLD_N} tasks, loaded ${all.length}`)
|
|
391
|
+
}
|
|
392
|
+
const devTasks = all.slice(0, DEV_N)
|
|
393
|
+
const holdTasks = all.slice(DEV_N)
|
|
394
|
+
for (const t of all) taskById.set(t.taskId, t)
|
|
395
|
+
|
|
396
|
+
const toScenario = (t: HumanEvalTask): HevScenario => ({ id: t.taskId, kind: 'humaneval' })
|
|
397
|
+
const scenarios = all.map(toScenario)
|
|
398
|
+
const holdoutScenarios = holdTasks.map(toScenario)
|
|
399
|
+
|
|
400
|
+
const baselinePolicy = { k: 5, repairRounds: 2, testgen: 6 }
|
|
401
|
+
const profile: AgentProfile = {
|
|
402
|
+
name: 'humaneval-structural-worker',
|
|
403
|
+
extensions: { [ROLLOUT_POLICY_EXTENSION]: baselinePolicy },
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
console.log('=== LIVE self-improvement campaign · improve() surface rollout-policy ===')
|
|
407
|
+
console.log(` worker: ${MODEL} @ ${BASE} (temp=${TEMP}, maxTokens=${MAX_TOKENS}, innerTurns=2)`)
|
|
408
|
+
console.log(
|
|
409
|
+
` DEV slice : HumanEval index [0, ${DEV_N}) — ${devTasks[0]?.taskId} .. ${devTasks[devTasks.length - 1]?.taskId} (n=${devTasks.length})`,
|
|
410
|
+
)
|
|
411
|
+
console.log(
|
|
412
|
+
` HELD-OUT slice : HumanEval index [${DEV_N}, ${DEV_N + HOLD_N}) — ${holdTasks[0]?.taskId} .. ${holdTasks[holdTasks.length - 1]?.taskId} (n=${holdTasks.length})`,
|
|
413
|
+
)
|
|
414
|
+
console.log(` baseline policy: ${JSON.stringify(baselinePolicy)}`)
|
|
415
|
+
console.log(
|
|
416
|
+
` budget: generations=${GENERATIONS} population<=${POPULATION} reps=1 concurrency=${CONCURRENCY} docker<=${DOCKER_CONCURRENCY} ceiling=$${DOLLARS}`,
|
|
417
|
+
)
|
|
418
|
+
console.log(` gate: library defaultProductionGate (paired bootstrap on held-out, ship iff CI.low > 0.05)`)
|
|
419
|
+
console.log(` runDir: ${RUN_DIR}`)
|
|
420
|
+
console.log(`\n profile BEFORE: ${JSON.stringify(profile)}\n`)
|
|
421
|
+
|
|
422
|
+
const result = await improve<HevScenario, CellArtifact>(profile, [], {
|
|
423
|
+
surface: 'rollout-policy',
|
|
424
|
+
scenarios,
|
|
425
|
+
judge: hiddenJudge,
|
|
426
|
+
agent: evaluateCell,
|
|
427
|
+
budget: {
|
|
428
|
+
generations: GENERATIONS,
|
|
429
|
+
populationSize: POPULATION,
|
|
430
|
+
maxConcurrency: CONCURRENCY,
|
|
431
|
+
holdoutScenarios,
|
|
432
|
+
reps: 1,
|
|
433
|
+
dollars: DOLLARS,
|
|
434
|
+
},
|
|
435
|
+
runDir: RUN_DIR,
|
|
436
|
+
// Deterministic proposer, empty findings channel: the improver's context is ONLY
|
|
437
|
+
// the DEV composites the loop accumulates — no distilled failure text, no trace
|
|
438
|
+
// paths, and (by the loop's own structure) never a held-out cell.
|
|
439
|
+
analyzeGeneration: null,
|
|
440
|
+
})
|
|
441
|
+
|
|
442
|
+
const loop = result.raw.raw
|
|
443
|
+
const wallMin = (Date.now() - started) / 60000
|
|
444
|
+
|
|
445
|
+
console.log('\n── DEV (train) results — what the improver saw ──')
|
|
446
|
+
const baseDev = passStats(loop.baselineCampaign)
|
|
447
|
+
console.log(
|
|
448
|
+
` gen -1 baseline ${serializeRolloutPolicy(structuralRolloutPolicyFromProfile(profile)!).padEnd(38)} DEV ${baseDev.passed}/${baseDev.scored} = ${pct(baseDev.rate)} (errored ${baseDev.errored})`,
|
|
449
|
+
)
|
|
450
|
+
for (const gen of loop.generations) {
|
|
451
|
+
const surfaceByHash = new Map(gen.surfaces.map((s) => [s.surfaceHash, s]))
|
|
452
|
+
const promotedHashes = new Set(gen.record.promoted)
|
|
453
|
+
for (const cand of gen.record.candidates) {
|
|
454
|
+
const s = surfaceByHash.get(cand.surfaceHash)
|
|
455
|
+
const stats = s ? passStats(s.campaign) : undefined
|
|
456
|
+
console.log(
|
|
457
|
+
` gen ${String(gen.record.generationIndex).padStart(2)} ${String(cand.label ?? '').padEnd(16)} ${String(s?.surface ?? '?').padEnd(38)} DEV ${stats ? `${stats.passed}/${stats.scored} = ${pct(stats.rate)} (errored ${stats.errored})` : '?'}${promotedHashes.has(cand.surfaceHash) ? ' [promoted]' : ''}`,
|
|
458
|
+
)
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
console.log(` training winner: ${String(loop.winnerSurface)}${loop.winnerLabel ? ` (${loop.winnerLabel})` : ''}`)
|
|
462
|
+
|
|
463
|
+
console.log('\n── HELD-OUT gate — the library decides ──')
|
|
464
|
+
const baseHold = passStats(loop.baselineOnHoldout)
|
|
465
|
+
const winHold = passStats(loop.winnerOnHoldout)
|
|
466
|
+
console.log(
|
|
467
|
+
` baseline on held-out : ${baseHold.passed}/${baseHold.scored} = ${pct(baseHold.rate)} (errored ${baseHold.errored})`,
|
|
468
|
+
)
|
|
469
|
+
console.log(
|
|
470
|
+
` winner on held-out : ${winHold.passed}/${winHold.scored} = ${pct(winHold.rate)} (errored ${winHold.errored})`,
|
|
471
|
+
)
|
|
472
|
+
console.log(` gate decision: ${result.gateDecision.toUpperCase()} (lift ${result.lift >= 0 ? '+' : ''}${result.lift.toFixed(3)})`)
|
|
473
|
+
for (const reason of loop.gateResult.reasons) console.log(` reason: ${reason}`)
|
|
474
|
+
for (const g of loop.gateResult.contributingGates) {
|
|
475
|
+
console.log(` gate[${g.name}] passed=${g.passed} detail=${JSON.stringify(g.detail).slice(0, 300)}`)
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
console.log('\n── ship/hold outcome ──')
|
|
479
|
+
console.log(` shipped: ${result.shipped}`)
|
|
480
|
+
console.log(` profile AFTER : ${JSON.stringify(result.profile)}`)
|
|
481
|
+
if (result.shipped) {
|
|
482
|
+
console.log(` policy change : ${serializeRolloutPolicy(structuralRolloutPolicyFromProfile(profile)!)} → ${serializeRolloutPolicy(structuralRolloutPolicyFromProfile(result.profile)!)}`)
|
|
483
|
+
} else {
|
|
484
|
+
console.log(' policy change : none (gate held — baseline policy stays)')
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
console.log('\n── spend / provenance ──')
|
|
488
|
+
console.log(
|
|
489
|
+
` cells ${spend.cells} · llm calls ${spend.llmCalls} · tokens ${spend.tokensIn} in / ${spend.tokensOut} out · router-priced $${spend.usd.toFixed(4)} · loop-reported $${result.raw.totalCostUsd.toFixed(4)}`,
|
|
490
|
+
)
|
|
491
|
+
console.log(` wall ${wallMin.toFixed(1)} min · runDir ${RUN_DIR} (cells.jsonl + campaign cells + loop provenance)`)
|
|
492
|
+
reapContainers()
|
|
493
|
+
process.exit(0)
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
main().catch((e) => {
|
|
497
|
+
console.error(e)
|
|
498
|
+
reapContainers()
|
|
499
|
+
process.exit(1)
|
|
500
|
+
})
|
package/src/mbpp-structural.mts
CHANGED
|
@@ -29,7 +29,8 @@ import { execFile, execFileSync } from 'node:child_process'
|
|
|
29
29
|
import { randomBytes } from 'node:crypto'
|
|
30
30
|
import { appendFileSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
|
31
31
|
import { tmpdir } from 'node:os'
|
|
32
|
-
import { join } from 'node:path'
|
|
32
|
+
import { join, resolve } from 'node:path'
|
|
33
|
+
import { fileURLToPath } from 'node:url'
|
|
33
34
|
import { extractCode } from './benchmarks/humaneval'
|
|
34
35
|
import { type PairedLift, pairedLift, pool } from './stats.mts'
|
|
35
36
|
|
|
@@ -107,7 +108,7 @@ export function loadMbpp(limit: number, offset = 0): { tasks: MbppTask[]; droppe
|
|
|
107
108
|
const solveInstruction =
|
|
108
109
|
'Write a Python function for the following task. Output the COMPLETE function definition (plus any imports it needs) inside a single ```python code block. Do not write tests or example calls.'
|
|
109
110
|
|
|
110
|
-
function basePrompt(task: MbppTask): string {
|
|
111
|
+
export function basePrompt(task: MbppTask): string {
|
|
111
112
|
return `${solveInstruction}\n\nTask: ${task.description}\nYour function must satisfy this example test:\n\`\`\`python\n${task.shownAssert}\n\`\`\``
|
|
112
113
|
}
|
|
113
114
|
|
|
@@ -655,8 +656,12 @@ async function main(): Promise<void> {
|
|
|
655
656
|
console.log(`\n VERDICT: ${verdict('full harness', rep)}; ${verdict('selection alone', sel)}`)
|
|
656
657
|
}
|
|
657
658
|
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
659
|
+
// Entrypoint guard (corpus-replay.mts precedent): campaign scripts import this module
|
|
660
|
+
// for loadMbpp/basePrompt; the benchmark itself only runs when executed directly.
|
|
661
|
+
if (process.argv[1] && fileURLToPath(import.meta.url) === resolve(process.argv[1])) {
|
|
662
|
+
main().catch((e) => {
|
|
663
|
+
reapContainers()
|
|
664
|
+
console.error(`mbpp-structural: ${e instanceof Error ? (e.stack ?? e.message) : String(e)}`)
|
|
665
|
+
process.exit(1)
|
|
666
|
+
})
|
|
667
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Autogenerate a self-contained observability dashboard (topology + telemetry) from a stream run.
|
|
3
|
+
Usage: python stream-observe.py <STREAM_DIR> [REF_DIR] > out.html
|
|
4
|
+
Reads <dir>/{ledger.jsonl, events.jsonl, plan.json}. Reproducible: re-run anytime for live state."""
|
|
5
|
+
import json, sys, os
|
|
6
|
+
from collections import Counter
|
|
7
|
+
from html import escape
|
|
8
|
+
|
|
9
|
+
def load(d):
|
|
10
|
+
def jl(p):
|
|
11
|
+
try: return [json.loads(l) for l in open(os.path.join(d,p))]
|
|
12
|
+
except FileNotFoundError: return []
|
|
13
|
+
def jf(p):
|
|
14
|
+
try: return json.load(open(os.path.join(d,p)))
|
|
15
|
+
except FileNotFoundError: return []
|
|
16
|
+
return jl('ledger.jsonl'), jl('events.jsonl'), jf('plan.json')
|
|
17
|
+
|
|
18
|
+
def curve(rows):
|
|
19
|
+
by={}
|
|
20
|
+
for r in rows: by.setdefault(r['streamIndex'],{})[r['arm']]=r
|
|
21
|
+
out=[]; cf=cl=n=0; costF=costL=fires=goodfires=0
|
|
22
|
+
for i in sorted(by):
|
|
23
|
+
f,l=by[i].get('F'),by[i].get('L')
|
|
24
|
+
if not f or not l: continue
|
|
25
|
+
sp=l.get('supervisorPlan') or {}
|
|
26
|
+
if sp.get('fired'): fires+=1
|
|
27
|
+
if sp.get('fired') and len(sp.get('plan') or '')>50: goodfires+=1
|
|
28
|
+
if f.get('error') or l.get('error'):
|
|
29
|
+
out.append({'i':i,'id':f['instanceId'].split('__')[-1],'err':(f.get('error') or l.get('error'))[:16]}); continue
|
|
30
|
+
n+=1; cf+=1 if f.get('hiddenResolved') else 0; cl+=1 if l.get('hiddenResolved') else 0
|
|
31
|
+
costF+=f.get('usd',0); costL+=l.get('usd',0)
|
|
32
|
+
out.append({'i':i,'id':f['instanceId'].split('__')[-1],'F':int(bool(f.get('hiddenResolved'))),'L':int(bool(l.get('hiddenResolved'))),
|
|
33
|
+
'cumF':round(cf/n,3),'cumL':round(cl/n,3),'recall':(l.get('recall') or {}).get('injectedChars',0),
|
|
34
|
+
'supFired':bool(sp.get('fired')),'supReason':sp.get('reason','')})
|
|
35
|
+
return out,{'scored':n,'F':cf,'L':cl,'costF':round(costF,2),'costL':round(costL,2),'fires':fires,'goodfires':goodfires}
|
|
36
|
+
|
|
37
|
+
sd=sys.argv[1]; ref=sys.argv[2] if len(sys.argv)>2 else sd
|
|
38
|
+
lrows,levents,lplan=load(sd)
|
|
39
|
+
rrows,_,_=load(ref)
|
|
40
|
+
lc,lstat=curve(lrows)
|
|
41
|
+
rc,rstat=curve(rrows)
|
|
42
|
+
data={'live':{'events':dict(Counter(e.get('type') for e in levents)) or {'(none)':0},'plan':lplan,'curve':lc,'stat':lstat,'dir':os.path.basename(sd)},
|
|
43
|
+
'ref':{'curve':rc,'stat':rstat,'dir':os.path.basename(ref)}}
|
|
44
|
+
tpl=open(os.path.join(os.path.dirname(__file__),'stream-observe.tpl.html')).read()
|
|
45
|
+
sys.stdout.write(tpl.replace('__DATA__', json.dumps(data)))
|