@tangle-network/agent-bench 0.7.0 → 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 +12 -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
|
@@ -6,13 +6,11 @@
|
|
|
6
6
|
* router 503-stormed. Rule encoded here: gate every arm on the endpoint that
|
|
7
7
|
* arm actually calls; supervisor arms MUST include the router-path probe.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* (single-quoted script, so it is never expanded — let alone logged — in this
|
|
12
|
-
* process).
|
|
9
|
+
* The containing experiment is launched through dotenvx, so probes read the already-scoped key
|
|
10
|
+
* from this process and enter Runtime through one exact AgentProfile.
|
|
13
11
|
*/
|
|
14
12
|
|
|
15
|
-
import {
|
|
13
|
+
import { runBenchRouterTurn } from '../router-turn'
|
|
16
14
|
import type { SecretsEnv } from './arms'
|
|
17
15
|
|
|
18
16
|
export type CapacityProbe = (signal?: AbortSignal) => Promise<boolean>
|
|
@@ -104,10 +102,10 @@ export async function waitForCapacity(gate: EndpointCapacityGate, signal?: Abort
|
|
|
104
102
|
|
|
105
103
|
export interface HttpProbeSpec {
|
|
106
104
|
url: string
|
|
105
|
+
provider: string
|
|
107
106
|
/** NAME of the env var holding the bearer key (resolved in the dotenvx child). */
|
|
108
107
|
apiKeyEnv: string
|
|
109
108
|
model: string
|
|
110
|
-
secrets: SecretsEnv
|
|
111
109
|
/** curl --max-time, seconds. Default 40 (probe-capacity.sh). */
|
|
112
110
|
maxTimeS?: number
|
|
113
111
|
/**
|
|
@@ -117,55 +115,54 @@ export interface HttpProbeSpec {
|
|
|
117
115
|
maxTokens?: number
|
|
118
116
|
}
|
|
119
117
|
|
|
120
|
-
export const ZAI_CODING_ENDPOINT = 'https://api.z.ai/api/coding/paas/v4
|
|
121
|
-
export const ROUTER_ENDPOINT = 'https://router.tangle.tools/v1
|
|
122
|
-
|
|
123
|
-
/** Build the probe request body (probe-body.json semantics). */
|
|
124
|
-
export function probeBody(model: string, maxTokens: number): string {
|
|
125
|
-
return JSON.stringify({
|
|
126
|
-
model,
|
|
127
|
-
messages: [{ role: 'user', content: 'Reply with the single word OK.' }],
|
|
128
|
-
max_tokens: maxTokens,
|
|
129
|
-
temperature: 0,
|
|
130
|
-
})
|
|
131
|
-
}
|
|
118
|
+
export const ZAI_CODING_ENDPOINT = 'https://api.z.ai/api/coding/paas/v4'
|
|
119
|
+
export const ROUTER_ENDPOINT = 'https://router.tangle.tools/v1'
|
|
132
120
|
|
|
133
121
|
/**
|
|
134
|
-
* Generic chat-completions probe: true
|
|
135
|
-
* within the time budget.
|
|
122
|
+
* Generic chat-completions probe through Runtime: true only when the selected model emits `OK`
|
|
123
|
+
* within the time budget.
|
|
136
124
|
*/
|
|
137
125
|
export function httpCapacityProbe(spec: HttpProbeSpec): CapacityProbe {
|
|
138
126
|
if (!/^[A-Z_][A-Z0-9_]*$/.test(spec.apiKeyEnv)) {
|
|
139
127
|
throw new Error(`invalid apiKeyEnv name: ${spec.apiKeyEnv}`)
|
|
140
128
|
}
|
|
141
|
-
const body = probeBody(spec.model, spec.maxTokens ?? 8000)
|
|
142
129
|
const maxTime = spec.maxTimeS ?? 40
|
|
143
130
|
return async (signal?: AbortSignal) => {
|
|
144
131
|
signal?.throwIfAborted()
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
132
|
+
const routerKey = process.env[spec.apiKeyEnv]
|
|
133
|
+
if (!routerKey) throw new Error(`${spec.apiKeyEnv} is required; launch through dotenvx`)
|
|
134
|
+
const turn = await runBenchRouterTurn(
|
|
135
|
+
{
|
|
136
|
+
routerBaseUrl: spec.url.replace(/\/chat\/completions\/?$/u, ''),
|
|
137
|
+
routerKey,
|
|
138
|
+
profile: {
|
|
139
|
+
name: `capacity-${spec.model}`,
|
|
140
|
+
harness: 'cli-base',
|
|
141
|
+
model: {
|
|
142
|
+
provider: spec.provider,
|
|
143
|
+
default: spec.model,
|
|
144
|
+
metadata: { temperature: 0, maxTokens: spec.maxTokens ?? 8000 },
|
|
145
|
+
},
|
|
146
|
+
prompt: { systemPrompt: 'Reply with the single word OK.' },
|
|
147
|
+
},
|
|
148
|
+
timeoutMs: maxTime * 1000,
|
|
149
|
+
signal,
|
|
150
|
+
},
|
|
151
|
+
'Capacity probe.',
|
|
152
|
+
)
|
|
153
|
+
return turn.finalText.trim() === 'OK'
|
|
163
154
|
}
|
|
164
155
|
}
|
|
165
156
|
|
|
166
157
|
/** probe-capacity.sh's z.ai coding-plan probe (the WORKER path). */
|
|
167
158
|
export function zaiCodingProbe(secrets: SecretsEnv, model = 'glm-5.2'): CapacityProbe {
|
|
168
|
-
|
|
159
|
+
void secrets
|
|
160
|
+
return httpCapacityProbe({
|
|
161
|
+
url: ZAI_CODING_ENDPOINT,
|
|
162
|
+
apiKeyEnv: 'ZAI_API_KEY',
|
|
163
|
+
provider: 'zai',
|
|
164
|
+
model,
|
|
165
|
+
})
|
|
169
166
|
}
|
|
170
167
|
|
|
171
168
|
/**
|
|
@@ -173,7 +170,13 @@ export function zaiCodingProbe(secrets: SecretsEnv, model = 'glm-5.2'): Capacity
|
|
|
173
170
|
* Supervisor arms must gate on this; probing only z.ai is the proven blind spot.
|
|
174
171
|
*/
|
|
175
172
|
export function routerProbe(secrets: SecretsEnv, model = 'glm-5.2'): CapacityProbe {
|
|
176
|
-
|
|
173
|
+
void secrets
|
|
174
|
+
return httpCapacityProbe({
|
|
175
|
+
url: ROUTER_ENDPOINT,
|
|
176
|
+
apiKeyEnv: 'TANGLE_API_KEY',
|
|
177
|
+
provider: 'tangle-router',
|
|
178
|
+
model,
|
|
179
|
+
})
|
|
177
180
|
}
|
|
178
181
|
|
|
179
182
|
/** The gates an arm must pass, by kind: solo → worker path; supervisor → BOTH paths. */
|
|
@@ -12,9 +12,8 @@
|
|
|
12
12
|
* gpt-5.5 / opus-4.8 slot in by editing the analyst spec list, never by a
|
|
13
13
|
* hardcoded requirement on an unrouted model.
|
|
14
14
|
* - analysts are blind: same bundle, no cross-talk, independent calls.
|
|
15
|
-
* -
|
|
16
|
-
*
|
|
17
|
-
* writes its banner to stdout, so stdout is only trusted for the marker).
|
|
15
|
+
* - the containing run is launched through dotenvx; every analyst enters Runtime through one
|
|
16
|
+
* exact AgentProfile and its event record is retained with the response.
|
|
18
17
|
*/
|
|
19
18
|
|
|
20
19
|
import { mkdir, readdir, readFile, writeFile } from 'node:fs/promises'
|
|
@@ -22,7 +21,7 @@ import { join } from 'node:path'
|
|
|
22
21
|
import { type AnalystFinding, makeFinding } from '@tangle-network/agent-eval'
|
|
23
22
|
import { findSupervisorRunDir, type SecretsEnv } from './arms'
|
|
24
23
|
import { ROUTER_ENDPOINT, sleepWithSignal } from './capacity'
|
|
25
|
-
import {
|
|
24
|
+
import { runBenchRouterTurn } from '../router-turn'
|
|
26
25
|
|
|
27
26
|
// ---------------------------------------------------------------------------
|
|
28
27
|
// Analyst specs.
|
|
@@ -32,6 +31,8 @@ export interface AnalystSpec {
|
|
|
32
31
|
/** Stable label, e.g. 'glm-5.2#1'. Used in fusion attribution + filenames. */
|
|
33
32
|
id: string
|
|
34
33
|
model: string
|
|
34
|
+
/** Provider identity stamped into the exact profile. Default: tangle-router. */
|
|
35
|
+
provider?: string
|
|
35
36
|
/** Chat-completions endpoint. Default: the Tangle router. */
|
|
36
37
|
url?: string
|
|
37
38
|
/** NAME of the env var holding the bearer key (resolved in the dotenvx child). */
|
|
@@ -247,8 +248,7 @@ export function parseAnalystFindings(text: string): AnalystRawFinding[] {
|
|
|
247
248
|
})
|
|
248
249
|
}
|
|
249
250
|
|
|
250
|
-
/**
|
|
251
|
-
* the child; response body lands in `outFile`, never on shared stdout).
|
|
251
|
+
/** Run one blind analyst through Runtime's exact-profile Router boundary.
|
|
252
252
|
* ONE retry on transport failure (5xx/524/timeout) — an edge flake was
|
|
253
253
|
* observed live in the calibration smoke; a parse failure is NOT retried
|
|
254
254
|
* (same prompt, same model ⇒ same bad shape). */
|
|
@@ -265,60 +265,63 @@ export async function runAnalyst(
|
|
|
265
265
|
return { analystId: spec.id, model: spec.model, ok: false, findings: [], error: `invalid apiKeyEnv name: ${apiKeyEnv}` }
|
|
266
266
|
}
|
|
267
267
|
const url = spec.url ?? ROUTER_ENDPOINT
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
268
|
+
const routerKey = process.env[apiKeyEnv]
|
|
269
|
+
if (!routerKey) {
|
|
270
|
+
return {
|
|
271
|
+
analystId: spec.id,
|
|
272
|
+
model: spec.model,
|
|
273
|
+
ok: false,
|
|
274
|
+
findings: [],
|
|
275
|
+
error: `${apiKeyEnv} is required; launch through dotenvx`,
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
void secrets
|
|
274
279
|
opts.signal?.throwIfAborted()
|
|
275
280
|
await mkdir(scratchDir, { recursive: true })
|
|
276
281
|
const outFile = join(scratchDir, `analyst-${spec.id.replace(/[^a-zA-Z0-9._-]/g, '_')}.response.json`)
|
|
277
282
|
const timeoutMs = opts.timeoutMs ?? 600_000
|
|
278
|
-
// Body via stdin (--data @-) so the payload never sits on a command line;
|
|
279
|
-
// the HTTP code is marker-anchored on stdout (dotenvx banners share stdout).
|
|
280
|
-
const script =
|
|
281
|
-
`curl -sS -o "$DIAG_OUT" -w "HTTP_CODE=%{http_code}" --max-time ${Math.ceil(timeoutMs / 1000) - 20} ` +
|
|
282
|
-
`-X POST "$DIAG_URL" -H "Authorization: Bearer $${apiKeyEnv}" -H "Content-Type: application/json" --data @-`
|
|
283
|
-
const argv = ['run', ...secrets.envFiles.flatMap((f) => ['-f', f]), '--', 'bash', '-c', script]
|
|
284
283
|
const attempts = 1 + (opts.retries ?? 1)
|
|
285
284
|
let transportError = ''
|
|
286
|
-
let
|
|
287
|
-
|
|
285
|
+
let content = ''
|
|
286
|
+
let tokens: AnalystReport['tokens']
|
|
287
|
+
for (let attempt = 0; attempt < attempts && content.length === 0; attempt++) {
|
|
288
288
|
opts.signal?.throwIfAborted()
|
|
289
289
|
if (attempt > 0) await sleepWithSignal(opts.retryDelayMs ?? 5_000, opts.signal)
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
290
|
+
try {
|
|
291
|
+
const turn = await runBenchRouterTurn(
|
|
292
|
+
{
|
|
293
|
+
routerBaseUrl: url.replace(/\/chat\/completions\/?$/u, ''),
|
|
294
|
+
routerKey,
|
|
295
|
+
profile: {
|
|
296
|
+
name: `diagnosis-${spec.id}`,
|
|
297
|
+
harness: 'cli-base',
|
|
298
|
+
model: {
|
|
299
|
+
provider: spec.provider ?? 'tangle-router',
|
|
300
|
+
default: spec.model,
|
|
301
|
+
metadata: {
|
|
302
|
+
temperature: spec.temperature ?? 0,
|
|
303
|
+
maxTokens: spec.maxTokens ?? 16_000,
|
|
304
|
+
},
|
|
305
|
+
},
|
|
306
|
+
prompt: { systemPrompt: 'Diagnose the supplied run evidence as an independent analyst.' },
|
|
307
|
+
},
|
|
308
|
+
timeoutMs,
|
|
309
|
+
signal: opts.signal,
|
|
310
|
+
},
|
|
311
|
+
analystPrompt(bundle),
|
|
312
|
+
)
|
|
313
|
+
content = turn.finalText
|
|
314
|
+
if (turn.usage.tokensKnown !== false) {
|
|
315
|
+
tokens = { input: turn.usage.input, output: turn.usage.output }
|
|
316
|
+
}
|
|
317
|
+
await writeFile(outFile, JSON.stringify(turn, null, 2))
|
|
318
|
+
} catch (cause) {
|
|
319
|
+
transportError = `router call failed (${(cause as Error).message}, attempt=${attempt + 1}/${attempts})`
|
|
303
320
|
}
|
|
304
321
|
}
|
|
305
|
-
if (
|
|
322
|
+
if (content.length === 0 && transportError) {
|
|
306
323
|
return { analystId: spec.id, model: spec.model, ok: false, findings: [], error: transportError }
|
|
307
324
|
}
|
|
308
|
-
let content = ''
|
|
309
|
-
let tokens: AnalystReport['tokens']
|
|
310
|
-
try {
|
|
311
|
-
const parsed = JSON.parse(await readFile(outFile, 'utf8')) as {
|
|
312
|
-
choices?: Array<{ message?: { content?: string } }>
|
|
313
|
-
usage?: { prompt_tokens?: number; completion_tokens?: number }
|
|
314
|
-
}
|
|
315
|
-
content = parsed.choices?.[0]?.message?.content ?? ''
|
|
316
|
-
if (parsed.usage) {
|
|
317
|
-
tokens = { input: parsed.usage.prompt_tokens ?? 0, output: parsed.usage.completion_tokens ?? 0 }
|
|
318
|
-
}
|
|
319
|
-
} catch (cause) {
|
|
320
|
-
return { analystId: spec.id, model: spec.model, ok: false, findings: [], error: `unparseable response body: ${(cause as Error).message}` }
|
|
321
|
-
}
|
|
322
325
|
if (content.trim().length === 0) {
|
|
323
326
|
return { analystId: spec.id, model: spec.model, ok: false, findings: [], error: 'empty content (max_tokens starvation?)', tokens }
|
|
324
327
|
}
|
|
@@ -31,7 +31,12 @@ import {
|
|
|
31
31
|
type SupervisorArmResult,
|
|
32
32
|
} from './arms.ts'
|
|
33
33
|
import { containerName, materializeWorkspace } from './materialize.ts'
|
|
34
|
-
import {
|
|
34
|
+
import {
|
|
35
|
+
gatesForArmKind,
|
|
36
|
+
httpCapacityProbe,
|
|
37
|
+
probeWindow,
|
|
38
|
+
waitForCapacity,
|
|
39
|
+
} from './capacity.ts'
|
|
35
40
|
import { run, runOk } from './proc.ts'
|
|
36
41
|
import {
|
|
37
42
|
createSerializedJudge,
|
|
@@ -1065,15 +1070,6 @@ describe('capacity gate', () => {
|
|
|
1065
1070
|
expect(await waitForCapacity(gate)).toBe(false)
|
|
1066
1071
|
})
|
|
1067
1072
|
|
|
1068
|
-
it('probe body defaults to max_tokens 8000 (glm-5.2 starves below it)', () => {
|
|
1069
|
-
expect(JSON.parse(probeBody('glm-5.2', 8000))).toEqual({
|
|
1070
|
-
model: 'glm-5.2',
|
|
1071
|
-
messages: [{ role: 'user', content: 'Reply with the single word OK.' }],
|
|
1072
|
-
max_tokens: 8000,
|
|
1073
|
-
temperature: 0,
|
|
1074
|
-
})
|
|
1075
|
-
})
|
|
1076
|
-
|
|
1077
1073
|
it('supervisor arms gate on BOTH worker and router paths; solo on worker only', () => {
|
|
1078
1074
|
const secrets = { secretsDir: '/dev/null', envFiles: ['agent-state.env'] }
|
|
1079
1075
|
expect(gatesForArmKind('solo', secrets).map((g) => g.name)).toEqual(['z.ai-coding'])
|
|
@@ -1081,9 +1077,13 @@ describe('capacity gate', () => {
|
|
|
1081
1077
|
})
|
|
1082
1078
|
|
|
1083
1079
|
it('rejects a non-env-shaped key name (no value smuggling)', () => {
|
|
1084
|
-
const secrets = { secretsDir: '/dev/null', envFiles: [] }
|
|
1085
1080
|
expect(() =>
|
|
1086
|
-
httpCapacityProbe({
|
|
1081
|
+
httpCapacityProbe({
|
|
1082
|
+
url: 'https://x',
|
|
1083
|
+
apiKeyEnv: 'k; cat /etc/passwd',
|
|
1084
|
+
provider: 'test',
|
|
1085
|
+
model: 'm',
|
|
1086
|
+
}),
|
|
1087
1087
|
).toThrow(/apiKeyEnv/)
|
|
1088
1088
|
})
|
|
1089
1089
|
})
|
|
@@ -41,6 +41,7 @@ import { mkdir, readFile, readdir, rename, rm, writeFile } from 'node:fs/promise
|
|
|
41
41
|
import { dirname, join } from 'node:path'
|
|
42
42
|
import {
|
|
43
43
|
type DispatchContext,
|
|
44
|
+
type ComparisonCost,
|
|
44
45
|
createRunCostLedger,
|
|
45
46
|
fsCampaignStorage,
|
|
46
47
|
type GepaOptimizationMethodConfig,
|
|
@@ -233,7 +234,7 @@ export function innerSmokeJudge(): JudgeConfig<SmokeVerdict, GepaSeatScenario> {
|
|
|
233
234
|
// ---------------------------------------------------------------------------
|
|
234
235
|
|
|
235
236
|
export const GEPA_PYTHON_INSTALL_HINT =
|
|
236
|
-
'install `agent-eval-rpc==0.
|
|
237
|
+
'install `agent-eval-rpc==0.144.6`, then install ' +
|
|
237
238
|
'`gepa[full] @ git+https://github.com/gepa-ai/gepa.git@f919db0a622e2e9f9204779b81fe00cc1b2d808f`'
|
|
238
239
|
|
|
239
240
|
export type GepaMethodFactory = (
|
|
@@ -326,6 +327,7 @@ export interface GepaSeatInnerRun {
|
|
|
326
327
|
tokenUsage: OptimizationTokenUsage
|
|
327
328
|
artifactDir: string
|
|
328
329
|
totalCostUsd: number
|
|
330
|
+
costProvenance: ComparisonCost['costProvenance']
|
|
329
331
|
accountingComplete: boolean
|
|
330
332
|
incompleteReasons: string[]
|
|
331
333
|
durationMs: number
|
|
@@ -532,10 +534,7 @@ function completeProvenance(
|
|
|
532
534
|
}
|
|
533
535
|
}
|
|
534
536
|
|
|
535
|
-
function assertCompleteCost(
|
|
536
|
-
cost: { totalCostUsd: number; accountingComplete: boolean; incompleteReasons: string[] },
|
|
537
|
-
seatName: string,
|
|
538
|
-
): void {
|
|
537
|
+
function assertCompleteCost(cost: ComparisonCost, seatName: string): void {
|
|
539
538
|
if (!Number.isFinite(cost.totalCostUsd) || cost.totalCostUsd < 0) {
|
|
540
539
|
throw new Error(`gepa seat '${seatName}': optimizer returned invalid total cost`)
|
|
541
540
|
}
|
|
@@ -550,6 +549,13 @@ function assertCompleteCost(
|
|
|
550
549
|
if (cost.accountingComplete !== (cost.incompleteReasons.length === 0)) {
|
|
551
550
|
throw new Error(`gepa seat '${seatName}': optimizer returned inconsistent cost accounting`)
|
|
552
551
|
}
|
|
552
|
+
if (cost.costProvenance.kind === 'uncaptured') {
|
|
553
|
+
if (cost.accountingComplete) {
|
|
554
|
+
throw new Error(`gepa seat '${seatName}': complete cost cannot have uncaptured provenance`)
|
|
555
|
+
}
|
|
556
|
+
} else if (cost.costProvenance.usd !== cost.totalCostUsd) {
|
|
557
|
+
throw new Error(`gepa seat '${seatName}': cost provenance does not match total cost`)
|
|
558
|
+
}
|
|
553
559
|
if (!cost.accountingComplete) {
|
|
554
560
|
throw new Error(
|
|
555
561
|
`gepa seat '${seatName}': cost accounting is incomplete: ${cost.incompleteReasons.join('; ') || 'no reason provided'}`,
|
|
@@ -823,6 +829,7 @@ export function gepaSeatAuthor(config: OuterLoopConfig, deps: GepaSeatDeps): Aut
|
|
|
823
829
|
: null,
|
|
824
830
|
...completeProvenance(result.provenance, spec.name),
|
|
825
831
|
totalCostUsd: result.cost.totalCostUsd,
|
|
832
|
+
costProvenance: result.cost.costProvenance,
|
|
826
833
|
accountingComplete: result.cost.accountingComplete,
|
|
827
834
|
incompleteReasons: [...result.cost.incompleteReasons],
|
|
828
835
|
durationMs: Date.now() - started,
|
|
@@ -34,6 +34,7 @@ import {
|
|
|
34
34
|
import { defaultRound4Config, type OuterLoopConfig } from './outer-loop.mts'
|
|
35
35
|
import { fanOutLoopsGenerator, type ProposerSpec, type SmokeRunner, type SmokeVerdict } from './proposer-fanout.mts'
|
|
36
36
|
import { captureProposerProvenance } from './proposer-provenance.mts'
|
|
37
|
+
import { officialOptimizerModel } from '../official-optimizer-config.mts'
|
|
37
38
|
import { runOk } from './proc.ts'
|
|
38
39
|
|
|
39
40
|
const SURFACE = 'extensions/pi/prompts/worker-coding-system.md'
|
|
@@ -370,6 +371,7 @@ describe('recordGepaSeatInnerRun', () => {
|
|
|
370
371
|
tokenUsage: result.tokenUsage,
|
|
371
372
|
artifactDir: result.artifactDir,
|
|
372
373
|
totalCostUsd: 0.25,
|
|
374
|
+
costProvenance: { kind: 'observed', usd: 0.25 },
|
|
373
375
|
accountingComplete: true,
|
|
374
376
|
incompleteReasons: [],
|
|
375
377
|
durationMs: 5,
|
|
@@ -426,24 +428,26 @@ describe('recordGepaSeatInnerRun', () => {
|
|
|
426
428
|
// ---------------------------------------------------------------------------
|
|
427
429
|
|
|
428
430
|
const fakeCtx = {} as unknown as DispatchContext
|
|
429
|
-
const
|
|
431
|
+
const testOptimizerEnv: NodeJS.ProcessEnv = {
|
|
432
|
+
TEST_OPTIMIZER_INPUT_USD_PER_MILLION: '1',
|
|
433
|
+
TEST_OPTIMIZER_CACHED_INPUT_USD_PER_MILLION: '0.1',
|
|
434
|
+
TEST_OPTIMIZER_CACHE_WRITE_USD_PER_MILLION: '1.25',
|
|
435
|
+
TEST_OPTIMIZER_OUTPUT_USD_PER_MILLION: '5',
|
|
436
|
+
TEST_OPTIMIZER_MAX_REQUESTS: '10',
|
|
437
|
+
TEST_OPTIMIZER_MAX_REQUEST_BYTES: '100000',
|
|
438
|
+
TEST_OPTIMIZER_MAX_RESPONSE_BYTES: '100000',
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
const testOptimizer = officialOptimizerModel({
|
|
442
|
+
env: testOptimizerEnv,
|
|
443
|
+
envPrefix: 'TEST_OPTIMIZER',
|
|
430
444
|
model: 'optimizer-model',
|
|
431
445
|
baseUrl: 'http://127.0.0.1:1/v1',
|
|
432
446
|
apiKey: 'optimizer-key',
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
maxResponseBytes: 100_000,
|
|
438
|
-
maxOutputTokensPerRequest: 2_000,
|
|
439
|
-
pricing: {
|
|
440
|
-
inputUsdPerMillion: 1,
|
|
441
|
-
cachedInputUsdPerMillion: 0.1,
|
|
442
|
-
cacheWriteUsdPerMillion: 1.25,
|
|
443
|
-
outputUsdPerMillion: 5,
|
|
444
|
-
},
|
|
445
|
-
},
|
|
446
|
-
}
|
|
447
|
+
maxCostUsd: 1,
|
|
448
|
+
maxOutputTokensPerRequest: 2_000,
|
|
449
|
+
callRef: 'test:optimizer-model',
|
|
450
|
+
})
|
|
447
451
|
|
|
448
452
|
const fullProvenance = (
|
|
449
453
|
runId = 'gepa-run',
|
|
@@ -504,7 +508,12 @@ const fakeGepaFactory =
|
|
|
504
508
|
}
|
|
505
509
|
return {
|
|
506
510
|
winnerSurface: best.surface,
|
|
507
|
-
cost: {
|
|
511
|
+
cost: {
|
|
512
|
+
totalCostUsd: 0.125,
|
|
513
|
+
costProvenance: { kind: 'observed', usd: 0.125 },
|
|
514
|
+
accountingComplete: true,
|
|
515
|
+
incompleteReasons: [],
|
|
516
|
+
},
|
|
508
517
|
durationMs: 1,
|
|
509
518
|
provenance: fullProvenance(),
|
|
510
519
|
}
|
|
@@ -737,7 +746,12 @@ describe('fanOutLoopsGenerator with the gepa seat', () => {
|
|
|
737
746
|
])
|
|
738
747
|
return {
|
|
739
748
|
winnerSurface: candidateA,
|
|
740
|
-
cost: {
|
|
749
|
+
cost: {
|
|
750
|
+
totalCostUsd: 0,
|
|
751
|
+
costProvenance: { kind: 'observed', usd: 0 },
|
|
752
|
+
accountingComplete: true,
|
|
753
|
+
incompleteReasons: [],
|
|
754
|
+
},
|
|
741
755
|
durationMs: 1,
|
|
742
756
|
provenance: fullProvenance('parallel-gepa', { evaluationCount: 2 }),
|
|
743
757
|
}
|
|
@@ -823,6 +837,7 @@ describe('fanOutLoopsGenerator with the gepa seat', () => {
|
|
|
823
837
|
winnerSurface: WINNER,
|
|
824
838
|
cost: {
|
|
825
839
|
totalCostUsd: 0.25,
|
|
840
|
+
costProvenance: { kind: 'uncaptured', usd: null },
|
|
826
841
|
accountingComplete: false,
|
|
827
842
|
incompleteReasons: ['optimizer model receipt missing'],
|
|
828
843
|
},
|
|
@@ -873,7 +888,12 @@ describe('fanOutLoopsGenerator with the gepa seat', () => {
|
|
|
873
888
|
if (rejected) throw rejected.reason
|
|
874
889
|
return {
|
|
875
890
|
winnerSurface: SEED,
|
|
876
|
-
cost: {
|
|
891
|
+
cost: {
|
|
892
|
+
totalCostUsd: 0,
|
|
893
|
+
costProvenance: { kind: 'uncaptured', usd: null },
|
|
894
|
+
accountingComplete: false,
|
|
895
|
+
incompleteReasons: [],
|
|
896
|
+
},
|
|
877
897
|
durationMs: 1,
|
|
878
898
|
provenance: fullProvenance('runaway'),
|
|
879
899
|
}
|
|
@@ -1009,24 +1029,16 @@ describe('integration: real adapter roundtrip', () => {
|
|
|
1009
1029
|
},
|
|
1010
1030
|
smokeInstanceId: 'astropy__astropy-13033',
|
|
1011
1031
|
scoreSplit: null,
|
|
1012
|
-
gepaOptimizer: {
|
|
1032
|
+
gepaOptimizer: officialOptimizerModel({
|
|
1033
|
+
env: testOptimizerEnv,
|
|
1034
|
+
envPrefix: 'TEST_OPTIMIZER',
|
|
1013
1035
|
model: 'test-optimizer',
|
|
1014
1036
|
baseUrl: `http://127.0.0.1:${address.port}/v1`,
|
|
1015
1037
|
apiKey: 'local-test-key',
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
maxResponseBytes: 100_000,
|
|
1021
|
-
maxOutputTokensPerRequest: 2_000,
|
|
1022
|
-
pricing: {
|
|
1023
|
-
inputUsdPerMillion: 1,
|
|
1024
|
-
cachedInputUsdPerMillion: 0.1,
|
|
1025
|
-
cacheWriteUsdPerMillion: 1.25,
|
|
1026
|
-
outputUsdPerMillion: 5,
|
|
1027
|
-
},
|
|
1028
|
-
},
|
|
1029
|
-
},
|
|
1038
|
+
maxCostUsd: 1,
|
|
1039
|
+
maxOutputTokensPerRequest: 2_000,
|
|
1040
|
+
callRef: 'test:integration-optimizer',
|
|
1041
|
+
}),
|
|
1030
1042
|
},
|
|
1031
1043
|
)
|
|
1032
1044
|
const result = await gen.generate({
|