@tangle-network/agent-bench 0.8.10 → 0.8.12
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 +10 -0
- package/package.json +6 -6
- package/src/aec-gate.mts +0 -238
- package/src/atom-humaneval.mts +0 -218
- package/src/david-attribution.mts +0 -97
- package/src/david-goliath.mts +0 -168
- package/src/decoder-live.mts +0 -133
- package/src/diverse-gate.mjs +0 -112
- package/src/hev-eval.mts +0 -101
- package/src/hev-improve.mts +0 -245
- package/src/humaneval-object-ablation.mts +0 -239
- package/src/trata-gate.mts +0 -243
package/src/trata-gate.mts
DELETED
|
@@ -1,243 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Trata hedge-bench gate — direct router completions (no sandbox/opencode).
|
|
3
|
-
*
|
|
4
|
-
* Trata is a text-analysis benchmark: each task is a financial analysis brief
|
|
5
|
-
* over embedded source docs. The worker is a single router completion (not an
|
|
6
|
-
* agentic loop), which is appropriate here: tasks are scored by an LLM judge on
|
|
7
|
-
* theme coverage, not on code execution or tool use.
|
|
8
|
-
*
|
|
9
|
-
* Usage:
|
|
10
|
-
* BENCH=trata-hedge WORKER_MODEL=deepseek-v4-flash JUDGE_MODEL=gemini-2.5-flash \
|
|
11
|
-
* TRATA_BENCH_ROOT=/tmp/trata-hedge-bench N=10 CONCURRENCY=5 \
|
|
12
|
-
* dotenvx run -f ~/company/devops/secrets/agent-state.env -- \
|
|
13
|
-
* pnpm exec tsx bench/src/trata-gate.mts
|
|
14
|
-
*
|
|
15
|
-
* Key env vars:
|
|
16
|
-
* BENCH adapter key (default: trata-hedge)
|
|
17
|
-
* WORKER_MODEL model for the analysis completion (default: deepseek-v4-flash)
|
|
18
|
-
* JUDGE_MODEL model for the 3-stage judge (default: gemini-2.5-pro)
|
|
19
|
-
* TRATA_BENCH_ROOT path to cloned trata-hedge-bench (default: /tmp/trata-hedge-bench)
|
|
20
|
-
* N how many tasks to run (default: 10; 0 = all 102)
|
|
21
|
-
* CONCURRENCY parallel worker slots (default: 5)
|
|
22
|
-
* IDS comma-separated task ids to run a specific subset
|
|
23
|
-
* ROUTER_BASE router base URL (default: https://router.tangle.tools/v1)
|
|
24
|
-
* CORPUS path to write JSONL run records (optional)
|
|
25
|
-
*
|
|
26
|
-
* Score is 0–4 / 4 = 0–100%; resolved = 4/4 (all themes + synthesis = sparse reward).
|
|
27
|
-
* Partial credit (1-3) is reported in the score distribution.
|
|
28
|
-
*/
|
|
29
|
-
|
|
30
|
-
import { appendFileSync } from 'node:fs'
|
|
31
|
-
import { resolveAdapter } from './adapters'
|
|
32
|
-
import type { BenchScore, BenchTask } from './benchmarks/types'
|
|
33
|
-
import { runBenchRouterTurn } from './router-turn'
|
|
34
|
-
import { runPool } from './run-pool'
|
|
35
|
-
|
|
36
|
-
function must(name: string): string {
|
|
37
|
-
const v = process.env[name]
|
|
38
|
-
if (!v) throw new Error(`env ${name} is required`)
|
|
39
|
-
return v
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// GEPA-optimised surface — +8.6pp holdout lift over the minimal baseline on deepseek-v4-flash
|
|
43
|
-
// (2 independent runs, CI [0, 12.5pp]; gate holds for caution, surface ships as default).
|
|
44
|
-
const ANALYST_SYSTEM = process.env.SYSTEM_PROMPT ?? [
|
|
45
|
-
'You are a senior financial analyst producing a structured investment memo.',
|
|
46
|
-
'Begin your response with exactly "ANALYSIS:" on its own line (nothing before it), then write your full analysis.',
|
|
47
|
-
'Structure your analysis with clearly labeled sections for each distinct analytical theme',
|
|
48
|
-
'(e.g., valuation, capital allocation, competitive dynamics, risk factors)',
|
|
49
|
-
'so that no major investment consideration is merged or omitted.',
|
|
50
|
-
'Benchmark the company explicitly against named sector peers, citing specific metrics such as',
|
|
51
|
-
'EV/EBITDA, P/E ratios, margin differentials, and growth premiums from the peer financial files.',
|
|
52
|
-
'Every factual claim must cite the filename (e.g., "per earnings_call/apo_q4_2025_earnings_call.txt").',
|
|
53
|
-
'Identify and verbatim-cite specific numerical targets from management guidance such as',
|
|
54
|
-
'earnings per share targets, margin percentages, growth rates, or AUM figures rather than paraphrasing approximately.',
|
|
55
|
-
'When evaluating capital allocation options, explicitly compute implied returns or internal rates of return (IRRs),',
|
|
56
|
-
'showing the arithmetic using price levels and targets from the source data.',
|
|
57
|
-
'Take a clear, decisive position — do not hedge with "it depends". Reconcile conflicting data points explicitly.',
|
|
58
|
-
].join(' ')
|
|
59
|
-
|
|
60
|
-
async function workerComplete(
|
|
61
|
-
task: BenchTask,
|
|
62
|
-
cfg: { routerBaseUrl: string; routerKey: string; model: string; timeoutMs: number },
|
|
63
|
-
): Promise<{ answer: string; inputTokens: number; outputTokens: number; durationMs: number }> {
|
|
64
|
-
const startedAt = Date.now()
|
|
65
|
-
const result = await runBenchRouterTurn(
|
|
66
|
-
{
|
|
67
|
-
routerBaseUrl: cfg.routerBaseUrl,
|
|
68
|
-
routerKey: cfg.routerKey,
|
|
69
|
-
profile: {
|
|
70
|
-
name: 'trata-financial-analyst',
|
|
71
|
-
harness: 'cli-base',
|
|
72
|
-
model: {
|
|
73
|
-
provider: 'tangle-router',
|
|
74
|
-
default: cfg.model,
|
|
75
|
-
metadata: {
|
|
76
|
-
temperature: 0,
|
|
77
|
-
maxTokens: Number(process.env.WORKER_MAX_TOKENS ?? 4096),
|
|
78
|
-
},
|
|
79
|
-
},
|
|
80
|
-
prompt: { systemPrompt: ANALYST_SYSTEM },
|
|
81
|
-
},
|
|
82
|
-
timeoutMs: cfg.timeoutMs,
|
|
83
|
-
},
|
|
84
|
-
task.prompt,
|
|
85
|
-
)
|
|
86
|
-
if (result.usage.tokensKnown === false) throw new Error('worker provider omitted token usage')
|
|
87
|
-
return {
|
|
88
|
-
answer: result.finalText,
|
|
89
|
-
inputTokens: result.usage.input,
|
|
90
|
-
outputTokens: result.usage.output,
|
|
91
|
-
durationMs: Date.now() - startedAt,
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
interface TaskResult {
|
|
96
|
-
taskId: string
|
|
97
|
-
answer: string
|
|
98
|
-
score: BenchScore
|
|
99
|
-
rawScore: number
|
|
100
|
-
inputTokens: number
|
|
101
|
-
outputTokens: number
|
|
102
|
-
durationMs: number
|
|
103
|
-
error?: string
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
async function runTask(
|
|
107
|
-
task: BenchTask,
|
|
108
|
-
workerCfg: { routerBaseUrl: string; routerKey: string; model: string; timeoutMs: number },
|
|
109
|
-
adapter: ReturnType<typeof resolveAdapter>,
|
|
110
|
-
): Promise<TaskResult> {
|
|
111
|
-
let answer = ''
|
|
112
|
-
let inputTokens = 0
|
|
113
|
-
let outputTokens = 0
|
|
114
|
-
let durationMs = 0
|
|
115
|
-
let error: string | undefined
|
|
116
|
-
|
|
117
|
-
try {
|
|
118
|
-
const result = await workerComplete(task, workerCfg)
|
|
119
|
-
answer = result.answer
|
|
120
|
-
inputTokens = result.inputTokens
|
|
121
|
-
outputTokens = result.outputTokens
|
|
122
|
-
durationMs = result.durationMs
|
|
123
|
-
} catch (err) {
|
|
124
|
-
error = err instanceof Error ? err.message : String(err)
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const score = answer.trim()
|
|
128
|
-
? await adapter.judge(task, answer).catch((err) => ({
|
|
129
|
-
resolved: false as const,
|
|
130
|
-
score: 0,
|
|
131
|
-
detail: JSON.stringify({ error: err instanceof Error ? err.message : String(err) }),
|
|
132
|
-
}))
|
|
133
|
-
: { resolved: false as const, score: 0, detail: JSON.stringify({ error: error ?? 'empty answer' }) }
|
|
134
|
-
|
|
135
|
-
const detail = score.detail ? (JSON.parse(score.detail) as { rawScore?: number }) : {}
|
|
136
|
-
return {
|
|
137
|
-
taskId: task.id,
|
|
138
|
-
answer,
|
|
139
|
-
score,
|
|
140
|
-
rawScore: detail.rawScore ?? (score.resolved ? 4 : 0),
|
|
141
|
-
inputTokens,
|
|
142
|
-
outputTokens,
|
|
143
|
-
durationMs,
|
|
144
|
-
error,
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
async function main(): Promise<void> {
|
|
149
|
-
const benchName = process.env.BENCH ?? 'trata-hedge'
|
|
150
|
-
const adapter = resolveAdapter(benchName)
|
|
151
|
-
await adapter.preflight()
|
|
152
|
-
|
|
153
|
-
const n = Number(process.env.N ?? 10)
|
|
154
|
-
const concurrency = Number(process.env.CONCURRENCY ?? 5)
|
|
155
|
-
const model = process.env.WORKER_MODEL ?? 'deepseek-v4-flash'
|
|
156
|
-
const routerBase = process.env.ROUTER_BASE ?? 'https://router.tangle.tools/v1'
|
|
157
|
-
const routerKey = must('TANGLE_API_KEY')
|
|
158
|
-
const corpusPath = process.env.CORPUS
|
|
159
|
-
const idsEnv = process.env.IDS
|
|
160
|
-
|
|
161
|
-
const loadOpts = idsEnv
|
|
162
|
-
? { ids: idsEnv.split(',').map((s) => s.trim()).filter(Boolean) }
|
|
163
|
-
: n > 0
|
|
164
|
-
? { limit: n }
|
|
165
|
-
: {}
|
|
166
|
-
const tasks = await adapter.loadTasks(loadOpts)
|
|
167
|
-
if (tasks.length === 0) throw new Error('no tasks loaded')
|
|
168
|
-
|
|
169
|
-
const workerCfg = { routerBaseUrl: routerBase, routerKey, model, timeoutMs: 180_000 }
|
|
170
|
-
|
|
171
|
-
console.log(
|
|
172
|
-
`trata-gate: ${tasks.length} tasks | model=${model} | judge=${process.env.JUDGE_MODEL ?? 'gemini-2.5-pro'} | concurrency=${concurrency}`,
|
|
173
|
-
)
|
|
174
|
-
|
|
175
|
-
let done = 0
|
|
176
|
-
const results: TaskResult[] = []
|
|
177
|
-
await runPool(tasks, concurrency, async (task) => {
|
|
178
|
-
const r = await runTask(task, workerCfg, adapter)
|
|
179
|
-
results.push(r)
|
|
180
|
-
done++
|
|
181
|
-
const icon = r.rawScore === 4 ? '✓' : r.rawScore >= 2 ? '~' : '·'
|
|
182
|
-
process.stdout.write(
|
|
183
|
-
` [${done}/${tasks.length}] ${task.id.slice(0, 60)}: ${icon} (${r.rawScore}/4)\n`,
|
|
184
|
-
)
|
|
185
|
-
return r
|
|
186
|
-
})
|
|
187
|
-
|
|
188
|
-
// Score distribution
|
|
189
|
-
const byRaw = [0, 0, 0, 0, 0] // index = rawScore 0-4
|
|
190
|
-
let totalInputTok = 0
|
|
191
|
-
let totalOutputTok = 0
|
|
192
|
-
let totalMs = 0
|
|
193
|
-
for (const r of results) {
|
|
194
|
-
byRaw[Math.min(r.rawScore, 4)]++
|
|
195
|
-
totalInputTok += r.inputTokens
|
|
196
|
-
totalOutputTok += r.outputTokens
|
|
197
|
-
totalMs += r.durationMs
|
|
198
|
-
}
|
|
199
|
-
const resolved = byRaw[4] ?? 0
|
|
200
|
-
const partial = (byRaw[2] ?? 0) + (byRaw[3] ?? 0)
|
|
201
|
-
const n_ = results.length
|
|
202
|
-
const pct = (x: number) => (n_ > 0 ? `${((x / n_) * 100).toFixed(1)}%` : 'n/a')
|
|
203
|
-
const meanScore = n_ > 0 ? results.reduce((s, r) => s + r.score.score, 0) / n_ : 0
|
|
204
|
-
|
|
205
|
-
console.log(`\n=== ${benchName} — trata-gate (n=${n_}, model=${model}) ===`)
|
|
206
|
-
console.log(` resolved (4/4): ${pct(resolved)} (${resolved}/${n_})`)
|
|
207
|
-
console.log(` partial credit (2-3): ${pct(partial)} (${partial}/${n_})`)
|
|
208
|
-
console.log(` mean score (0..1): ${meanScore.toFixed(3)}`)
|
|
209
|
-
console.log(` score dist [0..4]: ${byRaw.join(' | ')}`)
|
|
210
|
-
console.log(
|
|
211
|
-
` tokens (in/out): ${(totalInputTok / 1000).toFixed(0)}k / ${(totalOutputTok / 1000).toFixed(0)}k`,
|
|
212
|
-
)
|
|
213
|
-
console.log(` wall time: ${(totalMs / 1000).toFixed(0)}s total`)
|
|
214
|
-
console.log(` errors: ${results.filter((r) => r.error).length}`)
|
|
215
|
-
|
|
216
|
-
if (corpusPath) {
|
|
217
|
-
for (const r of results) {
|
|
218
|
-
appendFileSync(
|
|
219
|
-
corpusPath,
|
|
220
|
-
JSON.stringify({
|
|
221
|
-
benchmark: benchName,
|
|
222
|
-
instanceId: r.taskId,
|
|
223
|
-
condition: `blind@1`,
|
|
224
|
-
model,
|
|
225
|
-
resolved: r.score.resolved,
|
|
226
|
-
rawScore: r.rawScore,
|
|
227
|
-
score: r.score.score,
|
|
228
|
-
detail: r.score.detail,
|
|
229
|
-
inputTokens: r.inputTokens,
|
|
230
|
-
outputTokens: r.outputTokens,
|
|
231
|
-
durationMs: r.durationMs,
|
|
232
|
-
...(r.error ? { error: r.error } : {}),
|
|
233
|
-
}) + '\n',
|
|
234
|
-
)
|
|
235
|
-
}
|
|
236
|
-
console.log(`\ncorpus written → ${corpusPath}`)
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
main().catch((err) => {
|
|
241
|
-
console.error(err instanceof Error ? (err.stack ?? err.message) : String(err))
|
|
242
|
-
process.exit(1)
|
|
243
|
-
})
|