@tangle-network/agent-bench 0.4.1 → 0.4.3

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.
Files changed (194) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/adapters.d.ts +4 -11
  3. package/dist/adapters.js +78 -41
  4. package/dist/adapters.js.map +1 -1
  5. package/dist/benchmarks/_harness.d.ts +48 -66
  6. package/dist/benchmarks/_harness.js +329 -33
  7. package/dist/benchmarks/_harness.js.map +1 -1
  8. package/dist/benchmarks/aec-bench.d.ts +4 -25
  9. package/dist/benchmarks/aec-bench.js +242 -7
  10. package/dist/benchmarks/aec-bench.js.map +1 -1
  11. package/dist/benchmarks/agentbench.d.ts +5 -13
  12. package/dist/benchmarks/agentbench.js +114 -9
  13. package/dist/benchmarks/agentbench.js.map +1 -1
  14. package/dist/benchmarks/appworld.d.ts +9 -29
  15. package/dist/benchmarks/appworld.js +317 -12
  16. package/dist/benchmarks/appworld.js.map +1 -1
  17. package/dist/benchmarks/bfcl.d.ts +5 -15
  18. package/dist/benchmarks/bfcl.js +264 -9
  19. package/dist/benchmarks/bfcl.js.map +1 -1
  20. package/dist/benchmarks/cad-design.d.ts +16 -41
  21. package/dist/benchmarks/cad-design.js +512 -6
  22. package/dist/benchmarks/cad-design.js.map +1 -1
  23. package/dist/benchmarks/cadbench.d.ts +4 -17
  24. package/dist/benchmarks/cadbench.js +2 -8
  25. package/dist/benchmarks/cadgenbench.d.ts +4 -20
  26. package/dist/benchmarks/cadgenbench.js +2 -8
  27. package/dist/benchmarks/commit0.d.ts +5 -27
  28. package/dist/benchmarks/commit0.js +187 -9
  29. package/dist/benchmarks/commit0.js.map +1 -1
  30. package/dist/benchmarks/crag.d.ts +4 -12
  31. package/dist/benchmarks/crag.js +110 -8
  32. package/dist/benchmarks/crag.js.map +1 -1
  33. package/dist/benchmarks/dabstep.d.ts +5 -15
  34. package/dist/benchmarks/dabstep.js +177 -9
  35. package/dist/benchmarks/dabstep.js.map +1 -1
  36. package/dist/benchmarks/enterpriseops-gym.d.ts +5 -34
  37. package/dist/benchmarks/enterpriseops-gym.js +236 -9
  38. package/dist/benchmarks/enterpriseops-gym.js.map +1 -1
  39. package/dist/benchmarks/finresearchbench.d.ts +4 -13
  40. package/dist/benchmarks/finresearchbench.js +218 -7
  41. package/dist/benchmarks/finresearchbench.js.map +1 -1
  42. package/dist/benchmarks/finsearchcomp.d.ts +8 -39
  43. package/dist/benchmarks/finsearchcomp.js +267 -6
  44. package/dist/benchmarks/finsearchcomp.js.map +1 -1
  45. package/dist/benchmarks/frames.d.ts +15 -37
  46. package/dist/benchmarks/frames.js +408 -11
  47. package/dist/benchmarks/frames.js.map +1 -1
  48. package/dist/benchmarks/hotpotqa.d.ts +4 -24
  49. package/dist/benchmarks/hotpotqa.js +250 -14
  50. package/dist/benchmarks/hotpotqa.js.map +1 -1
  51. package/dist/benchmarks/humaneval.d.ts +19 -37
  52. package/dist/benchmarks/humaneval.js +279 -16
  53. package/dist/benchmarks/humaneval.js.map +1 -1
  54. package/dist/benchmarks/mind2web.d.ts +7 -34
  55. package/dist/benchmarks/mind2web.js +257 -8
  56. package/dist/benchmarks/mind2web.js.map +1 -1
  57. package/dist/benchmarks/nomiracl.d.ts +4 -13
  58. package/dist/benchmarks/nomiracl.js +146 -8
  59. package/dist/benchmarks/nomiracl.js.map +1 -1
  60. package/dist/benchmarks/open-rag-bench.d.ts +4 -12
  61. package/dist/benchmarks/open-rag-bench.js +110 -8
  62. package/dist/benchmarks/open-rag-bench.js.map +1 -1
  63. package/dist/benchmarks/programbench.d.ts +5 -29
  64. package/dist/benchmarks/programbench.js +161 -9
  65. package/dist/benchmarks/programbench.js.map +1 -1
  66. package/dist/benchmarks/rag-shared.d.ts +21 -20
  67. package/dist/benchmarks/rag-shared.js +245 -38
  68. package/dist/benchmarks/rag-shared.js.map +1 -1
  69. package/dist/benchmarks/ragbench.d.ts +4 -14
  70. package/dist/benchmarks/ragbench.js +127 -8
  71. package/dist/benchmarks/ragbench.js.map +1 -1
  72. package/dist/benchmarks/simpleqa.d.ts +17 -44
  73. package/dist/benchmarks/simpleqa.js +293 -10
  74. package/dist/benchmarks/simpleqa.js.map +1 -1
  75. package/dist/benchmarks/swe-bench.d.ts +23 -36
  76. package/dist/benchmarks/swe-bench.js +234 -13
  77. package/dist/benchmarks/swe-bench.js.map +1 -1
  78. package/dist/benchmarks/t2-ragbench.d.ts +4 -12
  79. package/dist/benchmarks/t2-ragbench.js +118 -8
  80. package/dist/benchmarks/t2-ragbench.js.map +1 -1
  81. package/dist/benchmarks/tau-bench-shared.d.ts +14 -22
  82. package/dist/benchmarks/tau-bench-shared.js +169 -9
  83. package/dist/benchmarks/tau-bench-shared.js.map +1 -1
  84. package/dist/benchmarks/tau2-bench.d.ts +5 -5
  85. package/dist/benchmarks/tau2-bench.js +28 -10
  86. package/dist/benchmarks/tau2-bench.js.map +1 -1
  87. package/dist/benchmarks/tau3-banking.d.ts +4 -13
  88. package/dist/benchmarks/tau3-banking.js +28 -8
  89. package/dist/benchmarks/tau3-banking.js.map +1 -1
  90. package/dist/benchmarks/terminal-bench.d.ts +4 -22
  91. package/dist/benchmarks/terminal-bench.js +140 -7
  92. package/dist/benchmarks/terminal-bench.js.map +1 -1
  93. package/dist/benchmarks/toollm.d.ts +5 -13
  94. package/dist/benchmarks/toollm.js +184 -9
  95. package/dist/benchmarks/toollm.js.map +1 -1
  96. package/dist/benchmarks/trata-hedge.d.ts +4 -30
  97. package/dist/benchmarks/trata-hedge.js +336 -6
  98. package/dist/benchmarks/trata-hedge.js.map +1 -1
  99. package/dist/benchmarks/types.d.ts +85 -94
  100. package/dist/benchmarks/types.js +1 -1
  101. package/dist/benchmarks/webarena-verified.d.ts +5 -13
  102. package/dist/benchmarks/webarena-verified.js +152 -9
  103. package/dist/benchmarks/webarena-verified.js.map +1 -1
  104. package/dist/cadbench-DpQWZHp4.js +285 -0
  105. package/dist/cadbench-DpQWZHp4.js.map +1 -0
  106. package/dist/cadgenbench-DRhczfsG.js +151 -0
  107. package/dist/cadgenbench-DRhczfsG.js.map +1 -0
  108. package/dist/index.d.ts +245 -293
  109. package/dist/index.js +1669 -1791
  110. package/dist/index.js.map +1 -1
  111. package/package.json +17 -14
  112. package/scripts/verify-packed-consumer.mjs +20 -17
  113. package/src/corpus.test.mts +13 -0
  114. package/src/corpus.ts +4 -0
  115. package/src/profile-coordinates.ts +2 -2
  116. package/src/rollout-ledger/backfill-swe-arena.mts +5 -1
  117. package/src/rollout-ledger/settle-capture.mts +7 -1
  118. package/src/search-bench/profiles.ts +1 -1
  119. package/src/skill-sandbox-smoke.mts +2 -1
  120. package/src/swe-arena/gepa-seat.mts +1 -1
  121. package/src/swe-arena/ledger-orphans.test.mts +36 -34
  122. package/dist/benchmarks/cadbench.js.map +0 -1
  123. package/dist/benchmarks/cadgenbench.js.map +0 -1
  124. package/dist/benchmarks/types.js.map +0 -1
  125. package/dist/chunk-53UPUNBZ.js +0 -325
  126. package/dist/chunk-53UPUNBZ.js.map +0 -1
  127. package/dist/chunk-5FEQDSCT.js +0 -211
  128. package/dist/chunk-5FEQDSCT.js.map +0 -1
  129. package/dist/chunk-5H5XV76F.js +0 -240
  130. package/dist/chunk-5H5XV76F.js.map +0 -1
  131. package/dist/chunk-67ACKDCX.js +0 -118
  132. package/dist/chunk-67ACKDCX.js.map +0 -1
  133. package/dist/chunk-7FKBWOQT.js +0 -147
  134. package/dist/chunk-7FKBWOQT.js.map +0 -1
  135. package/dist/chunk-BEN6IF2X.js +0 -221
  136. package/dist/chunk-BEN6IF2X.js.map +0 -1
  137. package/dist/chunk-BZY5QARD.js +0 -120
  138. package/dist/chunk-BZY5QARD.js.map +0 -1
  139. package/dist/chunk-C7T7WEK2.js +0 -103
  140. package/dist/chunk-C7T7WEK2.js.map +0 -1
  141. package/dist/chunk-CLIKAXKH.js +0 -276
  142. package/dist/chunk-CLIKAXKH.js.map +0 -1
  143. package/dist/chunk-CWIOBFSP.js +0 -197
  144. package/dist/chunk-CWIOBFSP.js.map +0 -1
  145. package/dist/chunk-CXDUTWQE.js +0 -318
  146. package/dist/chunk-CXDUTWQE.js.map +0 -1
  147. package/dist/chunk-DWALFME7.js +0 -182
  148. package/dist/chunk-DWALFME7.js.map +0 -1
  149. package/dist/chunk-EEOC6QPJ.js +0 -144
  150. package/dist/chunk-EEOC6QPJ.js.map +0 -1
  151. package/dist/chunk-EIETHPD5.js +0 -321
  152. package/dist/chunk-EIETHPD5.js.map +0 -1
  153. package/dist/chunk-GC2EPS6L.js +0 -130
  154. package/dist/chunk-GC2EPS6L.js.map +0 -1
  155. package/dist/chunk-GCHL6XPM.js +0 -169
  156. package/dist/chunk-GCHL6XPM.js.map +0 -1
  157. package/dist/chunk-HQ5HCCKF.js +0 -142
  158. package/dist/chunk-HQ5HCCKF.js.map +0 -1
  159. package/dist/chunk-HVW25KSX.js +0 -208
  160. package/dist/chunk-HVW25KSX.js.map +0 -1
  161. package/dist/chunk-INNOYXCP.js +0 -387
  162. package/dist/chunk-INNOYXCP.js.map +0 -1
  163. package/dist/chunk-J6BU3NTM.js +0 -251
  164. package/dist/chunk-J6BU3NTM.js.map +0 -1
  165. package/dist/chunk-JSQOUKXS.js +0 -149
  166. package/dist/chunk-JSQOUKXS.js.map +0 -1
  167. package/dist/chunk-JTHWEDEW.js +0 -32
  168. package/dist/chunk-JTHWEDEW.js.map +0 -1
  169. package/dist/chunk-NRMGT25X.js +0 -116
  170. package/dist/chunk-NRMGT25X.js.map +0 -1
  171. package/dist/chunk-PA2ZKHJC.js +0 -230
  172. package/dist/chunk-PA2ZKHJC.js.map +0 -1
  173. package/dist/chunk-PUIRNYI7.js +0 -189
  174. package/dist/chunk-PUIRNYI7.js.map +0 -1
  175. package/dist/chunk-QZZEAHWJ.js +0 -136
  176. package/dist/chunk-QZZEAHWJ.js.map +0 -1
  177. package/dist/chunk-SEVJPLZC.js +0 -260
  178. package/dist/chunk-SEVJPLZC.js.map +0 -1
  179. package/dist/chunk-TBKU5XQI.js +0 -228
  180. package/dist/chunk-TBKU5XQI.js.map +0 -1
  181. package/dist/chunk-UPAMRDX4.js +0 -233
  182. package/dist/chunk-UPAMRDX4.js.map +0 -1
  183. package/dist/chunk-VQRS7VUC.js +0 -342
  184. package/dist/chunk-VQRS7VUC.js.map +0 -1
  185. package/dist/chunk-WG7TM7UV.js +0 -30
  186. package/dist/chunk-WG7TM7UV.js.map +0 -1
  187. package/dist/chunk-X3BTXCJ4.js +0 -262
  188. package/dist/chunk-X3BTXCJ4.js.map +0 -1
  189. package/dist/chunk-XXFF3RRD.js +0 -162
  190. package/dist/chunk-XXFF3RRD.js.map +0 -1
  191. package/dist/chunk-ZFNOM7WR.js +0 -27
  192. package/dist/chunk-ZFNOM7WR.js.map +0 -1
  193. package/dist/chunk-ZNCCYTFG.js +0 -170
  194. package/dist/chunk-ZNCCYTFG.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
1
+ {"version":3,"file":"finresearchbench.js","names":[],"sources":["../../src/benchmarks/finresearchbench.ts"],"sourcesContent":["/**\n * FinResearchBench-compatible adapter.\n *\n * The paper defines a logic-tree Agent-as-a-Judge benchmark for financial\n * research reports, but there is no stable public scorer package wired here.\n * Live mode therefore requires a local data export whose rows carry the official\n * judge prompt/template/logic tree. The adapter refuses to invent a judge.\n */\n\nimport { readFile, stat } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport { benchRoot } from './_harness'\nimport type { BenchmarkAdapter, BenchScore, BenchTask, LoadOptions } from './types'\n\nconst FIXTURES = join(benchRoot, 'fixtures', 'finresearchbench.json')\n\ninterface FinResearchRecord {\n id: string\n question: string\n category?: string\n reference_answer?: string\n reference_report?: string\n logic_tree?: unknown\n rubric?: unknown\n judge_system_prompt?: string\n judge_prompt_template?: string\n}\n\ninterface FinResearchMeta {\n id: string\n category: string\n question: string\n referenceAnswer: string\n referenceReport: string\n logicTree: unknown\n rubric: unknown\n judgeSystemPrompt?: string\n judgePromptTemplate?: string\n scoring: 'official-logic-tree-judge' | 'fixture-exact-reference'\n}\n\nconst dataFile = (): string | undefined => process.env.FINRESEARCHBENCH_DATA_FILE\n\nfunction routerConfig(): { baseUrl: string; key: string; model: string } {\n const key = process.env.TANGLE_API_KEY\n if (!key) throw new Error('TANGLE_API_KEY is required for FinResearchBench live LLM judging')\n return {\n baseUrl: process.env.ROUTER_BASE ?? 'https://router.tangle.tools/v1',\n key,\n model: process.env.FINRESEARCHBENCH_JUDGE_MODEL ?? process.env.JUDGE_MODEL ?? 'deepseek-v4-flash',\n }\n}\n\nasync function assertReadable(path: string, label: string): Promise<void> {\n try {\n await stat(path)\n } catch (err) {\n throw new Error(`FinResearchBench: missing ${label} at ${path} (${err instanceof Error ? err.message : err})`)\n }\n}\n\nfunction readRecords(raw: string): FinResearchRecord[] {\n const trimmed = raw.trim()\n if (trimmed.startsWith('[')) return JSON.parse(trimmed) as FinResearchRecord[]\n return trimmed\n .split(/\\r?\\n/)\n .map((line) => line.trim())\n .filter((line) => line.length > 0)\n .map((line) => JSON.parse(line) as FinResearchRecord)\n}\n\nfunction assertLiveJudgeFields(records: readonly FinResearchRecord[], source: string): void {\n const missing = records\n .filter((row) => !row.judge_system_prompt || !row.judge_prompt_template)\n .map((row) => row.id)\n if (missing.length > 0) {\n throw new Error(\n `FinResearchBench live rows from ${source} missing official judge prompts for ${missing.length}/${records.length} row(s): ${missing.slice(0, 5).join(', ')}. ` +\n 'Use a benchmark export with judge_system_prompt and judge_prompt_template, or do not score this benchmark live.',\n )\n }\n}\n\nfunction rowToTask(row: FinResearchRecord, fixturesMode: boolean): BenchTask {\n const referenceAnswer = row.reference_answer ?? ''\n const referenceReport = row.reference_report ?? referenceAnswer\n const meta: FinResearchMeta = {\n id: row.id,\n category: row.category ?? 'unknown',\n question: row.question,\n referenceAnswer,\n referenceReport,\n logicTree: row.logic_tree ?? null,\n rubric: row.rubric ?? null,\n judgeSystemPrompt: row.judge_system_prompt,\n judgePromptTemplate: row.judge_prompt_template,\n scoring: fixturesMode ? 'fixture-exact-reference' : 'official-logic-tree-judge',\n }\n return {\n id: row.id,\n split: row.category,\n prompt: [\n 'Complete this FinResearchBench financial research task.',\n 'Produce a decision-grade research answer with explicit reasoning, evidence, and final conclusion.',\n '',\n row.question,\n ].join('\\n'),\n metadata: meta as unknown as Record<string, unknown>,\n }\n}\n\nfunction readMeta(task: BenchTask): FinResearchMeta {\n const md = task.metadata\n if (!md || typeof md.question !== 'string') {\n throw new Error(`FinResearchBench task ${task.id} missing metadata — loadTasks did not populate it`)\n }\n return md as unknown as FinResearchMeta\n}\n\nfunction selectRows(rows: FinResearchRecord[], opts: LoadOptions, fixturesMode: boolean): BenchTask[] {\n let tasks = rows.map((row) => rowToTask(row, fixturesMode))\n if (opts.split) tasks = tasks.filter((task) => task.split === opts.split)\n if (opts.ids) {\n const want = new Set(opts.ids)\n tasks = tasks.filter((task) => want.has(task.id))\n } else if (opts.limit !== undefined) {\n tasks = tasks.slice(0, opts.limit)\n }\n if (tasks.length === 0) throw new Error(`FinResearchBench: no tasks matched ${JSON.stringify(opts)}`)\n return tasks\n}\n\nasync function loadFixtures(opts: LoadOptions): Promise<BenchTask[]> {\n const records = readRecords(await readFile(FIXTURES, 'utf8'))\n console.warn(`[finresearchbench] FINRESEARCHBENCH_FIXTURES=1 — loading ${records.length} adapter fixtures`)\n return selectRows(records, opts, true)\n}\n\nasync function loadOfficialTasks(path: string, opts: LoadOptions): Promise<BenchTask[]> {\n const records = readRecords(await readFile(path, 'utf8'))\n assertLiveJudgeFields(records, path)\n return selectRows(records, opts, false)\n}\n\nfunction fillTemplate(meta: FinResearchMeta, response: string): string {\n const template = meta.judgePromptTemplate\n if (!template) throw new Error(`FinResearchBench task ${meta.id} missing judge_prompt_template`)\n return template\n .replaceAll('{question}', meta.question)\n .replaceAll('{response}', response)\n .replaceAll('{reference_answer}', meta.referenceAnswer)\n .replaceAll('{reference_report}', meta.referenceReport)\n .replaceAll('{logic_tree}', JSON.stringify(meta.logicTree, null, 2))\n .replaceAll('{rubric}', JSON.stringify(meta.rubric, null, 2))\n}\n\nfunction parseJudgeScore(content: string): { score: number; raw: unknown } {\n const candidates: string[] = []\n for (const m of content.matchAll(/```(?:json)?\\s*([\\s\\S]*?)```/g)) candidates.push(m[1].trim())\n for (const m of content.matchAll(/\\{[\\s\\S]*?\\}/g)) candidates.push(m[0])\n candidates.push(content.trim())\n for (const candidate of candidates) {\n let parsed: Record<string, unknown>\n try {\n parsed = JSON.parse(candidate) as Record<string, unknown>\n } catch {\n continue\n }\n const raw = parsed.score ?? parsed.overall_score ?? parsed.total_score ?? parsed.answer_score\n const n = typeof raw === 'number' ? raw : typeof raw === 'string' ? Number(raw) : NaN\n if (!Number.isFinite(n)) continue\n if (n < 0) throw new Error(`FinResearchBench judge score is negative: ${JSON.stringify(parsed)}`)\n const score = n <= 1 ? n : n <= 10 ? n / 10 : n / 100\n if (score > 1) throw new Error(`FinResearchBench judge score outside supported range: ${JSON.stringify(parsed)}`)\n return { score, raw: parsed }\n }\n throw new Error(`FinResearchBench judge produced no parseable JSON score: ${content.slice(0, 400)}`)\n}\n\nasync function runOfficialJudge(meta: FinResearchMeta, response: string): Promise<BenchScore> {\n if (!meta.judgeSystemPrompt) throw new Error(`FinResearchBench task ${meta.id} missing judge_system_prompt`)\n const router = routerConfig()\n const res = await fetch(`${router.baseUrl}/chat/completions`, {\n method: 'POST',\n headers: { 'content-type': 'application/json', authorization: `Bearer ${router.key}` },\n body: JSON.stringify({\n model: router.model,\n temperature: 0,\n messages: [\n { role: 'system', content: meta.judgeSystemPrompt },\n { role: 'user', content: fillTemplate(meta, response) },\n ],\n }),\n })\n if (!res.ok) throw new Error(`FinResearchBench judge HTTP ${res.status}: ${(await res.text()).slice(0, 300)}`)\n const body = (await res.json()) as { choices?: Array<{ message?: { content?: string } }> }\n const content = body.choices?.[0]?.message?.content\n if (typeof content !== 'string') throw new Error(`FinResearchBench judge returned no message content: ${JSON.stringify(body).slice(0, 300)}`)\n const { score, raw } = parseJudgeScore(content)\n return {\n resolved: score >= Number(process.env.FINRESEARCHBENCH_PASS_THRESHOLD ?? 0.8),\n score,\n detail: JSON.stringify({ scoring: meta.scoring, category: meta.category, judgeModel: router.model, raw }),\n }\n}\n\nfunction normalizeText(value: string): string {\n return value.toLowerCase().replace(/\\s+/g, ' ').trim()\n}\n\nfunction scoreFixture(meta: FinResearchMeta, artifact: string): BenchScore {\n const answer = normalizeText(meta.referenceAnswer || meta.referenceReport)\n const response = normalizeText(artifact)\n const score = answer.length > 0 && response.includes(answer) ? 1 : 0\n return {\n resolved: score === 1,\n score,\n detail: JSON.stringify({ scoring: meta.scoring, category: meta.category }),\n }\n}\n\nexport function createFinResearchBenchAdapter(): BenchmarkAdapter {\n const fixturesMode = process.env.FINRESEARCHBENCH_FIXTURES === '1'\n\n return {\n name: 'finresearchbench',\n\n async preflight() {\n if (fixturesMode) {\n await assertReadable(FIXTURES, 'fixture file')\n return\n }\n const file = dataFile()\n if (!file) {\n throw new Error(\n 'FINRESEARCHBENCH_DATA_FILE is required. Fix: export the official FinResearchBench rows as JSON/JSONL with judge_system_prompt and judge_prompt_template fields, then set FINRESEARCHBENCH_DATA_FILE=/path/to/export.jsonl.',\n )\n }\n routerConfig()\n await assertReadable(file, 'data file')\n await loadOfficialTasks(file, { limit: 1 })\n },\n\n async loadTasks(opts: LoadOptions = {}) {\n if (fixturesMode) return loadFixtures(opts)\n const file = dataFile()\n if (!file) throw new Error('FINRESEARCHBENCH_DATA_FILE is required to load FinResearchBench rows')\n return loadOfficialTasks(file, opts)\n },\n\n async goldArtifact(task: BenchTask) {\n const meta = readMeta(task)\n return meta.referenceReport || meta.referenceAnswer || undefined\n },\n\n async judge(task: BenchTask, artifact: string): Promise<BenchScore> {\n const meta = readMeta(task)\n if (fixturesMode) return scoreFixture(meta, artifact)\n if (artifact.trim().length === 0) {\n return {\n resolved: false,\n score: 0,\n detail: JSON.stringify({ scoring: meta.scoring, category: meta.category, reason: 'empty answer' }),\n }\n }\n return runOfficialJudge(meta, artifact)\n },\n }\n}\n"],"mappings":"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"}
@@ -1,37 +1,5 @@
1
- import { BenchmarkAdapter } from './types.js';
2
- import '@tangle-network/agent-runtime/loops';
3
-
4
- /**
5
- * FinSearchComp adapter (randomtutu/FinSearchComp). Agentic financial search +
6
- * reasoning. Worker artifact = the agent's free-form final answer text (zh/en).
7
- *
8
- * Each record is SELF-CONTAINED and carries its OWN judge: a per-record
9
- * `judge_system_prompt` + a `judge_prompt_template` with {prompt},
10
- * {response_reference} (gold answer WITH embedded tolerance/scoring-criteria),
11
- * and {response} (the student answer) placeholders. We faithfully REPLICATE the
12
- * benchmark's judge — fill the template, run it under the record's system prompt
13
- * via the pinned router (temperature 0), and parse the JSON the judge emits. The
14
- * judge returns an `answer_score` field; 1/positive = resolved, else not.
15
- * This matches the FinSearchComp leaderboard (Grok-4-web 68.9%).
16
- *
17
- * There is NO deterministic Tier-1 path here: the gold answer is prose with
18
- * region-specific units, embedded tolerances ("允许1%的误差"), and multi-point
19
- * scoring criteria. The benchmark intends its own LLM judge to be the arbiter,
20
- * so the judge IS the score — fail loud on unparseable judge output (never
21
- * default to resolved).
22
- *
23
- * SCOPE: T2 (Simple_Historical_Lookup) + T3 (Complex_Historical_Investigation)
24
- * are fully self-contained and supported. T1 (Time_Sensitive_Data_Fetching)
25
- * needs a live market snapshot / akshare ground truth to fill the judge's
26
- * {ground_truth} slot — it is SKIPPED in loadTasks and flagged in preflight.
27
- * See {@link T1Seam}.
28
- *
29
- * Requires for a live run: network access to the GitHub-hosted dataset JSON and
30
- * a TANGLE_API_KEY for the judge. For offline/CI verification, loadTasks falls back
31
- * to the committed fixtures (bench/fixtures/finsearchcomp.json) with an explicit
32
- * console.warn — never a silent fallback.
33
- */
34
-
1
+ import { BenchmarkAdapter } from "./types.js";
2
+ //#region src/benchmarks/finsearchcomp.d.ts
35
3
  /**
36
4
  * Typed seam for T1 (Time_Sensitive_Data_Fetching). T1 records score against a
37
5
  * live market snapshot the judge consumes via a {ground_truth} template slot;
@@ -40,10 +8,11 @@ import '@tangle-network/agent-runtime/loops';
40
8
  * and filling {ground_truth}. Until then T1 is excluded from loadTasks.
41
9
  */
42
10
  interface T1Seam {
43
- promptId: string;
44
- /** Resolver that returns the real-time ground-truth block for the {ground_truth} slot. */
45
- resolveGroundTruth(promptId: string): Promise<string>;
11
+ promptId: string;
12
+ /** Resolver that returns the real-time ground-truth block for the {ground_truth} slot. */
13
+ resolveGroundTruth(promptId: string): Promise<string>;
46
14
  }
47
15
  declare function createFinsearchcompAdapter(): BenchmarkAdapter;
48
-
49
- export { type T1Seam, createFinsearchcompAdapter };
16
+ //#endregion
17
+ export { T1Seam, createFinsearchcompAdapter };
18
+ //# sourceMappingURL=finsearchcomp.d.ts.map
@@ -1,7 +1,268 @@
1
- import {
2
- createFinsearchcompAdapter
3
- } from "../chunk-UPAMRDX4.js";
4
- export {
5
- createFinsearchcompAdapter
6
- };
1
+ import { readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ //#region src/benchmarks/finsearchcomp.ts
5
+ /**
6
+ * FinSearchComp adapter (randomtutu/FinSearchComp). Agentic financial search +
7
+ * reasoning. Worker artifact = the agent's free-form final answer text (zh/en).
8
+ *
9
+ * Each record is SELF-CONTAINED and carries its OWN judge: a per-record
10
+ * `judge_system_prompt` + a `judge_prompt_template` with {prompt},
11
+ * {response_reference} (gold answer WITH embedded tolerance/scoring-criteria),
12
+ * and {response} (the student answer) placeholders. We faithfully REPLICATE the
13
+ * benchmark's judge — fill the template, run it under the record's system prompt
14
+ * via the pinned router (temperature 0), and parse the JSON the judge emits. The
15
+ * judge returns an `answer_score` field; 1/positive = resolved, else not.
16
+ * This matches the FinSearchComp leaderboard (Grok-4-web 68.9%).
17
+ *
18
+ * There is NO deterministic Tier-1 path here: the gold answer is prose with
19
+ * region-specific units, embedded tolerances ("允许1%的误差"), and multi-point
20
+ * scoring criteria. The benchmark intends its own LLM judge to be the arbiter,
21
+ * so the judge IS the score — fail loud on unparseable judge output (never
22
+ * default to resolved).
23
+ *
24
+ * SCOPE: T2 (Simple_Historical_Lookup) + T3 (Complex_Historical_Investigation)
25
+ * are fully self-contained and supported. T1 (Time_Sensitive_Data_Fetching)
26
+ * needs a live market snapshot / akshare ground truth to fill the judge's
27
+ * {ground_truth} slot — it is SKIPPED in loadTasks and flagged in preflight.
28
+ * See {@link T1Seam}.
29
+ *
30
+ * Requires for a live run: network access to the GitHub-hosted dataset JSON and
31
+ * a TANGLE_API_KEY for the judge. For offline/CI verification, loadTasks falls back
32
+ * to the committed fixtures (bench/fixtures/finsearchcomp.json) with an explicit
33
+ * console.warn — never a silent fallback.
34
+ */
35
+ const FIXTURES = join(fileURLToPath(new URL("../..", import.meta.url)), "fixtures", "finsearchcomp.json");
36
+ const DATASET_URL = "https://raw.githubusercontent.com/randomtutu/FinSearchComp/main/data/finsearchcomp_data.json";
37
+ /** Only these are self-contained (gold + own judge, no external ground truth). */
38
+ const SUPPORTED_TYPES = /* @__PURE__ */ new Set(["T2", "T3"]);
39
+ /**
40
+ * Worker contract appended to every task prompt. The judge is paraphrase- and
41
+ * keyword-aware ("答案为", "the answer is"), so we do not impose a sentinel; we
42
+ * pass the whole artifact through as {response}. We DO ask the worker to lead
43
+ * with its final value so a truncated artifact still carries the answer.
44
+ */
45
+ const WORKER_CONTRACT = [
46
+ "",
47
+ "Research this financial question using live web/market sources and answer it.",
48
+ "State your final answer explicitly, with the exact units and precision the question requests.",
49
+ "Respect any tolerance the question implies; show the value you settled on, not just intermediate work."
50
+ ].join("\n");
51
+ /** "(T2)Simple_Historical_Lookup_001" → "T2"; throws on an unknown/missing prefix. */
52
+ function parseTaskType(promptId) {
53
+ const m = promptId.match(/^\((T[123])\)/);
54
+ if (!m) throw new Error(`FinSearchComp prompt_id has no (T1|T2|T3) prefix: ${JSON.stringify(promptId)}`);
55
+ return m[1];
56
+ }
57
+ /** "Simple_Historical_Lookup(Greater China)" → "Greater China"; '' when absent. */
58
+ function parseRegion(label) {
59
+ const m = label.match(/\(([^()]+)\)\s*$/);
60
+ return m ? m[1].trim() : "";
61
+ }
62
+ function recordToTask(rec) {
63
+ const taskType = parseTaskType(rec.prompt_id);
64
+ const meta = {
65
+ promptId: rec.prompt_id,
66
+ label: rec.label,
67
+ taskType,
68
+ region: parseRegion(rec.label),
69
+ responseReference: rec.response_reference,
70
+ judgePromptTemplate: rec.judge_prompt_template,
71
+ judgeSystemPrompt: rec.judge_system_prompt,
72
+ rawPrompt: rec.prompt
73
+ };
74
+ return {
75
+ id: rec.prompt_id,
76
+ split: taskType,
77
+ prompt: rec.prompt + WORKER_CONTRACT,
78
+ metadata: meta
79
+ };
80
+ }
81
+ function readMeta(task) {
82
+ const md = task.metadata;
83
+ if (!md || typeof md.responseReference !== "string" || typeof md.judgePromptTemplate !== "string" || typeof md.judgeSystemPrompt !== "string" || typeof md.rawPrompt !== "string") throw new Error(`FinSearchComp task ${task.id} missing judge metadata — loadTasks did not populate it`);
84
+ return md;
85
+ }
86
+ /** Fill the per-record judge_prompt_template. {ground_truth} is T1-only and unused for T2/T3. */
87
+ function fillJudgePrompt(meta, response) {
88
+ return meta.judgePromptTemplate.replaceAll("{prompt}", meta.rawPrompt).replaceAll("{response_reference}", meta.responseReference).replaceAll("{response}", response);
89
+ }
90
+ function judgeRouter() {
91
+ const key = process.env.TANGLE_API_KEY;
92
+ if (!key) throw new Error("TANGLE_API_KEY is required for the FinSearchComp per-record LLM judge");
93
+ const model = process.env.JUDGE_MODEL ?? "deepseek-v4-flash";
94
+ return {
95
+ baseUrl: process.env.ROUTER_BASE ?? "https://router.tangle.tools/v1",
96
+ key,
97
+ model
98
+ };
99
+ }
100
+ /**
101
+ * The judge emits a JSON object with an `answer_score`. In this dataset it is a
102
+ * flat integer ({"answer_score": 1}); we also accept the nested-array form
103
+ * ([[1]] → read [0][0]) the upstream scorer can produce. 1/positive = resolved.
104
+ * A "null" sentinel (judge could not obtain ground truth) is NOT a pass.
105
+ */
106
+ function readAnswerScore(value) {
107
+ let v = value;
108
+ while (Array.isArray(v)) v = v[0];
109
+ if (v === null || v === void 0 || v === "null") return {
110
+ resolved: false,
111
+ score: 0,
112
+ raw: value
113
+ };
114
+ const n = typeof v === "number" ? v : typeof v === "string" ? Number(v) : NaN;
115
+ if (!Number.isFinite(n)) throw new Error(`FinSearchComp judge answer_score not numeric: ${JSON.stringify(value)}`);
116
+ return {
117
+ resolved: n >= 1,
118
+ score: n <= 0 ? 0 : n >= 1 ? 1 : n,
119
+ raw: value
120
+ };
121
+ }
122
+ /** Extract the first JSON object carrying `answer_score` from judge output; throw if none. */
123
+ function parseJudgeOutput(content) {
124
+ const candidates = [];
125
+ for (const m of content.matchAll(/```(?:json)?\s*([\s\S]*?)```/g)) candidates.push(m[1].trim());
126
+ for (const m of content.matchAll(/\{[^{}]*"answer_score"[\s\S]*?\}/g)) candidates.push(m[0]);
127
+ candidates.push(content.trim());
128
+ for (const c of candidates) {
129
+ let parsed;
130
+ try {
131
+ parsed = JSON.parse(c);
132
+ } catch {
133
+ continue;
134
+ }
135
+ if ("answer_score" in parsed) return readAnswerScore(parsed.answer_score);
136
+ }
137
+ throw new Error(`FinSearchComp judge produced no parseable {"answer_score": …}: ${content.slice(0, 400)}`);
138
+ }
139
+ /** Run the record's own judge via the router. Fail loud on transport/parse errors. */
140
+ async function runRecordJudge(meta, response, router) {
141
+ const res = await fetch(`${router.baseUrl}/chat/completions`, {
142
+ method: "POST",
143
+ headers: {
144
+ "content-type": "application/json",
145
+ authorization: `Bearer ${router.key}`
146
+ },
147
+ body: JSON.stringify({
148
+ model: router.model,
149
+ temperature: 0,
150
+ messages: [{
151
+ role: "system",
152
+ content: meta.judgeSystemPrompt
153
+ }, {
154
+ role: "user",
155
+ content: fillJudgePrompt(meta, response)
156
+ }]
157
+ })
158
+ });
159
+ if (!res.ok) throw new Error(`FinSearchComp judge HTTP ${res.status}: ${(await res.text()).slice(0, 300)}`);
160
+ const body = await res.json();
161
+ const content = body.choices?.[0]?.message?.content;
162
+ if (typeof content !== "string") throw new Error(`FinSearchComp judge returned no message content: ${JSON.stringify(body).slice(0, 300)}`);
163
+ const { resolved, score, raw } = parseJudgeOutput(content);
164
+ return {
165
+ resolved,
166
+ score,
167
+ detail: JSON.stringify({
168
+ taskType: meta.taskType,
169
+ region: meta.region,
170
+ answerScore: raw,
171
+ judgeModel: router.model
172
+ })
173
+ };
174
+ }
175
+ function selectRecords(records, opts) {
176
+ let tasks = records.filter((r) => SUPPORTED_TYPES.has(parseTaskType(r.prompt_id))).map(recordToTask);
177
+ if (opts.split) tasks = tasks.filter((t) => t.split === opts.split);
178
+ if (opts.ids) {
179
+ const want = new Set(opts.ids);
180
+ tasks = tasks.filter((t) => want.has(t.id));
181
+ } else if (opts.limit !== void 0) tasks = balanceByType(tasks, opts.limit);
182
+ return tasks;
183
+ }
184
+ /** Round-robin across task types (T2/T3) so a `limit` pulls a balanced mix
185
+ * rather than slicing the type-ordered dataset's first N (which yields all
186
+ * T2). Deterministic — preserves dataset order within each type. */
187
+ function balanceByType(tasks, limit) {
188
+ const queues = /* @__PURE__ */ new Map();
189
+ for (const t of tasks) {
190
+ const k = parseTaskType(t.id);
191
+ const q = queues.get(k);
192
+ if (q) q.push(t);
193
+ else queues.set(k, [t]);
194
+ }
195
+ const lanes = [...queues.values()];
196
+ const out = [];
197
+ for (let i = 0; out.length < limit; i++) {
198
+ const before = out.length;
199
+ for (const lane of lanes) {
200
+ if (i < lane.length) out.push(lane[i]);
201
+ if (out.length >= limit) break;
202
+ }
203
+ if (out.length === before) break;
204
+ }
205
+ return out;
206
+ }
207
+ async function loadFixtures(opts) {
208
+ return selectRecords(JSON.parse(await readFile(FIXTURES, "utf8")), opts);
209
+ }
210
+ async function fetchDataset() {
211
+ const res = await fetch(DATASET_URL);
212
+ if (!res.ok) throw new Error(`FinSearchComp dataset HTTP ${res.status} fetching ${DATASET_URL}`);
213
+ const data = await res.json();
214
+ if (!Array.isArray(data) || data.length === 0) throw new Error(`FinSearchComp dataset empty or not an array: ${DATASET_URL}`);
215
+ return data;
216
+ }
217
+ function createFinsearchcompAdapter() {
218
+ const fixturesMode = process.env.FINSEARCHCOMP_FIXTURES === "1";
219
+ return {
220
+ name: "finsearchcomp",
221
+ async preflight() {
222
+ judgeRouter();
223
+ console.warn("[finsearchcomp] T1 (Time_Sensitive_Data_Fetching) is NOT scored: it needs a live market snapshot / akshare ground truth for the judge {ground_truth} slot. loadTasks supports T2 + T3 only; wire T1Seam to add T1.");
224
+ if (fixturesMode) {
225
+ await readFile(FIXTURES, "utf8").catch((err) => {
226
+ throw new Error(`FINSEARCHCOMP_FIXTURES=1 but ${FIXTURES} unreadable: ${err instanceof Error ? err.message : err}`);
227
+ });
228
+ return;
229
+ }
230
+ const res = await fetch(DATASET_URL, { method: "HEAD" }).catch((err) => {
231
+ throw new Error(`finsearchcomp preflight failed reaching ${DATASET_URL}: ${err instanceof Error ? err.message : err}\nFix: ensure network access to raw.githubusercontent.com, or set FINSEARCHCOMP_FIXTURES=1 to run against the committed fixtures offline.`);
232
+ });
233
+ if (!res.ok) throw new Error(`finsearchcomp preflight: dataset HEAD ${res.status} for ${DATASET_URL}. Set FINSEARCHCOMP_FIXTURES=1 to run against the committed fixtures offline.`);
234
+ },
235
+ async loadTasks(opts = {}) {
236
+ if (fixturesMode) return loadFixtures(opts);
237
+ let records;
238
+ try {
239
+ records = await fetchDataset();
240
+ } catch (err) {
241
+ console.warn(`[finsearchcomp] live dataset fetch failed (${err instanceof Error ? err.message : err}); falling back to committed fixtures at ${FIXTURES}`);
242
+ return loadFixtures(opts);
243
+ }
244
+ return selectRecords(records, opts);
245
+ },
246
+ async goldArtifact(task) {
247
+ return readMeta(task).responseReference;
248
+ },
249
+ async judge(task, artifact) {
250
+ const meta = readMeta(task);
251
+ const response = artifact.trim();
252
+ if (response.length === 0) return {
253
+ resolved: false,
254
+ score: 0,
255
+ detail: JSON.stringify({
256
+ taskType: meta.taskType,
257
+ region: meta.region,
258
+ reason: "empty answer"
259
+ })
260
+ };
261
+ return runRecordJudge(meta, response, judgeRouter());
262
+ }
263
+ };
264
+ }
265
+ //#endregion
266
+ export { createFinsearchcompAdapter };
267
+
7
268
  //# sourceMappingURL=finsearchcomp.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
1
+ {"version":3,"file":"finsearchcomp.js","names":[],"sources":["../../src/benchmarks/finsearchcomp.ts"],"sourcesContent":["/**\n * FinSearchComp adapter (randomtutu/FinSearchComp). Agentic financial search +\n * reasoning. Worker artifact = the agent's free-form final answer text (zh/en).\n *\n * Each record is SELF-CONTAINED and carries its OWN judge: a per-record\n * `judge_system_prompt` + a `judge_prompt_template` with {prompt},\n * {response_reference} (gold answer WITH embedded tolerance/scoring-criteria),\n * and {response} (the student answer) placeholders. We faithfully REPLICATE the\n * benchmark's judge — fill the template, run it under the record's system prompt\n * via the pinned router (temperature 0), and parse the JSON the judge emits. The\n * judge returns an `answer_score` field; 1/positive = resolved, else not.\n * This matches the FinSearchComp leaderboard (Grok-4-web 68.9%).\n *\n * There is NO deterministic Tier-1 path here: the gold answer is prose with\n * region-specific units, embedded tolerances (\"允许1%的误差\"), and multi-point\n * scoring criteria. The benchmark intends its own LLM judge to be the arbiter,\n * so the judge IS the score — fail loud on unparseable judge output (never\n * default to resolved).\n *\n * SCOPE: T2 (Simple_Historical_Lookup) + T3 (Complex_Historical_Investigation)\n * are fully self-contained and supported. T1 (Time_Sensitive_Data_Fetching)\n * needs a live market snapshot / akshare ground truth to fill the judge's\n * {ground_truth} slot — it is SKIPPED in loadTasks and flagged in preflight.\n * See {@link T1Seam}.\n *\n * Requires for a live run: network access to the GitHub-hosted dataset JSON and\n * a TANGLE_API_KEY for the judge. For offline/CI verification, loadTasks falls back\n * to the committed fixtures (bench/fixtures/finsearchcomp.json) with an explicit\n * console.warn — never a silent fallback.\n */\n\nimport { readFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport type { BenchmarkAdapter, BenchScore, BenchTask, LoadOptions } from './types'\n\nconst BENCH_ROOT = fileURLToPath(new URL('../..', import.meta.url))\nconst FIXTURES = join(BENCH_ROOT, 'fixtures', 'finsearchcomp.json')\n\nconst DATASET_URL =\n 'https://raw.githubusercontent.com/randomtutu/FinSearchComp/main/data/finsearchcomp_data.json'\n\n/** Task-type prefix on prompt_id, e.g. \"(T2)Simple_Historical_Lookup_001\". */\ntype TaskType = 'T1' | 'T2' | 'T3'\n/** Only these are self-contained (gold + own judge, no external ground truth). */\nconst SUPPORTED_TYPES: ReadonlySet<TaskType> = new Set<TaskType>(['T2', 'T3'])\n\n/**\n * Worker contract appended to every task prompt. The judge is paraphrase- and\n * keyword-aware (\"答案为\", \"the answer is\"), so we do not impose a sentinel; we\n * pass the whole artifact through as {response}. We DO ask the worker to lead\n * with its final value so a truncated artifact still carries the answer.\n */\nconst WORKER_CONTRACT = [\n '',\n 'Research this financial question using live web/market sources and answer it.',\n 'State your final answer explicitly, with the exact units and precision the question requests.',\n 'Respect any tolerance the question implies; show the value you settled on, not just intermediate work.',\n].join('\\n')\n\n/**\n * Typed seam for T1 (Time_Sensitive_Data_Fetching). T1 records score against a\n * live market snapshot the judge consumes via a {ground_truth} template slot;\n * the adapter does NOT fabricate that snapshot. Wiring T1 means supplying a\n * resolver that fetches the akshare/market value for the record at judge time\n * and filling {ground_truth}. Until then T1 is excluded from loadTasks.\n */\nexport interface T1Seam {\n promptId: string\n /** Resolver that returns the real-time ground-truth block for the {ground_truth} slot. */\n resolveGroundTruth(promptId: string): Promise<string>\n}\n\ninterface FinSearchRecord {\n prompt_id: string\n prompt: string\n response_reference: string\n judge_prompt_template: string\n judge_system_prompt: string\n label: string\n}\n\ninterface FinSearchMeta {\n promptId: string\n label: string\n taskType: TaskType\n region: string\n responseReference: string\n judgePromptTemplate: string\n judgeSystemPrompt: string\n rawPrompt: string\n}\n\n/** \"(T2)Simple_Historical_Lookup_001\" → \"T2\"; throws on an unknown/missing prefix. */\nfunction parseTaskType(promptId: string): TaskType {\n const m = promptId.match(/^\\((T[123])\\)/)\n if (!m) throw new Error(`FinSearchComp prompt_id has no (T1|T2|T3) prefix: ${JSON.stringify(promptId)}`)\n return m[1] as TaskType\n}\n\n/** \"Simple_Historical_Lookup(Greater China)\" → \"Greater China\"; '' when absent. */\nfunction parseRegion(label: string): string {\n const m = label.match(/\\(([^()]+)\\)\\s*$/)\n return m ? m[1].trim() : ''\n}\n\nfunction recordToTask(rec: FinSearchRecord): BenchTask {\n const taskType = parseTaskType(rec.prompt_id)\n const meta: FinSearchMeta = {\n promptId: rec.prompt_id,\n label: rec.label,\n taskType,\n region: parseRegion(rec.label),\n responseReference: rec.response_reference,\n judgePromptTemplate: rec.judge_prompt_template,\n judgeSystemPrompt: rec.judge_system_prompt,\n rawPrompt: rec.prompt,\n }\n return {\n id: rec.prompt_id,\n split: taskType,\n prompt: rec.prompt + WORKER_CONTRACT,\n metadata: meta as unknown as Record<string, unknown>,\n }\n}\n\nfunction readMeta(task: BenchTask): FinSearchMeta {\n const md = task.metadata\n if (\n !md ||\n typeof md.responseReference !== 'string' ||\n typeof md.judgePromptTemplate !== 'string' ||\n typeof md.judgeSystemPrompt !== 'string' ||\n typeof md.rawPrompt !== 'string'\n ) {\n throw new Error(`FinSearchComp task ${task.id} missing judge metadata — loadTasks did not populate it`)\n }\n return md as unknown as FinSearchMeta\n}\n\n/** Fill the per-record judge_prompt_template. {ground_truth} is T1-only and unused for T2/T3. */\nfunction fillJudgePrompt(meta: FinSearchMeta, response: string): string {\n return meta.judgePromptTemplate\n .replaceAll('{prompt}', meta.rawPrompt)\n .replaceAll('{response_reference}', meta.responseReference)\n .replaceAll('{response}', response)\n}\n\ninterface JudgeRouter {\n baseUrl: string\n key: string\n model: string\n}\n\nfunction judgeRouter(): JudgeRouter {\n const key = process.env.TANGLE_API_KEY\n if (!key) throw new Error('TANGLE_API_KEY is required for the FinSearchComp per-record LLM judge')\n const model = process.env.JUDGE_MODEL ?? 'deepseek-v4-flash'\n const baseUrl = process.env.ROUTER_BASE ?? 'https://router.tangle.tools/v1'\n return { baseUrl, key, model }\n}\n\n/**\n * The judge emits a JSON object with an `answer_score`. In this dataset it is a\n * flat integer ({\"answer_score\": 1}); we also accept the nested-array form\n * ([[1]] → read [0][0]) the upstream scorer can produce. 1/positive = resolved.\n * A \"null\" sentinel (judge could not obtain ground truth) is NOT a pass.\n */\nfunction readAnswerScore(value: unknown): { resolved: boolean; score: number; raw: unknown } {\n let v: unknown = value\n // unwrap nested-array form: [[s]] / [s]\n while (Array.isArray(v)) v = v[0]\n if (v === null || v === undefined || v === 'null') {\n return { resolved: false, score: 0, raw: value }\n }\n const n = typeof v === 'number' ? v : typeof v === 'string' ? Number(v) : NaN\n if (!Number.isFinite(n)) {\n throw new Error(`FinSearchComp judge answer_score not numeric: ${JSON.stringify(value)}`)\n }\n // Their scale: 1 = correct, 0 = wrong. Treat any positive as resolved; clamp to 0..1.\n const resolved = n >= 1\n const score = n <= 0 ? 0 : n >= 1 ? 1 : n\n return { resolved, score, raw: value }\n}\n\n/** Extract the first JSON object carrying `answer_score` from judge output; throw if none. */\nfunction parseJudgeOutput(content: string): { resolved: boolean; score: number; raw: unknown } {\n // Prefer a fenced block, then fall back to scanning for a {...} with answer_score.\n const candidates: string[] = []\n for (const m of content.matchAll(/```(?:json)?\\s*([\\s\\S]*?)```/g)) candidates.push(m[1].trim())\n for (const m of content.matchAll(/\\{[^{}]*\"answer_score\"[\\s\\S]*?\\}/g)) candidates.push(m[0])\n candidates.push(content.trim())\n for (const c of candidates) {\n let parsed: { answer_score?: unknown }\n try {\n parsed = JSON.parse(c) as { answer_score?: unknown }\n } catch {\n continue\n }\n if ('answer_score' in parsed) return readAnswerScore(parsed.answer_score)\n }\n throw new Error(\n `FinSearchComp judge produced no parseable {\"answer_score\": …}: ${content.slice(0, 400)}`,\n )\n}\n\n/** Run the record's own judge via the router. Fail loud on transport/parse errors. */\nasync function runRecordJudge(meta: FinSearchMeta, response: string, router: JudgeRouter): Promise<BenchScore> {\n const res = await fetch(`${router.baseUrl}/chat/completions`, {\n method: 'POST',\n headers: { 'content-type': 'application/json', authorization: `Bearer ${router.key}` },\n body: JSON.stringify({\n model: router.model,\n temperature: 0,\n messages: [\n { role: 'system', content: meta.judgeSystemPrompt },\n { role: 'user', content: fillJudgePrompt(meta, response) },\n ],\n }),\n })\n if (!res.ok) {\n throw new Error(`FinSearchComp judge HTTP ${res.status}: ${(await res.text()).slice(0, 300)}`)\n }\n const body = (await res.json()) as { choices?: Array<{ message?: { content?: string } }> }\n const content = body.choices?.[0]?.message?.content\n if (typeof content !== 'string') {\n throw new Error(`FinSearchComp judge returned no message content: ${JSON.stringify(body).slice(0, 300)}`)\n }\n const { resolved, score, raw } = parseJudgeOutput(content)\n return {\n resolved,\n score,\n detail: JSON.stringify({\n taskType: meta.taskType,\n region: meta.region,\n answerScore: raw,\n judgeModel: router.model,\n }),\n }\n}\n\nfunction selectRecords(records: FinSearchRecord[], opts: LoadOptions): BenchTask[] {\n let tasks = records\n .filter((r) => SUPPORTED_TYPES.has(parseTaskType(r.prompt_id)))\n .map(recordToTask)\n if (opts.split) tasks = tasks.filter((t) => t.split === opts.split)\n if (opts.ids) {\n const want = new Set(opts.ids)\n tasks = tasks.filter((t) => want.has(t.id))\n } else if (opts.limit !== undefined) {\n tasks = balanceByType(tasks, opts.limit)\n }\n return tasks\n}\n\n/** Round-robin across task types (T2/T3) so a `limit` pulls a balanced mix\n * rather than slicing the type-ordered dataset's first N (which yields all\n * T2). Deterministic — preserves dataset order within each type. */\nfunction balanceByType(tasks: BenchTask[], limit: number): BenchTask[] {\n const queues = new Map<TaskType, BenchTask[]>()\n for (const t of tasks) {\n const k = parseTaskType(t.id)\n const q = queues.get(k)\n if (q) q.push(t)\n else queues.set(k, [t])\n }\n const lanes = [...queues.values()]\n const out: BenchTask[] = []\n for (let i = 0; out.length < limit; i++) {\n const before = out.length\n for (const lane of lanes) {\n if (i < lane.length) out.push(lane[i])\n if (out.length >= limit) break\n }\n if (out.length === before) break // all lanes exhausted\n }\n return out\n}\n\nasync function loadFixtures(opts: LoadOptions): Promise<BenchTask[]> {\n const records = JSON.parse(await readFile(FIXTURES, 'utf8')) as FinSearchRecord[]\n return selectRecords(records, opts)\n}\n\nasync function fetchDataset(): Promise<FinSearchRecord[]> {\n const res = await fetch(DATASET_URL)\n if (!res.ok) throw new Error(`FinSearchComp dataset HTTP ${res.status} fetching ${DATASET_URL}`)\n const data = (await res.json()) as FinSearchRecord[]\n if (!Array.isArray(data) || data.length === 0) {\n throw new Error(`FinSearchComp dataset empty or not an array: ${DATASET_URL}`)\n }\n return data\n}\n\nexport function createFinsearchcompAdapter(): BenchmarkAdapter {\n const fixturesMode = process.env.FINSEARCHCOMP_FIXTURES === '1'\n\n return {\n name: 'finsearchcomp',\n\n async preflight() {\n // The per-record LLM judge is the arbiter in both modes — its router\n // config must exist or no score can be produced.\n judgeRouter()\n console.warn(\n '[finsearchcomp] T1 (Time_Sensitive_Data_Fetching) is NOT scored: it needs a live ' +\n 'market snapshot / akshare ground truth for the judge {ground_truth} slot. ' +\n 'loadTasks supports T2 + T3 only; wire T1Seam to add T1.',\n )\n if (fixturesMode) {\n await readFile(FIXTURES, 'utf8').catch((err) => {\n throw new Error(\n `FINSEARCHCOMP_FIXTURES=1 but ${FIXTURES} unreadable: ${err instanceof Error ? err.message : err}`,\n )\n })\n return\n }\n const res = await fetch(DATASET_URL, { method: 'HEAD' }).catch((err) => {\n throw new Error(\n `finsearchcomp preflight failed reaching ${DATASET_URL}: ${err instanceof Error ? err.message : err}\\n` +\n `Fix: ensure network access to raw.githubusercontent.com, or set FINSEARCHCOMP_FIXTURES=1 ` +\n `to run against the committed fixtures offline.`,\n )\n })\n if (!res.ok) {\n throw new Error(\n `finsearchcomp preflight: dataset HEAD ${res.status} for ${DATASET_URL}. ` +\n `Set FINSEARCHCOMP_FIXTURES=1 to run against the committed fixtures offline.`,\n )\n }\n },\n\n async loadTasks(opts: LoadOptions = {}) {\n if (fixturesMode) return loadFixtures(opts)\n let records: FinSearchRecord[]\n try {\n records = await fetchDataset()\n } catch (err) {\n console.warn(\n `[finsearchcomp] live dataset fetch failed (${err instanceof Error ? err.message : err}); ` +\n `falling back to committed fixtures at ${FIXTURES}`,\n )\n return loadFixtures(opts)\n }\n return selectRecords(records, opts)\n },\n\n async goldArtifact(task: BenchTask) {\n // Gold artifact = the record's response_reference fed back as the student\n // answer, so verify-judge proves gold→resolved through the SAME per-record\n // judge the real artifact takes. The reference may carry tolerance/criteria\n // prose; the judge is built to read the value out of it.\n const meta = readMeta(task)\n return meta.responseReference\n },\n\n async judge(task: BenchTask, artifact: string): Promise<BenchScore> {\n const meta = readMeta(task)\n const response = artifact.trim()\n if (response.length === 0) {\n // Fail-closed — the benchmark's own judges score an empty answer 0.\n return {\n resolved: false,\n score: 0,\n detail: JSON.stringify({ taskType: meta.taskType, region: meta.region, reason: 'empty answer' }),\n }\n }\n return runRecordJudge(meta, response, judgeRouter())\n },\n }\n}\n"],"mappings":"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"}
@@ -1,28 +1,5 @@
1
- import { BenchmarkAdapter } from './types.js';
2
- import '@tangle-network/agent-runtime/loops';
3
-
4
- /**
5
- * FRAMES adapter (google/frames-benchmark). Worker artifact = a single free-text
6
- * final answer string (optionally preceded by a citations block). FRAMES ships,
7
- * per item, a short gold `Answer` PLUS gold `wiki_links` (the Wikipedia URLs
8
- * needed to answer) — the gold-citation column is what lets the loop's critic
9
- * check citation coverage deterministically.
10
- *
11
- * Judge is two-tier, deterministic-first:
12
- * Tier 1 — normalized exact / token-boundary containment match (no model tokens).
13
- * Tier 2 — a constrained binary equivalence gate via a pinned LLM (JUDGE_MODEL,
14
- * temperature 0), fired ONLY when Tier 1 misses, to absorb the
15
- * paraphrase/alias equivalence FRAMES intends to allow.
16
- *
17
- * score is binary (resolved ? 1 : 0) — FRAMES has no partial credit. The judge
18
- * resolves strictly on the Answer value; wiki_links are a SOFT signal surfaced
19
- * in `detail` for the critic, never a hard pass criterion.
20
- *
21
- * Requires for a live run: the bench `.venv` with `datasets` installed and a
22
- * JUDGE_MODEL router key. For offline/CI verification set FRAMES_FIXTURES=1 to
23
- * load the committed fixtures (bench/fixtures/frames.json) — no HF download.
24
- */
25
-
1
+ import { BenchmarkAdapter } from "./types.js";
2
+ //#region src/benchmarks/frames.d.ts
26
3
  /**
27
4
  * Typed seam for the future dynamic-topology research worker. The benchmark
28
5
  * adapter scores a plain `string` artifact (the BenchmarkAdapter contract); the
@@ -30,18 +7,18 @@ import '@tangle-network/agent-runtime/loops';
30
7
  * `finalAnswer` (+ optional `CITATIONS:` block) into that string before judging.
31
8
  */
32
9
  interface ResearchTask {
33
- id: string;
34
- question: string;
35
- /** Gold short answer — judge resolves strictly against this. */
36
- gold: string;
37
- /** Gold Wikipedia URLs — SOFT citation-coverage signal for the critic, never a hard gate. */
38
- goldSources: string[];
39
- /** FRAMES reasoning_types, e.g. 'numerical | temporal' — slices the scorecard by hop-type. */
40
- reasoningTypes: string;
10
+ id: string;
11
+ question: string;
12
+ /** Gold short answer — judge resolves strictly against this. */
13
+ gold: string;
14
+ /** Gold Wikipedia URLs — SOFT citation-coverage signal for the critic, never a hard gate. */
15
+ goldSources: string[];
16
+ /** FRAMES reasoning_types, e.g. 'numerical | temporal' — slices the scorecard by hop-type. */
17
+ reasoningTypes: string;
41
18
  }
42
19
  interface ResearchAnswer {
43
- finalAnswer: string;
44
- citations: string[];
20
+ finalAnswer: string;
21
+ citations: string[];
45
22
  }
46
23
  /** Port of the SQuAD/GAIA normalize_answer routine, plus number/date canonicalization. */
47
24
  declare function normalizeAnswer(input: string): string;
@@ -55,5 +32,6 @@ declare function parseFinalAnswer(artifact: string): string;
55
32
  /** Extract cited URLs from a `CITATIONS:` block and any bare URLs in the artifact. */
56
33
  declare function parseCitations(artifact: string): string[];
57
34
  declare function createFramesAdapter(): BenchmarkAdapter;
58
-
59
- export { type ResearchAnswer, type ResearchTask, createFramesAdapter, normalizeAnswer, parseCitations, parseFinalAnswer };
35
+ //#endregion
36
+ export { ResearchAnswer, ResearchTask, createFramesAdapter, normalizeAnswer, parseCitations, parseFinalAnswer };
37
+ //# sourceMappingURL=frames.d.ts.map