niceeval 0.9.1 → 0.10.2

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 (432) hide show
  1. package/INDEX.md +31 -33
  2. package/dist/agents/types.d.ts +20 -17
  3. package/dist/context/types.d.ts +21 -21
  4. package/dist/i18n/en.d.ts +10 -2
  5. package/dist/i18n/zh-CN.d.ts +14 -6
  6. package/dist/o11y/execution-tree.d.ts +103 -0
  7. package/dist/o11y/otlp/select.d.ts +22 -0
  8. package/dist/report/{react → assets}/colors.d.ts +8 -0
  9. package/dist/report/{react → assets}/colors.js +23 -0
  10. package/dist/report/built-in/index.d.ts +2 -2
  11. package/dist/report/built-in/index.js +2 -2
  12. package/dist/report/built-in/standard.d.ts +8 -1
  13. package/dist/report/built-in/standard.js +17 -7
  14. package/dist/report/components/attempt-detail/AttemptAssertions.d.ts +6 -0
  15. package/dist/report/components/attempt-detail/AttemptAssertions.js +17 -0
  16. package/dist/report/components/attempt-detail/AttemptConversation.d.ts +10 -0
  17. package/dist/report/components/attempt-detail/AttemptConversation.js +74 -0
  18. package/dist/report/components/attempt-detail/AttemptDiagnostics.d.ts +6 -0
  19. package/dist/report/components/attempt-detail/AttemptDiagnostics.js +7 -0
  20. package/dist/report/components/attempt-detail/AttemptDiff.d.ts +6 -0
  21. package/dist/report/components/attempt-detail/AttemptDiff.js +11 -0
  22. package/dist/report/components/attempt-detail/AttemptError.d.ts +6 -0
  23. package/dist/report/components/attempt-detail/AttemptError.js +16 -0
  24. package/dist/report/components/attempt-detail/AttemptFixPrompt.d.ts +6 -0
  25. package/dist/report/components/attempt-detail/AttemptFixPrompt.js +7 -0
  26. package/dist/report/components/attempt-detail/AttemptSource.d.ts +6 -0
  27. package/dist/report/components/attempt-detail/AttemptSource.js +97 -0
  28. package/dist/report/components/attempt-detail/AttemptSummary.d.ts +8 -0
  29. package/dist/report/components/attempt-detail/AttemptSummary.js +20 -0
  30. package/dist/report/components/attempt-detail/AttemptTimeline.d.ts +6 -0
  31. package/dist/report/components/attempt-detail/AttemptTimeline.js +28 -0
  32. package/dist/report/components/attempt-detail/AttemptTrace.d.ts +6 -0
  33. package/dist/report/components/attempt-detail/AttemptTrace.js +29 -0
  34. package/dist/report/components/attempt-detail/AttemptUsage.d.ts +6 -0
  35. package/dist/report/components/attempt-detail/AttemptUsage.js +20 -0
  36. package/dist/report/components/attempt-detail/compute.d.ts +24 -0
  37. package/dist/report/components/attempt-detail/compute.js +297 -0
  38. package/dist/report/components/attempt-detail/faces.d.ts +14 -0
  39. package/dist/report/components/attempt-detail/faces.js +239 -0
  40. package/dist/report/components/attempt-detail/index.d.ts +38 -0
  41. package/dist/report/components/attempt-detail/index.js +547 -0
  42. package/dist/report/{react → components}/cell.d.ts +5 -3
  43. package/dist/report/{react → components}/cell.js +3 -3
  44. package/dist/report/{react → components/entity-lists}/AttemptList.d.ts +8 -4
  45. package/dist/report/{react → components/entity-lists}/AttemptList.js +16 -10
  46. package/dist/report/{react → components/entity-lists}/EvalList.d.ts +3 -3
  47. package/dist/report/{react → components/entity-lists}/EvalList.js +0 -0
  48. package/dist/report/{react → components/entity-lists}/ExperimentList.d.ts +4 -5
  49. package/dist/report/{react → components/entity-lists}/ExperimentList.js +12 -11
  50. package/dist/report/components/entity-lists/compute.d.ts +23 -0
  51. package/dist/report/components/entity-lists/compute.js +171 -0
  52. package/dist/report/components/entity-lists/faces.d.ts +5 -0
  53. package/dist/report/components/entity-lists/faces.js +175 -0
  54. package/dist/report/components/entity-lists/index.d.ts +49 -0
  55. package/dist/report/components/entity-lists/index.js +169 -0
  56. package/dist/report/{react → components}/fixtures.d.ts +2 -2
  57. package/dist/report/{react → components}/fixtures.js +1 -1
  58. package/dist/report/{react → components/metric-views}/DeltaTable.d.ts +2 -2
  59. package/dist/report/{react → components/metric-views}/DeltaTable.js +4 -4
  60. package/dist/report/{react → components/metric-views}/MetricBars.d.ts +3 -3
  61. package/dist/report/{react → components/metric-views}/MetricBars.js +3 -3
  62. package/dist/report/{react → components/metric-views}/MetricLine.d.ts +2 -2
  63. package/dist/report/{react → components/metric-views}/MetricLine.js +8 -5
  64. package/dist/report/{react → components/metric-views}/MetricMatrix.d.ts +3 -3
  65. package/dist/report/{react → components/metric-views}/MetricMatrix.js +4 -4
  66. package/dist/report/components/metric-views/MetricScatter.d.ts +11 -0
  67. package/dist/report/{react → components/metric-views}/MetricScatter.js +27 -60
  68. package/dist/report/{react → components/metric-views}/MetricTable.d.ts +3 -3
  69. package/dist/report/{react → components/metric-views}/MetricTable.js +4 -4
  70. package/dist/report/{react → components/metric-views}/Scoreboard.d.ts +2 -2
  71. package/dist/report/{react → components/metric-views}/Scoreboard.js +3 -3
  72. package/dist/report/components/metric-views/compute.d.ts +89 -0
  73. package/dist/report/{compute.js → components/metric-views/compute.js} +32 -415
  74. package/dist/report/components/metric-views/faces.d.ts +13 -0
  75. package/dist/report/components/metric-views/faces.js +381 -0
  76. package/dist/report/components/metric-views/index.d.ts +50 -0
  77. package/dist/report/components/metric-views/index.js +273 -0
  78. package/dist/report/{text → components/metric-views}/plot.js +1 -1
  79. package/dist/report/components/shared-compute.d.ts +22 -0
  80. package/dist/report/components/shared-compute.js +47 -0
  81. package/dist/report/components/shared-faces.d.ts +9 -0
  82. package/dist/report/components/shared-faces.js +26 -0
  83. package/dist/report/components/shared.d.ts +68 -0
  84. package/dist/report/components/shared.js +125 -0
  85. package/dist/report/{react → components/site-components}/CopyFixPrompt.d.ts +2 -2
  86. package/dist/report/{react → components/site-components}/CopyFixPrompt.js +2 -2
  87. package/dist/report/{react → components/site-components}/HeroCard.d.ts +2 -2
  88. package/dist/report/{react → components/site-components}/HeroCard.js +2 -2
  89. package/dist/report/{react → components/site-components}/ScopeWarnings.d.ts +2 -2
  90. package/dist/report/{react → components/site-components}/ScopeWarnings.js +3 -3
  91. package/dist/report/{react → components/site-components}/TraceWaterfall.d.ts +3 -3
  92. package/dist/report/{react → components/site-components}/TraceWaterfall.js +5 -5
  93. package/dist/report/components/site-components/compute.d.ts +28 -0
  94. package/dist/report/components/site-components/compute.js +132 -0
  95. package/dist/report/components/site-components/faces.d.ts +21 -0
  96. package/dist/report/components/site-components/faces.js +75 -0
  97. package/dist/report/components/site-components/index.d.ts +74 -0
  98. package/dist/report/components/site-components/index.js +220 -0
  99. package/dist/report/{scope-warnings.d.ts → components/site-components/scope-warnings.d.ts} +2 -2
  100. package/dist/report/{scope-warnings.js → components/site-components/scope-warnings.js} +2 -2
  101. package/dist/report/{react → components/summaries}/ScopeSummary.d.ts +2 -2
  102. package/dist/report/{react → components/summaries}/ScopeSummary.js +17 -8
  103. package/dist/report/components/summaries/compute.d.ts +7 -0
  104. package/dist/report/components/summaries/compute.js +51 -0
  105. package/dist/report/components/summaries/faces.d.ts +7 -0
  106. package/dist/report/components/summaries/faces.js +38 -0
  107. package/dist/report/components/summaries/index.d.ts +27 -0
  108. package/dist/report/components/summaries/index.js +78 -0
  109. package/dist/report/definition/grid-layout.d.ts +50 -0
  110. package/dist/report/definition/grid-layout.js +89 -0
  111. package/dist/report/{primitives.d.ts → definition/primitives.d.ts} +34 -4
  112. package/dist/report/{primitives.js → definition/primitives.js} +101 -10
  113. package/dist/report/{report.d.ts → definition/report.d.ts} +36 -65
  114. package/dist/report/{report.js → definition/report.js} +58 -90
  115. package/dist/report/{text/table.d.ts → definition/table-text.d.ts} +2 -2
  116. package/dist/report/{text/table.js → definition/table-text.js} +2 -2
  117. package/dist/report/{tree.d.ts → definition/tree.d.ts} +43 -11
  118. package/dist/report/{tree.js → definition/tree.js} +9 -11
  119. package/dist/report/index.d.ts +33 -18
  120. package/dist/report/index.js +23 -14
  121. package/dist/report/{aggregate.d.ts → model/aggregate.d.ts} +12 -4
  122. package/dist/report/{aggregate.js → model/aggregate.js} +28 -8
  123. package/dist/report/{flag.d.ts → model/flag.d.ts} +14 -1
  124. package/dist/report/{flag.js → model/flag.js} +35 -0
  125. package/dist/report/{format.d.ts → model/format.d.ts} +13 -7
  126. package/dist/report/{format.js → model/format.js} +79 -9
  127. package/dist/report/{locale.d.ts → model/locale.d.ts} +17 -17
  128. package/dist/report/{locale.js → model/locale.js} +31 -33
  129. package/dist/report/{metrics.d.ts → model/metrics.d.ts} +2 -2
  130. package/dist/report/{metrics.js → model/metrics.js} +2 -2
  131. package/dist/report/{types.d.ts → model/types.d.ts} +154 -20
  132. package/dist/report/react/index.d.ts +32 -21
  133. package/dist/report/react/index.js +33 -21
  134. package/dist/report/{load.d.ts → runtime/load.d.ts} +1 -1
  135. package/dist/report/{load.js → runtime/load.js} +1 -1
  136. package/dist/report/runtime/text.d.ts +67 -0
  137. package/dist/report/runtime/text.js +114 -0
  138. package/dist/report/runtime/web.d.ts +35 -0
  139. package/dist/report/runtime/web.js +70 -0
  140. package/dist/results/annotated-source.d.ts +87 -0
  141. package/dist/results/attempt-evidence.d.ts +74 -0
  142. package/dist/results/attempt-source.d.ts +15 -0
  143. package/dist/results/select.d.ts +11 -1
  144. package/dist/results/select.js +27 -5
  145. package/dist/runner/eval-selection.d.ts +26 -0
  146. package/dist/runner/feedback/sink.d.ts +26 -1
  147. package/dist/runner/types.d.ts +151 -24
  148. package/dist/sandbox/docker.d.ts +6 -0
  149. package/dist/sandbox/e2b.d.ts +0 -1
  150. package/dist/sandbox/types.d.ts +23 -21
  151. package/dist/scoring/display.d.ts +7 -2
  152. package/dist/scoring/display.js +42 -4
  153. package/dist/scoring/types.d.ts +3 -3
  154. package/dist/shared/aggregate.d.ts +14 -0
  155. package/dist/shared/aggregate.js +31 -0
  156. package/dist/shared/types.d.ts +6 -1
  157. package/docs-site/images/logo-dark.svg +7 -0
  158. package/docs-site/images/logo.svg +6 -5
  159. package/docs-site/zh/README.md +8 -7
  160. package/docs-site/zh/examples/ai-agent-application.mdx +5 -5
  161. package/docs-site/zh/examples/coding-agent-extensions.mdx +4 -4
  162. package/docs-site/zh/examples/index.mdx +3 -3
  163. package/docs-site/zh/examples/integrations/ai-sdk-v7.mdx +6 -6
  164. package/docs-site/zh/examples/integrations/claude-sdk.mdx +3 -3
  165. package/docs-site/zh/examples/integrations/codex-sdk.mdx +3 -3
  166. package/docs-site/zh/examples/integrations/langgraph.mdx +3 -3
  167. package/docs-site/zh/examples/integrations/pi-sdk.mdx +2 -2
  168. package/docs-site/zh/explanation/adapter.mdx +19 -19
  169. package/docs-site/zh/explanation/assert.mdx +9 -9
  170. package/docs-site/zh/explanation/drive.mdx +4 -4
  171. package/docs-site/zh/explanation/evals.mdx +8 -8
  172. package/docs-site/zh/explanation/experiment.mdx +8 -8
  173. package/docs-site/zh/explanation/hitl.mdx +13 -13
  174. package/docs-site/zh/explanation/judge.mdx +3 -3
  175. package/docs-site/zh/explanation/overview.mdx +7 -7
  176. package/docs-site/zh/explanation/runner.mdx +17 -13
  177. package/docs-site/zh/explanation/tier.mdx +6 -6
  178. package/docs-site/zh/index.mdx +14 -14
  179. package/docs-site/zh/introduction.mdx +13 -16
  180. package/docs-site/zh/reference/builtin-agents.mdx +61 -28
  181. package/docs-site/zh/reference/capabilities.mdx +7 -27
  182. package/docs-site/zh/reference/cli.mdx +36 -33
  183. package/docs-site/zh/reference/define-agent.mdx +32 -31
  184. package/docs-site/zh/reference/define-config.mdx +6 -6
  185. package/docs-site/zh/reference/define-eval.mdx +33 -20
  186. package/docs-site/zh/reference/events.mdx +4 -4
  187. package/docs-site/zh/reference/expect.mdx +3 -3
  188. package/docs-site/zh/reference/official-adapters.mdx +17 -17
  189. package/docs-site/zh/reference/report-components.mdx +195 -121
  190. package/docs-site/zh/reference/results-data.mdx +13 -13
  191. package/docs-site/zh/troubleshooting/debug-sandbox.mdx +11 -11
  192. package/docs-site/zh/troubleshooting/debugging.mdx +40 -16
  193. package/docs-site/zh/{how-to → tutorials}/agent-feedback-loop.mdx +38 -38
  194. package/docs-site/zh/tutorials/agent-onboarding.mdx +100 -0
  195. package/docs-site/zh/tutorials/authoring.mdx +211 -0
  196. package/docs-site/zh/{how-to → tutorials}/ci-integration.mdx +8 -8
  197. package/docs-site/zh/{how-to → tutorials}/connect-otel.mdx +22 -22
  198. package/docs-site/zh/{how-to → tutorials}/connect-your-agent.mdx +58 -63
  199. package/docs-site/zh/tutorials/custom-reports.mdx +453 -0
  200. package/docs-site/zh/{how-to → tutorials}/dataset-fanout.mdx +4 -4
  201. package/docs-site/zh/tutorials/experiments.mdx +102 -0
  202. package/docs-site/zh/{how-to → tutorials}/fixtures.mdx +9 -9
  203. package/docs-site/zh/{how-to → tutorials}/publish-report.mdx +6 -6
  204. package/docs-site/zh/tutorials/quickstart.mdx +18 -18
  205. package/docs-site/zh/{how-to → tutorials}/reporters.mdx +6 -6
  206. package/docs-site/zh/{how-to → tutorials}/sandbox-agent.mdx +6 -6
  207. package/docs-site/zh/{how-to → tutorials}/sandbox-providers.mdx +41 -19
  208. package/docs-site/zh/{how-to → tutorials}/scoring-guide.mdx +4 -4
  209. package/docs-site/zh/{how-to → tutorials}/viewing-results.mdx +60 -57
  210. package/docs-site/zh/tutorials/write-experiment.mdx +353 -0
  211. package/docs-site/zh/{how-to → tutorials}/write-send.mdx +40 -40
  212. package/package.json +10 -7
  213. package/src/agents/bub.ts +21 -8
  214. package/src/agents/claude-code.ts +23 -10
  215. package/src/agents/codex.test.ts +22 -7
  216. package/src/agents/codex.ts +22 -9
  217. package/src/agents/index.ts +2 -2
  218. package/src/agents/openai-compat.ts +1 -1
  219. package/src/agents/post-setup.ts +45 -22
  220. package/src/agents/streaming.ts +1 -1
  221. package/src/agents/types.ts +20 -17
  222. package/src/agents/ui-message-stream.test.ts +10 -0
  223. package/src/agents/ui-message-stream.ts +12 -1
  224. package/src/cli.ts +75 -59
  225. package/src/context/types.ts +21 -21
  226. package/src/define.ts +13 -0
  227. package/src/i18n/en.ts +27 -12
  228. package/src/i18n/zh-CN.ts +27 -12
  229. package/src/index.ts +2 -0
  230. package/src/o11y/prices.json +176 -99
  231. package/src/report/{react → assets}/colors.ts +22 -0
  232. package/src/report/{react → assets}/enhance.js +1 -33
  233. package/src/report/assets/styles.css +1785 -0
  234. package/src/report/built-in/index.tsx +2 -2
  235. package/src/report/built-in/standard.tsx +18 -6
  236. package/src/report/components/attempt-detail/AttemptAssertions.tsx +67 -0
  237. package/src/report/components/attempt-detail/AttemptConversation.tsx +158 -0
  238. package/src/report/components/attempt-detail/AttemptDiagnostics.tsx +38 -0
  239. package/src/report/components/attempt-detail/AttemptDiff.tsx +35 -0
  240. package/src/report/components/attempt-detail/AttemptError.tsx +31 -0
  241. package/src/report/components/attempt-detail/AttemptFixPrompt.tsx +26 -0
  242. package/src/report/components/attempt-detail/AttemptSource.tsx +224 -0
  243. package/src/report/components/attempt-detail/AttemptSummary.tsx +74 -0
  244. package/src/report/components/attempt-detail/AttemptTimeline.tsx +108 -0
  245. package/src/report/components/attempt-detail/AttemptTrace.tsx +63 -0
  246. package/src/report/components/attempt-detail/AttemptUsage.tsx +29 -0
  247. package/src/report/components/attempt-detail/attempt-components.test.tsx +758 -0
  248. package/src/report/components/attempt-detail/compute.ts +336 -0
  249. package/src/report/components/attempt-detail/faces.ts +260 -0
  250. package/src/report/components/attempt-detail/index.tsx +600 -0
  251. package/src/report/components/attempt-detail/validate.test.ts +252 -0
  252. package/src/report/{react → components}/cell.tsx +6 -3
  253. package/src/report/{report.test.ts → components/compute.test.ts} +316 -46
  254. package/src/report/{react → components/entity-lists}/AttemptList.tsx +24 -16
  255. package/src/report/{react → components/entity-lists}/EvalList.tsx +0 -0
  256. package/src/report/{react → components/entity-lists}/ExperimentList.tsx +33 -27
  257. package/src/report/components/entity-lists/compute.ts +196 -0
  258. package/src/report/components/entity-lists/faces.ts +208 -0
  259. package/src/report/components/entity-lists/index.tsx +238 -0
  260. package/src/report/components/entity-lists/validate.test.ts +114 -0
  261. package/src/report/{react → components}/fixtures.ts +2 -2
  262. package/src/report/{react → components/metric-views}/DeltaTable.tsx +5 -5
  263. package/src/report/{react → components/metric-views}/MetricBars.tsx +5 -5
  264. package/src/report/{react → components/metric-views}/MetricLine.tsx +9 -6
  265. package/src/report/{react → components/metric-views}/MetricMatrix.tsx +6 -6
  266. package/src/report/{react → components/metric-views}/MetricScatter.tsx +42 -70
  267. package/src/report/{react → components/metric-views}/MetricTable.tsx +6 -6
  268. package/src/report/{react → components/metric-views}/Scoreboard.tsx +4 -4
  269. package/src/report/{compute.ts → components/metric-views/compute.ts} +51 -477
  270. package/src/report/components/metric-views/faces.ts +423 -0
  271. package/src/report/components/metric-views/index.tsx +352 -0
  272. package/src/report/{text → components/metric-views}/plot.ts +1 -1
  273. package/src/report/components/metric-views/validate.test.ts +211 -0
  274. package/src/report/{react → components}/render.test.tsx +66 -26
  275. package/src/report/components/shared-compute.ts +59 -0
  276. package/src/report/components/shared-faces.ts +30 -0
  277. package/src/report/components/shared.ts +192 -0
  278. package/src/report/{react → components/site-components}/CopyFixPrompt.tsx +3 -3
  279. package/src/report/{react → components/site-components}/HeroCard.tsx +3 -3
  280. package/src/report/{react → components/site-components}/ScopeWarnings.tsx +4 -4
  281. package/src/report/{react → components/site-components}/TraceWaterfall.tsx +13 -10
  282. package/src/report/components/site-components/compute.ts +144 -0
  283. package/src/report/components/site-components/faces.ts +80 -0
  284. package/src/report/components/site-components/index.tsx +265 -0
  285. package/src/report/{scope-warnings.ts → components/site-components/scope-warnings.ts} +3 -3
  286. package/src/report/{site-components.test.tsx → components/site-components/site-components.test.tsx} +23 -21
  287. package/src/report/components/site-components/validate.test.ts +89 -0
  288. package/src/report/{react → components/summaries}/ScopeSummary.tsx +26 -11
  289. package/src/report/components/summaries/compute.ts +56 -0
  290. package/src/report/components/summaries/faces.ts +48 -0
  291. package/src/report/components/summaries/index.tsx +127 -0
  292. package/src/report/components/summaries/validate.test.ts +49 -0
  293. package/src/report/definition/grid-layout.test.ts +124 -0
  294. package/src/report/definition/grid-layout.ts +146 -0
  295. package/src/report/{primitives.tsx → definition/primitives.tsx} +168 -18
  296. package/src/report/{report.ts → definition/report.ts} +98 -165
  297. package/src/report/{shell-head.test.ts → definition/shell-head.test.ts} +2 -2
  298. package/src/report/{text/table.ts → definition/table-text.ts} +4 -4
  299. package/src/report/{tree.ts → definition/tree.ts} +56 -20
  300. package/src/report/index.ts +128 -66
  301. package/src/report/{aggregate.ts → model/aggregate.ts} +31 -9
  302. package/src/report/{flag.ts → model/flag.ts} +39 -1
  303. package/src/report/{format.ts → model/format.ts} +89 -10
  304. package/src/report/{locale.ts → model/locale.ts} +34 -36
  305. package/src/report/{metrics.ts → model/metrics.ts} +3 -3
  306. package/src/report/{types.ts → model/types.ts} +159 -22
  307. package/src/report/react/index.tsx +54 -26
  308. package/src/report/{dual-render.test.tsx → runtime/dual-render.test.tsx} +601 -53
  309. package/src/report/runtime/host.test.ts +44 -0
  310. package/src/report/runtime/host.ts +138 -0
  311. package/src/report/{load.ts → runtime/load.ts} +1 -1
  312. package/src/report/runtime/text.ts +192 -0
  313. package/src/report/runtime/web.ts +106 -0
  314. package/src/results/attempt-evidence.ts +5 -1
  315. package/src/results/host-equivalence.test.ts +53 -2
  316. package/src/results/select.ts +29 -5
  317. package/src/runner/attempt.test.ts +48 -1
  318. package/src/runner/attempt.ts +62 -42
  319. package/src/runner/cleanup-timeout.test.ts +16 -0
  320. package/src/runner/cleanup-timeout.ts +23 -0
  321. package/src/runner/discover.ts +1 -2
  322. package/src/runner/eval-selection.test.ts +187 -0
  323. package/src/runner/eval-selection.ts +109 -0
  324. package/src/runner/experiment-cleanup-registry.test.ts +89 -0
  325. package/src/runner/experiment-cleanup-registry.ts +39 -0
  326. package/src/runner/experiment-labels.test.ts +71 -0
  327. package/src/runner/feedback/agent.test.ts +33 -1
  328. package/src/runner/feedback/agent.ts +12 -16
  329. package/src/runner/feedback/ci.test.ts +35 -1
  330. package/src/runner/feedback/ci.ts +16 -18
  331. package/src/runner/feedback/coordinator.ts +28 -0
  332. package/src/runner/feedback/human.test.ts +108 -1
  333. package/src/runner/feedback/human.ts +71 -21
  334. package/src/runner/feedback/reducer.test.ts +64 -0
  335. package/src/runner/feedback/reducer.ts +26 -0
  336. package/src/runner/feedback/sink.ts +40 -1
  337. package/src/runner/fingerprint.ts +2 -1
  338. package/src/runner/report.test.ts +14 -0
  339. package/src/runner/report.ts +3 -1
  340. package/src/runner/run.test.ts +390 -12
  341. package/src/runner/run.ts +259 -26
  342. package/src/runner/sandbox-selection.test.ts +13 -3
  343. package/src/runner/sandbox-selection.ts +4 -2
  344. package/src/runner/types.ts +160 -24
  345. package/src/sandbox/docker.ts +7 -0
  346. package/src/sandbox/e2b-reconcile.test.ts +125 -0
  347. package/src/sandbox/e2b.ts +47 -12
  348. package/src/sandbox/types.ts +23 -21
  349. package/src/scoring/display.test.ts +28 -2
  350. package/src/scoring/display.ts +38 -5
  351. package/src/scoring/types.ts +3 -3
  352. package/src/shared/aggregate.test.ts +52 -0
  353. package/src/shared/aggregate.ts +29 -0
  354. package/src/shared/types.ts +6 -1
  355. package/src/show/command.test.ts +34 -0
  356. package/src/show/command.ts +16 -0
  357. package/src/show/compose.ts +1 -1
  358. package/src/show/index.ts +56 -20
  359. package/src/show/render.ts +15 -239
  360. package/src/show/show.test.ts +127 -34
  361. package/src/view/app/App.test.tsx +1 -1
  362. package/src/view/app/App.tsx +119 -47
  363. package/src/view/app/i18n.ts +7 -147
  364. package/src/view/app/lib/attempt-dialog.test.ts +65 -0
  365. package/src/view/app/lib/attempt-dialog.ts +69 -0
  366. package/src/view/app/main.tsx +0 -1
  367. package/src/view/app/types.ts +3 -86
  368. package/src/view/artifact-serving.test.ts +22 -38
  369. package/src/view/client-dist/app.css +1 -1
  370. package/src/view/client-dist/app.js +15 -22
  371. package/src/view/data.test.ts +48 -36
  372. package/src/view/data.ts +105 -83
  373. package/src/view/index.ts +1 -1
  374. package/src/view/server.ts +32 -2
  375. package/src/view/shared/types.ts +6 -31
  376. package/src/view/site-parity.test.ts +24 -1
  377. package/src/view/site.ts +144 -17
  378. package/src/view/styles.css +0 -805
  379. package/src/view/view-report.test.ts +143 -22
  380. package/dist/report/components.d.ts +0 -179
  381. package/dist/report/components.js +0 -544
  382. package/dist/report/compute.d.ts +0 -139
  383. package/dist/report/react/ExperimentComparison.d.ts +0 -10
  384. package/dist/report/react/ExperimentComparison.js +0 -12
  385. package/dist/report/react/MetricScatter.d.ts +0 -9
  386. package/dist/report/react/format.d.ts +0 -3
  387. package/dist/report/react/format.js +0 -7
  388. package/dist/report/text/faces.d.ts +0 -45
  389. package/dist/report/text/faces.js +0 -671
  390. package/dist/report/web.d.ts +0 -32
  391. package/dist/report/web.js +0 -48
  392. package/docs-site/zh/how-to/authoring.mdx +0 -162
  393. package/docs-site/zh/how-to/custom-reports.mdx +0 -414
  394. package/docs-site/zh/how-to/experiments.mdx +0 -86
  395. package/docs-site/zh/how-to/write-experiment.mdx +0 -164
  396. package/src/report/components.tsx +0 -925
  397. package/src/report/react/ExperimentComparison.tsx +0 -73
  398. package/src/report/react/format.ts +0 -9
  399. package/src/report/react/styles.css +0 -945
  400. package/src/report/text/faces.ts +0 -767
  401. package/src/report/web.ts +0 -77
  402. package/src/show/report-host.test.ts +0 -205
  403. package/src/show/report-host.ts +0 -389
  404. package/src/view/app/components/AttemptModal.tsx +0 -496
  405. package/src/view/app/components/CodeView.test.tsx +0 -142
  406. package/src/view/app/components/CodeView.tsx +0 -310
  407. package/src/view/app/components/CopyControls.tsx +0 -106
  408. package/src/view/app/components/Trace.tsx +0 -100
  409. package/src/view/app/components/Transcript.tsx +0 -157
  410. package/src/view/app/components/ui/badge.tsx +0 -21
  411. package/src/view/app/lib/artifact-url.ts +0 -17
  412. package/src/view/app/lib/attempt-route.test.ts +0 -80
  413. package/src/view/app/lib/attempt-route.ts +0 -52
  414. package/src/view/app/lib/format.ts +0 -70
  415. package/src/view/app/lib/guards.test.ts +0 -108
  416. package/src/view/app/lib/guards.ts +0 -71
  417. package/src/view/app/lib/rows.ts +0 -22
  418. package/src/view/app/lib/transcript-data.tsx +0 -151
  419. package/src/view/app/lib/verdict.ts +0 -23
  420. package/src/view/app/shared.ts +0 -8
  421. /package/dist/report/{react → components/metric-views}/chart-math.d.ts +0 -0
  422. /package/dist/report/{react → components/metric-views}/chart-math.js +0 -0
  423. /package/dist/report/{text → components/metric-views}/plot.d.ts +0 -0
  424. /package/dist/report/{react → components/site-components}/PoweredBy.d.ts +0 -0
  425. /package/dist/report/{react → components/site-components}/PoweredBy.js +0 -0
  426. /package/dist/report/{text/layout.d.ts → model/text-layout.d.ts} +0 -0
  427. /package/dist/report/{text/layout.js → model/text-layout.js} +0 -0
  428. /package/dist/report/{types.js → model/types.js} +0 -0
  429. /package/src/report/{react → components/metric-views}/chart-math.test.ts +0 -0
  430. /package/src/report/{react → components/metric-views}/chart-math.ts +0 -0
  431. /package/src/report/{react → components/site-components}/PoweredBy.tsx +0 -0
  432. /package/src/report/{text/layout.ts → model/text-layout.ts} +0 -0
@@ -4,41 +4,57 @@ sidebarTitle: "报告组件"
4
4
  description: "报告文件里能摆的全部官方双面组件:每个组件回答什么问题、怎么调用、网页面长什么样、终端字符输出长什么样。"
5
5
  ---
6
6
 
7
- 报告文件里的每个组件都是双面的:网页面是 React 渲染,终端面是字符渲染,两面吃同一份算好的数据,走哪扇门由宿主决定(见[自定义报告](/zh/how-to/custom-reports))。每个组件的数据算法挂在它自己身上,组件名后打个点就找到,算和画永远配对。本页逐个列出官方组件:它展示哪一层数据、在报告里怎么调用、终端输出长什么样。
7
+ 报告文件里的每个组件都是双面的:网页面是 React 渲染,终端面是字符渲染,两面吃同一份算好的数据,走哪扇门由宿主决定(见[自定义报告](/zh/tutorials/custom-reports))。每个组件要么直接写省略取数的写法(省略 `input`/`data` 时用当前 Scope 自动取数),要么显式传入自己算好的数据——两种写法产出完全相同,选哪种只看你要不要在取数和渲染之间插入自己的 JavaScript。本页逐个列出官方组件:它展示哪一层数据、在报告里怎么调用、终端输出长什么样。
8
8
 
9
9
  ## 名称与用途
10
10
 
11
- 英文术语描述组件形态,API 名是代码里的导出名。组件分四类:默认组合件按可比组组织完整比较;实体列表逐项展示 experiment、Eval Attempt;汇总组件概括整批 Selection;指标图形把指定维度聚合成值。三个实体列表的 `.data(selection)` 都返回普通数组,报告作者先用 JavaScript `.filter()` 收窄,再把 `items` 交给组件。过滤条件不藏在组件里。中文正文首次提到时用“中文名(`API 名`)”,后续可以只写中文名或 `API 名`。
11
+ 英文术语描述组件形态,API 名是代码里的导出名。默认组合件比较当前 Scope;范围摘要概括整批结果;实体列表逐项展示 experiment、评估用例或 Attempt;指标图形把指定维度聚合成值。
12
12
 
13
13
  | 分类 | 中文名 | English | API | 主展示单位 |
14
14
  | --- | --- | --- | --- | --- |
15
- | 组合 | 实验组比较 | Experiment comparison | `ExperimentComparison` | experiment 父目录分组;每组独立的摘要、散点与实验列表 |
16
- | 汇总 | 运行总览 | Run overview | `RunOverview` | 一批 Selection;汇总其中的 experiment、Eval 和 Attempt |
17
- | 汇总 | 组摘要 | Group summary | `GroupSummary` | 收窄后的一批 Selection;汇总一组 experiment Eval |
18
- | 实体列表 | 实验列表 | Experiment list | `ExperimentList` | 每项一个 experiment;展开到该 experiment Eval |
19
- | 实体列表 | Eval 列表 | Eval list | `EvalList` | 每项一个 experiment × Eval;展开到该 Eval 的 Attempt |
15
+ | 组合 | 实验比较 | Experiment comparison | `ExperimentComparison` | 当前 Scope 的摘要、散点与实验列表 |
16
+ | 汇总 | 范围摘要 | Scope summary | `ScopeSummary` | 一批结果:时间窗、数量、两级判定计票、端到端通过率与总成本 |
17
+ | 实体列表 | 实验列表 | Experiment list | `ExperimentList` | 每项一个 experiment;展开到该 experiment 的评估用例 |
18
+ | 实体列表 | 评估用例列表 | Eval list | `EvalList` | 每项一个 experiment × 评估用例;展开到该评估用例的 Attempt |
20
19
  | 实体列表 | Attempt 列表 | Attempt list | `AttemptList` | 每项一个 Attempt;显示断言、错误、Judge 评语和证据 |
20
+ | 实体列表 | 失败列表 | Failure list | `FailureList` | `AttemptList` 的成品过滤:只列 failed / errored,按开始时间倒序 |
21
21
  | 指标图形 | 指标表 | Metric table | `MetricTable` | 一个可配置行维度;每格是一个聚合指标值 |
22
22
  | 指标图形 | 指标矩阵 | Metric matrix | `MetricMatrix` | 两个可配置维度的交叉格;每格是一个聚合指标值 |
23
23
  | 指标图形 | 分组条形图 | Grouped bar chart | `MetricBars` | 两个可配置维度形成分组和系列;每根条是一个聚合指标值 |
24
- | 指标图形 | 成绩单 | Scoreboard | `Scoreboard` | 每行一个可配置维度值;按 Eval 和 Eval 分组计算分数 |
24
+ | 指标图形 | 成绩单 | Scoreboard | `Scoreboard` | 每行一个可配置维度值;按固定题集算总分和分科得分 |
25
25
  | 指标图形 | 指标散点图 | Metric scatter plot | `MetricScatter` | 每点一个可配置维度值,通常是 experiment;坐标是两个聚合指标值 |
26
- | 指标图形 | 指标趋势图 | Metric line chart | `MetricLine` | 每点一个 experiment;横轴是 experiment flag,纵轴是聚合指标值 |
26
+ | 指标图形 | 指标趋势图 | Metric line chart | `MetricLine` | 每点一个 experiment;横轴是数值配置变量,纵轴是聚合指标值 |
27
27
  | 指标图形 | 成对差异表 | Paired delta table | `DeltaTable` | 每行一对 experiment 或结果快照;格内是指标值及差值 |
28
28
 
29
- `Row`、`Col`、`Section`、`Text`、`Style` 和 `Table` 是排版原语,不计算结果,因此不算一种分析图。它们的中文统称分别是行、列、分节、文本、样式和表格;代码里始终使用 API 名。
29
+ `Row`、`Col`、`Grid`、`Section`、`Stat`、`Text`、`Style`、`Table`、`Tabs` 和 `Tab` 是十个排版原语,不计算结果,因此不算一种分析图。它们的中文统称依次是行、列、网格、分节、摘要项、文本、样式、表格、标签页和标签;代码里始终使用 API 名。
30
30
 
31
31
  所有组件共守同一套契约,下面不再逐个重复:
32
32
 
33
33
  - **诚实渲染。** 缺数据渲染 `—` 不补 0;覆盖不全的格子带 `12/15` 角标;截断如实标注剩余数量与原始 artifact 路径。
34
34
  - **排序与方向随指标的 `better`。** higher 的指标降序、lower 的升序,「好」的一头恒在上、在右上。
35
- - **证据室深链。** 网页面的格子、点和条目深链到 Attempt 详情;终端面用同一 Attempt locator 交给 `niceeval show @<id>`。
36
- - **终端输出形成反馈闭环。** 每个 Attempt 有一个以 `@` 开头的短 locator,例如 `@1k2m9qrs`。它唯一指向 experiment、结果快照、Eval Attempt。`✓` / `✗` / `!` / `–` 是 passed / failed / errored / skipped。列表不用字母缩写编码证据可用性——locator 本身就是证据入口;打开 attempt(`niceeval show @<locator>`)后再列出实际可用的证据命令。执行步骤统一包含消息、thinking、tool call/result 和 Skill load;OTel 只给这些步骤补时间,不另开一份输出。
37
- - **两面同口径,网页面不依赖 JS 也完整。** 排序在计算时由 `sort` 定死,终端和网页看到同一份基准顺序;下钻是普通链接、展开折叠用 `<details>`。网页面另有一层浏览操作:点表头就地重排、榜单行过滤、图表点位悬停看数值——只影响眼前的视图,不改数据口径,刷新即回基准顺序;浏览器禁用 JS 时这些操作消失,内容一样不少(悬停信息退化为图内提示)。
35
+ - **深链到 Attempt 详情。** 网页面的格子、点和条目深链到 Attempt 详情页;终端面用同一 Attempt 定位符交给 `niceeval show @<id>`。
36
+ - **终端输出形成反馈闭环。** 每个 Attempt 有一个以 `@` 开头的短定位符,例如 `@1k2m9qrs`。它唯一指向 experiment、结果快照、评估用例和 Attempt。`✓` / `✗` / `!` / `–` 是 passed / failed / errored / skipped。列表不用字母缩写编码证据可用性——定位符本身就是证据入口;打开 attempt(`niceeval show @<定位符>`)后再列出实际可用的证据命令。执行步骤统一包含消息、thinking、tool call/result 和 Skill load;OTel 只给这些步骤补时间,不另开一份输出。
37
+ - **两面同口径,网页面不依赖 JS 也完整。** 排序在计算时由 `sort` 定死,终端和网页看到同一份基准顺序;下钻是普通链接,展开折叠用 `<details>`。网页面另有一层浏览操作:点表头就地重排、榜单行过滤、图表点位悬停看数值——只影响眼前的视图,不改数据口径,刷新即回基准顺序;浏览器禁用 JS 时这些操作消失,内容一样不少(悬停信息退化为图内提示)。
38
+
39
+ ## 两种写法,同一份数据
40
+
41
+ 每个组件的 props 分两种写法,选哪种都行:
42
+
43
+ ```tsx
44
+ // 省略 input/data:用宿主注入的当前 Scope 自动取数
45
+ <MetricTable rows="agent" columns={[endToEndPassRate, costUSD]} filter />
46
+
47
+ // 自己先算好数据再传:中间可以插入任意 JavaScript 加工
48
+ <MetricTable data={await metricTableData(scope, { rows: "agent", columns: [endToEndPassRate, costUSD] })} filter />
49
+ ```
50
+
51
+ 两种写法产出完全相同;同一个组件同时给出 `data` 和取数选项会报错,两者二选一。所有计算函数的第一个参数都是 Scope(或手工挑的结果快照数组);产出的数据都是普通可序列化 JSON,可以先存下来、传给别的进程,或者原样喂给对应组件的 `data` prop。`niceeval/report/react` 入口的同名组件只收 `data`,不做取数——那一层是纯 React 渲染,见[自定义报告](/zh/tutorials/custom-reports)。
52
+
53
+ `evals` 是数据获取阶段唯一的过滤选项:评估用例 id 前缀,与 CLI 位置参数同语义,在聚合**之前**收窄题集。实体列表(`ExperimentList` / `EvalList` / `AttemptList`)不设这个选项——它们逐实体成行,取数后用普通数组 `.filter()` 收窄,效果和任何专门选项完全一样。
38
54
 
39
55
  ## 排版原语
40
56
 
41
- `Row` / `Col` / `Section` / `Text` 负责摆版面,一次摆放两个面各自成立:`Col` 纵向堆叠;`Row` 网页横排、终端字符分栏(宽度不够自动降级纵向);`Section` 是带标题的块;`Text` 是说明文字。另有 `<Style>{css}</Style>` 给自定义组件带样式:web 面吐 `<style>` 标签、text 面渲染为空——静态导出不打包用户代码,`className` 引用的 CSS 靠它随树走。
57
+ `Row` / `Col` / `Section` / `Text` 负责摆版面,一次摆放两个面各自成立:`Col` 纵向堆叠;`Row` 网页横排、终端字符分栏(宽度不够自动降级纵向);`Section` 是带标题的块;`Text` 是说明文字。另有 `<Style>{css}</Style>` 给自定义组件带样式:网页面吐 `<style>` 标签、终端面渲染为空——静态导出不打包用户代码,`className` 引用的 CSS 靠它随树走。
42
58
 
43
59
  ```tsx
44
60
  <Col>
@@ -90,78 +106,116 @@ description: "报告文件里能摆的全部官方双面组件:每个组件回
90
106
  浏览/表单填写 — — @1qrdcfq8
91
107
  ```
92
108
 
93
- 列宽按**终端显示宽度**算,中文和全角字符记 2 列——中文题目名、中文 agent 名都不会把表撕歪。`align: "right"` 让数字列右对齐,小数点自然对齐。格子是 `null` 就渲染 `—`,不补 0。行上带 `locator` 就多出一列 attempt:网页面链到证据室,终端面列出 locator,直接喂给 `niceeval show <locator>`。
109
+ 列宽按**终端显示宽度**算,中文和全角字符记 2 列——中文题目名、中文 agent 名都不会把表撕歪。`align: "right"` 让数字列右对齐,小数点自然对齐。格子是 `null` 就渲染 `—`,不补 0。行上带 `locator` 就多出一列 attempt:网页面链到 Attempt 详情,终端面列出定位符,直接喂给 `niceeval show <定位符>`。
94
110
 
95
111
  表比终端宽时先压最宽的左对齐列(按显示宽度折行),右对齐列不折行——数字折行读不了;压到下限仍放不下,就从右侧丢列,并在表下如实报丢了几列,不静默截断。
96
112
 
97
- 指标表、指标矩阵、成绩单和成对差异表的终端面就建在 `Table` 上,所以你的表和官方的表用的是同一把尺子。表格之外的形态要自己排字符时,用[自定义报告](/zh/how-to/custom-reports)「换形态」一节里那套文本排版函数。
113
+ 某一列的格子只想显示前几行时,给这一列加 `maxLines`:超出的行丢弃,最后一行按显示宽度收口成 `…`;表头不受这个限制。网页面不消费这个字段——格子的高度由你自己的样式决定。
114
+
115
+ 指标表、指标矩阵、成绩单和成对差异表的终端面就建在 `Table` 上,所以你的表和官方的表用的是同一把尺子。表格之外的形态要自己排字符时,用[自定义报告](/zh/tutorials/custom-reports)「换形态」一节里那套文本排版函数。
98
116
 
99
- ## 实验组比较(`ExperimentComparison`)
117
+ ## 摘要格(`Grid` / `Stat`)
100
118
 
101
- `niceeval show` / `view` 不传 `--report` 时使用的默认组合件。它先按 experiment id 的完整父目录分组,再为每组分别计算组摘要、成本 × 端到端成功率散点和实验列表:
119
+ 一批 label-value 的速览数字(耗时、成本、参与率这类)用 `Grid` 摆格子、`Stat` 摆每格的内容,两面都自动排版,不用自己写 CSS 或对齐字符:
102
120
 
103
121
  ```tsx
104
- <ExperimentComparison data={await ExperimentComparison.data(selection)} />
122
+ <Grid columns={3} variant="boxed">
123
+ <Stat label="总耗时" value="12m 30s" />
124
+ <Stat label="总成本" value="$1.86" />
125
+ <Stat label="通过率" value="87%" detail="26 / 30" tone="positive" />
126
+ </Grid>
127
+ ```
128
+
129
+ ```text
130
+ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
131
+ │ 总耗时 │ │ 总成本 │ │ 通过率 │
132
+ │ 12m 30s │ │ $1.86 │ │ 87% │
133
+ │ │ │ │ │ 26 / 30 │
134
+ └─────────────┘ └─────────────┘ └─────────────┘
105
135
  ```
106
136
 
107
- `compare/bub` `compare/codex` 属于 `compare`,可以横向比较;`dev-e2b/bub` 属于另一个组,不会进入同一张图、同一条 series 或同一张表。顶层 experiment 没有父目录时,以自己的完整 id 形成单例组。网页面持有完整组索引并一次聚焦一组,切组不重新读取 Selection;终端面命中多组时只显示组索引与单组查看命令,命中单组时才展开详情。浏览器禁用 JS 时,每组仍以独立 `<details>` 保留完整内容。
137
+ `columns` 是网页宽屏下最多摆几列,必须是正整数;容器变窄或终端变窄时两个面各自减列,不丢任何一格。`variant="boxed"` 给每一格描边,默认 `"plain"` 无框;`density="compact"` 收紧格内留白,字号也跟着降一档,不改变内容。
138
+
139
+ `Stat.value` 收 `LocalizedText`、`number` 或 `null`:`null` 显示 `—`,数字 `0` 照常显示成 `0`,不会被当成缺数据。`detail` 是主值下面的一行小字,省略就不留空行。`tone`(`neutral` / `positive` / `negative` / `warning`)是你对这个数字的语义判断,只给主值上色,组件不会替你从正负号或阈值猜。
108
140
 
109
- 这个分区只属于默认组合件。下面的 `MetricScatter`、`MetricTable` `ExperimentList` 都忠实消费调用方传入的数据;自定义报告把跨组 Selection 传给它们,就表示明确选择跨组分析。
141
+ `Grid` 只管排版,不读 Scope、不聚合指标——`Stat.value` 必须是你已经算好的显示值。需要保留覆盖率角标(`12/15`)和证据引用时,继续用指标表这类数据组件;只有真正自由的摘要卡片才用 `Grid` / `Stat`。一格里要放两个 `Stat` 时,用 `Col` 把它们包起来,`Grid` 仍然只把这个 `Col` 算作一格。
110
142
 
111
- ## 运行总览(`RunOverview`)
143
+ `Section` 也多一个可选的 `meta`:标题行右侧的短元信息,比如「6/6 完成」。网页面和标题同一行右对齐,终端面空间不够时换到下一行,缩进两格。
112
144
 
113
- 每张报告开头「这批数据是什么」:几个配置、几道题、通过分布、总成本、何时跑的;Selection 里的挑选警告随数据一起进来、直接显示在条内,诚实不用你另外传一遍。
145
+ ## 实验比较(`ExperimentComparison`)
146
+
147
+ `niceeval show` / `view` 不传 `--report` 时使用的默认组合件。它把同一份 Scope 显式传给 `ScopeSummary`、成本 × 端到端通过率的 `MetricScatter` 和 `ExperimentList` 三个组件,自己不产出数据,也不合并三者的结果:
114
148
 
115
149
  ```tsx
116
- <RunOverview data={await RunOverview.data(selection)} />
150
+ <ExperimentComparison />
117
151
  ```
118
152
 
119
- ```text
120
- 2 experiments · 15 evals · 42 attempts · composed from 3 runs · latest 2026-07-09T10-00
121
- passed 26 · failed 12 · errored 4 · skipped 0 · $0.93 · 9m 15s
122
- ! snapshot compare/codex-gpt-5.4 predates latest run by 2 days
123
- ```
153
+ 每个 experiment 的评估用例集合来自快照记录的 `selectedEvalIds`。网页面与终端面都显示完整 Scope;需要子集时用 `--exp` 收窄,或在自定义报告里对 Scope 先 `.filter()` 再传给 `input`。
124
154
 
125
- ## 组摘要(`GroupSummary`)
155
+ 实验列表行的显示名不需要额外配置:experiment id 共享公共目录前缀时(比如都在 `compare/` 下),行标签自动缩成各自的最短唯一后缀,不显示重复的前缀;末段撞名的 id 会自动加长到能互相区分为止。完整 id 始终是排序、过滤和折叠展开用的身份键,只是显示文字变短了。
126
156
 
127
- 一组 experiment(比如按目录前缀归在同一个 `<Section>` 里的那些 experiment)的紧凑统计块,回答「这组题综合算下来过了多少、花了多少、什么时候跑的」。组件本身不分组——先用 `Selection.filter` Selection 收窄到这一组,再喂给 `GroupSummary.data`:
157
+ 组卡使用 `Pass rate / 通过率`、`Experiments / 实验`、`Evals / Eval`、`Attempts / Attempt`、`Eval results / Eval 结果`、`Total cost / 总成本` 这套字段标签,不在标签里重复“数”“次”或“计票”。时间显示为本地化到分钟的 `Last run / 最近运行` 或 `Run range / 运行范围`,不直接暴露 ISO 字符串;成本数据覆盖不全时写明“`63/72 次有成本数据`”,不显示没有上下文的 `63/72`。六项 KPI 在宽卡片保持同一行,空间不足时按三项或两项一组换行,避免总成本单独掉到下一行。
128
158
 
129
- ```tsx
130
- const scoped = selection.filter((s) => s.experimentId.startsWith("compare/"));
159
+ 下面的 `MetricScatter`、`ScopeSummary` 和 `ExperimentList` 同样忠实消费调用方传入的数据,不推导隐藏范围。它等价于把三个组件按下面这样手工摆放——想自定义顺序或搭配其它组件时,照这个形状写:
131
160
 
132
- <Section title="compare">
133
- <GroupSummary data={await GroupSummary.data(scoped)} />
134
- </Section>
161
+ ```tsx
162
+ export const MyComparison = defineComponent((_props, ctx) => (
163
+ <Col>
164
+ <ScopeSummary input={ctx.scope} />
165
+ <MetricScatter input={ctx.scope} points="experiment" x={costUSD} y={endToEndPassRate} />
166
+ <ExperimentList input={ctx.scope} filter />
167
+ </Col>
168
+ ));
135
169
  ```
136
170
 
137
- ```text
138
- Pass rate 73.3% · 2 experiments · 15 evals · failed 3 · errored 1 · $0.93
139
- latest 2026-07-09T10:00:00Z
171
+ ## 范围摘要(`ScopeSummary`)
172
+
173
+ 每张报告开头「这批数据是什么」:几个配置、几道题、通过分布、总成本、什么时候跑的。评估用例的身份键是 `experimentId + evalId`:同一个评估用例在不同 experiment 中运行时算两个独立评估用例,数量和判定计票都按这个身份算。
174
+
175
+ ```tsx
176
+ <ScopeSummary /> // 当前 Scope 的摘要,Eval 级计票(默认)
177
+ <ScopeSummary votes="attempt" /> // 同一份数据,改看 Attempt 原始计票
140
178
  ```
141
179
 
142
- 通过率是 eval 级折叠计票口径:同一 eval 的多轮 attempt 先折成一个判定(任一轮通过则算通过,否则取最严重的),`passed / (passed + failed + errored)`,`skipped` 不进分母。页头 `RunOverview` 使用 `endToEndPassRate` 的两级聚合:先算同一道题各 Attempt 的端到端成功率,再跨题平均。两者的聚合粒度不同,但都会让 errored 降低成功率。`GroupSummary` 回答「这组题最终过了几道」,`RunOverview` 回答「每次实际运行交付成功结果的比例」,不要互相替代。`evals` 按 `experimentId + eval id` 的完整身份键去重——组里两个 experiment 各自的同名 eval 算两道题,不会被误合并成一道。`errored` 为 0 时这一段省略,但 `verdicts.errored` 这个数据字段本身永远在,省略只发生在渲染层。`totalCostUSD` 是组内可测 attempt 成本求和,一个 attempt 都没报成本时是 `null`,两面都渲染缺数据而不是 `$0`。
180
+ 数据本身恒携带两级计票,`votes` 只决定显示哪一级:
181
+
182
+ - `votes="eval"`(默认):每个 experiment × 评估用例先按「任一轮 passed 即 passed,否则 failed > errored > skipped」折成最终判定后计票,回答「多少道题最终通过」。
183
+ - `votes="attempt"`:Attempt 原始计票,不折叠,回答「实际跑的每一轮各是什么结果」。
143
184
 
144
- 组摘要是普通的公开组件,`GroupSummary.data` 是普通的公开计算函数——想在自己的报告里按目录前缀分组、每组摆一块,按上面的写法收窄 Selection 再调它就行。
185
+ 两级计票与端到端通过率互不换算:通过率来自官方两级指标口径,不是从计票现场重新加总。选择警告不在这份数据里——警告的呈现件是 `ScopeWarnings` 组件,同一份事实不在页面上出现两次。
186
+
187
+ 收窄范围时在自定义组件里显式传 `input`:
188
+
189
+ ```tsx
190
+ const CompareSummary = defineComponent((_props, ctx) => (
191
+ <ScopeSummary input={ctx.scope.filter((s) => s.experimentId.startsWith("compare/"))} />
192
+ ));
193
+ ```
145
194
 
146
195
  ## 实验列表(`ExperimentList`)
147
196
 
148
- 每项固定代表一个 experiment。主行显示 experiment id、agent、model、flags、Eval 判定构成、通过率、Tokens、成本和耗时;展开后显示这个 experiment 的 Eval 列表。Eval 父行显示折叠判定、Attempt 数、平均耗时和平均成本;下面每个 Attempt 再显示该轮自己的失败摘要。失败内容只出现一次,不会在 Eval 与唯一 Attempt 上重复。它不接受列配置——这是 experiment 的诊断视图,不是通用指标表。默认 `ExperimentComparison` 每次只把一个可比组的 items 交给它;组件本身不猜组边界。
197
+ 每项固定代表一个 experiment。主行显示 experiment id、agent、model、flags、评估用例判定构成、通过率、Tokens、成本和耗时。默认 `ExperimentComparison` 把当前 Scope 的全部条目交给它;组件本身不猜边界。
149
198
 
150
- 默认比较已经用组名作面板 / 段落标题,所以组内每行的 experiment 标签会去掉这层文件夹前缀,只显示 id 末段(和同组散点的点标签一致),不在每行重复文件夹名;完整 id 仍是排序、着色和身份的依据。独立使用 `ExperimentList`、不告诉它相对哪个组时,显示完整 id
199
+ 行标签默认缩成 experiment id 在当前列表里的最短唯一后缀——末段唯一就只显示末段,撞名的 id 各自向前多取一段直到能区分为止(与 `MetricScatter` 散点的点标签同一算法)。排序、过滤和折叠展开始终用完整 id,不受显示名影响。中文副行用“`8 个 Eval`”而不是“`8 道题`”。
151
200
 
152
201
  ```tsx
153
- const experiments = await ExperimentList.data(selection);
202
+ <ExperimentList filter />
203
+ ```
154
204
 
155
- <ExperimentList
156
- items={experiments.filter((exp) => exp.verdicts.errored === 0)}
157
- />
205
+ 要按前缀、agent 或状态过滤,先取数再用普通数组 `.filter()`:
206
+
207
+ ```tsx
208
+ export const ProdExperiments = defineComponent(async (_props, ctx) => {
209
+ const items = await experimentListData(ctx.scope);
210
+ return <ExperimentList data={items.filter((x) => x.experimentId.startsWith("prod/"))} filter />;
211
+ });
158
212
  ```
159
213
 
160
- `niceeval show` 先输出 experiment 比较表,再按 experiment 展开 Eval / Attempt 父子表。Eval 父行给题级平均值,Attempt 子行给这一轮的失败摘要和 locator:
214
+ `niceeval show` 先输出 experiment 比较表,再按 experiment 展开评估用例 / Attempt 父子表。评估用例父行给题级平均值,Attempt 子行给这一轮的失败摘要和定位符:
161
215
 
162
216
  ```text
163
- Experiment Model Agent Avg duration E2E pass rate Result Tokens Est. cost
164
- bub-gpt-5.4 gpt-5.4 bub 41.0s 50% 1 passed / 1 failed 42k $0.08
217
+ Experiment Model Agent Avg. time Pass rate Results Tokens Cost
218
+ bub-gpt-5.4 gpt-5.4 bub 41.0s 50% 1 passed · 1 failed 42k $0.08
165
219
 
166
220
  bub-gpt-5.4
167
221
  Status Eval / Attempt Result Duration Cost
@@ -172,20 +226,19 @@ Status Eval / Attempt Result Duratio
172
226
  ✗ └─ @1nx4dpqr calledTool("get_weather") · no calls 43.0s $0.04
173
227
  ```
174
228
 
175
- locator `experimentId + snapshot.startedAt + evalId + attempt index` 的不可变身份确定,复制或发布结果后保持不变。宿主在当前结果根解析 locator;不存在或发生冲突时直接报错,不回退到“最新一次”。`@` 前缀让它与 Eval ID 前缀选择器无歧义。
229
+ 定位符由 `experimentId + snapshot.startedAt + evalId + attempt 序号` 的不可变身份确定,复制或发布结果后保持不变。宿主在当前结果根解析定位符;不存在或发生冲突时直接报错,不回退到“最新一次”。`@` 前缀让它与评估用例 ID 前缀选择器无歧义。
176
230
 
177
- `.data(selection)` 返回普通的 `ExperimentListItem[]`,顺序按 experiment id 稳定排列。要只看某个 agent、目录前缀或运行状态,直接过滤数组。组件不提供另一套 `filter` 查询语法。网页面的文本搜索只是临时浏览操作,不改变传入的 items;终端面始终输出完整的传入数组。
231
+ `experimentListData(scope)` 返回普通的 `ExperimentListItem[]`,顺序按 experiment id 稳定排列。要只看某个 agent、目录前缀或运行状态,直接过滤数组。组件不提供另一套查询语法。网页面的文本搜索只是临时浏览操作,不改变传入的条目;终端面始终输出完整的传入数组。
178
232
 
179
- ## Eval 列表(`EvalList`)
233
+ ## 评估用例列表(`EvalList`)
180
234
 
181
- 每项固定代表一个 `experimentId + evalId`,因为同一个 Eval 跑在两个 experiment 上是两条不同结果。主行显示判定、Attempt 数、聚合分数、平均成本和平均耗时;展开后显示这个 Eval 的 Attempt 列表,由每个 Attempt 行显示该轮自己的失败原因。Eval 主行不挑某一轮的失败原因冒充题级结论。
235
+ 每项固定代表一个 `experimentId + evalId`,因为同一个评估用例跑在两个 experiment 上是两条不同结果。主行显示判定、Attempt 数、聚合分数、平均成本和平均耗时;展开后显示这个评估用例的 Attempt 列表,由每个 Attempt 行显示该轮自己的失败原因。评估用例主行不挑某一轮的失败原因冒充题级结论。
182
236
 
183
237
  ```tsx
184
- const evals = await EvalList.data(selection);
185
-
186
- <EvalList
187
- items={evals.filter((item) => item.verdict !== "passed")}
188
- />
238
+ export const FailingEvals = defineComponent(async (_props, ctx) => {
239
+ const items = await evalListData(ctx.scope);
240
+ return <EvalList data={items.filter((item) => item.verdict !== "passed")} />;
241
+ });
189
242
  ```
190
243
 
191
244
  `niceeval show` 每个 `experiment × Eval` 输出一项,Attempt 仍用同一套紧凑 ID 和证据位。每项只给一条模板:
@@ -201,24 +254,24 @@ fixtures/button · compare/bub-gpt-5.4 · errored
201
254
  score — · 1 attempt · 2m 00s · $0.09
202
255
  @1c3h6twx! · command timed out after 120s
203
256
 
204
- inspect: niceeval show @<id> [--eval|--execution|--diff]
257
+ inspect: niceeval show @<id> [--source|--execution|--diff]
205
258
  ```
206
259
 
207
- `.data(selection)` 返回普通的 `EvalListItem[]`。按 Eval id 前缀、experiment、判定或分数过滤都使用数组 `.filter()`;组件只负责渲染传入的 items。
260
+ `evalListData(scope)` 返回普通的 `EvalListItem[]`。按评估用例 id 前缀、experiment、判定或分数过滤都使用数组 `.filter()`;组件只负责渲染传入的条目。
208
261
 
209
262
  ## Attempt 列表(`AttemptList`)
210
263
 
211
- 每项固定代表一个 Attempt,显示 experiment、Eval、Attempt 序号、判定、耗时、成本、失败断言、结构化 error 的一层摘要、Judge 评语和证据链接。diagnostics、cause 和 stack 留给 locator 下钻详情,避免比较列表被基础设施日志撑开。它既能列失败证据,也能列通过样本,不把 verdict 过滤写死在组件名里。
264
+ 每项固定代表一个 Attempt,显示 experiment、评估用例、Attempt 序号、判定、耗时、成本、失败断言、结构化 error 的一层摘要、Judge 评语和证据链接。diagnostics、cause 和 stack 留给定位符下钻详情,避免比较列表被基础设施日志撑开。它既能列失败证据,也能列通过样本,不把判定过滤写死在组件名里。
212
265
 
213
266
  ```tsx
214
- const attempts = await AttemptList.data(selection);
215
-
216
- <AttemptList
217
- items={attempts.filter((item) => item.verdict === "failed" || item.verdict === "errored")}
218
- />
267
+ export const RecentFailures = defineComponent(async (_props, ctx) => {
268
+ const all = await attemptListData(ctx.scope);
269
+ const failed = all.filter((x) => x.verdict === "failed" || x.verdict === "errored");
270
+ return <AttemptList data={failed.slice(0, 20)} total={failed.length} />;
271
+ });
219
272
  ```
220
273
 
221
- `niceeval show` 每项完整输出一个 Attempt,不折叠到 Eval 汇总。这里已经是叶子层,只在末尾给一条与该 Attempt 可用证据对应的模板:
274
+ `niceeval show` 每项完整输出一个 Attempt,不折叠到评估用例汇总。这里已经是叶子层,只在末尾给一条与该 Attempt 可用证据对应的模板:
222
275
 
223
276
  ```text
224
277
  ✗ @1k2m9qrs · weather/brooklyn · compare/bub-gpt-5.4 · 41s · $0.04
@@ -229,41 +282,53 @@ const attempts = await AttemptList.data(selection);
229
282
 
230
283
  ! @1c3h6twx · fixtures/button · compare/bub-gpt-5.4 · 2m 00s · $0.09
231
284
  command timed out after 120s
232
- inspect: niceeval show @<id> [--eval|--execution|--diff]
285
+ inspect: niceeval show @<id> [--source|--execution|--diff]
233
286
 
234
287
  (3 more not shown · showing 20 of 23)
235
288
  ```
236
289
 
237
- 要展示哪些 Attempt,过滤返回的 `AttemptListItem[]`。`limit` 也由报告作者在数组上用 `.slice(0, 20)` 表达,截断时把原始数量交给组件的 `total`,组件据此显示“还有 n 项未展示”,不静默截断。
290
+ 要展示哪些 Attempt,过滤返回的 `AttemptListItem[]`。截断数量也由报告作者在数组上用 `.slice(0, 20)` 表达,截断时把原始数量交给组件的 `total`,组件据此显示“还有 n 项未展示”,不静默截断。
291
+
292
+ ## 失败列表(`FailureList`)
293
+
294
+ 「现在有哪些失败要处理」是每份报告都要的固定区块,工具箱直接提供成品组合件,不用每次都重写同一段取数过滤。它和上面 `AttemptList` 的手工写法完全等价,只是把最常见的一种过滤打包好了:收 `verdict` 为 failed 或 errored 的 Attempt,按开始时间倒序(最近的失败在前),截断到 `limit`(默认 20)。
295
+
296
+ ```tsx
297
+ <FailureList limit={30} />
298
+ ```
299
+
300
+ 要按 agent、成本或其它口径筛选,回到手写 `AttemptList` 的写法自己加工数组;`FailureList` 只覆盖这一种最常见的问题。
238
301
 
239
302
  ## 指标表(`MetricTable`)
240
303
 
241
- 一行一个维度值、一列一个指标的对比表,回答「谁整体更好」。行维度、指标列、排序全部可换,自定义指标(`defineMetric`)与内置指标同列。
304
+ 一行一个维度值,一列一个指标,回答「谁整体更好」。行维度、指标列、排序全部可换,自定义指标(`defineMetric`)与内置指标同列。
242
305
 
243
306
  ```tsx
244
- <MetricTable data={await MetricTable.data(selection, {
245
- rows: "agent",
246
- columns: [endToEndPassRate, codeLines, costUSD],
247
- sort: endToEndPassRate,
248
- })} />
307
+ <MetricTable
308
+ rows="agent"
309
+ columns={[endToEndPassRate, examScore, costUSD, durationMs]}
310
+ sort={endToEndPassRate}
311
+ evals="coding/"
312
+ filter
313
+ />
249
314
  ```
250
315
 
251
316
  ```text
252
- agent pass rate code lines cost
253
- bub 87% 312 lines $0.42
254
- codex 80% 12/15 355 lines $0.51
317
+ agent pass rate examScore cost
318
+ bub 87% 0.91 $0.42
319
+ codex 80% 12/15 0.86 $0.51
255
320
  ```
256
321
 
257
- 两面都按 `sort` 预排,基准顺序一致——要固定换一种排序,改一行重跑。`12/15` 角标表示该格 15 个 attempt 里只有 12 个测得了这个指标。
322
+ 两面都按 `sort` 预排,基准顺序一致——要固定换一种排序,改一行重跑。`12/15` 角标表示该格 15 个 attempt 里只有 12 个测得了这个指标。`sort` 必须是 `columns` 中同一个 Metric 实例且声明了 `better`,否则报错;省略时按行 key 字典序,避免为方向不明的指标猜顺序。`filter` 只给网页面加行过滤框,不改变数据或终端输出。
258
323
 
259
- `rows: "experiment"` 时每行自动带 agent 与 model 列——结果里现成的元信息,不用配置。`MetricTable` 不展开实体层级:要看 experiment、Eval Attempt 的固定诊断字段,分别使用三个实体列表;要自由换维度和指标列,使用指标表。
324
+ `rows: "experiment"` 时每行自动带 agent 与 model 列——结果里现成的元信息,不用配置。`MetricTable` 不展开实体层级:要看 experiment、评估用例或 Attempt 的固定诊断字段,用上面三个实体列表;要自由换维度和指标列,用指标表。
260
325
 
261
326
  ## 指标矩阵(`MetricMatrix`)
262
327
 
263
328
  行 × 列两个维度、格子里一个指标,回答「哪道题谁挂了」。稀疏渲染:没有样本的格子空着,不编数。
264
329
 
265
330
  ```tsx
266
- <MetricMatrix data={await MetricMatrix.data(selection, { rows: "eval", columns: "agent", cell: endToEndPassRate })} />
331
+ <MetricMatrix rows="eval" columns="agent" cell={endToEndPassRate} />
267
332
  ```
268
333
 
269
334
  ```text
@@ -279,14 +344,14 @@ next: niceeval show geometry/area
279
344
 
280
345
  ## 分组条形图(`MetricBars`)
281
346
 
282
- 同一份矩阵数据的另一种摆法(`MetricBars.data` 就是 `MetricMatrix.data` 的别名):按组并排比大小,回答「每个科目上谁领先、差多少」。组维度一组条、系列维度一根条、条长是指标值——benchmark 发布图(Terminal-Bench、BrowseComp 各一组,每个 agent 一根柱)就是这个形状。
347
+ 同一份矩阵数据的另一种摆法:按组并排比大小,回答「每个科目上谁领先、差多少」。组维度一组条、系列维度一根条、条长是指标值——benchmark 发布图(Terminal-Bench、BrowseComp 各一组,每个 agent 一根柱)就是这个形状。
283
348
 
284
349
  ```tsx
285
- <MetricBars data={await MetricBars.data(selection, {
286
- rows: "evalGroup", // 一组条 = 一个科目/benchmark
287
- columns: "agent", // 一根条 = 一个 agent
288
- cell: endToEndPassRate,
289
- })} />
350
+ <MetricBars
351
+ rows="evalGroup" // 一组条 = 一个科目/benchmark
352
+ columns="agent" // 一根条 = 一个 agent
353
+ cell={endToEndPassRate}
354
+ />
290
355
  ```
291
356
 
292
357
  ```text
@@ -298,19 +363,24 @@ geometry
298
363
  codex —
299
364
  ```
300
365
 
301
- 网页面是竖向分组柱:柱顶标数值,系列颜色与其它组件的稳定配色一致,图例自动生成。终端面横向条形,字符宽度即刻度,同组内按值排序(方向随 `better`)。`better: "lower"` 的指标(成本、耗时)条形反向填充,短条恒为「好」。
366
+ 网页面是竖向分组柱:柱顶标数值,系列颜色与其它组件的稳定配色一致,图例自动生成。终端面横向条形,字符宽度即刻度,同组内按值排序(方向随 `better`)。`better: "lower"` 的指标(成本、耗时)条形反向填充,短条恒为「好」。`MetricMatrix` 和 `MetricBars` 写同一份取数选项时,两个组件不会重复计算——只要 `input` 与选项相同,底层数据只算一次。
302
367
 
303
368
  ## 成绩单(`Scoreboard`)
304
369
 
305
- 总分 + 分科小计,回答「这套题它能得几分」。逐题分值制:权重按 eval id 前缀配置,分母对所有被打分者恒定,没跑到的题挣 0 分并如实报 `missing`。
370
+ 总分 + 分科小计,回答「这套题它能得几分」。逐题分值制:权重按评估用例 id 前缀配置,分母对所有被打分者恒定,没跑到的题挣 0 分并如实报 `missing`。
306
371
 
307
372
  ```tsx
308
- <Scoreboard data={await Scoreboard.data(selection, {
309
- rows: "agent",
310
- subjects: "evalGroup",
311
- weights: { "algebra/": 2 },
312
- fullMarks: 100,
313
- })} />
373
+ <Scoreboard
374
+ rows="agent"
375
+ questions={[
376
+ "security/sql-injection",
377
+ "security/path-traversal",
378
+ "correctness/retry",
379
+ ]}
380
+ weights={{ "security/": 3, "correctness/": 2 }}
381
+ fullMarks={100}
382
+ score={examScore}
383
+ />
314
384
  ```
315
385
 
316
386
  ```text
@@ -319,21 +389,20 @@ bub 86.5/100 45/50 41.5/50
319
389
  codex 71.0/100 40/50 31/50 (1 missing)
320
390
  ```
321
391
 
392
+ `questions` 是显式固定题集,不从已观测的 Attempt 并集猜——所有配置都没跑到的题仍然留在分母里按 0 分计。分数为 `null`(跑了但测不了)与完全没跑到的题分开计数(分别是 `unscorable` 和 `unrun`),成绩单能回答「这 0 分是没去考还是考了判不了」。`score` 默认是 `examScore`,每道题必须产出 `[0, 1]`;总分是 `fullMarks × earned / possible`。
393
+
322
394
  ## 指标散点图(`MetricScatter`)
323
395
 
324
396
  每个点一个配置、两个指标各占一轴,回答「又好又便宜的是谁」。`series` 把同 agent 不同档位的点连成线;`better` 驱动轴向——`lower` 的轴反向画,「好」的角落恒在右上。
325
397
 
326
398
  ```tsx
327
- <MetricScatter
328
- selection={selection}
329
- points="experiment" // 每个点 = 一个配置的聚合
330
- series="agent" // agent 的档位连成线
331
- x={costUSD}
332
- y={endToEndPassRate}
333
- />
399
+ <MetricScatter points="experiment" series="agent" x={costUSD} y={endToEndPassRate} />
400
+
401
+ // 同族变体连线:同 line 值的实验一色成线,connect 连出基线 → 变体的位移
402
+ <MetricScatter points="experiment" series={label("line")} connect x={costUSD} y={endToEndPassRate} />
334
403
  ```
335
404
 
336
- 直接把 `selection` 传给它,宿主渲染前替你算好数据。`MetricScatter` 不根据 experiment id 自动分组;默认 `ExperimentComparison` 会先收窄到一个可比组,再逐组调用它。要在自己已经跑起来的 React 应用里嵌这张图、或数据是预先算好的,改传 `data`:`<MetricScatter data={await MetricScatter.data(selection, { points: "experiment", series: "agent", x: costUSD, y: endToEndPassRate })} />`;同时传 `data` 和 `selection`、或两者都不传,类型检查都会报错。
405
+ `MetricScatter` 直接消费传入的 Scope,宿主渲染前替你算好数据;默认 `ExperimentComparison` 也把当前 Scope 原样交给它。要嵌预先算好的数据,改传 `data`。
337
406
 
338
407
  ```text
339
408
  pass ↑ (好 → 右上)
@@ -347,18 +416,15 @@ pass ↑ (好 → 右上)
347
416
  A bub-high B bub-medium C codex-high D codex-low
348
417
  ```
349
418
 
350
- 网页面点带悬停提示(值与 `samples/total`,禁用 JS 时退化为图内提示)、同系列连线、点击深链下钻。终端面用字母标点、图例列在图下;x 或 y 缺数据的点两个面都不画,注脚如实报「n 个点缺数据」;点太密排不下时降级为坐标表,不硬挤。画得出来的点是 0 个(x 或 y 全缺数据)时,两个面都明说这两个指标没有可用数据,不留一片空白;1 个点也照常出图——组件从不因为点不够就整块消失,让你不知道图为什么没了。维度槽也收自定义维度(`{ name, of }`,从 attempt 已有数据算组名)和 `flag()`(experiment 声明的变量),怎么选见[自定义报告](/zh/how-to/custom-reports)的「换分组」一节。
419
+ 网页面点带悬停提示(值与 `samples/total`,禁用 JS 时退化为图内提示)、同系列连线、点击深链下钻。终端面用字母标点,图例列在图下;x 或 y 缺数据的点两个面都不画,注脚如实报「n 个点缺数据」;点太密排不下时降级为坐标表,不硬挤。画得出来的点是 0 个时,两个面都明说这两个指标没有可用数据,不留一片空白;1 个点也照常出图。维度槽也收自定义维度和 `flag()`(experiment 声明的变量),怎么选见[自定义报告](/zh/tutorials/custom-reports)的「换分组」一节。
351
420
 
352
421
  ## 指标趋势图(`MetricLine`)
353
422
 
354
- x 是有序变量、每个系列一条线,回答「变量拧大,分数怎么走」——并行 agent 数 × 模拟延迟 × 得分这类 scaling 图就是它。与 `MetricScatter` 的分工:散点图的两轴都是测出来的指标(找优势前沿),趋势图的 x 是你配置的变量(看趋势)。变量在 experiment 的 `flags` 里声明,报告用 `flag()` 直接引用,不从 experiment 命名里解析。
423
+ x 是有序变量、每个系列一条线,回答「变量拧大,分数怎么走」——并行 agent 数 × 模拟延迟 × 得分这类 scaling 图就是它。与 `MetricScatter` 的分工:散点图的两轴都是测出来的指标(找优势前沿),趋势图的 x 是你配置的变量(看趋势)。变量在 experiment 的 flags 里声明,报告用 `flag()` 或数值型 flag 助手直接引用,不从 experiment 命名里解析。
355
424
 
356
425
  ```tsx
357
- <MetricLine data={await MetricLine.data(selection, {
358
- x: flag("latencyMs", { label: "Simulated latency", unit: "ms" }),
359
- series: flag("agents", { label: (v) => `${v} agents` }),
360
- y: endToEndPassRate,
361
- })} />
426
+ const budget = numericFlag("budget", { label: "Token budget", unit: "tokens" });
427
+ <MetricLine x={budget} series="agent" y={endToEndPassRate} />
362
428
  ```
363
429
 
364
430
  ```text
@@ -376,20 +442,26 @@ pass ↑
376
442
  A 1 agents B 4 agents C 16 agents
377
443
  ```
378
444
 
379
- 每个点是一个 experiment 的聚合,与其它组件同一台两级聚合引擎,`samples` / `total` / `refs` 一应俱全;同系列的点按 x 排序连线。网页面每个点可 hover、可深链下钻。终端面同系列共用一个字母、沿 x 排布,趋势肉眼可读;y 缺数据的点不画、注脚报数,点太密降级为坐标表。
445
+ 每个点是一个 experiment 的聚合,与其它组件同一套指标引擎;同系列的点按 x 排序连线。网页面每个点可 hover、可深链下钻。终端面同系列共用一个字母、沿 x 排布,趋势肉眼可读;y 缺数据的点不画、注脚报数,点太密降级为坐标表。
380
446
 
381
447
  ## 成对差异表(`DeltaTable`)
382
448
 
383
- 每行一对配置、每列一个指标,格子里 A、B、Δ 三个值,回答「这个开关值不值」「这次修复翻转了什么」。涨跌好坏由 `better` 判定,任一侧缺数据 Δ 显示为缺,不硬算。「这次 vs 上次」(同一配置的两个结果快照)也是它:`pairs` 的 `a` / `b` 除 experiment id 外也收快照键 `<experimentId> @ <startedAt>`——时间轴对比要用到旧快照,`results.latest()` 里没有,配手挑的快照数组(如 `[exp.latest, exp.snapshots[1]]`)用。
449
+ 每行一对配置、每列一个指标,格子里 A、B、Δ 三个值,回答「这个开关值不值」「这次修复翻转了什么」。涨跌好坏由 `better` 判定,任一侧缺数据 Δ 显示为缺,不硬算。
384
450
 
385
451
  ```tsx
386
- <DeltaTable data={await DeltaTable.data(selection, {
387
- pairs: [
388
- { a: "compare/bub", b: "compare/bub--agents-md", label: "bub" },
389
- { a: "compare/codex", b: "compare/codex--agents-md", label: "codex" },
390
- ],
391
- metrics: [endToEndPassRate, costUSD],
392
- })} />
452
+ // 字面形态:逐对声明,label 自定义
453
+ <DeltaTable
454
+ by="experiment"
455
+ pairs={[{ label: "memory", a: "compare/baseline", b: "compare/with-memory" }]}
456
+ metrics={[endToEndPassRate, costUSD, durationMs]}
457
+ />
458
+
459
+ // 派生形态:按 flag 机械配对,加实验不用改报告
460
+ <DeltaTable
461
+ by="experiment"
462
+ pairs={pairsByFlag("memory")}
463
+ metrics={[endToEndPassRate, costUSD, durationMs]}
464
+ />
393
465
  ```
394
466
 
395
467
  ```text
@@ -398,6 +470,8 @@ bub 87% → 93% +6% $0.42 → $0.45 +$0.03
398
470
  codex 80% → 80% ±0 $0.51 → — —
399
471
  ```
400
472
 
473
+ 「这次 vs 上次」(同一配置的两个结果快照)也是它:`pairs` 的 `a` / `b` 除 experiment id 外也收快照键 `<experimentId> @ <startedAt>`——手挑的快照数组(比如某个实验的最新一次和上一次)配这种写法。`pairsByFlag(name)` 按一个 flag 机械导出全部 A/B 对:实验矩阵是「同配置开关某个 flag」时,配对关系本来就是 experiment 配置的推论,手抄 id 字面量等于把配置复写进报告,加实验后报告会静默缺行。
474
+
401
475
  ## 官方组件之外
402
476
 
403
- 以上摆法都表达不了时,用 `defineComponent` 写自己的双面组件——网页怎么渲染、终端字符怎么排,两个面你都说了算,写法见[自定义报告](/zh/how-to/custom-reports)的「换形态」一节。
477
+ 以上摆法都表达不了时,用 `defineComponent` 写自己的双面组件——网页怎么渲染、终端字符怎么排,两个面你都说了算,写法见[自定义报告](/zh/tutorials/custom-reports)的「换形态」一节。