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 不进组件数据(宿主统一显示);
@@ -10,14 +10,11 @@
10
10
  // - 显式传入的列表(questions / pairs / metrics)保留声明顺序,从数据发现的维度 domain
11
11
  // 按稳定 key 字典序;
12
12
  // - core 中立:只认 Metric / Dimension 接口,不出现具体 agent 名的分支。
13
- import { comparabilityConfigOf, deepEqualJson } from "../results/select.js";
14
- import { evalLevelStats, foldEvalVerdict } from "../shared/verdict.js";
15
- import { experimentGroupOf } from "../shared/aggregate.js";
16
- import { assertUniqueMetricNames, axisValueOf, collectItems, computeCell, dimensionKey, dimensionName, evalGroupOf, evalIdOf, experimentIdOf, filterItems, fullEvalKey, groupItems, locatorOf, refDisplayKey, resolveInput, toColumn, } from "./aggregate.js";
17
- import { attemptCostUSD, costUSD, durationMs, endToEndPassRate, examScore, tokens } from "./metrics.js";
18
- import { formatMetricValue, formatPlainNumber, localizedDisplay } from "./format.js";
19
- import { compactAssertionSummary, primaryAssertionSummary, summaryText } from "../scoring/display.js";
20
- import { defineMetric } from "./metrics.js";
13
+ import { comparabilityConfigOf, deepEqualJson } from "../../../results/select.js";
14
+ import { assertUniqueMetricNames, axisValueOf, collectItems, computeCell, dimensionKey, dimensionName, evalGroupOf, evalIdOf, experimentIdOf, filterItems, fullEvalKey, groupItems, locatorOf, refDisplayKey, resolveInput, seriesKey, seriesName, toColumn, } from "../../model/aggregate.js";
15
+ import { examScore } from "../../model/metrics.js";
16
+ import { formatMetricValue, formatPlainNumber, localizedDisplay } from "../../model/format.js";
17
+ import { selectedEvalsOnly } from "../shared-compute.js";
21
18
  export async function metricTableData(input, options) {
22
19
  assertUniqueMetricNames(options.columns, "metricTableData columns");
23
20
  if (options.sort !== undefined) {
@@ -91,266 +88,6 @@ export async function metricMatrixData(input, options) {
91
88
  cells,
92
89
  };
93
90
  }
94
- // ───────────────────────── 实体列表(experimentListData / evalListData / attemptListData)─────────────────────────
95
- /**
96
- * 一次 attempt 的单行结果摘要(Scoring display 契约):failed 取主失败断言摘要(不含
97
- * "+N more",N 单独进 moreFailures),errored 取结构化 error 的一层摘要
98
- * (phase · code · message),passed / skipped 为 null。
99
- */
100
- function failureSummaryOf(result) {
101
- if (result.verdict === "errored" && result.error !== undefined) {
102
- const parts = [result.error.phase, result.error.code, result.error.message].filter((part) => typeof part === "string" && part.length > 0);
103
- return { summary: summaryText(parts.join(" · ")), more: 0 };
104
- }
105
- if (result.verdict === "failed" || result.verdict === "errored") {
106
- const primary = primaryAssertionSummary(result.assertions, result.verdict);
107
- if (primary !== undefined) {
108
- return {
109
- summary: compactAssertionSummary({ ...primary, additionalFailures: 0 }),
110
- more: primary.additionalFailures,
111
- };
112
- }
113
- if (result.verdict === "errored" && result.skipReason !== undefined) {
114
- return { summary: summaryText(result.skipReason), more: 0 };
115
- }
116
- return { summary: null, more: 0 };
117
- }
118
- return { summary: null, more: 0 };
119
- }
120
- /** AttemptList / ExperimentList / EvalList 共用的叶子构造:一个 Item → 一个 AttemptListItem。 */
121
- async function attemptListItemOf(item) {
122
- const result = item.attempt.result;
123
- const { summary, more } = failureSummaryOf(result);
124
- return {
125
- experimentId: experimentIdOf(item),
126
- evalId: evalIdOf(item),
127
- attempt: result.attempt,
128
- agent: result.agent,
129
- verdict: result.verdict,
130
- failureSummary: summary,
131
- moreFailures: more,
132
- examScore: await computeCell(examScore, [item]),
133
- durationMs: result.durationMs,
134
- costUSD: attemptCostUSD(result),
135
- locator: locatorOf(item),
136
- };
137
- }
138
- /** `attemptListData(input)`:每个 Attempt 一项,顺序取自 Scope 展平顺序(不重排)。 */
139
- export async function attemptListData(input) {
140
- const { snapshots } = resolveInput(input);
141
- const items = collectItems(snapshots);
142
- return Promise.all(items.map((item) => attemptListItemOf(item)));
143
- }
144
- /** `evalListData(input)`:每个 `experimentId + evalId` 一项,按 evalId 再按 experimentId 升序。 */
145
- export async function evalListData(input) {
146
- const { snapshots } = resolveInput(input);
147
- const items = collectItems(snapshots);
148
- const groups = new Map();
149
- for (const item of items) {
150
- const key = fullEvalKey(item);
151
- const list = groups.get(key);
152
- if (list)
153
- list.push(item);
154
- else
155
- groups.set(key, [item]);
156
- }
157
- const out = [];
158
- for (const group of groups.values()) {
159
- const sorted = [...group].sort((a, b) => a.attempt.result.attempt - b.attempt.result.attempt);
160
- const verdict = foldEvalVerdict(sorted.map((item) => item.attempt.result));
161
- const attempts = await Promise.all(sorted.map((item) => attemptListItemOf(item)));
162
- out.push({
163
- experimentId: experimentIdOf(sorted[0]),
164
- evalId: evalIdOf(sorted[0]),
165
- verdict,
166
- examScore: await computeCell(examScore, sorted),
167
- durationMs: await computeCell(durationMs, sorted),
168
- costUSD: await computeCell(costUSD, sorted),
169
- attempts,
170
- });
171
- }
172
- out.sort((a, b) => a.evalId.localeCompare(b.evalId) || a.experimentId.localeCompare(b.experimentId));
173
- return out;
174
- }
175
- /**
176
- * `experimentListData(input)`:每个 experiment 一项,展开到每道 Eval;初始按端到端成功率
177
- * 从高到低(缺数据沉底,同分按 id)。一行只有一套 agent / model / flags 是输入约束:
178
- * 宿主注入的 current() Scope 保证每个 experiment 只由可比性配置一致的快照拼成;作者自选
179
- * Snapshot[] 时若同一 experiment 混入不一致的可比性配置,按完整用户反馈失败并指引——
180
- * 看跨配置演化用 snapshot 维度或 MetricLine,不把两套配置拼成一行冒充单一配置。
181
- */
182
- export async function experimentListData(input) {
183
- const { snapshots } = resolveInput(input);
184
- // 可比性配置单义检查:同一 experiment 的输入快照必须共享一套可比性配置。
185
- const configByExperiment = new Map();
186
- for (const snapshot of snapshots) {
187
- const config = comparabilityConfigOf(snapshot);
188
- const existing = configByExperiment.get(snapshot.experimentId);
189
- if (existing === undefined) {
190
- configByExperiment.set(snapshot.experimentId, { snapshot, config });
191
- }
192
- else if (!deepEqualJson(existing.config, config)) {
193
- throw new Error(`experimentListData got inconsistent comparability configs for experiment "${snapshot.experimentId}" ` +
194
- `(snapshots ${existing.snapshot.startedAt} and ${snapshot.startedAt} differ in agent/model/reasoningEffort/flags/budget/timeoutMs/sandbox). ` +
195
- "One row shows one configuration — it cannot honestly merge two. To chart evolution across configs, " +
196
- 'use the "snapshot" dimension or MetricLine; to show the current level, pass results.current() which selects a single config per experiment.');
197
- }
198
- }
199
- const items = collectItems(snapshots);
200
- const groups = groupItems(items, "experiment");
201
- const out = [];
202
- for (const [experimentId, group] of groups) {
203
- const stats = summarizeItems(group);
204
- const newest = [...group].sort((a, b) => b.snapshot.startedAt.localeCompare(a.snapshot.startedAt))[0];
205
- const evalGroups = groupItems(group, "eval");
206
- const evalRows = [];
207
- for (const [evalId, evalItems] of evalGroups) {
208
- const sorted = [...evalItems].sort((a, b) => a.attempt.result.attempt - b.attempt.result.attempt);
209
- const verdict = foldEvalVerdict(sorted.map((item) => item.attempt.result));
210
- const attempts = await Promise.all(sorted.map((item) => attemptListItemOf(item)));
211
- evalRows.push({
212
- evalId,
213
- verdict,
214
- durationMs: await computeCell(durationMs, sorted),
215
- costUSD: await computeCell(costUSD, sorted),
216
- attempts,
217
- });
218
- }
219
- const experiment = newest.snapshot.experiment ?? newest.attempt.result.experiment;
220
- const model = newest.attempt.result.model ?? newest.snapshot.model;
221
- out.push({
222
- experimentId,
223
- agent: newest.snapshot.agent || newest.attempt.result.agent,
224
- ...(model !== undefined ? { model } : {}),
225
- ...(experiment?.flags ? { flags: experiment.flags } : {}),
226
- evalVerdicts: stats.verdicts,
227
- endToEndPassRate: await computeCell(endToEndPassRate, group),
228
- costUSD: await computeCell(costUSD, group),
229
- durationMs: await computeCell(durationMs, group),
230
- tokens: await computeCell(tokens, group),
231
- evals: stats.evals,
232
- attempts: stats.attempts,
233
- lastRunAt: stats.lastRunAt,
234
- evalRows,
235
- });
236
- }
237
- // 初始态按端到端成功率(endToEndPassRate)从高到低,缺数据沉底;同分按 experiment id 稳定排序。
238
- out.sort((a, b) => {
239
- const va = a.endToEndPassRate.value;
240
- const vb = b.endToEndPassRate.value;
241
- if (va === null && vb === null)
242
- return a.experimentId.localeCompare(b.experimentId);
243
- if (va === null)
244
- return 1;
245
- if (vb === null)
246
- return -1;
247
- return vb - va || a.experimentId.localeCompare(b.experimentId);
248
- });
249
- return out;
250
- }
251
- // ───────────────────────── scopeSummaryData ─────────────────────────
252
- /** costUSD 的求和投影:两级都 sum(题内多轮求和 + 跨题求和 = 全量求和),display 走 $。 */
253
- const totalCostMetric = defineMetric({
254
- name: "total-cost",
255
- label: costUSD.label,
256
- unit: "$",
257
- value: costUSD.value,
258
- aggregate: { perEval: "sum", acrossEvals: "sum" },
259
- });
260
- function tallyOf() {
261
- return { passed: 0, failed: 0, errored: 0, skipped: 0 };
262
- }
263
- /** 一批 Item 的组级统计(experimentListData / scopeSummaryData 共用)。 */
264
- function summarizeItems(items) {
265
- const experimentIds = new Set();
266
- for (const item of items)
267
- experimentIds.add(experimentIdOf(item));
268
- const stats = evalLevelStats(items.map((item) => ({ verdict: item.attempt.result.verdict, key: fullEvalKey(item) })), (r) => r.key);
269
- let lastRunAt;
270
- for (const item of items) {
271
- const startedAt = item.snapshot.startedAt;
272
- if (lastRunAt === undefined || startedAt > lastRunAt)
273
- lastRunAt = startedAt;
274
- }
275
- return {
276
- experiments: experimentIds.size,
277
- evals: stats.evals,
278
- attempts: items.length,
279
- verdicts: { passed: stats.passed, failed: stats.failed, errored: stats.errored, skipped: stats.skipped },
280
- lastRunAt,
281
- };
282
- }
283
- /**
284
- * `scopeSummaryData(input)`:范围摘要——快照时间窗、experiment / eval / attempt 数、
285
- * 两级判定计票、端到端成功率与总成本(docs/feature/reports/library/summaries.md)。
286
- * data 恒携带两级计票;成功率来自官方两级指标引擎,不从任一计票重算。
287
- */
288
- export async function scopeSummaryData(input) {
289
- const { snapshots } = resolveInput(input);
290
- const items = collectItems(snapshots);
291
- let earliest = null;
292
- let latest = null;
293
- for (const snapshot of snapshots) {
294
- if (earliest === null || snapshot.startedAt < earliest)
295
- earliest = snapshot.startedAt;
296
- if (latest === null || snapshot.startedAt > latest)
297
- latest = snapshot.startedAt;
298
- }
299
- const stats = summarizeItems(items);
300
- const attemptVerdicts = tallyOf();
301
- for (const item of items)
302
- attemptVerdicts[item.attempt.result.verdict] += 1;
303
- return {
304
- range: { earliestStartedAt: earliest, latestStartedAt: latest },
305
- experiments: stats.experiments,
306
- evals: stats.evals,
307
- attempts: stats.attempts,
308
- evalVerdicts: stats.verdicts,
309
- attemptVerdicts,
310
- endToEndPassRate: await computeCell(endToEndPassRate, items),
311
- totalCostUSD: await computeCell(totalCostMetric, items),
312
- };
313
- }
314
- // ───────────────────────── experimentComparisonData ─────────────────────────
315
- /** 完整父路径是组键;没有父路径的 experiment 不能互相比,自己形成单例组。 */
316
- export function experimentComparisonGroupKey(experimentId) {
317
- return experimentGroupOf(experimentId) ?? experimentId;
318
- }
319
- /** 每组散点的唯一口径:默认 definition 与公开计算共用,不各写一份。 */
320
- const COMPARISON_SCATTER_OPTIONS = {
321
- points: "experiment",
322
- series: "agent",
323
- x: costUSD,
324
- y: endToEndPassRate,
325
- };
326
- /**
327
- * `experimentComparisonData(input)`:先把 input 按可比组分区(experiment id 的完整父路径),
328
- * 再为每组分别计算 ScopeSummary、成本 × 端到端成功率散点和 ExperimentList——分区发生在任何
329
- * 指标计算之前,组外 attempt 不可能污染该组的坐标尺度、series、成功率、成本、排序或缺数据计数。
330
- */
331
- export async function experimentComparisonData(input) {
332
- const { snapshots } = resolveInput(input);
333
- const snapshotsByGroup = new Map();
334
- for (const snapshot of snapshots) {
335
- const key = experimentComparisonGroupKey(snapshot.experimentId);
336
- const group = snapshotsByGroup.get(key);
337
- if (group)
338
- group.push(snapshot);
339
- else
340
- snapshotsByGroup.set(key, [snapshot]);
341
- }
342
- const groups = await Promise.all([...snapshotsByGroup.entries()]
343
- .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
344
- .map(async ([key, groupSnapshots]) => {
345
- const [summary, scatter, experiments] = await Promise.all([
346
- scopeSummaryData(groupSnapshots),
347
- metricScatterData(groupSnapshots, COMPARISON_SCATTER_OPTIONS),
348
- experimentListData(groupSnapshots),
349
- ]);
350
- return { key, summary, scatter, experiments };
351
- }));
352
- return { groups };
353
- }
354
91
  /**
355
92
  * 固定题集分母:未跑题按 0 分计入 `notRun`,跑了但指标为 null 的题按 0 分计入 `unscorable`,
356
93
  * 两个计数不合并——成绩单能回答「这 0 分是没去考还是考了判不了」。组件不从已观测 attempt
@@ -497,7 +234,7 @@ function subjectDisplay(earned, possible) {
497
234
  return `${formatPlainNumber(earned)}/${formatPlainNumber(possible)} (${formatMetricValue(ratio, "%")})`;
498
235
  }
499
236
  export async function metricScatterData(input, options) {
500
- const { snapshots } = resolveInput(input);
237
+ const snapshots = selectedEvalsOnly(resolveInput(input).snapshots);
501
238
  const items = filterItems(collectItems(snapshots), options.evals);
502
239
  const groups = groupItems(items, options.points);
503
240
  const rows = [];
@@ -505,14 +242,14 @@ export async function metricScatterData(input, options) {
505
242
  rows.push({
506
243
  key,
507
244
  // 组内取第一条解析系列:点维度细于系列维度时(experiment ⊂ agent)天然一致
508
- ...(options.series ? { series: dimensionKey(options.series, group[0]) } : {}),
245
+ ...(options.series ? { series: seriesKey(options.series, group[0]) } : {}),
509
246
  x: await computeCell(options.x, group),
510
247
  y: await computeCell(options.y, group), // 任一轴 null 的点留在 rows 里:组件不画,但注脚要报的数就从这里数
511
248
  });
512
249
  }
513
250
  return {
514
251
  pointDimension: dimensionName(options.points),
515
- ...(options.series ? { seriesDimension: dimensionName(options.series) } : {}),
252
+ ...(options.series ? { seriesDimension: seriesName(options.series) } : {}),
516
253
  x: toColumn(options.x),
517
254
  y: toColumn(options.y),
518
255
  rows,
@@ -544,7 +281,7 @@ export async function metricLineData(input, options) {
544
281
  "not vary per attempt — a per-attempt quantity is material for the y metric, not an x axis. " +
545
282
  "Fix of() to read experiment-level configuration (numericFlag()/numericRunConfig() do this by construction).");
546
283
  }
547
- const series = options.series ? dimensionKey(options.series, item) : undefined;
284
+ const series = options.series ? seriesKey(options.series, item) : undefined;
548
285
  const bucketKey = `${series ?? ""}${x === null ? "null" : String(x)}`;
549
286
  const bucket = buckets.get(bucketKey);
550
287
  if (bucket)
@@ -579,7 +316,7 @@ export async function metricLineData(input, options) {
579
316
  label: options.x.label ?? options.x.name,
580
317
  ...(options.x.unit !== undefined ? { unit: options.x.unit } : {}),
581
318
  },
582
- ...(options.series ? { seriesDimension: dimensionName(options.series) } : {}),
319
+ ...(options.series ? { seriesDimension: seriesName(options.series) } : {}),
583
320
  y: toColumn(options.y),
584
321
  rows,
585
322
  };
@@ -587,8 +324,10 @@ export async function metricLineData(input, options) {
587
324
  // ───────────────────────── deltaTableData 与 pairsByFlag ─────────────────────────
588
325
  /**
589
326
  * 按 flag 派生 A/B 对(docs/feature/reports/library/metric-views.md「DeltaTable」):
590
- * 配对域 = 同可比组 + 删除该 flag 后可比性配置深相等;a 取 baseline(缺省 = 未声明该 flag),
591
- * b 侧该 flag 的每个其它取值各成一对;label 自动 `<a 末段> · <flag>=<显示键>`。
327
+ * 配对域 = input Scope 内删除该 flag 后可比性配置深相等(不额外按 experiment id 的目录前缀分组
328
+ * ——architecture.md「Scope 是默认报告的比较边界」同一条契约);a baseline(缺省 = 未声明该
329
+ * flag),b 侧该 flag 的每个其它取值各成一对;label 自动 `<完整 a experiment id> · <flag>=<显示键>`,
330
+ * 排序仍按 a 的末段、再按 flag 显示键(不受完整 id 的字符串差异影响)。
592
331
  */
593
332
  export function pairsByFlag(name, options) {
594
333
  if (typeof name !== "string" || name.length === 0) {
@@ -603,12 +342,12 @@ export function pairsByFlag(name, options) {
603
342
  function isFlagPairs(pairs) {
604
343
  return typeof pairs === "object" && pairs !== null && !Array.isArray(pairs) && pairs.kind === "flagPairs";
605
344
  }
606
- /** experiment id 相对可比组的末段。 */
345
+ /** experiment id 的末段(最后一个 `/` 之后;无 `/` 时是完整 id)。 */
607
346
  function experimentTail(experimentId) {
608
347
  const slash = experimentId.lastIndexOf("/");
609
348
  return slash === -1 ? experimentId : experimentId.slice(slash + 1);
610
349
  }
611
- /** 派生配对:同可比组 + 删除该 flag 后可比性配置深相等。返回 pair 列表与配对域实验数。 */
350
+ /** 派生配对:input Scope 内删除该 flag 后可比性配置深相等。返回 pair 列表与配对域实验数。 */
612
351
  function derivePairsByFlag(snapshots, spec) {
613
352
  // 每个 experiment 取最新快照的配置(current() Scope 天然一实验一快照)。
614
353
  const byExperiment = new Map();
@@ -624,8 +363,9 @@ function derivePairsByFlag(snapshots, spec) {
624
363
  const flagValue = config.flags?.[spec.flag];
625
364
  const reduced = { ...config, flags: { ...config.flags } };
626
365
  delete reduced.flags[spec.flag];
627
- const group = experimentGroupOf(id) ?? id;
628
- entries.push({ id, flagValue, bucket: `${group}${JSON.stringify(sortedJson(reduced))}` });
366
+ // 配对域只是 input Scope + 删除该 flag 后的可比性配置深相等;不额外按 experiment id
367
+ // 目录前缀分组——组件不从路径猜比较边界(architecture.md「Scope 是默认报告的比较边界」)
368
+ entries.push({ id, flagValue, bucket: JSON.stringify(sortedJson(reduced)) });
629
369
  }
630
370
  const baseline = spec.baseline; // undefined = 未声明该 flag 的实验作 a
631
371
  const buckets = new Map();
@@ -636,29 +376,29 @@ function derivePairsByFlag(snapshots, spec) {
636
376
  else
637
377
  buckets.set(entry.bucket, [entry]);
638
378
  }
639
- const pairs = [];
379
+ const derived = [];
640
380
  for (const bucket of buckets.values()) {
641
381
  const aSide = bucket.filter((e) => deepEqualJson(e.flagValue, baseline));
642
382
  const bSide = bucket.filter((e) => !deepEqualJson(e.flagValue, baseline));
643
383
  for (const a of aSide) {
644
384
  for (const b of bSide) {
645
- pairs.push({
646
- a: a.id,
647
- b: b.id,
648
- label: `${experimentTail(a.id)} · ${spec.flag}=${refDisplayKey(b.flagValue)[0]}`,
649
- });
385
+ const flagKey = refDisplayKey(b.flagValue)[0];
386
+ // label 用完整 a experiment id 自动命名,不截断成末段——目录前缀不同的两个实验
387
+ // 配成一对时,末段可能撞名,完整 id 才能唯一标识这一对来自哪个实验。
388
+ derived.push({ a: a.id, b: b.id, label: `${a.id} · ${spec.flag}=${flagKey}`, flagKey });
650
389
  }
651
390
  }
652
391
  }
653
- pairs.sort((p, q) => {
392
+ // 排序只看 a 的末段与 flag 显示键,不看完整 label 字符串——完整 a id 只服务显示,
393
+ // 不该让目录前缀的字符串差异打乱本该相邻的 a 末段分组。
394
+ derived.sort((p, q) => {
654
395
  const ta = experimentTail(p.a);
655
396
  const tb = experimentTail(q.a);
656
397
  if (ta !== tb)
657
398
  return ta < tb ? -1 : 1;
658
- const la = p.label;
659
- const lb = q.label;
660
- return la < lb ? -1 : la > lb ? 1 : 0;
399
+ return p.flagKey < q.flagKey ? -1 : p.flagKey > q.flagKey ? 1 : 0;
661
400
  });
401
+ const pairs = derived.map(({ a, b, label }) => ({ a, b, label }));
662
402
  return { pairs, experiments: byExperiment.size };
663
403
  }
664
404
  /** 对象键递归排序(派生配对的 bucket 键用;undefined 字段剔除)。 */
@@ -774,126 +514,3 @@ function deltaOutcome(metric, delta) {
774
514
  const better = metric.better ?? "higher";
775
515
  return (delta > 0) === (better === "higher") ? "improved" : "regressed";
776
516
  }
777
- // ───────────────────────── 站点组件的计算函数(hero / warnings / fix prompt / trace)─────────────────────────
778
- /**
779
- * `heroData(input)`:站点标题区的运行 meta——`latestStartedAt` 取范围内最新快照的开始时间
780
- * (空范围为 null,不编造当前时间),`snapshots` 计贡献当前水位的快照数
781
- * (docs/feature/reports/library/site-components.md「HeroCard」)。
782
- */
783
- export async function heroData(input) {
784
- const { snapshots } = resolveInput(input);
785
- let latest = null;
786
- for (const snapshot of snapshots) {
787
- if (latest === null || snapshot.startedAt > latest)
788
- latest = snapshot.startedAt;
789
- }
790
- return { latestStartedAt: latest, snapshots: snapshots.length };
791
- }
792
- /**
793
- * `scopeWarningsData(input)`:Scope 携带的挑选警告原样透出;`input` 是裸 `Snapshot[]` 时
794
- * 没有挑选过程、没有警告,返回空数组,也如实
795
- * (docs/feature/reports/library/site-components.md「ScopeWarnings」)。
796
- */
797
- export async function scopeWarningsData(input) {
798
- return resolveInput(input).warnings;
799
- }
800
- /**
801
- * `copyFixPromptData(input)`:把范围内全部失败(verdict 为 failed / errored 的 attempt)
802
- * 整理成一段可交给 coding agent 的修复 prompt——逐失败含 eval id、主失败摘要与 attempt
803
- * 下钻命令(`niceeval show @<locator>`)。prompt 面向 agent,固定英文
804
- * (docs/feature/reports/library/site-components.md「CopyFixPrompt」)。
805
- */
806
- export async function copyFixPromptData(input) {
807
- const items = await attemptListData(input);
808
- const failures = items.filter((item) => item.verdict === "failed" || item.verdict === "errored");
809
- if (failures.length === 0)
810
- return { prompt: "", failures: 0 };
811
- const lines = failures
812
- .map((item, i) => {
813
- const reason = item.failureSummary === null
814
- ? null
815
- : item.moreFailures > 0
816
- ? `${item.failureSummary} (+${item.moreFailures} more failures)`
817
- : item.failureSummary;
818
- return [
819
- `${i + 1}. eval "${item.evalId}" [experiment ${item.experimentId}] — ${item.verdict}`,
820
- reason ? ` reason: ${reason}` : null,
821
- ` inspect: niceeval show ${item.locator}`,
822
- ]
823
- .filter(Boolean)
824
- .join("\n");
825
- })
826
- .join("\n");
827
- const experiments = [...new Set(failures.map((item) => item.experimentId))].join(" / ");
828
- const prompt = [
829
- "Fix the failing evals from this niceeval run.",
830
- "",
831
- "## Failures",
832
- lines,
833
- "",
834
- "## Steps",
835
- "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.",
836
- "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.",
837
- "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.",
838
- `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.`,
839
- "5. Run `npx niceeval show` and confirm these failures are gone.",
840
- ].join("\n");
841
- return { prompt, failures: failures.length };
842
- }
843
- /** TraceSpan 的语义角色 → 瀑布摘要的 kind:turn 归入 agent(一轮就是一次 agent 调用),未识别落 other。 */
844
- function waterfallKindOf(kind) {
845
- switch (kind) {
846
- case "agent":
847
- case "turn":
848
- return "agent";
849
- case "model":
850
- return "model";
851
- case "tool":
852
- return "tool";
853
- default:
854
- return "other";
855
- }
856
- }
857
- /**
858
- * `traceWaterfallData(input)`:每个 attempt 一行的执行时间瀑布摘要。span 事实只来自
859
- * trace artifact(经 AttemptHandle 懒加载的 canonical OTel span);runner 生命周期节点
860
- * (`result.phases`)不进瀑布。行内只汇总顶层 span(parentSpanId 缺失或不在本 trace 内),
861
- * 按 startOffsetMs 升序;trace 缺失或为空时 `durationMs` 为 null、行照常出现
862
- * (docs/feature/reports/library/site-components.md「TraceWaterfall」)。
863
- */
864
- export async function traceWaterfallData(input) {
865
- const { snapshots } = resolveInput(input);
866
- const items = collectItems(snapshots);
867
- return Promise.all(items.map(async (item) => {
868
- const spans = await item.attempt.trace();
869
- if (spans === null || spans.length === 0) {
870
- return {
871
- experimentId: experimentIdOf(item),
872
- evalId: evalIdOf(item),
873
- locator: locatorOf(item),
874
- durationMs: null,
875
- spans: [],
876
- };
877
- }
878
- const t0 = Math.min(...spans.map((s) => s.startMs));
879
- const t1 = Math.max(...spans.map((s) => s.endMs));
880
- const ids = new Set(spans.map((s) => s.spanId));
881
- const topLevel = spans.filter((s) => s.parentSpanId === undefined || !ids.has(s.parentSpanId));
882
- const summaries = topLevel
883
- .map((s) => ({
884
- name: s.name,
885
- kind: waterfallKindOf(s.kind),
886
- startOffsetMs: s.startMs - t0,
887
- durationMs: s.endMs - s.startMs,
888
- failed: s.status === "error",
889
- }))
890
- .sort((a, b) => a.startOffsetMs - b.startOffsetMs);
891
- return {
892
- experimentId: experimentIdOf(item),
893
- evalId: evalIdOf(item),
894
- locator: locatorOf(item),
895
- durationMs: Math.max(0, t1 - t0),
896
- spans: summaries,
897
- };
898
- }));
899
- }
@@ -0,0 +1,13 @@
1
+ import type { DeltaData, LineData, MatrixData, ScatterData, ScoreboardData, TableData } from "../../model/types.ts";
2
+ import type { TextContext } from "../../definition/tree.ts";
3
+ export declare function tableText(data: TableData, ctx: TextContext): string;
4
+ export declare function matrixText(data: MatrixData, ctx: TextContext): string;
5
+ export declare function barsText(data: MatrixData, ctx: TextContext): string;
6
+ export declare function scoreboardText(data: ScoreboardData, ctx: TextContext): string;
7
+ export interface ScatterTextOptions {
8
+ /** series 内按 x 升序成线:坐标图内不画折线,图例以 ` → ` 串联并给逐段位移摘要。 */
9
+ connect?: boolean;
10
+ }
11
+ export declare function scatterText(data: ScatterData, ctx: TextContext, opts?: ScatterTextOptions): string;
12
+ export declare function lineText(data: LineData, ctx: TextContext): string;
13
+ export declare function deltaText(data: DeltaData, ctx: TextContext): string;