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