@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.0",
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.198.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 '../../src/runtime/profile-chat-client'
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 = profileOptimizerModelCall({
110
- profile,
111
- context: 'official optimizer model',
112
- executor,
113
- pricing: budget.pricing,
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 type {
8
- DispatchContext,
9
- OptimizationMethodProvenance,
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 } })