@tangle-network/agent-bench 0.9.0 → 0.9.1
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
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.9.1
|
|
4
|
+
|
|
5
|
+
Omni agent engines use the configured optimizer model through the metered Anthropic endpoint.
|
|
6
|
+
Optimizer requests retain their exact output limits within the shared request ceiling.
|
|
7
|
+
The Runtime dependency follows 0.199.0.
|
|
8
|
+
|
|
3
9
|
## 0.8.34
|
|
4
10
|
|
|
5
11
|
The dependency range requires Knowledge 14.0.3 for the compatible Eval cohort.
|
package/HARNESS.md
CHANGED
|
@@ -109,6 +109,8 @@ Exact score equality is usually the wrong parity criterion for stochastic system
|
|
|
109
109
|
- **AutoResearch / Prime Agent** — agentic search over an editable research surface. Treat the agent, tools, and external evaluator as separate identities.
|
|
110
110
|
- **Meta-Harness** — search over harness or orchestration behavior; preserve the same outcome evaluator.
|
|
111
111
|
- **Omni** — phase-one portfolio search followed by a fresh phase-two optimizer seeded from the best phase-one artifact. The matched phase-one budgets and the phase boundary are part of the protocol.
|
|
112
|
+
Bench enables Eval’s metered Anthropic endpoint for the CLI engines and binds their model to the selected optimizer model.
|
|
113
|
+
Each admitted optimizer request executes with its requested output limit; Eval enforces the configured ceiling.
|
|
112
114
|
- **Trace analysts** — evidence producers. Measure finding quality against labeled traces before using findings to steer search.
|
|
113
115
|
- **Prime Agent RLM and DSPy RLM** — alternative analyst/context engines, not optimization methods by themselves. Compare them on the same trace questions, evidence requirements, context budgets, and downstream decisions.
|
|
114
116
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/agent-bench",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Benchmark adapters and execution for agent-runtime across coding, tool-use, RAG, memory, browser, and terminal tasks.",
|
|
6
6
|
"repository": {
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"@tangle-network/agent-interface": "^2.3.0",
|
|
30
30
|
"@tangle-network/agent-knowledge": "^14.0.3",
|
|
31
31
|
"@tangle-network/sandbox": ">=0.36.4 <0.38.0",
|
|
32
|
-
"@tangle-network/agent-runtime": "^0.
|
|
32
|
+
"@tangle-network/agent-runtime": "^0.199.0"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@arethetypeswrong/cli": "0.18.5",
|
|
@@ -7,8 +7,7 @@ import {
|
|
|
7
7
|
canonicalAgentProfileDigest,
|
|
8
8
|
canonicalCandidateDigest,
|
|
9
9
|
} from '@tangle-network/agent-interface'
|
|
10
|
-
import { profileOptimizerModelCall } from '
|
|
11
|
-
import type { RouterSeam } from '../../src/runtime/supervise/runtime'
|
|
10
|
+
import { profileOptimizerModelCall, type RouterSeam } from '@tangle-network/agent-runtime/kernel'
|
|
12
11
|
|
|
13
12
|
function requiredNonNegativeNumber(
|
|
14
13
|
env: NodeJS.ProcessEnv,
|
|
@@ -73,6 +72,7 @@ export function officialOptimizerModel(options: {
|
|
|
73
72
|
temperature?: number
|
|
74
73
|
reasoningEffort?: NonNullable<AgentProfile['model']>['reasoningEffort']
|
|
75
74
|
callRef?: string
|
|
75
|
+
anthropicEndpoint?: boolean
|
|
76
76
|
complete?: RouterSeam['complete']
|
|
77
77
|
}): OpenAICompatibleOptimizerModel {
|
|
78
78
|
const { env } = options
|
|
@@ -106,12 +106,20 @@ export function officialOptimizerModel(options: {
|
|
|
106
106
|
routerKey: options.apiKey,
|
|
107
107
|
...(options.complete ? { complete: options.complete } : {}),
|
|
108
108
|
}
|
|
109
|
-
const call =
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
109
|
+
const call: OpenAICompatibleOptimizerModel['call'] = (request) =>
|
|
110
|
+
profileOptimizerModelCall({
|
|
111
|
+
// Eval admits the request against the budget; execute its exact output limit.
|
|
112
|
+
profile: {
|
|
113
|
+
...profile,
|
|
114
|
+
model: {
|
|
115
|
+
...profile.model,
|
|
116
|
+
maxVisibleOutputTokens: request.request.maxTokens ?? options.maxOutputTokensPerRequest,
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
context: 'official optimizer model',
|
|
120
|
+
executor,
|
|
121
|
+
pricing: budget.pricing,
|
|
122
|
+
})(request)
|
|
115
123
|
return {
|
|
116
124
|
model: options.model,
|
|
117
125
|
callRef:
|
|
@@ -122,6 +130,7 @@ export function officialOptimizerModel(options: {
|
|
|
122
130
|
})}`,
|
|
123
131
|
call,
|
|
124
132
|
budget,
|
|
133
|
+
...(options.anthropicEndpoint === true ? { anthropicEndpoint: true } : {}),
|
|
125
134
|
}
|
|
126
135
|
}
|
|
127
136
|
|
|
@@ -130,7 +130,7 @@ export type GepaSeatRecipe = Extract<
|
|
|
130
130
|
/** Build the bounded recipe for a seat. The TOTAL inner-evaluation budget is
|
|
131
131
|
* exactly `maxMetricCalls`. The adapter's local callback enforces the sum
|
|
132
132
|
* of per-run limits, and the seat's own dispatch wrapper re-enforces it. */
|
|
133
|
-
export function recipeForSeat(spec: GepaSeatSpec): GepaSeatRecipe {
|
|
133
|
+
export function recipeForSeat(spec: GepaSeatSpec, optimizerModel?: string): GepaSeatRecipe {
|
|
134
134
|
const calls = spec.maxMetricCalls ?? DEFAULT_MAX_METRIC_CALLS
|
|
135
135
|
const cost = spec.maxProposerCostUsd ?? DEFAULT_MAX_PROPOSER_COST_USD
|
|
136
136
|
if (spec.engine === 'gepa') {
|
|
@@ -145,6 +145,7 @@ export function recipeForSeat(spec: GepaSeatSpec): GepaSeatRecipe {
|
|
|
145
145
|
engine,
|
|
146
146
|
maxEvaluations: perExplore,
|
|
147
147
|
maxProposerCostUsd: perRunCost,
|
|
148
|
+
...(engine !== 'gepa' && optimizerModel ? { engineConfig: { model: optimizerModel } } : {}),
|
|
148
149
|
}))
|
|
149
150
|
return {
|
|
150
151
|
kind: 'omni',
|
|
@@ -758,6 +759,7 @@ export function gepaSeatAuthor(config: OuterLoopConfig, deps: GepaSeatDeps): Aut
|
|
|
758
759
|
: officialOptimizerModel({
|
|
759
760
|
env: process.env,
|
|
760
761
|
envPrefix: 'GEPA_OPTIMIZER',
|
|
762
|
+
anthropicEndpoint: spec.engine === 'omni',
|
|
761
763
|
model: process.env.GEPA_OPTIMIZER_MODEL ?? config.arm.driverModel,
|
|
762
764
|
baseUrl:
|
|
763
765
|
process.env.GEPA_OPTIMIZER_BASE_URL ??
|
|
@@ -771,7 +773,7 @@ export function gepaSeatAuthor(config: OuterLoopConfig, deps: GepaSeatDeps): Aut
|
|
|
771
773
|
}))
|
|
772
774
|
const method = factory({
|
|
773
775
|
name: `gepa-seat:${spec.name}`,
|
|
774
|
-
recipe,
|
|
776
|
+
recipe: recipeForSeat(spec, optimizer?.model),
|
|
775
777
|
objective,
|
|
776
778
|
evaluationId: gepaSeatEvaluationId({
|
|
777
779
|
smokeInstanceId: deps.smokeInstanceId,
|
|
@@ -4,9 +4,12 @@ import { mkdir, mkdtemp, readFile, readdir, rm, writeFile } from 'node:fs/promis
|
|
|
4
4
|
import { createServer } from 'node:http'
|
|
5
5
|
import { tmpdir } from 'node:os'
|
|
6
6
|
import { join } from 'node:path'
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
import {
|
|
8
|
+
gepaOptimizationMethod,
|
|
9
|
+
createRunCostLedger,
|
|
10
|
+
fsCampaignStorage,
|
|
11
|
+
type DispatchContext,
|
|
12
|
+
type OptimizationMethodProvenance,
|
|
10
13
|
} from '@tangle-network/agent-eval/campaign'
|
|
11
14
|
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
|
12
15
|
import { ACTIVATION_PREDICATE_RELPATH, parseActivationPredicate } from './activation.mts'
|
|
@@ -89,6 +92,68 @@ describe('validateGepaSeat', () => {
|
|
|
89
92
|
// ---------------------------------------------------------------------------
|
|
90
93
|
|
|
91
94
|
describe('recipeForSeat', () => {
|
|
95
|
+
it.each(['gepa', 'omni'] as const)('transports the real %s optimizer configuration', async (engine) => {
|
|
96
|
+
const runDir = await mkdtemp(join(tmpdir(), 'gepa-seat-transport-'))
|
|
97
|
+
const requests: unknown[] = []
|
|
98
|
+
try {
|
|
99
|
+
const optimizer = officialOptimizerModel({
|
|
100
|
+
env: { OPT_INPUT_USD_PER_MILLION: '1', OPT_CACHED_INPUT_USD_PER_MILLION: '1', OPT_CACHE_WRITE_USD_PER_MILLION: '1', OPT_OUTPUT_USD_PER_MILLION: '1' },
|
|
101
|
+
envPrefix: 'OPT', model: 'fixture-model', baseUrl: 'http://127.0.0.1:1/v1', apiKey: 'fixture-key',
|
|
102
|
+
maxCostUsd: 1, maxOutputTokensPerRequest: 100, anthropicEndpoint: engine === 'omni',
|
|
103
|
+
complete: async (request) => {
|
|
104
|
+
requests.push(request)
|
|
105
|
+
return { model: 'fixture-model', choices: [{ message: { content: 'ok' }, finish_reason: 'stop' }], usage: { prompt_tokens: 2, completion_tokens: 1, cost: 0.000003 } }
|
|
106
|
+
},
|
|
107
|
+
})
|
|
108
|
+
const spec = seat({ engine })
|
|
109
|
+
validateGepaSeat(spec)
|
|
110
|
+
const runtime = {
|
|
111
|
+
python: { implementation: 'CPython', version: '3.12.0' },
|
|
112
|
+
bridge: { package: 'agent-eval-rpc', version: 'fixture', sourceUrl: 'https://github.com/tangle-network/agent-eval', revision: 'fixture', sourceSha256: 'a'.repeat(64) },
|
|
113
|
+
optimizer: { package: 'gepa', version: 'fixture', sourceUrl: 'https://github.com/gepa-ai/gepa', revision: 'fixture', sourceSha256: 'b'.repeat(64) },
|
|
114
|
+
engineModules: [],
|
|
115
|
+
}
|
|
116
|
+
// Only the child optimizer is substituted; the real Eval proxy calls Runtime.
|
|
117
|
+
const bridge = join(runDir, 'transport.mjs')
|
|
118
|
+
await writeFile(bridge, `
|
|
119
|
+
import fs from 'node:fs'
|
|
120
|
+
const input = JSON.parse(fs.readFileSync(process.argv[process.argv.indexOf('--input') + 1], 'utf8'))
|
|
121
|
+
if (input.operation === 'inspect') {
|
|
122
|
+
fs.writeFileSync(process.argv[process.argv.indexOf('--output') + 1], JSON.stringify({ runtime: ${JSON.stringify(runtime)} }))
|
|
123
|
+
} else {
|
|
124
|
+
const omni = input.recipe.kind === 'omni'
|
|
125
|
+
if (omni && input.recipe.explore.some(run => run.engine !== 'gepa' && run.engineConfig.model !== input.modelProxy.model)) throw new Error('wrong engine model')
|
|
126
|
+
const send = maxTokens => fetch(input.modelProxy.baseUrl + (omni ? '/messages' : '/chat/completions'), {
|
|
127
|
+
method: 'POST',
|
|
128
|
+
headers: { 'content-type': 'application/json', authorization: 'Bearer ' + input.modelProxy.apiKey, 'x-api-key': input.modelProxy.apiKey, 'anthropic-version': '2023-06-01' },
|
|
129
|
+
body: JSON.stringify({ model: input.modelProxy.model, max_tokens: maxTokens, messages: [{ role: 'user', content: 'transport proof' }] }),
|
|
130
|
+
})
|
|
131
|
+
const overBudget = await send(101)
|
|
132
|
+
if (overBudget.ok) throw new Error('output ceiling was not enforced')
|
|
133
|
+
await overBudget.text()
|
|
134
|
+
const response = await send(10)
|
|
135
|
+
if (!response.ok) throw new Error('model transport failed: ' + response.status + ' ' + await response.text())
|
|
136
|
+
await response.json()
|
|
137
|
+
throw new Error('transport-handshake-complete')
|
|
138
|
+
}
|
|
139
|
+
`)
|
|
140
|
+
const method = gepaOptimizationMethod({
|
|
141
|
+
recipe: recipeForSeat(spec, optimizer.model), optimizer,
|
|
142
|
+
objective: 'Check transport only', evaluationId: 'bench-transport',
|
|
143
|
+
runner: { command: process.execPath, args: [bridge] },
|
|
144
|
+
})
|
|
145
|
+
await expect(method.optimize({
|
|
146
|
+
baselineSurface: 'seed', trainScenarios: [{ id: 'train', kind: 'fixture' }], selectionScenarios: [{ id: 'selection', kind: 'fixture' }],
|
|
147
|
+
dispatchWithSurface: async () => 'unused',
|
|
148
|
+
judges: [{ name: 'fixture', dimensions: [{ key: 'score', description: 'fixture' }], score: async () => ({ dimensions: { score: 1 }, composite: 1 }) }],
|
|
149
|
+
runDir, seed: 42, runOptions: {}, costLedger: createRunCostLedger({ storage: fsCampaignStorage(), runDir: join(runDir, 'cost') }),
|
|
150
|
+
})).rejects.toThrow('transport-handshake-complete')
|
|
151
|
+
expect(requests).toHaveLength(1)
|
|
152
|
+
} finally {
|
|
153
|
+
await rm(runDir, { recursive: true, force: true })
|
|
154
|
+
}
|
|
155
|
+
})
|
|
156
|
+
|
|
92
157
|
it("'gepa' is one bounded engine run carrying the full budget (default 10)", () => {
|
|
93
158
|
const recipe = recipeForSeat(seat() as GepaSeatSpec)
|
|
94
159
|
expect(recipe).toMatchObject({ kind: 'engine', run: { engine: 'gepa', maxEvaluations: DEFAULT_MAX_METRIC_CALLS } })
|