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
@@ -0,0 +1,453 @@
1
+ ---
2
+ title: "编写自定义报告"
3
+ sidebarTitle: "自定义报告"
4
+ description: "用 defineReport、内置报告组件和双面组件编写一份同时用于 niceeval show 与 niceeval view 的自定义报告。"
5
+ ---
6
+
7
+ [查看结果](/zh/tutorials/viewing-results)介绍 `niceeval show` 的终端输出和 `niceeval view` 的网页输出。本教程用相同的结果数据编写考试成绩单、代码行数表和质量 × 成本图等自定义报告。
8
+
9
+ 一份报告对应一个报告文件:一棵由内置组件搭起来的树。`niceeval show` 和 `niceeval view` 打开结果、选出当前范围(Scope),交给这棵树渲染。把文件路径传给 `--report`,同一份定义在终端和网页都能渲染。自定义报告同样支持 Attempt 证据链接、`--results` 指定结果根和静态导出。
10
+
11
+ ## show / view 的默认报告也是一份报告文件
12
+
13
+ `niceeval show` / `view` 不传 `--report` 时渲染的默认报告不是私有实现,而是包里自带的一个普通报告文件:报告、Attempts、追踪三个导航页,加一个不进导航、按 Attempt 定位符打开的详情页,每页都由 `niceeval/report` 公开导出的组件搭成。
14
+
15
+ 这份默认报告本身以 `standard` 为名从 `niceeval/report/built-in` 导出。只想在默认报告上加站点标题、GitHub 链接或统计脚本时,用 `extends` 在它上面叠自己的外壳,页面内容一行不用写;NiceEval 升级带来的页面改进也会自动跟过来:
16
+
17
+ ```tsx
18
+ // reports/branded.tsx —— 默认报告整站 + 自己的标题和链接
19
+ import { defineReport } from "niceeval/report";
20
+ import { standard } from "niceeval/report/built-in";
21
+
22
+ export default defineReport({
23
+ extends: standard,
24
+ title: "Memory Evals",
25
+ links: [{ label: "GitHub", href: "https://github.com/you/repo" }],
26
+ });
27
+ ```
28
+
29
+ `niceeval/report/built-in` 是内置报告的集合,每份一个名字;今天只有 `standard`,以后新增的内置报告也从这里按名字导入。想改页面内容本身,用同一批公开组件自己搭——你的报告文件能逐字写出同样的首页,也能只留自己要的部分:
30
+
31
+ ```tsx
32
+ import { ExperimentComparison } from "niceeval/report";
33
+
34
+ <ExperimentComparison />
35
+ ```
36
+
37
+ `ExperimentComparison` 就是默认首页:一行组件直接对当前 Scope 计算范围摘要、成本 × 端到端成功率散点图和实验明细表,不需要额外取数。
38
+
39
+ | | 网页(人看) | 终端(agent 和你看) |
40
+ | --- | --- | --- |
41
+ | 官方默认 | 分组比较报告(网页面) | 同一分组比较报告(文本面) |
42
+ | 自定义报告 | `niceeval view --report reports/exam.tsx` | `niceeval show --report reports/exam.tsx` |
43
+
44
+ 自定义报告分为三个层次:
45
+
46
+ 1. **调整布局**:用内置组件和 `Row` / `Col` 重新排列版面,需要自己分组或过滤时写一个组合组件。
47
+ 2. **换口径**:`defineMetric` 定义自己的指标,`flag()` / `label()` / `runConfig()` 定义自己的分组和坐标轴。
48
+ 3. **自定义组件**:表格用 `<Table>` 自定义列,其它展示用 `defineComponent` 分别实现网页和终端渲染。
49
+
50
+ ## 创建报告文件
51
+
52
+ 先交代唯一的前置:报告文件是 `.tsx`,写它的项目要装 `react`(写自定义组件的 web 面还要 `@types/react`),tsconfig 里 `compilerOptions.jsx` 设为 `"react-jsx"`。裸跑 `niceeval show` / `niceeval view` 不需要这些——只有自己写报告文件才需要。
53
+
54
+ 用 `defineReport` 声明一棵树。宿主打开结果目录(包括 `--results` 指定的结果根),按默认规则选出当前 Scope,再把这份 Scope 交给树里每个组件——组件自己知道怎么从 Scope 取数,报告文件不需要手工传参:
55
+
56
+ ```tsx
57
+ // reports/exam.tsx —— 终端和网页共用一份定义
58
+ import {
59
+ defineReport, Col, Section,
60
+ ExperimentComparison, Scoreboard, examScore,
61
+ } from "niceeval/report";
62
+
63
+ export default defineReport(
64
+ <Col>
65
+ <ExperimentComparison />
66
+ <Section title="考试成绩单">
67
+ <Scoreboard
68
+ rows="agent"
69
+ questions={["security/sql-injection", "correctness/retry"]}
70
+ score={examScore}
71
+ />
72
+ </Section>
73
+ </Col>,
74
+ );
75
+ ```
76
+
77
+ ```bash
78
+ niceeval show --report reports/exam.tsx # 终端:同一棵树走文本面
79
+ niceeval view --report reports/exam.tsx # 网页:渲染网页面,Attempt 链接进入证据页
80
+ ```
81
+
82
+ `ExperimentComparison`、`Scoreboard` 这类组件省略 `input` 时都默认吃宿主选出的当前 Scope——不用在报告文件里手工取数再喂给它们。Scope 的挑选规则是:对每个实验、每道评估用例,取该实验历史运行里最新的那次判定;只按前缀重跑了一部分评估用例时,其余评估用例的判定从更早的运行补齐,不会因为一次局部重跑就整体退回某一份残缺快照。
83
+
84
+ 默认挑法不合口径,或者要按子集分别展示时,不能在报告树里直接写 JavaScript——树只负责声明,取数发生在组合组件里。用 `defineComponent` 写一个组合组件,函数体里能拿到 `ctx.scope`(当前 Scope)和 `ctx.results`(结果根的完整读取面,取历史快照用它),用普通 JavaScript 加工后再传给下游组件:
85
+
86
+ ```tsx
87
+ import { ScopeSummary, Section, defineComponent } from "niceeval/report";
88
+
89
+ const ProdSummary = defineComponent((_props: {}, ctx) => (
90
+ <Section title="生产实验">
91
+ <ScopeSummary input={ctx.scope.filter((s) => s.experimentId.startsWith("prod/"))} />
92
+ </Section>
93
+ ));
94
+ ```
95
+
96
+ 命令行的范围先作用在挑选上,组件拿到的就是收窄到这个范围后的 Scope:位置参数的评估用例 id 前缀收窄 Scope 覆盖的评估用例(覆盖提醒的分母同样收窄到范围内),`--results` 把结果根换成指定目录,`--exp` 让 Scope 只留该实验。`--history` 与 `--report` 互斥——趋势在报告里用 `ctx.results` 自己组织;证据视图(`--source` / `--execution` / `--diff`)只看证据,不渲染报告。
97
+
98
+ 报告树里的组件有两种数据形态。像上面这样省略 `data`、直接传计算选项(`rows`、`columns`、`questions` 这类)是 spec 形态,组件自己在渲染前取数;需要先用 JavaScript 过滤或加工时,改用组件配套的 `xxxData(scope, options)` 函数手工取数,再把结果传给 `data` prop——两种写法产出完全相同,选哪种只看要不要在取数和渲染之间插入自己的逻辑。完整的双形态契约和每个组件的字段见[报告组件](/zh/reference/report-components)。
99
+
100
+ 选择警告(`ScopeWarnings`)组件显示覆盖不全、快照过期、运行未完成和快照读取失败等提醒,按实验分组,组头列出实验名、问题标签和可复制的重跑命令,每条原文位于可展开区域中。默认报告的每一页都包含该组件;自定义报告需要显示提醒时,在页首添加 `<ScopeWarnings />`。
101
+
102
+ 页面里的每个组件都是**双面**的:网页面是 React 渲染,终端面是字符渲染,两面吃同一份算好的数据。实体列表按 experiment → Eval → Attempt 展示事实;指标表、矩阵、条形图、成绩单、散点图、趋势图和差异表展示聚合值。完整清单见[报告组件](/zh/reference/report-components)。网页面的实体、格子和点深链到 Attempt 详情,终端面印出对应的 `niceeval show <eval id>` 下钻命令。
103
+
104
+ 默认报告没有特权:它的四个页面全部由公开组件搭成。需要同样的当前 Scope 比较就写 `<ExperimentComparison />`;要子集就在报告里先 `filter`,或从 CLI 用 `--exp` 收窄。
105
+
106
+ ## 排版:内置排版原语
107
+
108
+ 排版原语也是双面组件,同一份布局会分别渲染到网页和终端:
109
+
110
+ - **`<Col>`**:纵向依次排列——网页是块级堆叠,终端是逐块输出。
111
+ - **`<Row>`**:并排——网页是横向排布,终端是字符分栏;终端宽度不够时自动降级为纵向,不硬挤。
112
+ - **`<Grid columns={N}>` / `<Stat>`**:自由摘要格,一格一个 label-value 卡片;`columns` 是网页宽屏下最多摆几列,窄屏和终端都会自动减列,不丢格。见下文「自由摘要格」。
113
+ - **`<Section title="…">`**:带标题的块,网页是标题层级,终端是标题行加缩进;可选 `meta` 是标题行右侧的短元信息,网页同行右对齐,终端放不下时换行。
114
+ - **`<Text>`**:说明文字,网页是段落,终端是折行文本。
115
+ - **`<Table>`**:自定义列的表——网页是 `<table>`,终端是按显示宽度对齐的字符表,中文列不撕歪。
116
+ - **`<Tabs>` / `<Tab title="…">`**:一页里的并列视图,网页是可切换的 tab 条,终端按顺序全部输出——tab 是浏览状态,不是页面,内容多到终端读不动时是升级成单独页面的信号。
117
+
118
+ ```tsx
119
+ <Row>
120
+ <Scoreboard rows="agent" questions={["security/sql-injection", "correctness/retry"]} score={examScore} />
121
+ <MetricTable rows="agent" columns={[endToEndPassRate, costUSD]} />
122
+ </Row>
123
+ ```
124
+
125
+ ```text
126
+ $ niceeval show --report reports/exam.tsx
127
+ 考试成绩单 │ agent pass cost
128
+ agent 总分 security correctness │ bub 87% $0.42
129
+ bub 86.5/100 45/50 41.5/50 │ codex 80% $0.51
130
+ ```
131
+
132
+ 页面树里只放双面组件和排版原语,不放裸 HTML 标签——终端面没法渲染一个 `<div>`。要自由内容,说明文字用 `<Text>`,更自由的走下面的自定义组件。
133
+
134
+ ## 按子集分组:组合组件 + filter
135
+
136
+ 需要按目录前缀把 Experiment 分成几组、每组单独摆一块摘要时,不需要专门的分组组件——写一个组合组件,在 `ctx.scope` 上 `filter`,把收窄后的 Scope 交给 `ScopeSummary`:
137
+
138
+ ```tsx
139
+ // reports/groups.tsx —— 按 experiment id 前缀分组,每组一块 ScopeSummary
140
+ import { Col, ScopeSummary, Section, defineComponent, defineReport } from "niceeval/report";
141
+
142
+ const GroupBlocks = defineComponent((_props: {}, ctx) => {
143
+ const prefixes = ["agents/codex/", "agents/claude/"];
144
+
145
+ return (
146
+ <Col>
147
+ {prefixes.map((prefix) => (
148
+ <Section key={prefix} title={prefix}>
149
+ <ScopeSummary input={ctx.scope.filter((s) => s.experimentId.startsWith(prefix))} />
150
+ </Section>
151
+ ))}
152
+ </Col>
153
+ );
154
+ });
155
+
156
+ export default defineReport(<GroupBlocks />);
157
+ ```
158
+
159
+ ```text
160
+ $ niceeval show --report reports/groups.tsx
161
+ agents/codex/
162
+ Pass rate 60% · 2 experiments · 6 evals · failed 1 · errored 1 · $1.50
163
+ ```
164
+
165
+ 在分组块后面加散点图或 Experiment 列表时,把同一份收窄后的 Scope 当 `input` 传给它们,或者用 `await experimentListData(scoped)` 拿到可自行过滤的数组。需要按其它维度(不是路径前缀)分组比较,用下文的 `MetricTable` 加自定义维度更直接。
166
+
167
+ ## 换口径:自定义指标
168
+
169
+ 实验、Eval、Attempt 的固定诊断字段由三个实体列表承接。下面的例子是另一种需求:用通用 `MetricTable` 自由换指标口径——每个指标的计算逻辑都挂在自己身上,换口径只需要换 `columns`:
170
+
171
+ ```tsx
172
+ // reports/golf.tsx —— code-golf:只比通过方案的改动行数
173
+ import {
174
+ MetricTable, costUSD, defineMetric, defineReport, endToEndPassRate,
175
+ } from "niceeval/report";
176
+
177
+ const changedLines = defineMetric({
178
+ name: "changed-lines",
179
+ label: { en: "Changed lines", "zh-CN": "改动行数" },
180
+ unit: "lines",
181
+ better: "lower",
182
+ where: (attempt) => attempt.result.verdict === "passed",
183
+ async value(attempt) {
184
+ const diff = await attempt.diff();
185
+ if (!diff) return null;
186
+ return Object.keys(diff.files)
187
+ .reduce((sum, path) => sum + (diff.get(path) ?? "").split("\n").length, 0);
188
+ },
189
+ });
190
+
191
+ export default defineReport(
192
+ <MetricTable
193
+ rows="agent"
194
+ columns={[endToEndPassRate, changedLines, costUSD]}
195
+ sort={endToEndPassRate}
196
+ />,
197
+ );
198
+ ```
199
+
200
+ ```text
201
+ $ niceeval show --report reports/golf.tsx
202
+ agent pass rate changed lines cost
203
+ bub 87% 312 lines $0.42
204
+ codex 80% 355 lines $0.51
205
+ ```
206
+
207
+ 格子里的终值是**两级折叠**出来的:同一道题的多个 attempt 先折成题级值(`perEval`),再跨题折成格子值(`acrossEvals`),两级默认都是平均。分两级不是形式主义——失败的题天然比通过的题 attempt 多(重试跑满、通过即停),平铺求均值会让分数和重试策略纠缠在一起。两级都能在 `defineMetric` 的 `aggregate` 里换:`aggregate: { perEval: "max", acrossEvals: "mean" }` 就是 pass@k(题内取最好一次,跨题取占比)。
208
+
209
+ 两个面继承同一套诚实契约:排序方向随指标的 `better`,覆盖不全的格子带 `12/15` 角标(15 个 attempt 里 12 个测得了该指标),缺数据渲染成 `—` 而不是 0。指标只定义一次,两个面共用——人在网页上看到的数字,就是 agent 在 stdout 里读到的数字,判断口径永远一致。给 agent 的指引也只多一行:跑 `niceeval show --report reports/golf.tsx`,读 stdout。
210
+
211
+ `label` 可以是一份文案,也可以按语言给:`label: { en: "Changed lines", "zh-CN": "改动行数" }`——查看器界面切语言时,按语言给的 label 跟着切;只给一份就两种语言都用它。指标算出来的数字本身不分语言。
212
+
213
+ 内置指标里 `endToEndPassRate` / `taskPassRate` / `executionReliability` / `costUSD` / `durationMs` / `tokens` 只读 Attempt 自带的判定、用量这些字段,任何一份结果目录都算得出。没有限定词的"成功率"使用 `endToEndPassRate`:passed 记 1,failed 和 errored 都记 0。`taskPassRate` 只在形成可信判定的样本上衡量答题质量,errored 不参与;展示它时应明确写"可判定任务通过率",不能简称成功率。要区分答题质量和执行问题,把 `endToEndPassRate`、`taskPassRate`、`executionReliability` 三列并排。`assistantTurns`(o11y 事件流里的 assistant turn 数)不一样,它读 `attempt.o11y()`——这份数据 `copySnapshots` 缺省会随行,但如果发布脚本显式给了 `artifacts` 列表又没把 `"o11y"` 写进去,它就不在发布根里(见[结果数据 API](/zh/reference/results-data)的"发布"一节),指标渲染成 `—`,不是 0。自己写的指标只要读了 `o11y()` / `diff()` 这类 artifact(就像上面 `changedLines` 读 `attempt.diff()`),发布前都要过一遍同样的检查。
214
+
215
+ ## 换分组:三种来源
216
+
217
+ 维度决定分组——表的行、矩阵的行列、散点的点、趋势图的 x 轴。每个维度槽收三种值:
218
+
219
+ - **内置维度**:`"agent"`、`"model"`、`"experiment"`、`"eval"`、`"evalGroup"`、`"snapshot"`,结果里现成的身份字段。
220
+ - **自定义维度**:`{ name, of }`,一个纯函数吃 attempt、吐组名。定义只住在报告文件里,不用改任何 experiment 文件。
221
+ - **声明式变量**:`flag()` / `label()` / `runConfig()`,分别引用 experiment 用 `flags`、`labels` 声明的变量和顶层运行配置。
222
+
223
+ ### 自定义维度:从已有数据派生分组
224
+
225
+ 分组能从现有结果数据**计算**出来时用自定义维度,不要求为了展示方式去改任何 experiment 文件。比如把不同模型折成厂商,按厂商比通过率:
226
+
227
+ ```tsx
228
+ // reports/vendor.tsx
229
+ import { MetricTable, costUSD, defineReport, endToEndPassRate } from "niceeval/report";
230
+ import type { CustomDimension } from "niceeval/report";
231
+
232
+ const vendor: CustomDimension = {
233
+ name: "vendor",
234
+ of: (a) => (a.snapshot.model?.startsWith("gpt-") ? "OpenAI" : "Anthropic"),
235
+ };
236
+
237
+ export default defineReport(
238
+ <MetricTable rows={vendor} columns={[endToEndPassRate, costUSD]} />,
239
+ );
240
+ ```
241
+
242
+ `of` 能读 attempt 的全部已有数据:`evalId`、`experimentId`、快照与判定里的字段。它只能派生、不能补造:如果两组 experiment 的差别只体现在文件命名里(`bub-baseline.ts` / `bub-mempal.ts`),`of` 就只能去解析这个名字——命名约定一改,分组静默散掉。这种时候变量该搬回配置,往下看。
243
+
244
+ ### 声明式变量:`flag()` / `label()` / `runConfig()`
245
+
246
+ 画"并行 agent 数 × 模拟延迟 × 得分"这类 scaling 趋势时,图上的变量不该编码进 experiment id(`ultra-16agents-300ms`),再在报告里解析字符串抠出来——那是约定不是配置,改个命名整张图就散。变量的家是 experiment 文件里声明的字段,三种来源对应三个构造器,不猜:
247
+
248
+ - **`flag()` / `numericFlag()`** 读 `ExperimentDef.flags`——agent 和 eval 运行时也能看见的参数,比如并发数、延迟档位。
249
+ - **`label()` / `numericLabel()`** 读 `ExperimentDef.labels`——只用于报告归类的标注,运行时不可见。
250
+ - **`runConfig()` / `numericRunConfig()`** 读顶层运行配置(`model`、`reasoningEffort`、`budget`、`runs` 这类),名字点明读的是这次运行落盘的配置。
251
+
252
+ ```ts
253
+ // experiments/ultra/agents-16.ts
254
+ import { defineExperiment } from "niceeval";
255
+ import { bub } from "../../agents/bub.ts";
256
+
257
+ export default defineExperiment({
258
+ agent: bub({ mode: "ultra" }),
259
+ flags: { agents: 16, latencyMs: 300 },
260
+ });
261
+ ```
262
+
263
+ 维度槽(`series` / `rows` / `columns` / `points`)用不加 `numeric` 前缀的版本按声明值分组;数值轴(`MetricLine` 的 `x`)必须用 `numericFlag()` / `numericLabel()` / `numericRunConfig()`,因为刻度要求真实数值:
264
+
265
+ ```tsx
266
+ import { MetricLine, defineReport, endToEndPassRate, flag, numericFlag } from "niceeval/report";
267
+
268
+ const agents = flag("agents", { label: "并行 agent 数" });
269
+ const latency = numericFlag("latencyMs", { label: "Simulated latency", unit: "ms" });
270
+
271
+ export default defineReport(
272
+ <MetricLine x={latency} series={agents} y={endToEndPassRate} />,
273
+ );
274
+ ```
275
+
276
+ 未声明该变量的 Experiment 在分组时归入"未配置",用作坐标轴时不绘制该点并在注脚中计数。Flags 和 labels 随快照落盘,因此历史 run 也能按当时声明的变量重新分组。
277
+
278
+ ## 换形态:表格用 Table,摘要卡片用 Grid/Stat,其余自己画
279
+
280
+ 内置组件未覆盖的展示分三类:表格使用排版原语 `<Table>`;label-value 的自由摘要卡片使用 `<Grid>` / `<Stat>`;通过率条形图、预算燃尽图和项目徽章这类真正需要自己画的展示,才用 `defineComponent` 编写双面组件。
281
+
282
+ ### 一张表:`<Table>`
283
+
284
+ 列是你定的,格子是你算好的显示值,`<Table>` 负责把网页和终端两个面都排整齐:
285
+
286
+ ```tsx
287
+ // reports/cost-board.tsx
288
+ import {
289
+ defineReport, defineComponent, Table,
290
+ costUSD, endToEndPassRate, metricTableData,
291
+ } from "niceeval/report";
292
+
293
+ const CostBoard = defineComponent(async (_props: {}, ctx) => {
294
+ const board = await metricTableData(ctx.scope, {
295
+ rows: "agent",
296
+ columns: [endToEndPassRate, costUSD],
297
+ });
298
+ return (
299
+ <Table
300
+ columns={[
301
+ { key: "agent", header: "Agent" },
302
+ { key: "pass", header: "通过率", align: "right" },
303
+ { key: "cost", header: "成本", align: "right" },
304
+ ]}
305
+ rows={board.rows.map((r) => ({
306
+ key: r.key,
307
+ cells: {
308
+ agent: r.key,
309
+ pass: r.cells[endToEndPassRate.name].display,
310
+ // 缺数据交 null,组件渲染成 —;不要自己填 0
311
+ cost: r.cells[costUSD.name].value === null ? null : r.cells[costUSD.name].display,
312
+ },
313
+ }))}
314
+ />
315
+ );
316
+ });
317
+
318
+ export default defineReport(<CostBoard />);
319
+ ```
320
+
321
+ ```text
322
+ $ niceeval show --report reports/cost-board.tsx
323
+ Agent 通过率 成本
324
+ bub 87% $0.42
325
+ codex 80% $0.51
326
+ 克劳德 — —
327
+ ```
328
+
329
+ 列宽按**终端显示宽度**计算,一个汉字占 2 列。`align: "right"` 让数字列右对齐。单元格值为 `null` 时渲染 `—`,不补 0。行上包含 `locator` 时会增加 Attempt 列;网页链接进入 Attempt 证据页,终端把 locator 交给 `niceeval show`。完整字段见[报告组件](/zh/reference/report-components)的"表格"一节。
330
+
331
+ ### 自由摘要格:`<Grid>` / `<Stat>`
332
+
333
+ 耗时、成本、参与率这类 label-value 速览数字,不用写 `defineComponent`——`<Grid>` 摆格子,`<Stat>` 摆每格的 label / 主值 / 辅助信息,两个面自动排版:
334
+
335
+ ```tsx
336
+ // reports/run-summary.tsx
337
+ import { Grid, Section, Stat, defineReport } from "niceeval/report";
338
+
339
+ export default defineReport(
340
+ <Section title="运行速览" meta="4/4 完成">
341
+ <Grid columns={2} variant="boxed">
342
+ <Stat label="总耗时" value="12m 30s" />
343
+ <Stat label="总成本" value="$1.86" />
344
+ <Stat label="通过率" value="87%" tone="positive" />
345
+ <Stat label="失败数" value={2} detail="见下方 Attempt 列表" tone="negative" />
346
+ </Grid>
347
+ </Section>,
348
+ );
349
+ ```
350
+
351
+ `columns` 是宽屏下最多摆几列,窄屏和终端都会自动减列,不丢任何一格;加 `variant="boxed"` 给每格描边,省略就是默认的 `plain` 无框。`value` 收 `LocalizedText`、`number` 或 `null`——`null` 显示 `—`,数字 `0` 照常显示成 `0`,不当成缺数据;`tone`(`positive` / `negative` / `warning`,默认 `neutral`)是你自己对这个数字的判断,只给主值上色,组件不会替你从正负号猜。
352
+
353
+ `Grid` 只管排版,不读 Scope、不聚合指标,`Stat.value` 必须是你已经算好的显示值。需要保留 `12/15` 这样的覆盖率角标和证据引用时,继续用[报告组件](/zh/reference/report-components)里的指标表这类数据组件;只有这种自由摘要卡片才用 `Grid` / `Stat`。
354
+
355
+ ### 不是表:`defineComponent` 加文本排版函数
356
+
357
+ `defineComponent` 声明两个渲染面,和 `defineExperiment` / `defineMetric` / `defineReport` 同一个家族。终端面要自己排字符,用 `niceeval/report` 导出的这组函数——官方组件排的就是这几把尺子:
358
+
359
+ | 函数 | 用途 |
360
+ | --- | --- |
361
+ | `stringWidth(text)` | 显示宽度:CJK 和全角字符记 2 列,其余记 1 列 |
362
+ | `padEnd(text, width)` | 按显示宽度在右侧补齐(左对齐) |
363
+ | `padStart(text, width)` | 按显示宽度在左侧补齐(右对齐,数字列用) |
364
+ | `wrapText(text, width)` | 按显示宽度折行,返回若干行 |
365
+ | `indent(block, prefix)` | 每行加缩进 |
366
+ | `bar(ratio, width)` | 字符条:`█` 填充、`░` 补齐到 `width` |
367
+ | `columns(blocks, widths, separator?)` | 多块并排 |
368
+
369
+ **不要用 `String.prototype.padEnd` / `padStart` 对齐终端输出。** 它们数的是 UTF-16 码元,不是显示列宽:一个汉字占 2 列,却只算 1 个码元。用它们补齐,中文一进来列就错位,而中文 agent 名和中文题目名恰恰是最常见的情形。列宽也要随内容和 `ctx.width` 算,不要硬编码一个数字。
370
+
371
+ ```tsx
372
+ // reports/passbars.tsx
373
+ import {
374
+ defineReport, defineComponent, Col, Style, metricTableData,
375
+ bar, padEnd, stringWidth, endToEndPassRate,
376
+ } from "niceeval/report";
377
+
378
+ interface BarRow { key: string; ratio: number | null; display: string }
379
+
380
+ const PassBars = defineComponent<{ rows: BarRow[] }>({
381
+ web({ rows }) {
382
+ return (
383
+ <ul className="passbars">
384
+ {rows.map((r) => (
385
+ <li key={r.key}>
386
+ <span>{r.key}</span>
387
+ <i style={{ width: `${(r.ratio ?? 0) * 100}%` }} />
388
+ <b>{r.ratio === null ? "—" : r.display}</b>
389
+ </li>
390
+ ))}
391
+ </ul>
392
+ );
393
+ },
394
+ text({ rows }, { width }) {
395
+ const label = Math.max(...rows.map((r) => stringWidth(r.key))); // 列宽随内容
396
+ const barWidth = Math.min(20, width - label - 8); // 也随可用列宽
397
+ return rows
398
+ .map((r) => {
399
+ const chart = r.ratio === null ? padEnd("—", barWidth) : bar(r.ratio, barWidth);
400
+ return `${padEnd(r.key, label)} ${chart} ${r.display}`;
401
+ })
402
+ .join("\n");
403
+ },
404
+ });
405
+
406
+ const PassBarsSection = defineComponent(async (_props: {}, ctx) => {
407
+ const board = await metricTableData(ctx.scope, { rows: "agent", columns: [endToEndPassRate] });
408
+ const rows = board.rows.map((r) => ({
409
+ key: r.key,
410
+ ratio: r.cells[endToEndPassRate.name].value, // 格子键锚在指标对象上,不裸写字符串
411
+ display: r.cells[endToEndPassRate.name].display,
412
+ }));
413
+ return (
414
+ <Col>
415
+ <Style>{`.passbars li { display: flex; gap: 8px; } .passbars i { background: #4a7; height: 12px; }`}</Style>
416
+ <PassBars rows={rows} />
417
+ </Col>
418
+ );
419
+ });
420
+
421
+ export default defineReport(<PassBarsSection />);
422
+ ```
423
+
424
+ ```text
425
+ $ niceeval show --report reports/passbars.tsx
426
+ bub █████████████████░░░ 87%
427
+ codex ████████████████░░░░ 80%
428
+ 克劳德 — —
429
+ ```
430
+
431
+ 组件的契约:
432
+
433
+ - **计算发生在拿到 `ctx` 的地方,渲染面是纯函数。** 组合组件的函数体里能 `await` 读 attempt 句柄、折数据;双面组件的 `web` 和 `text` 只认已经算好的 props,零 IO、同步——这条边界让同一棵树能被烘进静态导出。
434
+ - **渲染面接收上下文参数。** `text(props, ctx)` 的 `ctx.width` 是可用列宽(`Row` 分栏后会变窄),`ctx.attemptCommand(ref)` 生成查看命令;`web(props, ctx)` 的 `ctx.attemptHref(ref)` 生成 Attempt 证据链接。自定义组件与内置组件使用相同证据页。
435
+ - **网页面静态渲染,不 hydrate。** 宿主在计算侧把 `web` 面渲染成静态 HTML,不打包你的代码进查看器:交互用普通链接和 `<details>`,与官方组件同一条静态契约。
436
+ - **样式随树带走。** 静态导出不打包你的代码,`className` 引用的 CSS 用内置原语 `<Style>{css}</Style>` 放进页面树——web 面吐 `<style>` 标签,text 面渲染为空,上面的例子就是这么给 `.passbars` 上样式的。
437
+ - **诚实契约同样适用。** 缺数据渲染 `—` 不补 0,截断如实标注剩余数量——上面例子里 `克劳德` 没有样本就是 `—`。
438
+
439
+ ## 发布:导出即静态站
440
+
441
+ 发布自定义报告和发布官方查看器是同一个动作——`--out` 加上 `--report`:
442
+
443
+ ```bash
444
+ niceeval view --report reports/exam.tsx --out site
445
+ ```
446
+
447
+ 产物是纯静态文件。报告页是首页,transcript、trace 和代码等 Attempt 证据位于同一站点,报告中的数值可以链接到对应证据。组件不 hydrate;页面内联脚本提供表头排序、行过滤和图表悬停。浏览器禁用 JavaScript 时页面仍完整可读。CI 上没有 `.niceeval/` 时,先用 `copySnapshots` 生成经过大小检查的结果目录,再执行导出,流程见[查看结果](/zh/tutorials/viewing-results#导出与静态托管)。
448
+
449
+ 双面组件的网页面是普通 React 组件,`xxxData` 函数是普通 TypeScript 函数。需要在现有内部面板中复用指标表时,可以 import 组件并传入数据。计算与渲染分开部署时(例如 CI 生成 JSON、另一个应用 fetch),两侧必须使用相同 NiceEval 版本;组件数据不带版本信息,兼容性跟随包版本。
450
+
451
+ ## 自定义报告的边界
452
+
453
+ 一次只渲染一份报告,`--report` 收显式文件路径——没有 `reports/` 目录自动发现、没有插件注册表、没有配置文件。自定义指标和自定义组件都住在你的报告文件里,随文件一起递入,宿主不为它们长任何注册面。不传 `--report` 时渲染的就是默认报告,你的报告和它是同级实现。
@@ -1,7 +1,7 @@
1
1
  ---
2
- title: "数据驱动测试(dataset fan-out):用多份数据运行同一套 eval"
2
+ title: "数据驱动测试(dataset fan-out):用多份数据运行同一套评估用例"
3
3
  sidebarTitle: "数据驱动测试"
4
- description: "从 .eval.ts 文件导出数组或 keyed record,将一套 eval 逻辑展开为多个 case。用 loadYaml 或 loadJson 读取外部数据集,并获得稳定 ID。"
4
+ description: "从 .eval.ts 文件导出数组或 keyed record,将一套评估逻辑展开为多个 case。用 loadYaml 或 loadJson 读取外部数据集,并获得稳定 ID。"
5
5
  ---
6
6
 
7
7
  数据驱动测试(dataset fan-out)适合大量结构相同、输入不同的测试。例如 SQL 生成、意图分类、检索问答和工具选择。
@@ -76,7 +76,7 @@ cases:
76
76
  sql: SELECT COUNT(*) FROM users;
77
77
  ```
78
78
 
79
- ## 过滤数据集 eval
79
+ ## 过滤数据集评估用例
80
80
 
81
81
  ```bash
82
82
  # 运行整个数据集
@@ -101,5 +101,5 @@ npx niceeval exp local swelancer/15193
101
101
  </Tabs>
102
102
 
103
103
  <Tip>
104
- 数据集适合“横向覆盖”,独立 eval 文件适合“行为复杂”的场景。
104
+ 数据集适合”横向覆盖”,独立评估用例文件适合”行为复杂”的场景。
105
105
  </Tip>
@@ -0,0 +1,103 @@
1
+ ---
2
+ title: "实验矩阵:用运行矩阵比较 agents 和 models"
3
+ sidebarTitle: "实验矩阵"
4
+ description: "使用 NiceEval experiments 让同一批评估用例横跨多个 agents、models 和 flags,比较 pass rate、成本和延迟。"
5
+ ---
6
+
7
+ Experiment 用于比较多组运行配置。典型比较包括 Claude Code 与 Codex 在同一批 Coding Agent 任务上的通过率、prompt 改动前后的成本,以及不同模型的延迟与质量。
8
+
9
+ ## 基本形状
10
+
11
+ **一个实验文件 = 一个配置**(一个 agent × 一个 model)。路径只生成 experiment id 和支持前缀选择:
12
+
13
+ ```text
14
+ experiments/
15
+ models/openai/gpt-5.4.ts
16
+ models/deepseek/v4-pro.ts
17
+ ```
18
+
19
+ ```ts
20
+ // experiments/models/openai/gpt-5.4.ts
21
+ import { defineExperiment } from "niceeval";
22
+ import { webAgent } from "../../adapter/adapter.ts";
23
+
24
+ export default defineExperiment({
25
+ description: "gpt-5.4: 对比模型",
26
+ agent: webAgent({ baseUrl: "http://127.0.0.1:5188" }),
27
+ model: "gpt-5.4", // 单个字符串;另一个模型就复制一份文件改这一行
28
+ runs: 2,
29
+ earlyExit: true,
30
+ });
31
+ ```
32
+
33
+ ```bash
34
+ npx niceeval exp models
35
+ ```
36
+
37
+ 另一个模型写另一个文件。默认 `show` / `view` 直接比较当前结果范围里的 experiments,不需要额外分组字段。
38
+
39
+ 多层目录只负责 id 和批量选择:
40
+
41
+ 某一格结果反常、需要单独复现时,用该配置的完整 id(`目录路径/文件名`)精确只跑这一格,不用先批量运行同目录配置:
42
+
43
+ ```bash
44
+ npx niceeval exp models/openai/gpt-5.4 # 精确 id
45
+ npx niceeval exp models/openai # 这一层目录下全部 experiment
46
+ ```
47
+
48
+ 目录里如果还有 `gpt-5.4-mini.ts` 这类共享前缀的变体,可以用文件名前缀选择:
49
+
50
+ ```bash
51
+ npx niceeval exp models/openai/gpt
52
+ ```
53
+
54
+ `defineExperiment` 的字段配置与 `flags` 传递方式见[写实验](/zh/tutorials/write-experiment)。
55
+
56
+ ## 可比较的运行维度
57
+
58
+ - 不同 Adapters。
59
+ - 不同模型(Tier 1 接入即可:应用接口暴露模型选择,`model` 经 `ctx.model` 透传)。
60
+ - 不同 prompts 或 feature flags(要求 Tier 3 接入:变体在应用内部,需要应用把它暴露成 experiment 可选的 flag,经 `flags` → `ctx.flags` 透传)。
61
+ - 不同 sandbox provider。
62
+ - 不同运行环境条件(比如装不装某个记忆工具的二进制、有没有预置状态):环境差异写在 `sandbox` spec 的 `.setup()` / `.teardown()` Hook 里,一个变体一个 experiment 文件,见 [Sandbox provider · 生命周期](/zh/tutorials/sandbox-providers#生命周期)。
63
+ - 同一任务的 pass@N。
64
+
65
+ Tier 1 / Tier 2 / Tier 3 的定义见 [Tier](/zh/explanation/tier)。
66
+
67
+ ## 查看结果
68
+
69
+ Experiment 输出通常按 `(agent, model, eval)` 维度展示:
70
+
71
+ ```text
72
+ api-validation claude-code+zod-skill pass@3 = 3/3 (100%) mean 34s
73
+ api-validation claude-code pass@3 = 1/3 (33%) mean 41s
74
+ ```
75
+
76
+ 除了 pass rate,还应该看平均耗时、token、成本和失败类型。
77
+
78
+ ```bash
79
+ npx niceeval show --exp models
80
+ npx niceeval view
81
+ ```
82
+
83
+ 每个 experiment 用自己的 `evals` 选择评估用例。函数形式会遍历所有已发现的评估用例:
84
+
85
+ ```ts
86
+ evals: (eval) =>
87
+ eval.id.startsWith("coding/") &&
88
+ eval.tags.includes("coding") &&
89
+ eval.environment !== "gpu"
90
+ ```
91
+
92
+ `eval.id` 是文件路径推导出的项目内 ID,不是绝对路径,可以直接用 `startsWith` / `includes` 判断。在[评估用例](/zh/tutorials/authoring#tags-与-environment)里的两个例子中,`coding/fix-button` 会被选中,`research/gpu-literature` 不会。实验快照记录解析后的 `selectedEvalIds`;报告直接读取它。
93
+
94
+ ## 设计 experiment 的建议
95
+
96
+ - 保持评估用例集合稳定,避免比较时混入新变量。
97
+ - 每个 cell 跑多个 attempts,尤其是非确定性 coding agent。
98
+ - 把预算和并发写清楚。
99
+ - 对“失败原因”做归类,不只看总分。
100
+
101
+ ## 与普通运行的关系
102
+
103
+ `npx niceeval exp <路径或 id>` 按文件身份运行;每个文件的 `evals` 决定它覆盖哪些评估用例,结果以 `selectedEvalIds` 落盘。
@@ -1,7 +1,7 @@
1
1
  ---
2
- title: "沙箱 Fixture:用任务评估 coding agents"
2
+ title: "Sandbox Fixture:用任务评估 coding agents"
3
3
  sidebarTitle: "Fixture"
4
- description: "用 .eval.ts 给 coding agent 准备隔离 workspace、发送真实任务,并用 sandbox 文件、命令、diff 和 judge 验证结果。"
4
+ description: "用 .eval.ts 给 coding agent 准备隔离 workspace、发送真实任务,并用 Sandbox 文件、命令、diff 和 judge 验证结果。"
5
5
  ---
6
6
 
7
7
  评估 coding agent 时,单纯检查一段回复不够。你通常要给它一个真实项目,让它读写文件、运行命令、提交改动,然后检查产物是否正确。[NiceEval](https://niceeval.com/) 的当前推荐写法是:用普通 `.eval.ts` 文件显式准备 sandbox workspace、发送任务并断言结果。
@@ -30,11 +30,11 @@ experiments/
30
30
  └─ with-skill.ts
31
31
  ```
32
32
 
33
- `workspaces/` agent 可见的初始项目。`.eval.ts` 控制什么时候上传 workspace、发什么 prompt、检查什么结果。`experiments/` 决定用哪个 agent、哪个模型、是否注入 Skill 或插件。
33
+ `workspaces/` 存放 Agent 可见的初始项目。`.eval.ts` 控制 Workspace 上传时机、任务 prompt 和结果检查。`experiments/` 选择 Agent、模型,以及是否注入 Skill 或插件。
34
34
 
35
35
  ## 准备 workspace
36
36
 
37
- eval 里把初始项目上传到 sandbox:
37
+ 在评估用例里把初始项目上传到 sandbox:
38
38
 
39
39
  ```ts
40
40
  import { defineEval } from "niceeval";
@@ -51,7 +51,7 @@ export default defineEval({
51
51
  });
52
52
  ```
53
53
 
54
- 也可以用 `t.sandbox.writeFiles()` 在单个 eval 里补充种子文件,适合小任务或安全探针。
54
+ 也可以用 `t.sandbox.writeFiles()` 在单个评估用例里补充种子文件,适合小任务或安全探针。
55
55
 
56
56
  ## 写任务 prompt
57
57
 
@@ -88,7 +88,7 @@ t.check(code, excludes(/process\.env\.\w+\s*\?\?/));
88
88
  t.sandbox.fileChanged("src/config/env.ts");
89
89
  ```
90
90
 
91
- `t.sandbox.fileChanged()` 是作用域断言:它在运行结束后用 sandbox diff 判断目标文件是否真的被修改。
91
+ `t.sandbox.fileChanged()` 是作用域断言:它在运行结束后用 Sandbox diff 判断目标文件是否真的被修改。
92
92
 
93
93
  ## 运行项目测试或探针脚本
94
94
 
@@ -125,7 +125,7 @@ t.check(result, commandSucceeded());
125
125
 
126
126
  ## 用 experiment 做对照组
127
127
 
128
- 不要把 agent 名、插件名或 URL 放进位置参数。experiment 决定“对着哪个 agent、怎么跑”;CLI eval 位置参数只过滤 eval ID。
128
+ 不要把 Agent 名、插件名或 URL 放进位置参数。Experiment 选择 Agent 并固定运行配置;CLI 的评估用例位置参数只过滤评估用例 ID。
129
129
 
130
130
  ```bash
131
131
  pnpm exec niceeval exp compare
@@ -136,9 +136,9 @@ pnpm exec niceeval exp compare api-validation
136
136
 
137
137
  - `experiments/baseline.ts`:裸 agent。
138
138
  - `experiments/with-skill.ts`:同一 agent + setup hook 注入 `CLAUDE.md`。
139
- - 两组使用同一批 eval、同一模型、同一 runs 和预算。
139
+ - 两组使用同一批评估用例、同一模型、同一 runs 和预算。
140
140
 
141
- ## 何时用 sandbox fixture
141
+ ## Sandbox Fixture 的适用场景
142
142
 
143
143
  - 需要 agent 修改真实文件。
144
144
  - 需要运行项目测试、构建或 lint。