niceeval 0.9.1 → 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 +14 -6
  6. package/dist/o11y/execution-tree.d.ts +103 -0
  7. package/dist/o11y/otlp/select.d.ts +22 -0
  8. package/dist/report/{react → assets}/colors.d.ts +8 -0
  9. package/dist/report/{react → assets}/colors.js +23 -0
  10. package/dist/report/built-in/index.d.ts +2 -2
  11. package/dist/report/built-in/index.js +2 -2
  12. package/dist/report/built-in/standard.d.ts +8 -1
  13. package/dist/report/built-in/standard.js +17 -7
  14. package/dist/report/components/attempt-detail/AttemptAssertions.d.ts +6 -0
  15. package/dist/report/components/attempt-detail/AttemptAssertions.js +17 -0
  16. package/dist/report/components/attempt-detail/AttemptConversation.d.ts +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} +43 -11
  118. package/dist/report/{tree.js → definition/tree.js} +9 -11
  119. package/dist/report/index.d.ts +33 -18
  120. package/dist/report/index.js +23 -14
  121. package/dist/report/{aggregate.d.ts → model/aggregate.d.ts} +12 -4
  122. package/dist/report/{aggregate.js → model/aggregate.js} +28 -8
  123. package/dist/report/{flag.d.ts → model/flag.d.ts} +14 -1
  124. package/dist/report/{flag.js → model/flag.js} +35 -0
  125. package/dist/report/{format.d.ts → model/format.d.ts} +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 +11 -1
  144. package/dist/results/select.js +27 -5
  145. package/dist/runner/eval-selection.d.ts +26 -0
  146. package/dist/runner/feedback/sink.d.ts +26 -1
  147. package/dist/runner/types.d.ts +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 +14 -0
  154. package/dist/shared/aggregate.js +31 -0
  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 +40 -16
  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 +6 -6
  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 +49 -49
  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 +71 -57
  224. package/src/context/types.ts +21 -21
  225. package/src/define.ts +13 -0
  226. package/src/i18n/en.ts +27 -12
  227. package/src/i18n/zh-CN.ts +27 -12
  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 +124 -106
  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} +56 -20
  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 +53 -2
  314. package/src/results/select.ts +29 -5
  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 +29 -0
  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 +56 -20
  356. package/src/show/render.ts +15 -239
  357. package/src/show/show.test.ts +127 -34
  358. package/src/view/app/App.test.tsx +1 -1
  359. package/src/view/app/App.tsx +119 -47
  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 +105 -83
  370. package/src/view/index.ts +1 -1
  371. package/src/view/server.ts +32 -2
  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 +0 -805
  376. package/src/view/view-report.test.ts +141 -22
  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
@@ -1,7 +1,7 @@
1
1
  // 计算函数(*Data):ReportInput → 一份组件数据。跑在 Node 侧,产物是算好的、可序列化的
2
- // 普通 JSON(终值 + 渲染提示,不含公式);渲染面(web/text)只做展示。
3
- // 它们是双面组件解析面的具名形式(MetricTable / metricTableData),与组件成对导出,
4
- // 只住在 niceeval/report(docs/feature/reports/library.md「数据计算与缓存边界」)。
2
+ // 普通 JSON(终值 + 渲染提示,不含公式);渲染面(web/text)只做展示。指标图形族
3
+ // (MetricTable / MetricMatrix / MetricBars / Scoreboard / MetricScatter / MetricLine /
4
+ // DeltaTable)的 *Data 与配套 Options 都住在这里(docs/feature/reports/library/metric-views.md)。
5
5
  //
6
6
  // 共同约定(docs/feature/reports/architecture.md「指标聚合不变量」):
7
7
  // - 第一参收 ReportInput = Scope | readonly Snapshot[];warnings 不进组件数据(宿主统一显示);
@@ -12,19 +12,10 @@
12
12
  // - core 中立:只认 Metric / Dimension 接口,不出现具体 agent 名的分支。
13
13
 
14
14
  import type {
15
- AttemptListItem,
16
- AttemptLocator,
17
- CopyFixPromptData,
18
15
  DeltaData,
19
16
  DeltaPair,
20
17
  DimensionInput,
21
- EvalListItem,
22
- ExperimentComparisonData,
23
- ExperimentComparisonGroupData,
24
- ExperimentListEvalRow,
25
- ExperimentListItem,
26
18
  FlagPairs,
27
- HeroData,
28
19
  LineData,
29
20
  MatrixData,
30
21
  Metric,
@@ -32,19 +23,14 @@ import type {
32
23
  NumericAxis,
33
24
  ReportInput,
34
25
  ScatterData,
35
- ScopeSummaryData,
36
- ScopeWarning,
37
26
  ScoreboardData,
27
+ SeriesInput,
38
28
  TableData,
39
- TraceSpanSummary,
40
- TraceWaterfallRow,
41
- VerdictTally,
42
- } from "./types.ts";
43
- import type { EvalResult, JsonValue, TraceSpan } from "../types.ts";
44
- import type { Snapshot } from "../results/types.ts";
45
- import { comparabilityConfigOf, deepEqualJson } from "../results/select.ts";
46
- import { evalLevelStats, foldEvalVerdict } from "../shared/verdict.ts";
47
- import { experimentGroupOf } from "../shared/aggregate.ts";
29
+ } from "../../model/types.ts";
30
+ import type { JsonValue } from "../../../types.ts";
31
+ import type { AttemptLocator } from "../../../results/locator.ts";
32
+ import type { Snapshot } from "../../../results/types.ts";
33
+ import { comparabilityConfigOf, deepEqualJson } from "../../../results/select.ts";
48
34
  import {
49
35
  assertUniqueMetricNames,
50
36
  axisValueOf,
@@ -52,10 +38,8 @@ import {
52
38
  computeCell,
53
39
  dimensionKey,
54
40
  dimensionName,
55
- displayValue,
56
41
  evalGroupOf,
57
42
  evalIdOf,
58
- evaluateMetric,
59
43
  experimentIdOf,
60
44
  filterItems,
61
45
  fullEvalKey,
@@ -63,15 +47,15 @@ import {
63
47
  locatorOf,
64
48
  refDisplayKey,
65
49
  resolveInput,
66
- snapshotKeyOf,
50
+ seriesKey,
51
+ seriesName,
67
52
  toColumn,
68
53
  type Item,
69
- } from "./aggregate.ts";
70
- import { attemptCostUSD, costUSD, durationMs, endToEndPassRate, examScore, tokens } from "./metrics.ts";
71
- import { formatMetricValue, formatPlainNumber, localizedDisplay } from "./format.ts";
72
- import { compactAssertionSummary, primaryAssertionSummary, summaryText } from "../scoring/display.ts";
73
- import { defineMetric } from "./metrics.ts";
74
- import type { LocalizedText } from "./locale.ts";
54
+ } from "../../model/aggregate.ts";
55
+ import { examScore } from "../../model/metrics.ts";
56
+ import { formatMetricValue, formatPlainNumber, localizedDisplay } from "../../model/format.ts";
57
+ import type { LocalizedText } from "../../model/locale.ts";
58
+ import { selectedEvalsOnly } from "../shared-compute.ts";
75
59
 
76
60
  // ───────────────────────── metricTableData ─────────────────────────
77
61
 
@@ -173,290 +157,6 @@ export async function metricMatrixData(input: ReportInput, options: MetricMatrix
173
157
  };
174
158
  }
175
159
 
176
- // ───────────────────────── 实体列表(experimentListData / evalListData / attemptListData)─────────────────────────
177
-
178
- /**
179
- * 一次 attempt 的单行结果摘要(Scoring display 契约):failed 取主失败断言摘要(不含
180
- * "+N more",N 单独进 moreFailures),errored 取结构化 error 的一层摘要
181
- * (phase · code · message),passed / skipped 为 null。
182
- */
183
- function failureSummaryOf(result: EvalResult): { summary: string | null; more: number } {
184
- if (result.verdict === "errored" && result.error !== undefined) {
185
- const parts = [result.error.phase, result.error.code, result.error.message].filter(
186
- (part): part is string => typeof part === "string" && part.length > 0,
187
- );
188
- return { summary: summaryText(parts.join(" · ")), more: 0 };
189
- }
190
- if (result.verdict === "failed" || result.verdict === "errored") {
191
- const primary = primaryAssertionSummary(result.assertions, result.verdict);
192
- if (primary !== undefined) {
193
- return {
194
- summary: compactAssertionSummary({ ...primary, additionalFailures: 0 }),
195
- more: primary.additionalFailures,
196
- };
197
- }
198
- if (result.verdict === "errored" && result.skipReason !== undefined) {
199
- return { summary: summaryText(result.skipReason), more: 0 };
200
- }
201
- return { summary: null, more: 0 };
202
- }
203
- return { summary: null, more: 0 };
204
- }
205
-
206
- /** AttemptList / ExperimentList / EvalList 共用的叶子构造:一个 Item → 一个 AttemptListItem。 */
207
- async function attemptListItemOf(item: Item): Promise<AttemptListItem> {
208
- const result = item.attempt.result;
209
- const { summary, more } = failureSummaryOf(result);
210
- return {
211
- experimentId: experimentIdOf(item),
212
- evalId: evalIdOf(item),
213
- attempt: result.attempt,
214
- agent: result.agent,
215
- verdict: result.verdict,
216
- failureSummary: summary,
217
- moreFailures: more,
218
- examScore: await computeCell(examScore, [item]),
219
- durationMs: result.durationMs,
220
- costUSD: attemptCostUSD(result),
221
- locator: locatorOf(item),
222
- };
223
- }
224
-
225
- /** `attemptListData(input)`:每个 Attempt 一项,顺序取自 Scope 展平顺序(不重排)。 */
226
- export async function attemptListData(input: ReportInput): Promise<AttemptListItem[]> {
227
- const { snapshots } = resolveInput(input);
228
- const items = collectItems(snapshots);
229
- return Promise.all(items.map((item) => attemptListItemOf(item)));
230
- }
231
-
232
- /** `evalListData(input)`:每个 `experimentId + evalId` 一项,按 evalId 再按 experimentId 升序。 */
233
- export async function evalListData(input: ReportInput): Promise<EvalListItem[]> {
234
- const { snapshots } = resolveInput(input);
235
- const items = collectItems(snapshots);
236
- const groups = new Map<string, Item[]>();
237
- for (const item of items) {
238
- const key = fullEvalKey(item);
239
- const list = groups.get(key);
240
- if (list) list.push(item);
241
- else groups.set(key, [item]);
242
- }
243
- const out: EvalListItem[] = [];
244
- for (const group of groups.values()) {
245
- const sorted = [...group].sort((a, b) => a.attempt.result.attempt - b.attempt.result.attempt);
246
- const verdict = foldEvalVerdict(sorted.map((item) => item.attempt.result));
247
- const attempts = await Promise.all(sorted.map((item) => attemptListItemOf(item)));
248
- out.push({
249
- experimentId: experimentIdOf(sorted[0]!),
250
- evalId: evalIdOf(sorted[0]!),
251
- verdict,
252
- examScore: await computeCell(examScore, sorted),
253
- durationMs: await computeCell(durationMs, sorted),
254
- costUSD: await computeCell(costUSD, sorted),
255
- attempts,
256
- });
257
- }
258
- out.sort((a, b) => a.evalId.localeCompare(b.evalId) || a.experimentId.localeCompare(b.experimentId));
259
- return out;
260
- }
261
-
262
- /**
263
- * `experimentListData(input)`:每个 experiment 一项,展开到每道 Eval;初始按端到端成功率
264
- * 从高到低(缺数据沉底,同分按 id)。一行只有一套 agent / model / flags 是输入约束:
265
- * 宿主注入的 current() Scope 保证每个 experiment 只由可比性配置一致的快照拼成;作者自选
266
- * Snapshot[] 时若同一 experiment 混入不一致的可比性配置,按完整用户反馈失败并指引——
267
- * 看跨配置演化用 snapshot 维度或 MetricLine,不把两套配置拼成一行冒充单一配置。
268
- */
269
- export async function experimentListData(input: ReportInput): Promise<ExperimentListItem[]> {
270
- const { snapshots } = resolveInput(input);
271
-
272
- // 可比性配置单义检查:同一 experiment 的输入快照必须共享一套可比性配置。
273
- const configByExperiment = new Map<string, { snapshot: Snapshot; config: unknown }>();
274
- for (const snapshot of snapshots) {
275
- const config = comparabilityConfigOf(snapshot);
276
- const existing = configByExperiment.get(snapshot.experimentId);
277
- if (existing === undefined) {
278
- configByExperiment.set(snapshot.experimentId, { snapshot, config });
279
- } else if (!deepEqualJson(existing.config, config)) {
280
- throw new Error(
281
- `experimentListData got inconsistent comparability configs for experiment "${snapshot.experimentId}" ` +
282
- `(snapshots ${existing.snapshot.startedAt} and ${snapshot.startedAt} differ in agent/model/reasoningEffort/flags/budget/timeoutMs/sandbox). ` +
283
- "One row shows one configuration — it cannot honestly merge two. To chart evolution across configs, " +
284
- 'use the "snapshot" dimension or MetricLine; to show the current level, pass results.current() which selects a single config per experiment.',
285
- );
286
- }
287
- }
288
-
289
- const items = collectItems(snapshots);
290
- const groups = groupItems(items, "experiment");
291
- const out: ExperimentListItem[] = [];
292
- for (const [experimentId, group] of groups) {
293
- const stats = summarizeItems(group);
294
- const newest = [...group].sort((a, b) => b.snapshot.startedAt.localeCompare(a.snapshot.startedAt))[0]!;
295
- const evalGroups = groupItems(group, "eval");
296
- const evalRows: ExperimentListEvalRow[] = [];
297
- for (const [evalId, evalItems] of evalGroups) {
298
- const sorted = [...evalItems].sort((a, b) => a.attempt.result.attempt - b.attempt.result.attempt);
299
- const verdict = foldEvalVerdict(sorted.map((item) => item.attempt.result));
300
- const attempts = await Promise.all(sorted.map((item) => attemptListItemOf(item)));
301
- evalRows.push({
302
- evalId,
303
- verdict,
304
- durationMs: await computeCell(durationMs, sorted),
305
- costUSD: await computeCell(costUSD, sorted),
306
- attempts,
307
- });
308
- }
309
- const experiment = newest.snapshot.experiment ?? newest.attempt.result.experiment;
310
- const model = newest.attempt.result.model ?? newest.snapshot.model;
311
- out.push({
312
- experimentId,
313
- agent: newest.snapshot.agent || newest.attempt.result.agent,
314
- ...(model !== undefined ? { model } : {}),
315
- ...(experiment?.flags ? { flags: experiment.flags } : {}),
316
- evalVerdicts: stats.verdicts,
317
- endToEndPassRate: await computeCell(endToEndPassRate, group),
318
- costUSD: await computeCell(costUSD, group),
319
- durationMs: await computeCell(durationMs, group),
320
- tokens: await computeCell(tokens, group),
321
- evals: stats.evals,
322
- attempts: stats.attempts,
323
- lastRunAt: stats.lastRunAt!,
324
- evalRows,
325
- });
326
- }
327
- // 初始态按端到端成功率(endToEndPassRate)从高到低,缺数据沉底;同分按 experiment id 稳定排序。
328
- out.sort((a, b) => {
329
- const va = a.endToEndPassRate.value;
330
- const vb = b.endToEndPassRate.value;
331
- if (va === null && vb === null) return a.experimentId.localeCompare(b.experimentId);
332
- if (va === null) return 1;
333
- if (vb === null) return -1;
334
- return vb - va || a.experimentId.localeCompare(b.experimentId);
335
- });
336
- return out;
337
- }
338
-
339
- // ───────────────────────── scopeSummaryData ─────────────────────────
340
-
341
- /** costUSD 的求和投影:两级都 sum(题内多轮求和 + 跨题求和 = 全量求和),display 走 $。 */
342
- const totalCostMetric = defineMetric({
343
- name: "total-cost",
344
- label: costUSD.label,
345
- unit: "$",
346
- value: costUSD.value,
347
- aggregate: { perEval: "sum", acrossEvals: "sum" },
348
- });
349
-
350
- function tallyOf(): VerdictTally {
351
- return { passed: 0, failed: 0, errored: 0, skipped: 0 };
352
- }
353
-
354
- /** 一批 Item 的组级统计(experimentListData / scopeSummaryData 共用)。 */
355
- function summarizeItems(items: Item[]): {
356
- experiments: number;
357
- evals: number;
358
- attempts: number;
359
- verdicts: VerdictTally;
360
- lastRunAt: string | undefined;
361
- } {
362
- const experimentIds = new Set<string>();
363
- for (const item of items) experimentIds.add(experimentIdOf(item));
364
- const stats = evalLevelStats(
365
- items.map((item) => ({ verdict: item.attempt.result.verdict, key: fullEvalKey(item) })),
366
- (r) => r.key,
367
- );
368
- let lastRunAt: string | undefined;
369
- for (const item of items) {
370
- const startedAt = item.snapshot.startedAt;
371
- if (lastRunAt === undefined || startedAt > lastRunAt) lastRunAt = startedAt;
372
- }
373
- return {
374
- experiments: experimentIds.size,
375
- evals: stats.evals,
376
- attempts: items.length,
377
- verdicts: { passed: stats.passed, failed: stats.failed, errored: stats.errored, skipped: stats.skipped },
378
- lastRunAt,
379
- };
380
- }
381
-
382
- /**
383
- * `scopeSummaryData(input)`:范围摘要——快照时间窗、experiment / eval / attempt 数、
384
- * 两级判定计票、端到端成功率与总成本(docs/feature/reports/library/summaries.md)。
385
- * data 恒携带两级计票;成功率来自官方两级指标引擎,不从任一计票重算。
386
- */
387
- export async function scopeSummaryData(input: ReportInput): Promise<ScopeSummaryData> {
388
- const { snapshots } = resolveInput(input);
389
- const items = collectItems(snapshots);
390
-
391
- let earliest: string | null = null;
392
- let latest: string | null = null;
393
- for (const snapshot of snapshots) {
394
- if (earliest === null || snapshot.startedAt < earliest) earliest = snapshot.startedAt;
395
- if (latest === null || snapshot.startedAt > latest) latest = snapshot.startedAt;
396
- }
397
-
398
- const stats = summarizeItems(items);
399
- const attemptVerdicts = tallyOf();
400
- for (const item of items) attemptVerdicts[item.attempt.result.verdict] += 1;
401
-
402
- return {
403
- range: { earliestStartedAt: earliest, latestStartedAt: latest },
404
- experiments: stats.experiments,
405
- evals: stats.evals,
406
- attempts: stats.attempts,
407
- evalVerdicts: stats.verdicts,
408
- attemptVerdicts,
409
- endToEndPassRate: await computeCell(endToEndPassRate, items),
410
- totalCostUSD: await computeCell(totalCostMetric, items),
411
- };
412
- }
413
-
414
- // ───────────────────────── experimentComparisonData ─────────────────────────
415
-
416
- /** 完整父路径是组键;没有父路径的 experiment 不能互相比,自己形成单例组。 */
417
- export function experimentComparisonGroupKey(experimentId: string): string {
418
- return experimentGroupOf(experimentId) ?? experimentId;
419
- }
420
-
421
- /** 每组散点的唯一口径:默认 definition 与公开计算共用,不各写一份。 */
422
- const COMPARISON_SCATTER_OPTIONS: MetricScatterOptions = {
423
- points: "experiment",
424
- series: "agent",
425
- x: costUSD,
426
- y: endToEndPassRate,
427
- };
428
-
429
- /**
430
- * `experimentComparisonData(input)`:先把 input 按可比组分区(experiment id 的完整父路径),
431
- * 再为每组分别计算 ScopeSummary、成本 × 端到端成功率散点和 ExperimentList——分区发生在任何
432
- * 指标计算之前,组外 attempt 不可能污染该组的坐标尺度、series、成功率、成本、排序或缺数据计数。
433
- */
434
- export async function experimentComparisonData(input: ReportInput): Promise<ExperimentComparisonData> {
435
- const { snapshots } = resolveInput(input);
436
- const snapshotsByGroup = new Map<string, Snapshot[]>();
437
- for (const snapshot of snapshots) {
438
- const key = experimentComparisonGroupKey(snapshot.experimentId);
439
- const group = snapshotsByGroup.get(key);
440
- if (group) group.push(snapshot);
441
- else snapshotsByGroup.set(key, [snapshot]);
442
- }
443
- const groups = await Promise.all(
444
- [...snapshotsByGroup.entries()]
445
- .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
446
- .map(async ([key, groupSnapshots]): Promise<ExperimentComparisonGroupData> => {
447
- const [summary, scatter, experiments] = await Promise.all([
448
- scopeSummaryData(groupSnapshots),
449
- metricScatterData(groupSnapshots, COMPARISON_SCATTER_OPTIONS),
450
- experimentListData(groupSnapshots),
451
- ]);
452
- return { key, summary, scatter, experiments };
453
- }),
454
- );
455
- return { groups };
456
- }
457
-
458
- // ───────────────────────── scoreboardData ─────────────────────────
459
-
460
160
  export interface ScoreboardOptions {
461
161
  rows: DimensionInput;
462
162
  /** 固定题集;eval id 必须唯一。元素引用运行时数据,类型放宽为普通数组,空数组在计算时报错。 */
@@ -644,13 +344,11 @@ function subjectDisplay(earned: number, possible: number): LocalizedText {
644
344
  return `${formatPlainNumber(earned)}/${formatPlainNumber(possible)} (${formatMetricValue(ratio, "%")})`;
645
345
  }
646
346
 
647
- // ───────────────────────── metricScatterData ─────────────────────────
648
-
649
347
  export interface MetricScatterOptions {
650
348
  /** 点维度:每个点 = 该组 attempt 的聚合。 */
651
349
  points: DimensionInput;
652
- /** 可选:只决定颜色和分组,默认不连线。 */
653
- series?: DimensionInput;
350
+ /** 决定颜色和图例归类,默认不连线(连线是呈现 prop `connect`);数组形态解析为复合维度。 */
351
+ series?: SeriesInput;
654
352
  x: Metric;
655
353
  y: Metric;
656
354
  /** eval id 前缀过滤,同 CLI 位置参数语义。 */
@@ -658,7 +356,7 @@ export interface MetricScatterOptions {
658
356
  }
659
357
 
660
358
  export async function metricScatterData(input: ReportInput, options: MetricScatterOptions): Promise<ScatterData> {
661
- const { snapshots } = resolveInput(input);
359
+ const snapshots = selectedEvalsOnly(resolveInput(input).snapshots);
662
360
  const items = filterItems(collectItems(snapshots), options.evals);
663
361
  const groups = groupItems(items, options.points);
664
362
  const rows: ScatterData["rows"] = [];
@@ -666,26 +364,25 @@ export async function metricScatterData(input: ReportInput, options: MetricScatt
666
364
  rows.push({
667
365
  key,
668
366
  // 组内取第一条解析系列:点维度细于系列维度时(experiment ⊂ agent)天然一致
669
- ...(options.series ? { series: dimensionKey(options.series, group[0]!) } : {}),
367
+ ...(options.series ? { series: seriesKey(options.series, group[0]!) } : {}),
670
368
  x: await computeCell(options.x, group),
671
369
  y: await computeCell(options.y, group), // 任一轴 null 的点留在 rows 里:组件不画,但注脚要报的数就从这里数
672
370
  });
673
371
  }
674
372
  return {
675
373
  pointDimension: dimensionName(options.points),
676
- ...(options.series ? { seriesDimension: dimensionName(options.series) } : {}),
374
+ ...(options.series ? { seriesDimension: seriesName(options.series) } : {}),
677
375
  x: toColumn(options.x),
678
376
  y: toColumn(options.y),
679
377
  rows,
680
378
  };
681
379
  }
682
380
 
683
- // ───────────────────────── metricLineData ─────────────────────────
684
-
685
381
  export interface MetricLineOptions {
686
- /** x 轴:NumericAxis(numericFlag() / numericRunConfig() 或自定义 of),不解析 experiment 命名。 */
382
+ /** x 轴:NumericAxis(numericFlag() / numericLabel() / numericRunConfig() 或自定义 of),不解析 experiment 命名。 */
687
383
  x: NumericAxis;
688
- series?: DimensionInput;
384
+ /** 数组形态解析为复合维度。 */
385
+ series?: SeriesInput;
689
386
  y: Metric;
690
387
  /** eval id 前缀过滤,同 CLI 位置参数语义。 */
691
388
  evals?: string | readonly string[];
@@ -719,7 +416,7 @@ export async function metricLineData(input: ReportInput, options: MetricLineOpti
719
416
  "Fix of() to read experiment-level configuration (numericFlag()/numericRunConfig() do this by construction).",
720
417
  );
721
418
  }
722
- const series = options.series ? dimensionKey(options.series, item) : undefined;
419
+ const series = options.series ? seriesKey(options.series, item) : undefined;
723
420
  const bucketKey = `${series ?? ""}${x === null ? "null" : String(x)}`;
724
421
  const bucket = buckets.get(bucketKey);
725
422
  if (bucket) bucket.items.push(item);
@@ -752,7 +449,7 @@ export async function metricLineData(input: ReportInput, options: MetricLineOpti
752
449
  label: options.x.label ?? options.x.name,
753
450
  ...(options.x.unit !== undefined ? { unit: options.x.unit } : {}),
754
451
  },
755
- ...(options.series ? { seriesDimension: dimensionName(options.series) } : {}),
452
+ ...(options.series ? { seriesDimension: seriesName(options.series) } : {}),
756
453
  y: toColumn(options.y),
757
454
  rows,
758
455
  };
@@ -762,8 +459,10 @@ export async function metricLineData(input: ReportInput, options: MetricLineOpti
762
459
 
763
460
  /**
764
461
  * 按 flag 派生 A/B 对(docs/feature/reports/library/metric-views.md「DeltaTable」):
765
- * 配对域 = 同可比组 + 删除该 flag 后可比性配置深相等;a 取 baseline(缺省 = 未声明该 flag),
766
- * b 侧该 flag 的每个其它取值各成一对;label 自动 `<a 末段> · <flag>=<显示键>`。
462
+ * 配对域 = input Scope 内删除该 flag 后可比性配置深相等(不额外按 experiment id 的目录前缀分组
463
+ * ——architecture.md「Scope 是默认报告的比较边界」同一条契约);a baseline(缺省 = 未声明该
464
+ * flag),b 侧该 flag 的每个其它取值各成一对;label 自动 `<完整 a experiment id> · <flag>=<显示键>`,
465
+ * 排序仍按 a 的末段、再按 flag 显示键(不受完整 id 的字符串差异影响)。
767
466
  */
768
467
  export function pairsByFlag(name: string, options?: { baseline?: JsonValue }): FlagPairs {
769
468
  if (typeof name !== "string" || name.length === 0) {
@@ -790,13 +489,13 @@ function isFlagPairs(pairs: DeltaTableOptions["pairs"]): pairs is FlagPairs {
790
489
  return typeof pairs === "object" && pairs !== null && !Array.isArray(pairs) && (pairs as FlagPairs).kind === "flagPairs";
791
490
  }
792
491
 
793
- /** experiment id 相对可比组的末段。 */
492
+ /** experiment id 的末段(最后一个 `/` 之后;无 `/` 时是完整 id)。 */
794
493
  function experimentTail(experimentId: string): string {
795
494
  const slash = experimentId.lastIndexOf("/");
796
495
  return slash === -1 ? experimentId : experimentId.slice(slash + 1);
797
496
  }
798
497
 
799
- /** 派生配对:同可比组 + 删除该 flag 后可比性配置深相等。返回 pair 列表与配对域实验数。 */
498
+ /** 派生配对:input Scope 内删除该 flag 后可比性配置深相等。返回 pair 列表与配对域实验数。 */
800
499
  function derivePairsByFlag(
801
500
  snapshots: readonly Snapshot[],
802
501
  spec: FlagPairs,
@@ -820,8 +519,9 @@ function derivePairsByFlag(
820
519
  const flagValue = config.flags?.[spec.flag];
821
520
  const reduced = { ...config, flags: { ...config.flags } };
822
521
  delete reduced.flags[spec.flag];
823
- const group = experimentGroupOf(id) ?? id;
824
- entries.push({ id, flagValue, bucket: `${group}${JSON.stringify(sortedJson(reduced))}` });
522
+ // 配对域只是 input Scope + 删除该 flag 后的可比性配置深相等;不额外按 experiment id
523
+ // 目录前缀分组——组件不从路径猜比较边界(architecture.md「Scope 是默认报告的比较边界」)
524
+ entries.push({ id, flagValue, bucket: JSON.stringify(sortedJson(reduced)) });
825
525
  }
826
526
 
827
527
  const baseline = spec.baseline; // undefined = 未声明该 flag 的实验作 a
@@ -832,28 +532,34 @@ function derivePairsByFlag(
832
532
  else buckets.set(entry.bucket, [entry]);
833
533
  }
834
534
 
835
- const pairs: DeltaPair[] = [];
535
+ interface DerivedPair {
536
+ a: string;
537
+ b: string;
538
+ label: string;
539
+ flagKey: string;
540
+ }
541
+ const derived: DerivedPair[] = [];
836
542
  for (const bucket of buckets.values()) {
837
543
  const aSide = bucket.filter((e) => deepEqualJson(e.flagValue, baseline));
838
544
  const bSide = bucket.filter((e) => !deepEqualJson(e.flagValue, baseline));
839
545
  for (const a of aSide) {
840
546
  for (const b of bSide) {
841
- pairs.push({
842
- a: a.id,
843
- b: b.id,
844
- label: `${experimentTail(a.id)} · ${spec.flag}=${refDisplayKey(b.flagValue)[0]}`,
845
- });
547
+ const flagKey = refDisplayKey(b.flagValue)[0]!;
548
+ // label 用完整 a experiment id 自动命名,不截断成末段——目录前缀不同的两个实验
549
+ // 配成一对时,末段可能撞名,完整 id 才能唯一标识这一对来自哪个实验。
550
+ derived.push({ a: a.id, b: b.id, label: `${a.id} · ${spec.flag}=${flagKey}`, flagKey });
846
551
  }
847
552
  }
848
553
  }
849
- pairs.sort((p, q) => {
554
+ // 排序只看 a 的末段与 flag 显示键,不看完整 label 字符串——完整 a id 只服务显示,
555
+ // 不该让目录前缀的字符串差异打乱本该相邻的 a 末段分组。
556
+ derived.sort((p, q) => {
850
557
  const ta = experimentTail(p.a);
851
558
  const tb = experimentTail(q.a);
852
559
  if (ta !== tb) return ta < tb ? -1 : 1;
853
- const la = p.label as string;
854
- const lb = q.label as string;
855
- return la < lb ? -1 : la > lb ? 1 : 0;
560
+ return p.flagKey < q.flagKey ? -1 : p.flagKey > q.flagKey ? 1 : 0;
856
561
  });
562
+ const pairs: DeltaPair[] = derived.map(({ a, b, label }) => ({ a, b, label }));
857
563
  return { pairs, experiments: byExperiment.size };
858
564
  }
859
565
 
@@ -974,135 +680,3 @@ function deltaOutcome(metric: Metric, delta: number | null): "improved" | "regre
974
680
  const better = metric.better ?? "higher";
975
681
  return (delta > 0) === (better === "higher") ? "improved" : "regressed";
976
682
  }
977
-
978
- // ───────────────────────── 站点组件的计算函数(hero / warnings / fix prompt / trace)─────────────────────────
979
-
980
- /**
981
- * `heroData(input)`:站点标题区的运行 meta——`latestStartedAt` 取范围内最新快照的开始时间
982
- * (空范围为 null,不编造当前时间),`snapshots` 计贡献当前水位的快照数
983
- * (docs/feature/reports/library/site-components.md「HeroCard」)。
984
- */
985
- export async function heroData(input: ReportInput): Promise<HeroData> {
986
- const { snapshots } = resolveInput(input);
987
- let latest: string | null = null;
988
- for (const snapshot of snapshots) {
989
- if (latest === null || snapshot.startedAt > latest) latest = snapshot.startedAt;
990
- }
991
- return { latestStartedAt: latest, snapshots: snapshots.length };
992
- }
993
-
994
- /**
995
- * `scopeWarningsData(input)`:Scope 携带的挑选警告原样透出;`input` 是裸 `Snapshot[]` 时
996
- * 没有挑选过程、没有警告,返回空数组,也如实
997
- * (docs/feature/reports/library/site-components.md「ScopeWarnings」)。
998
- */
999
- export async function scopeWarningsData(input: ReportInput): Promise<readonly ScopeWarning[]> {
1000
- return resolveInput(input).warnings;
1001
- }
1002
-
1003
- /**
1004
- * `copyFixPromptData(input)`:把范围内全部失败(verdict 为 failed / errored 的 attempt)
1005
- * 整理成一段可交给 coding agent 的修复 prompt——逐失败含 eval id、主失败摘要与 attempt
1006
- * 下钻命令(`niceeval show @<locator>`)。prompt 面向 agent,固定英文
1007
- * (docs/feature/reports/library/site-components.md「CopyFixPrompt」)。
1008
- */
1009
- export async function copyFixPromptData(input: ReportInput): Promise<CopyFixPromptData> {
1010
- const items = await attemptListData(input);
1011
- const failures = items.filter((item) => item.verdict === "failed" || item.verdict === "errored");
1012
- if (failures.length === 0) return { prompt: "", failures: 0 };
1013
- const lines = failures
1014
- .map((item, i) => {
1015
- const reason =
1016
- item.failureSummary === null
1017
- ? null
1018
- : item.moreFailures > 0
1019
- ? `${item.failureSummary} (+${item.moreFailures} more failures)`
1020
- : item.failureSummary;
1021
- return [
1022
- `${i + 1}. eval "${item.evalId}" [experiment ${item.experimentId}] — ${item.verdict}`,
1023
- reason ? ` reason: ${reason}` : null,
1024
- ` inspect: niceeval show ${item.locator}`,
1025
- ]
1026
- .filter(Boolean)
1027
- .join("\n");
1028
- })
1029
- .join("\n");
1030
- const experiments = [...new Set(failures.map((item) => item.experimentId))].join(" / ");
1031
- const prompt = [
1032
- "Fix the failing evals from this niceeval run.",
1033
- "",
1034
- "## Failures",
1035
- lines,
1036
- "",
1037
- "## Steps",
1038
- "1. niceeval is NOT in your training data. Read the relevant guide in `node_modules/niceeval/docs-site/` (English at the top level, Chinese under `zh/`) before changing anything.",
1039
- "2. For each failure, run its inspect command above to see the verdict and assertions; add `--execution` for the full agent transcript (tool calls included), `--timing` for the execution timeline, and `--diff` for the workspace diff.",
1040
- "3. Decide which side the defect is on: the program under test, or the eval itself (over-tight assertion, wrong fixture, missing setup). Fix that side; do not weaken assertions just to turn the run green.",
1041
- `4. Re-run: \`npx niceeval exp ${experiments || "<experiment>"} <eval-id-prefix>\`. Already-passing evals are skipped by the fingerprint cache; pass \`--force\` to re-run everything.`,
1042
- "5. Run `npx niceeval show` and confirm these failures are gone.",
1043
- ].join("\n");
1044
- return { prompt, failures: failures.length };
1045
- }
1046
-
1047
- /** TraceSpan 的语义角色 → 瀑布摘要的 kind:turn 归入 agent(一轮就是一次 agent 调用),未识别落 other。 */
1048
- function waterfallKindOf(kind: TraceSpan["kind"]): TraceSpanSummary["kind"] {
1049
- switch (kind) {
1050
- case "agent":
1051
- case "turn":
1052
- return "agent";
1053
- case "model":
1054
- return "model";
1055
- case "tool":
1056
- return "tool";
1057
- default:
1058
- return "other";
1059
- }
1060
- }
1061
-
1062
- /**
1063
- * `traceWaterfallData(input)`:每个 attempt 一行的执行时间瀑布摘要。span 事实只来自
1064
- * trace artifact(经 AttemptHandle 懒加载的 canonical OTel span);runner 生命周期节点
1065
- * (`result.phases`)不进瀑布。行内只汇总顶层 span(parentSpanId 缺失或不在本 trace 内),
1066
- * 按 startOffsetMs 升序;trace 缺失或为空时 `durationMs` 为 null、行照常出现
1067
- * (docs/feature/reports/library/site-components.md「TraceWaterfall」)。
1068
- */
1069
- export async function traceWaterfallData(input: ReportInput): Promise<readonly TraceWaterfallRow[]> {
1070
- const { snapshots } = resolveInput(input);
1071
- const items = collectItems(snapshots);
1072
- return Promise.all(
1073
- items.map(async (item): Promise<TraceWaterfallRow> => {
1074
- const spans = await item.attempt.trace();
1075
- if (spans === null || spans.length === 0) {
1076
- return {
1077
- experimentId: experimentIdOf(item),
1078
- evalId: evalIdOf(item),
1079
- locator: locatorOf(item),
1080
- durationMs: null,
1081
- spans: [],
1082
- };
1083
- }
1084
- const t0 = Math.min(...spans.map((s) => s.startMs));
1085
- const t1 = Math.max(...spans.map((s) => s.endMs));
1086
- const ids = new Set(spans.map((s) => s.spanId));
1087
- const topLevel = spans.filter((s) => s.parentSpanId === undefined || !ids.has(s.parentSpanId));
1088
- const summaries = topLevel
1089
- .map(
1090
- (s): TraceSpanSummary => ({
1091
- name: s.name,
1092
- kind: waterfallKindOf(s.kind),
1093
- startOffsetMs: s.startMs - t0,
1094
- durationMs: s.endMs - s.startMs,
1095
- failed: s.status === "error",
1096
- }),
1097
- )
1098
- .sort((a, b) => a.startOffsetMs - b.startOffsetMs);
1099
- return {
1100
- experimentId: experimentIdOf(item),
1101
- evalId: evalIdOf(item),
1102
- locator: locatorOf(item),
1103
- durationMs: Math.max(0, t1 - t0),
1104
- spans: summaries,
1105
- };
1106
- }),
1107
- );
1108
- }