niceeval 0.6.1 → 0.7.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.
Files changed (378) hide show
  1. package/INDEX.md +23 -23
  2. package/README.zh.md +6 -6
  3. package/dist/agents/types.d.ts +69 -7
  4. package/dist/context/types.d.ts +32 -12
  5. package/dist/i18n/en.d.ts +54 -0
  6. package/dist/i18n/zh-CN.d.ts +57 -3
  7. package/dist/o11y/types.d.ts +16 -2
  8. package/dist/report/aggregate.d.ts +32 -24
  9. package/dist/report/aggregate.js +158 -50
  10. package/dist/report/built-in/index.d.ts +2 -0
  11. package/dist/report/built-in/index.js +8 -0
  12. package/dist/report/components.d.ts +93 -160
  13. package/dist/report/components.js +377 -114
  14. package/dist/report/compute.d.ts +87 -81
  15. package/dist/report/compute.js +597 -417
  16. package/dist/report/flag.d.ts +32 -6
  17. package/dist/report/flag.js +92 -4
  18. package/dist/report/format.d.ts +19 -11
  19. package/dist/report/format.js +30 -13
  20. package/dist/report/index.d.ts +16 -16
  21. package/dist/report/index.js +20 -21
  22. package/dist/report/load.js +3 -2
  23. package/dist/report/locale.d.ts +57 -33
  24. package/dist/report/locale.js +122 -56
  25. package/dist/report/metrics.d.ts +23 -4
  26. package/dist/report/metrics.js +110 -25
  27. package/dist/report/primitives.d.ts +48 -15
  28. package/dist/report/primitives.js +135 -26
  29. package/dist/report/react/AttemptList.d.ts +9 -7
  30. package/dist/report/react/AttemptList.js +17 -10
  31. package/dist/report/react/DeltaTable.js +19 -18
  32. package/dist/report/react/EvalList.d.ts +4 -4
  33. package/dist/report/react/EvalList.js +0 -0
  34. package/dist/report/react/ExperimentComparison.d.ts +10 -0
  35. package/dist/report/react/ExperimentComparison.js +12 -0
  36. package/dist/report/react/ExperimentList.d.ts +4 -3
  37. package/dist/report/react/ExperimentList.js +17 -18
  38. package/dist/report/react/MetricBars.js +5 -4
  39. package/dist/report/react/MetricLine.js +12 -5
  40. package/dist/report/react/MetricMatrix.js +1 -1
  41. package/dist/report/react/MetricScatter.js +59 -28
  42. package/dist/report/react/MetricTable.js +2 -12
  43. package/dist/report/react/ScopeSummary.d.ts +10 -0
  44. package/dist/report/react/ScopeSummary.js +17 -0
  45. package/dist/report/react/Scoreboard.js +6 -6
  46. package/dist/report/react/cell.js +2 -2
  47. package/dist/report/react/chart-math.d.ts +23 -6
  48. package/dist/report/react/chart-math.js +71 -19
  49. package/dist/report/react/fixtures.d.ts +5 -9
  50. package/dist/report/react/fixtures.js +110 -147
  51. package/dist/report/react/index.d.ts +15 -5
  52. package/dist/report/react/index.js +18 -7
  53. package/dist/report/report.d.ts +137 -16
  54. package/dist/report/report.js +259 -28
  55. package/dist/report/text/faces.d.ts +17 -19
  56. package/dist/report/text/faces.js +253 -184
  57. package/dist/report/text/plot.js +1 -1
  58. package/dist/report/text/table.js +38 -7
  59. package/dist/report/tree.d.ts +90 -40
  60. package/dist/report/tree.js +252 -94
  61. package/dist/report/types.d.ts +247 -284
  62. package/dist/report/types.js +4 -3
  63. package/dist/report/web.d.ts +21 -5
  64. package/dist/report/web.js +42 -16
  65. package/dist/results/select.d.ts +38 -16
  66. package/dist/results/select.js +73 -25
  67. package/dist/results/types.d.ts +49 -14
  68. package/dist/runner/feedback/sink.d.ts +110 -0
  69. package/dist/runner/types.d.ts +513 -22
  70. package/dist/sandbox/docker.d.ts +23 -2
  71. package/dist/sandbox/e2b.d.ts +15 -1
  72. package/dist/sandbox/errors.d.ts +30 -3
  73. package/dist/sandbox/io-retry.d.ts +17 -0
  74. package/dist/sandbox/registry.d.ts +2 -0
  75. package/dist/sandbox/resolve.d.ts +18 -5
  76. package/dist/sandbox/retry.d.ts +11 -1
  77. package/dist/sandbox/types.d.ts +39 -5
  78. package/dist/sandbox/vercel.d.ts +7 -1
  79. package/dist/scoring/coverage.d.ts +30 -0
  80. package/dist/scoring/display.d.ts +21 -0
  81. package/dist/scoring/display.js +120 -0
  82. package/dist/scoring/types.d.ts +103 -20
  83. package/dist/shared/aggregate.d.ts +4 -2
  84. package/dist/shared/aggregate.js +8 -7
  85. package/dist/shared/types.d.ts +28 -0
  86. package/dist/tty-line.d.ts +0 -4
  87. package/dist/util.d.ts +23 -0
  88. package/docs-site/zh/README.md +44 -0
  89. package/docs-site/zh/examples/ai-agent-application.mdx +63 -0
  90. package/docs-site/zh/examples/coding-agent-extensions.mdx +57 -0
  91. package/docs-site/zh/examples/index.mdx +50 -0
  92. package/docs-site/zh/{concepts → explanation}/adapter.mdx +31 -13
  93. package/docs-site/zh/{concepts → explanation}/assert.mdx +7 -7
  94. package/docs-site/zh/{concepts → explanation}/drive.mdx +8 -8
  95. package/docs-site/zh/{concepts → explanation}/evals.mdx +4 -4
  96. package/docs-site/zh/{concepts → explanation}/experiment.mdx +8 -8
  97. package/docs-site/zh/{concepts → explanation}/hitl.mdx +8 -8
  98. package/docs-site/zh/{concepts → explanation}/judge.mdx +5 -5
  99. package/docs-site/zh/{concepts → explanation}/overview.mdx +11 -11
  100. package/docs-site/zh/{guides → explanation}/runner.mdx +18 -8
  101. package/docs-site/zh/{concepts → explanation}/tier.mdx +6 -6
  102. package/docs-site/zh/{guides → how-to}/agent-feedback-loop.mdx +35 -33
  103. package/docs-site/zh/{guides → how-to}/authoring.mdx +35 -2
  104. package/docs-site/zh/{guides → how-to}/ci-integration.mdx +23 -12
  105. package/docs-site/zh/{guides → how-to}/connect-otel.mdx +6 -6
  106. package/docs-site/zh/{guides → how-to}/connect-your-agent.mdx +47 -21
  107. package/docs-site/zh/{guides → how-to}/custom-reports.mdx +34 -39
  108. package/docs-site/zh/{guides → how-to}/dataset-fanout.mdx +25 -3
  109. package/docs-site/zh/{guides → how-to}/experiments.mdx +12 -5
  110. package/docs-site/zh/how-to/publish-report.mdx +105 -0
  111. package/docs-site/zh/{guides → how-to}/reporters.mdx +2 -2
  112. package/docs-site/zh/{guides → how-to}/sandbox-agent.mdx +56 -7
  113. package/docs-site/zh/how-to/sandbox-providers.mdx +350 -0
  114. package/docs-site/zh/{guides → how-to}/scoring-guide.mdx +4 -4
  115. package/docs-site/zh/{guides → how-to}/viewing-results.mdx +82 -39
  116. package/docs-site/zh/{guides → how-to}/write-experiment.mdx +6 -4
  117. package/docs-site/zh/{guides → how-to}/write-send.mdx +30 -14
  118. package/docs-site/zh/index.mdx +24 -26
  119. package/docs-site/zh/introduction.mdx +8 -8
  120. package/docs-site/zh/reference/builtin-agents.mdx +32 -5
  121. package/docs-site/zh/reference/capabilities.mdx +8 -8
  122. package/docs-site/zh/reference/cli.mdx +40 -12
  123. package/docs-site/zh/reference/define-agent.mdx +58 -5
  124. package/docs-site/zh/reference/define-config.mdx +1 -1
  125. package/docs-site/zh/reference/define-eval.mdx +42 -9
  126. package/docs-site/zh/reference/events.mdx +3 -3
  127. package/docs-site/zh/reference/expect.mdx +26 -1
  128. package/docs-site/zh/{guides → reference}/official-adapters.mdx +32 -8
  129. package/docs-site/zh/{guides → reference}/report-components.mdx +45 -33
  130. package/docs-site/zh/{guides → reference}/results-data.mdx +21 -13
  131. package/docs-site/zh/troubleshooting/debug-sandbox.mdx +57 -0
  132. package/docs-site/zh/troubleshooting/debugging.mdx +212 -0
  133. package/docs-site/zh/{quickstart.mdx → tutorials/quickstart.mdx} +5 -17
  134. package/package.json +10 -2
  135. package/src/agents/ai-sdk-otel.test.ts +1 -0
  136. package/src/agents/ai-sdk.test.ts +3 -0
  137. package/src/agents/ai-sdk.ts +3 -0
  138. package/src/agents/bub-install-spec.test.ts +34 -0
  139. package/src/agents/bub-install-spec.ts +32 -0
  140. package/src/agents/bub.ts +31 -32
  141. package/src/agents/claude-code.test.ts +130 -9
  142. package/src/agents/claude-code.ts +76 -4
  143. package/src/agents/codex.test.ts +189 -40
  144. package/src/agents/codex.ts +155 -14
  145. package/src/agents/coding-cli-versions.test.ts +15 -0
  146. package/src/agents/coding-cli-versions.ts +3 -0
  147. package/src/agents/index.ts +13 -2
  148. package/src/agents/langgraph.test.ts +204 -0
  149. package/src/agents/langgraph.ts +495 -0
  150. package/src/agents/marketplace.ts +85 -0
  151. package/src/agents/native-config.test.ts +179 -0
  152. package/src/agents/native-config.ts +267 -0
  153. package/src/agents/openai-compat.test.ts +1 -0
  154. package/src/agents/openai-compat.ts +1 -1
  155. package/src/agents/openclaw.test.ts +31 -0
  156. package/src/agents/openclaw.ts +171 -0
  157. package/src/agents/plugin-config.test.ts +1 -0
  158. package/src/agents/sdk-streams.test.ts +79 -0
  159. package/src/agents/sdk-streams.ts +55 -10
  160. package/src/agents/skills.test.ts +1 -0
  161. package/src/agents/streaming.test.ts +3 -9
  162. package/src/agents/streaming.ts +2 -2
  163. package/src/agents/types.ts +71 -8
  164. package/src/agents/ui-message-stream.test.ts +3 -0
  165. package/src/cli.ts +446 -124
  166. package/src/context/context.test.ts +51 -12
  167. package/src/context/context.ts +162 -30
  168. package/src/context/session.test.ts +2 -1
  169. package/src/context/session.ts +115 -7
  170. package/src/context/types.ts +30 -12
  171. package/src/define.test.ts +13 -8
  172. package/src/define.ts +25 -4
  173. package/src/expect/index.ts +53 -23
  174. package/src/i18n/en.ts +81 -17
  175. package/src/i18n/zh-CN.ts +80 -17
  176. package/src/o11y/cost.test.ts +1 -0
  177. package/src/o11y/execution-tree.test.ts +1 -20
  178. package/src/o11y/otlp/mappers/claude-code.test.ts +1 -0
  179. package/src/o11y/otlp/parse.test.ts +1 -0
  180. package/src/o11y/otlp/turn-otel.test.ts +1 -0
  181. package/src/o11y/parsers/bub.test.ts +1 -0
  182. package/src/o11y/parsers/claude-code.test.ts +1 -34
  183. package/src/o11y/parsers/openclaw.test.ts +154 -0
  184. package/src/o11y/parsers/openclaw.ts +310 -0
  185. package/src/o11y/prices.json +746 -311
  186. package/src/o11y/tool-names.test.ts +1 -0
  187. package/src/o11y/types.ts +16 -2
  188. package/src/report/aggregate.ts +178 -61
  189. package/src/report/built-in/index.tsx +9 -0
  190. package/src/report/components.tsx +625 -279
  191. package/src/report/compute.ts +723 -491
  192. package/src/report/dual-render.test.tsx +741 -1024
  193. package/src/report/flag.ts +104 -12
  194. package/src/report/format.ts +32 -12
  195. package/src/report/index.ts +119 -46
  196. package/src/report/load.ts +3 -2
  197. package/src/report/locale.ts +136 -65
  198. package/src/report/metrics.ts +108 -25
  199. package/src/report/primitives.tsx +196 -45
  200. package/src/report/react/AttemptList.tsx +30 -43
  201. package/src/report/react/DeltaTable.tsx +63 -45
  202. package/src/report/react/EvalList.tsx +0 -0
  203. package/src/report/react/ExperimentComparison.tsx +73 -0
  204. package/src/report/react/ExperimentList.tsx +50 -32
  205. package/src/report/react/MetricBars.tsx +5 -4
  206. package/src/report/react/MetricLine.tsx +13 -8
  207. package/src/report/react/MetricMatrix.tsx +2 -2
  208. package/src/report/react/MetricScatter.tsx +86 -34
  209. package/src/report/react/MetricTable.tsx +4 -76
  210. package/src/report/react/ScopeSummary.tsx +86 -0
  211. package/src/report/react/Scoreboard.tsx +28 -10
  212. package/src/report/react/cell.tsx +2 -2
  213. package/src/report/react/chart-math.test.ts +85 -0
  214. package/src/report/react/chart-math.ts +101 -22
  215. package/src/report/react/enhance.js +89 -5
  216. package/src/report/react/fixtures.ts +114 -154
  217. package/src/report/react/index.tsx +24 -39
  218. package/src/report/react/render.test.tsx +138 -158
  219. package/src/report/react/styles.css +243 -82
  220. package/src/report/report.test.ts +779 -841
  221. package/src/report/report.ts +423 -41
  222. package/src/report/text/faces.ts +290 -193
  223. package/src/report/text/plot.ts +1 -1
  224. package/src/report/text/table.ts +44 -7
  225. package/src/report/tree.ts +362 -104
  226. package/src/report/types.ts +261 -271
  227. package/src/report/web.ts +63 -20
  228. package/src/results/annotated-source.test.ts +62 -9
  229. package/src/results/annotated-source.ts +64 -6
  230. package/src/results/attempt-evidence.test.ts +13 -11
  231. package/src/results/attempt-evidence.ts +20 -13
  232. package/src/results/attempt-source.ts +6 -3
  233. package/src/results/copy.ts +150 -60
  234. package/src/results/host-equivalence.test.ts +34 -20
  235. package/src/results/index.ts +12 -4
  236. package/src/results/locator.test.ts +1 -22
  237. package/src/results/open.ts +15 -5
  238. package/src/results/publish.ts +149 -0
  239. package/src/results/results.test.ts +89 -54
  240. package/src/results/select.ts +104 -34
  241. package/src/results/truncate.ts +90 -0
  242. package/src/results/types.ts +43 -14
  243. package/src/results/writer.ts +31 -13
  244. package/src/runner/attempt.test.ts +138 -7
  245. package/src/runner/attempt.ts +603 -104
  246. package/src/runner/discover.test.ts +47 -0
  247. package/src/runner/discover.ts +36 -2
  248. package/src/runner/eval-source.test.ts +1 -27
  249. package/src/runner/feedback/agent.test.ts +504 -0
  250. package/src/runner/feedback/agent.ts +409 -0
  251. package/src/runner/feedback/ci.test.ts +562 -0
  252. package/src/runner/feedback/ci.ts +401 -0
  253. package/src/runner/feedback/coordinator.test.ts +317 -0
  254. package/src/runner/feedback/coordinator.ts +397 -0
  255. package/src/runner/feedback/failure.ts +40 -0
  256. package/src/runner/feedback/human.test.ts +616 -0
  257. package/src/runner/feedback/human.ts +535 -0
  258. package/src/runner/feedback/index.ts +66 -0
  259. package/src/runner/feedback/io.ts +78 -0
  260. package/src/runner/feedback/profile.test.ts +50 -0
  261. package/src/runner/feedback/profile.ts +58 -0
  262. package/src/runner/feedback/reducer.test.ts +395 -0
  263. package/src/runner/feedback/reducer.ts +260 -0
  264. package/src/runner/feedback/renderer.ts +82 -0
  265. package/src/runner/feedback/sink.ts +203 -0
  266. package/src/runner/feedback/testing.ts +106 -0
  267. package/src/runner/ledger.test.ts +230 -0
  268. package/src/runner/ledger.ts +329 -0
  269. package/src/runner/report.test.ts +128 -3
  270. package/src/runner/report.ts +33 -9
  271. package/src/runner/reporters/artifacts.ts +8 -2
  272. package/src/runner/reporters/braintrust.test.ts +8 -7
  273. package/src/runner/reporters/braintrust.ts +9 -2
  274. package/src/runner/reporters/index.ts +2 -2
  275. package/src/runner/reporters/json.test.ts +162 -0
  276. package/src/runner/reporters/json.ts +35 -8
  277. package/src/runner/reporters/shared.ts +1 -5
  278. package/src/runner/run.test.ts +760 -3
  279. package/src/runner/run.ts +243 -37
  280. package/src/runner/sandbox-prep.ts +3 -42
  281. package/src/runner/timing.ts +158 -0
  282. package/src/runner/types.ts +518 -22
  283. package/src/sandbox/checkpoint.test.ts +55 -0
  284. package/src/sandbox/checkpoint.ts +29 -8
  285. package/src/sandbox/cli-commands.ts +407 -0
  286. package/src/sandbox/docker.ts +115 -16
  287. package/src/sandbox/e2b-agent-template.test.ts +56 -0
  288. package/src/sandbox/e2b-agent-template.ts +94 -0
  289. package/src/sandbox/e2b.ts +74 -9
  290. package/src/sandbox/errors.ts +111 -4
  291. package/src/sandbox/index.ts +2 -0
  292. package/src/sandbox/io-retry.test.ts +58 -0
  293. package/src/sandbox/io-retry.ts +45 -0
  294. package/src/sandbox/keep-registry.test.ts +86 -0
  295. package/src/sandbox/keep-registry.ts +142 -0
  296. package/src/sandbox/keep.ts +178 -0
  297. package/src/sandbox/paths.test.ts +1 -0
  298. package/src/sandbox/paths.ts +19 -8
  299. package/src/sandbox/registry.ts +20 -3
  300. package/src/sandbox/resolve.ts +76 -11
  301. package/src/sandbox/retry.test.ts +70 -0
  302. package/src/sandbox/retry.ts +46 -4
  303. package/src/sandbox/types.ts +44 -6
  304. package/src/sandbox/vercel.ts +43 -20
  305. package/src/scoring/collector.ts +60 -17
  306. package/src/scoring/coverage.ts +95 -0
  307. package/src/scoring/diff.ts +81 -0
  308. package/src/scoring/display.test.ts +121 -0
  309. package/src/scoring/display.ts +133 -0
  310. package/src/scoring/evidence.test.ts +189 -0
  311. package/src/scoring/judge.test.ts +142 -0
  312. package/src/scoring/judge.ts +15 -18
  313. package/src/scoring/scoped.ts +217 -50
  314. package/src/scoring/types.ts +117 -20
  315. package/src/scoring/verdict.ts +16 -4
  316. package/src/shared/aggregate.ts +8 -6
  317. package/src/shared/types.ts +31 -0
  318. package/src/show/compose.ts +50 -67
  319. package/src/show/index.ts +127 -56
  320. package/src/show/render.ts +662 -131
  321. package/src/show/report-host.test.ts +188 -0
  322. package/src/show/report-host.ts +375 -0
  323. package/src/show/show.test.ts +320 -54
  324. package/src/tty-line.ts +8 -26
  325. package/src/util.test.ts +1 -0
  326. package/src/util.ts +41 -0
  327. package/src/view/app/App.test.tsx +69 -0
  328. package/src/view/app/App.tsx +144 -48
  329. package/src/view/app/components/AttemptModal.tsx +423 -11
  330. package/src/view/app/components/CodeView.tsx +41 -14
  331. package/src/view/app/components/CopyControls.tsx +2 -2
  332. package/src/view/app/i18n.ts +37 -17
  333. package/src/view/app/lib/attempt-route.test.ts +1 -0
  334. package/src/view/app/lib/verdict.ts +7 -9
  335. package/src/view/app/main.tsx +13 -8
  336. package/src/view/app/pages/{RunsPage.tsx → AttemptsPage.tsx} +6 -6
  337. package/src/view/app/types.ts +4 -1
  338. package/src/view/artifact-serving.test.ts +2 -1
  339. package/src/view/client-dist/app.css +1 -1
  340. package/src/view/client-dist/app.js +17 -17
  341. package/src/view/data.test.ts +10 -3
  342. package/src/view/data.ts +155 -49
  343. package/src/view/index.ts +56 -41
  344. package/src/view/server.ts +37 -15
  345. package/src/view/shared/types.ts +34 -5
  346. package/src/view/styles.css +227 -0
  347. package/src/view/view-report.test.ts +167 -62
  348. package/dist/report/built-ins/experiment-comparison.d.ts +0 -1
  349. package/dist/report/built-ins/experiment-comparison.js +0 -13
  350. package/dist/report/built-ins/index.d.ts +0 -1
  351. package/dist/report/built-ins/index.js +0 -2
  352. package/dist/report/react/GroupSummary.d.ts +0 -8
  353. package/dist/report/react/GroupSummary.js +0 -8
  354. package/dist/report/react/RunOverview.d.ts +0 -8
  355. package/dist/report/react/RunOverview.js +0 -12
  356. package/docs-site/zh/example/ai-agent-application.mdx +0 -152
  357. package/docs-site/zh/example/claude-code-codex-plugin.mdx +0 -167
  358. package/docs-site/zh/example/claude-code-codex-skill.mdx +0 -152
  359. package/docs-site/zh/example/showcase.mdx +0 -39
  360. package/docs-site/zh/guides/publish-report.mdx +0 -91
  361. package/docs-site/zh/guides/sandbox-providers.mdx +0 -102
  362. package/src/report/built-in-user-parity.test.tsx +0 -640
  363. package/src/report/built-ins/experiment-comparison.tsx +0 -19
  364. package/src/report/built-ins/index.ts +0 -2
  365. package/src/report/react/GroupSummary.tsx +0 -66
  366. package/src/report/react/RunOverview.tsx +0 -109
  367. package/src/runner/reporters/console.ts +0 -70
  368. package/src/runner/reporters/live.test.ts +0 -56
  369. package/src/runner/reporters/live.ts +0 -247
  370. package/src/runner/reporters/quiet.test.ts +0 -66
  371. package/src/runner/reporters/quiet.ts +0 -49
  372. package/src/runner/reporters/table.ts +0 -277
  373. /package/docs-site/zh/{example/tier1-ai-sdk-v7.mdx → examples/integrations/ai-sdk-v7.mdx} +0 -0
  374. /package/docs-site/zh/{example/tier1-claude-sdk.mdx → examples/integrations/claude-sdk.mdx} +0 -0
  375. /package/docs-site/zh/{example/tier1-codex-sdk.mdx → examples/integrations/codex-sdk.mdx} +0 -0
  376. /package/docs-site/zh/{example/tier1-langgraph.mdx → examples/integrations/langgraph.mdx} +0 -0
  377. /package/docs-site/zh/{example/tier1-pi-sdk.mdx → examples/integrations/pi-sdk.mdx} +0 -0
  378. /package/docs-site/zh/{guides → how-to}/fixtures.mdx +0 -0
@@ -1,66 +0,0 @@
1
- // GroupSummary:一组 experiment(如自定义报告里同一 <Section> 内的全部 experiment)的
2
- // 紧凑摘要块——恢复旧 GroupSelector 卡片的信息密度(通过率、experiment/eval 数、failed、
3
- // errored、总成本、最后运行时间),但只渲染 GroupSummaryData 算好的 MetricCell / 计数,
4
- // 不现场重算比例(docs/feature/reports/library.md「GroupSummary」)。errored 为 0 时省略该片段(旧卡片同一姿态),
5
- // 但这只是渲染取舍——数据侧 verdicts.errored 字段本身永不省略。
6
-
7
- import type { ReactElement } from "react";
8
- import type { GroupSummaryData } from "../types.ts";
9
- import { DEFAULT_REPORT_LOCALE, localeText, type ReportLocale } from "../locale.ts";
10
- import { MetricCellView } from "./cell.tsx";
11
- import { cx, formatUSD } from "./format.ts";
12
-
13
- export function GroupSummary({
14
- data,
15
- className,
16
- locale = DEFAULT_REPORT_LOCALE,
17
- }: {
18
- data: GroupSummaryData;
19
- className?: string;
20
- locale?: ReportLocale;
21
- }): ReactElement {
22
- const missing = <span className="nre-missing">{localeText(locale, "cell.missing")}</span>;
23
- return (
24
- <div className={cx("nre", "nre-group-summary", className)}>
25
- <dl className="nre-group-kpis">
26
- <div className="nre-group-kpi nre-group-kpi-rate">
27
- <dt>{localeText(locale, "overview.passRate")}</dt>
28
- <dd>
29
- <MetricCellView cell={data.passRate} locale={locale} />
30
- </dd>
31
- </div>
32
- <div className="nre-group-kpi">
33
- <dt>{localeText(locale, "groupSummary.experiments")}</dt>
34
- <dd>{data.experiments}</dd>
35
- </div>
36
- <div className="nre-group-kpi">
37
- <dt>{localeText(locale, "overview.evals")}</dt>
38
- <dd>{data.evals}</dd>
39
- </div>
40
- <div className="nre-group-kpi">
41
- <dt>{localeText(locale, "overview.attempts")}</dt>
42
- <dd>{data.attempts}</dd>
43
- </div>
44
- <div className="nre-group-kpi">
45
- <dt>{localeText(locale, "verdict.failed")}</dt>
46
- <dd className="nre-verdict-failed">{data.verdicts.failed}</dd>
47
- </div>
48
- {/* errored 为 0 省略这一片段(旧 GroupSelector 卡片同一姿态);数据字段本身不受影响 */}
49
- {data.verdicts.errored > 0 && (
50
- <div className="nre-group-kpi">
51
- <dt>{localeText(locale, "verdict.errored")}</dt>
52
- <dd className="nre-verdict-errored">{data.verdicts.errored}</dd>
53
- </div>
54
- )}
55
- <div className="nre-group-kpi">
56
- <dt>{localeText(locale, "overview.totalCost")}</dt>
57
- {/* totalCostUSD 全缺 = null:显示缺数据,不编 $0 */}
58
- <dd>{data.totalCostUSD === null ? missing : formatUSD(data.totalCostUSD)}</dd>
59
- </div>
60
- </dl>
61
- {data.lastRunAt && (
62
- <p className="nre-group-summary-time">{localeText(locale, "latestRun", { run: data.lastRunAt })}</p>
63
- )}
64
- </div>
65
- );
66
- }
@@ -1,109 +0,0 @@
1
- // RunOverview:页头 KPI 条——「这批数据是什么」。
2
- // 数字下面标注数据来源(几个快照、何时跑的);warnings 有内容时直接显示在条内,
3
- // 诚实不靠使用者记得渲染(docs/feature/reports/library.md「RunOverview」行为清单)。
4
-
5
- import type { ReactElement } from "react";
6
- import type { OverviewData } from "../types.ts";
7
- import { DEFAULT_REPORT_LOCALE, localeText, type ReportLocale } from "../locale.ts";
8
- import { cx, formatDurationMs, formatUSD } from "./format.ts";
9
-
10
- export function RunOverview({
11
- data,
12
- className,
13
- locale = DEFAULT_REPORT_LOCALE,
14
- }: {
15
- data: OverviewData;
16
- className?: string;
17
- locale?: ReportLocale;
18
- }): ReactElement {
19
- const { totals } = data;
20
- const missing = <span className="nre-missing">{localeText(locale, "cell.missing")}</span>;
21
- // 通过率只渲染 computeCell 算好的 MetricCell,不现场重算——覆盖率角标复用
22
- // MetricCellView 的缺数据/coverage 语义(缺数据 = 显式文案,绝不画 0%)
23
- const { passRate } = totals;
24
- const passRateNode =
25
- passRate.value === null ? (
26
- missing
27
- ) : (
28
- <>
29
- {passRate.display}
30
- {passRate.samples < passRate.total && (
31
- <sup
32
- className="nre-coverage"
33
- title={localeText(locale, "cell.coverageTitle", { samples: passRate.samples, total: passRate.total })}
34
- >
35
- {passRate.samples}/{passRate.total}
36
- </sup>
37
- )}
38
- </>
39
- );
40
-
41
- return (
42
- <header className={cx("nre", "nre-overview", className)}>
43
- <dl className="nre-kpis">
44
- <div className="nre-kpi">
45
- <dt>{localeText(locale, "overview.snapshots")}</dt>
46
- <dd>{data.snapshots.length}</dd>
47
- </div>
48
- <div className="nre-kpi">
49
- <dt>{localeText(locale, "overview.evals")}</dt>
50
- <dd>{totals.evals}</dd>
51
- </div>
52
- <div className="nre-kpi">
53
- <dt>{localeText(locale, "overview.attempts")}</dt>
54
- <dd>{totals.attempts}</dd>
55
- </div>
56
- <div className="nre-kpi">
57
- <dt>{localeText(locale, "overview.passRate")}</dt>
58
- <dd>{passRateNode}</dd>
59
- </div>
60
- <div className="nre-kpi">
61
- <dt>{localeText(locale, "overview.totalCost")}</dt>
62
- {/* costUSD 全缺 = null:显示缺数据,不编 $0 */}
63
- <dd>{totals.costUSD === null ? missing : formatUSD(totals.costUSD)}</dd>
64
- </div>
65
- <div className="nre-kpi">
66
- <dt>{localeText(locale, "overview.totalDuration")}</dt>
67
- <dd>{formatDurationMs(totals.durationMs)}</dd>
68
- </div>
69
- </dl>
70
-
71
- <p className="nre-verdicts">
72
- <span className="nre-verdict nre-verdict-passed">
73
- {localeText(locale, "verdict.passed")} {totals.passed}
74
- </span>
75
- <span className="nre-verdict nre-verdict-failed">
76
- {localeText(locale, "verdict.failed")} {totals.failed}
77
- </span>
78
- <span className="nre-verdict nre-verdict-errored">
79
- {localeText(locale, "verdict.errored")} {totals.errored}
80
- </span>
81
- <span className="nre-verdict nre-verdict-skipped">
82
- {localeText(locale, "verdict.skipped")} {totals.skipped}
83
- </span>
84
- </p>
85
-
86
- {/* 数据来源:哪些快照、何时跑的——报告的数字都从这里来 */}
87
- <p className="nre-source">
88
- {localeText(locale, "overview.source", { n: data.snapshots.length })}
89
- {data.snapshots.map((s) => (
90
- <span key={`${s.experimentId}@${s.startedAt}`} className="nre-source-snapshot">
91
- {s.experimentId}({s.agent}
92
- {s.model ? ` · ${s.model}` : ""})@ {s.startedAt}
93
- </span>
94
- ))}
95
- </p>
96
-
97
- {/* Selection 的警告(残缺快照等)直接渲染在条内,不静默;结构化字段供程序判断,这里打 message */}
98
- {data.warnings.length > 0 && (
99
- <ul className="nre-warnings">
100
- {data.warnings.map((w, i) => (
101
- <li key={i} className="nre-warning" data-kind={w.kind}>
102
- {w.message}
103
- </li>
104
- ))}
105
- </ul>
106
- )}
107
- </header>
108
- );
109
- }
@@ -1,70 +0,0 @@
1
- // 控制台报告器:流式逐行输出,失败断言内联展开,末尾出效率三件套(时间 / token / $)。
2
-
3
- import type { EvalResult, Reporter, RunSummary } from "../../types.ts";
4
- import { t } from "../../i18n/index.ts";
5
- import { formatCost, formatDuration, formatTokens, renderRunReport } from "./table.ts";
6
- import { verdictSymbol } from "./shared.ts";
7
-
8
- export function Console(): Reporter {
9
- return {
10
- onRunStart(evals, _agent, shape) {
11
- // compare(多 agent / 多 model)或 runs>1 时,实际 attempt 数 > eval 数。头部如实报清,
12
- // 否则「本次运行 5 个 eval」会和末尾「5 passed, 5 failed」(按 attempt 计 10)对不上。
13
- const n = shape?.evals ?? evals.length;
14
- const extra =
15
- shape && shape.totalRuns > n
16
- ? t("report.runStartExtra", { configs: shape.configs, totalRuns: shape.totalRuns })
17
- : "";
18
- process.stdout.write(t("report.runStart", { count: n, extra, concurrency: shape?.maxConcurrency ?? "?" }));
19
- },
20
- onEvalComplete(result: EvalResult) {
21
- const sym = verdictSymbol(result.verdict);
22
- const tok = (result.usage?.inputTokens ?? 0) + (result.usage?.outputTokens ?? 0);
23
- // requests > 0 但 tokens = 0 → agent 跑了但不上报用量(如 bub);显示 — 而非误导性的 0
24
- const tokStr = tok > 0 ? `${formatTokens(tok)} tok` : (result.usage?.requests ?? 0) > 0 ? `— tok` : `0 tok`;
25
- const cost = result.estimatedCostUSD !== undefined ? ` ${formatCost(result.estimatedCostUSD)}` : "";
26
- const who = result.model ? `${result.agent}/${result.model}` : result.agent;
27
- const meta = `(${formatDuration(result.durationMs)} ${tokStr}${cost})`;
28
- const label = result.verdict === "passed" ? "" : ` ${formatVerdict(result.verdict)}`;
29
- process.stdout.write(` ${sym} ${result.id}${label} [${who}] ${meta}\n`);
30
-
31
- if (result.skipReason) {
32
- process.stdout.write(` ○ ${t("report.skipped")}: ${result.skipReason}\n`);
33
- }
34
- if (result.error) {
35
- process.stdout.write(` ! ${t("report.error")}: ${truncate(result.error, 400)}\n`);
36
- }
37
- let lastGroup: string | undefined;
38
- for (const a of result.assertions) {
39
- if (a.passed) continue;
40
- if (a.group !== undefined && a.group !== lastGroup) {
41
- process.stdout.write(` ▸ ${a.group}\n`);
42
- }
43
- lastGroup = a.group;
44
- const sev = a.severity === "gate" ? t("report.gate") : t("report.soft");
45
- const thr = a.threshold !== undefined
46
- ? t("report.assertionThreshold", { score: a.score.toFixed(2), threshold: a.threshold })
47
- : "";
48
- const indent = a.group !== undefined ? " " : " ";
49
- process.stdout.write(`${indent}- ${sev}: ${a.name}${thr}${a.detail ? ` — ${truncate(a.detail, 300)}` : ""}\n`);
50
- }
51
- },
52
- onRunComplete(summary: RunSummary) {
53
- process.stdout.write(renderRunReport(summary));
54
- },
55
- };
56
- }
57
-
58
- function formatVerdict(verdict: string): string {
59
- switch (verdict) {
60
- case "passed": return t("report.passed");
61
- case "failed": return t("report.failed");
62
- case "errored": return t("report.errored");
63
- case "skipped": return t("report.skipped");
64
- default: return verdict;
65
- }
66
- }
67
-
68
- function truncate(s: string, n: number): string {
69
- return s.length > n ? s.slice(0, n) + "…" : s;
70
- }
@@ -1,56 +0,0 @@
1
- import { afterEach, describe, expect, it, vi } from "vitest";
2
- import { Live, type LiveRow } from "./live.ts";
3
-
4
- function withMockTty<T>(fn: () => T): { writes: string[]; result: T } {
5
- const writes: string[] = [];
6
- vi.spyOn(process.stderr, "write").mockImplementation((chunk: unknown) => {
7
- writes.push(String(chunk));
8
- return true;
9
- });
10
- Object.defineProperty(process.stderr, "isTTY", { value: true, configurable: true });
11
- Object.defineProperty(process.stderr, "columns", { value: 120, configurable: true });
12
- Object.defineProperty(process.stderr, "rows", { value: 40, configurable: true });
13
- return { writes, result: fn() };
14
- }
15
-
16
- describe("Live carried rows", () => {
17
- afterEach(() => {
18
- vi.restoreAllMocks();
19
- });
20
-
21
- it("renders a carried row as already-done from the first frame, not waiting for a slot", () => {
22
- const rows: LiveRow[] = [
23
- { evalId: "memory/carried-fail", who: "codex-e2b", total: 1, carriedVerdict: "failed" },
24
- { evalId: "memory/fresh", who: "codex-e2b", total: 1 },
25
- ];
26
- const live = Live(rows, 2);
27
-
28
- const { writes } = withMockTty(() => {
29
- live.onRunStart?.([], { name: "codex", kind: "sandbox" } as never, {
30
- evals: 2,
31
- configs: 1,
32
- totalRuns: 1,
33
- maxConcurrency: 5,
34
- });
35
- });
36
- vi.spyOn(process.stdout, "write").mockImplementation(() => true);
37
- live.onRunComplete?.({
38
- startedAt: "",
39
- finishedAt: "",
40
- durationMs: 0,
41
- results: [],
42
- totalRuns: 1,
43
- } as never);
44
-
45
- const out = writes.join("");
46
- const carriedLine = out.split("\n").find((l) => l.includes("memory/carried-fail"));
47
- const freshLine = out.split("\n").find((l) => l.includes("memory/fresh"));
48
-
49
- expect(carriedLine).toBeDefined();
50
- expect(carriedLine).toContain("✗");
51
- expect(carriedLine).not.toContain("waiting");
52
-
53
- expect(freshLine).toBeDefined();
54
- expect(freshLine).toContain("waiting");
55
- });
56
- });
@@ -1,247 +0,0 @@
1
- // Live terminal reporter:在 TTY 终端里渲染实时状态表,每个 (eval, who) 对占一行。
2
- // spinner 每 80ms 刷新;attempt 完成后行内显示 ✓/✗/~ 符号。
3
- // onRunComplete 时清除状态表,打印和网页榜单同口径的表格报告。
4
-
5
- import type { Reporter, ReporterEvent, RunShape, RunSummary } from "../../types.ts";
6
- import { t } from "../../i18n/index.ts";
7
- import { renderRunReport } from "./table.ts";
8
- import { verdictSymbol, WAITING_SYM } from "./shared.ts";
9
- import { runWho } from "../types.ts";
10
- import { onBeforeExternalTerminalWrite } from "../../tty-line.ts";
11
-
12
- const SPINNER = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
13
-
14
- // 进度日志里的基础设施噪声:OTLP 端口、remote-agent 启动提示、trace span 计数。
15
- // 这些在行尾显示毫无意义,直接丢弃。
16
- const NOISE_PATTERNS = [
17
- /^OTLP /,
18
- /^使用 remote agent/,
19
- /^using remote agent/,
20
- /^驱动 agent/,
21
- /^driving agent/,
22
- /^trace:\d/,
23
- /^agent tracing/,
24
- /^agent setup/,
25
- ];
26
-
27
- function isNoise(msg: string): boolean {
28
- return NOISE_PATTERNS.some((p) => p.test(msg));
29
- }
30
-
31
- export interface LiveRow {
32
- evalId: string;
33
- who: string;
34
- /** 该 (evalId, who) 对的总 attempt 数(含 earlyExit 估算值)。 */
35
- total: number;
36
- /**
37
- * 设置时表示这一行从第一帧起就是携入(carry)的结果,不会真的调度 attempt——直接按这个
38
- * verdict 渲染成已完成,不经过 waiting → spinner 的过程。没有它,carry 掉的行会卡在
39
- * "waiting for a slot"直到进程退出:它永远等不到 eval:start,因为 run.ts 压根不会为
40
- * 它派发 attempt。
41
- */
42
- carriedVerdict?: string;
43
- }
44
-
45
- export interface LiveReporter extends Reporter {
46
- /** 被 RunOptions.onProgress 调用,更新行尾的 lastMsg。 */
47
- progress(evalId: string, who: string, msg: string): void;
48
- }
49
-
50
- interface RowState extends LiveRow {
51
- completed: number;
52
- lastMsg: string;
53
- dominantVerdict: string | undefined;
54
- /** true 一旦拿到并发名额、attempt effect 真正开始跑;之前是排队等待,不该转圈误导。 */
55
- started: boolean;
56
- }
57
-
58
- export function Live(rows: LiveRow[], totalAttempts: number): LiveReporter {
59
- const stateMap = new Map<string, RowState>();
60
- const keyOrder: string[] = [];
61
- let totalCompleted = 0;
62
-
63
- for (const r of rows) {
64
- const k = `${r.evalId}|${r.who}`;
65
- if (!stateMap.has(k)) {
66
- const carried = r.carriedVerdict !== undefined;
67
- stateMap.set(k, {
68
- ...r,
69
- completed: carried ? r.total : 0,
70
- lastMsg: "",
71
- dominantVerdict: r.carriedVerdict,
72
- started: carried,
73
- });
74
- keyOrder.push(k);
75
- if (carried) totalCompleted += r.total;
76
- } else {
77
- // 同一 (evalId, who) 可能在多个 agentRun 里出现(不应发生,但做防御)
78
- stateMap.get(k)!.total += r.total;
79
- }
80
- }
81
-
82
- let spinFrame = 0;
83
- let drawnLines = 0; // 上次 draw() 写了多少行,用于 \x1B[nA 回跳
84
- let intervalId: ReturnType<typeof setInterval> | undefined;
85
- let shape: RunShape | undefined;
86
- let unsubscribeExternalWrite: (() => void) | undefined;
87
-
88
- const cols = () => process.stderr.columns || 100;
89
-
90
- function renderRow(state: RowState, frame: number): string {
91
- const done = state.completed >= state.total;
92
- const sym = done
93
- ? verdictSymbol(state.dominantVerdict ?? "")
94
- : state.started
95
- ? SPINNER[frame % SPINNER.length]
96
- : WAITING_SYM;
97
-
98
- const evalCol = state.evalId.slice(0, 24).padEnd(24);
99
- const whoCol = `[${state.who}]`.slice(0, 26).padEnd(26);
100
- const cntCol = `${state.completed}/${state.total}`.padEnd(5);
101
-
102
- const prefix = ` ${sym} ${evalCol} ${whoCol} ${cntCol} `;
103
- const budget = Math.max(0, cols() - prefix.length - 1);
104
- const msg = done ? "" : state.started ? state.lastMsg.slice(0, budget) : t("live.waiting").slice(0, budget);
105
-
106
- return `\x1B[2K${prefix}${msg}`;
107
- }
108
-
109
- function renderHeader(): string {
110
- const hdr = shape
111
- ? t("live.running", {
112
- totalRuns: shape.totalRuns,
113
- evals: shape.evals,
114
- configs: shape.configs,
115
- concurrency: shape.maxConcurrency,
116
- completed: totalCompleted,
117
- total: totalAttempts,
118
- })
119
- : t("live.runningUnknown", { completed: totalCompleted, total: totalAttempts });
120
- return `\x1B[2K${hdr}`;
121
- }
122
-
123
- // 终端放不下全部行时,光标回跳会被屏幕顶端截断,导致每帧往下追加整表。
124
- // 所以按终端高度截断:优先显示运行中的行,放不下的折叠成一行摘要。
125
- function frameLines(frame: number): string[] {
126
- const lines = [renderHeader()];
127
- const termRows = process.stderr.rows || 30;
128
- // 预留:1 行表头 + 1 行防止末尾换行触发滚动
129
- const budget = Math.max(1, termRows - 2);
130
-
131
- if (keyOrder.length <= budget) {
132
- for (const k of keyOrder) lines.push(renderRow(stateMap.get(k)!, frame));
133
- return lines;
134
- }
135
-
136
- const running: string[] = [];
137
- const waiting: string[] = [];
138
- const done: string[] = [];
139
- for (const k of keyOrder) {
140
- const s = stateMap.get(k)!;
141
- if (s.completed >= s.total) done.push(k);
142
- else if (s.started) running.push(k);
143
- else waiting.push(k);
144
- }
145
- // 选出要显示的 key(运行中 > 等待 > 已完成),但按原始顺序渲染,避免行来回跳动
146
- const shown = new Set([...running, ...waiting, ...done].slice(0, budget - 1));
147
- for (const k of keyOrder) {
148
- if (shown.has(k)) lines.push(renderRow(stateMap.get(k)!, frame));
149
- }
150
- lines.push(
151
- `\x1B[2K ${t("live.more", {
152
- hidden: keyOrder.length - shown.size,
153
- running: running.filter((k) => !shown.has(k)).length,
154
- waiting: waiting.filter((k) => !shown.has(k)).length,
155
- done: done.filter((k) => !shown.has(k)).length,
156
- })}`,
157
- );
158
- return lines;
159
- }
160
-
161
- function draw(frame: number) {
162
- if (!process.stderr.isTTY) return;
163
-
164
- const lines = frameLines(frame);
165
- let out = drawnLines > 0 ? `\x1B[${drawnLines}A` : "";
166
- out += lines.join("\n") + "\n";
167
- // 本帧比上帧短(行完成后折叠、终端拉高)时,清掉下方残留的旧行
168
- const extra = drawnLines - lines.length;
169
- if (extra > 0) {
170
- out += "\x1B[2K\n".repeat(extra) + `\x1B[${extra}A`;
171
- }
172
- process.stderr.write(out);
173
- drawnLines = lines.length;
174
- }
175
-
176
- function clearDisplay() {
177
- if (!process.stderr.isTTY || drawnLines === 0) return;
178
- // 回到起点,逐行清空
179
- process.stderr.write(`\x1B[${drawnLines}A`);
180
- for (let i = 0; i < drawnLines; i++) {
181
- process.stderr.write("\x1B[2K\n");
182
- }
183
- process.stderr.write(`\x1B[${drawnLines}A`);
184
- drawnLines = 0;
185
- }
186
-
187
- return {
188
- progress(evalId, who, msg) {
189
- if (isNoise(msg)) return;
190
- const state = stateMap.get(`${evalId}|${who}`);
191
- if (state) state.lastMsg = msg;
192
- // 不在这里 draw();由 interval 驱动,避免每条日志都刷屏
193
- },
194
-
195
- onEvent(event: ReporterEvent) {
196
- if (event.type !== "eval:start") return;
197
- const who = runWho({ agentName: event.agent.name, model: event.model, experimentId: event.experimentId });
198
- const state = stateMap.get(`${event.eval.id}|${who}`);
199
- if (state) state.started = true;
200
- },
201
-
202
- onRunStart(_evals, _agent, s) {
203
- shape = s;
204
- // sandbox teardown 失败、budget 不可执行这类独立诊断行可能绕开 progress()/onEvalComplete
205
- // 直接落地(见 tty-line.ts)。订阅后先把已画的表格清掉、drawnLines 归零,下一帧从新起点
206
- // 重画,不然回跳量和实际光标错位,越滚越多。
207
- unsubscribeExternalWrite = onBeforeExternalTerminalWrite(() => clearDisplay());
208
- // 初始渲染:让用户看到行表
209
- draw(0);
210
- intervalId = setInterval(() => {
211
- spinFrame = (spinFrame + 1) % SPINNER.length;
212
- draw(spinFrame);
213
- }, 80);
214
- },
215
-
216
- onEvalComplete(result) {
217
- const who = runWho({ agentName: result.agent, model: result.model, experimentId: result.experimentId });
218
- const state = stateMap.get(`${result.id}|${who}`);
219
- if (state) {
220
- state.completed += 1;
221
- totalCompleted += 1;
222
- const prev = state.dominantVerdict;
223
- if (!prev || (prev === "passed" && result.verdict !== "passed")) {
224
- state.dominantVerdict = result.verdict;
225
- }
226
- } else {
227
- totalCompleted += 1;
228
- }
229
- },
230
-
231
- onRunComplete(summary) {
232
- if (intervalId) {
233
- clearInterval(intervalId);
234
- intervalId = undefined;
235
- }
236
- if (unsubscribeExternalWrite) {
237
- unsubscribeExternalWrite();
238
- unsubscribeExternalWrite = undefined;
239
- }
240
- // 最后刷一帧(所有行都 done)
241
- draw(spinFrame);
242
- clearDisplay();
243
-
244
- process.stdout.write(renderRunReport(summary));
245
- },
246
- };
247
- }
@@ -1,66 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { quietLine } from "./quiet.ts";
3
- import type { EvalResult } from "../../types.ts";
4
-
5
- function baseResult(overrides: Partial<EvalResult> = {}): EvalResult {
6
- return {
7
- id: "algebra/quadratic",
8
- agent: "codex",
9
- verdict: "passed",
10
- attempt: 0,
11
- durationMs: 42_000,
12
- assertions: [],
13
- ...overrides,
14
- };
15
- }
16
-
17
- describe("quietLine", () => {
18
- it("passed / skipped 静默(返回 undefined)", () => {
19
- expect(quietLine(baseResult())).toBeUndefined();
20
- expect(quietLine(baseResult({ verdict: "skipped", skipReason: "no api key" }))).toBeUndefined();
21
- });
22
-
23
- it("errored 带 eval id、[who] 与截断后的 error", () => {
24
- const line = quietLine(
25
- baseResult({
26
- verdict: "errored",
27
- model: "gpt-5",
28
- error: "sandbox create failed: e2b timeout after 3.9s",
29
- }),
30
- );
31
- expect(line).toContain("algebra/quadratic");
32
- expect(line).toContain("[codex/gpt-5]");
33
- expect(line).toContain("errored");
34
- expect(line).toContain("e2b timeout after 3.9s");
35
- });
36
-
37
- it("[who] 与进度行同源:有 experimentId 时用其 basename", () => {
38
- const line = quietLine(
39
- baseResult({ verdict: "errored", experimentId: "compare/xxx--agents-md", error: "boom" }),
40
- );
41
- expect(line).toContain("[xxx--agents-md]");
42
- });
43
-
44
- it("failed 无 error 时取首个失败断言(severity、阈值、detail)", () => {
45
- const line = quietLine(
46
- baseResult({
47
- verdict: "failed",
48
- assertions: [
49
- { name: "compiles", severity: "gate", score: 1, passed: true },
50
- { name: "closedQA", severity: "gate", score: 0.2, passed: false, threshold: 0.7, detail: "wrong city" },
51
- ],
52
- }),
53
- );
54
- expect(line).toContain("failed");
55
- expect(line).toContain("closedQA");
56
- expect(line).toContain("0.20");
57
- expect(line).toContain("wrong city");
58
- expect(line).not.toContain("compiles");
59
- });
60
-
61
- it("超长 error 截断到 200 字符并加省略号", () => {
62
- const line = quietLine(baseResult({ verdict: "errored", error: "x".repeat(500) }))!;
63
- expect(line).toContain("…");
64
- expect(line.length).toBeLessThan(300);
65
- });
66
- });
@@ -1,49 +0,0 @@
1
- // Quiet 报告器:--quiet 下的最小结果流。进度流照旧(attempt 无 onProgress 时直写 stderr),
2
- // 这里只补「坏结果」:verdict 为 errored / failed 的结果各写一行 stderr,passed / skipped
3
- // 静默 —— 保证 --quiet 下起沙箱失败这类执行错不会全程无声、只能事后读 summary.json。
4
- // 结果流仍走统一的 Reporter 管线,不在 attempt 里散写。
5
-
6
- import type { EvalResult, Reporter } from "../../types.ts";
7
- import { t } from "../../i18n/index.ts";
8
- import { runWho } from "../types.ts";
9
- import { verdictSymbol } from "./shared.ts";
10
-
11
- /** error / 断言 detail 的截断上限;比 Console 的 400 更紧,--quiet 只要能定位问题。 */
12
- const DETAIL_MAX = 200;
13
-
14
- /** 纯函数:一条结果 → 该写 stderr 的行;passed / skipped 返回 undefined(静默)。 */
15
- export function quietLine(result: EvalResult): string | undefined {
16
- if (result.verdict !== "errored" && result.verdict !== "failed") return undefined;
17
- // [who] 与 attempt 进度行同源(runWho),两条流才能对上同一个运行配置。
18
- const who = runWho({ agentName: result.agent, model: result.model, experimentId: result.experimentId });
19
- const verdict = result.verdict === "errored" ? t("report.errored") : t("report.failed");
20
- const detail =
21
- result.error !== undefined
22
- ? `${t("report.error")}: ${truncate(result.error, DETAIL_MAX)}`
23
- : firstFailedAssertion(result);
24
- return ` ${verdictSymbol(result.verdict)} ${result.id} ${verdict} [${who}]${detail ? ` ${detail}` : ""}\n`;
25
- }
26
-
27
- function firstFailedAssertion(result: EvalResult): string | undefined {
28
- const a = result.assertions.find((x) => !x.passed);
29
- if (!a) return undefined;
30
- const sev = a.severity === "gate" ? t("report.gate") : t("report.soft");
31
- const thr =
32
- a.threshold !== undefined
33
- ? t("report.assertionThreshold", { score: a.score.toFixed(2), threshold: a.threshold })
34
- : "";
35
- return `${sev}: ${a.name}${thr}${a.detail ? ` — ${truncate(a.detail, DETAIL_MAX)}` : ""}`;
36
- }
37
-
38
- function truncate(s: string, n: number): string {
39
- return s.length > n ? s.slice(0, n) + "…" : s;
40
- }
41
-
42
- export function Quiet(): Reporter {
43
- return {
44
- onEvalComplete(result: EvalResult) {
45
- const line = quietLine(result);
46
- if (line !== undefined) process.stderr.write(line);
47
- },
48
- };
49
- }