niceeval 0.6.1 → 0.7.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 (378) hide show
  1. package/INDEX.md +23 -23
  2. package/README.zh.md +6 -6
  3. package/dist/agents/types.d.ts +69 -7
  4. package/dist/context/types.d.ts +32 -12
  5. package/dist/i18n/en.d.ts +54 -0
  6. package/dist/i18n/zh-CN.d.ts +57 -3
  7. package/dist/o11y/types.d.ts +16 -2
  8. package/dist/report/aggregate.d.ts +32 -24
  9. package/dist/report/aggregate.js +158 -50
  10. package/dist/report/built-in/index.d.ts +2 -0
  11. package/dist/report/built-in/index.js +8 -0
  12. package/dist/report/components.d.ts +93 -160
  13. package/dist/report/components.js +377 -114
  14. package/dist/report/compute.d.ts +87 -81
  15. package/dist/report/compute.js +597 -417
  16. package/dist/report/flag.d.ts +32 -6
  17. package/dist/report/flag.js +92 -4
  18. package/dist/report/format.d.ts +19 -11
  19. package/dist/report/format.js +30 -13
  20. package/dist/report/index.d.ts +16 -16
  21. package/dist/report/index.js +20 -21
  22. package/dist/report/load.js +3 -2
  23. package/dist/report/locale.d.ts +57 -33
  24. package/dist/report/locale.js +122 -56
  25. package/dist/report/metrics.d.ts +23 -4
  26. package/dist/report/metrics.js +110 -25
  27. package/dist/report/primitives.d.ts +48 -15
  28. package/dist/report/primitives.js +135 -26
  29. package/dist/report/react/AttemptList.d.ts +9 -7
  30. package/dist/report/react/AttemptList.js +17 -10
  31. package/dist/report/react/DeltaTable.js +19 -18
  32. package/dist/report/react/EvalList.d.ts +4 -4
  33. package/dist/report/react/EvalList.js +0 -0
  34. package/dist/report/react/ExperimentComparison.d.ts +10 -0
  35. package/dist/report/react/ExperimentComparison.js +12 -0
  36. package/dist/report/react/ExperimentList.d.ts +4 -3
  37. package/dist/report/react/ExperimentList.js +17 -18
  38. package/dist/report/react/MetricBars.js +5 -4
  39. package/dist/report/react/MetricLine.js +12 -5
  40. package/dist/report/react/MetricMatrix.js +1 -1
  41. package/dist/report/react/MetricScatter.js +59 -28
  42. package/dist/report/react/MetricTable.js +2 -12
  43. package/dist/report/react/ScopeSummary.d.ts +10 -0
  44. package/dist/report/react/ScopeSummary.js +17 -0
  45. package/dist/report/react/Scoreboard.js +6 -6
  46. package/dist/report/react/cell.js +2 -2
  47. package/dist/report/react/chart-math.d.ts +23 -6
  48. package/dist/report/react/chart-math.js +71 -19
  49. package/dist/report/react/fixtures.d.ts +5 -9
  50. package/dist/report/react/fixtures.js +110 -147
  51. package/dist/report/react/index.d.ts +15 -5
  52. package/dist/report/react/index.js +18 -7
  53. package/dist/report/report.d.ts +137 -16
  54. package/dist/report/report.js +259 -28
  55. package/dist/report/text/faces.d.ts +17 -19
  56. package/dist/report/text/faces.js +253 -184
  57. package/dist/report/text/plot.js +1 -1
  58. package/dist/report/text/table.js +38 -7
  59. package/dist/report/tree.d.ts +90 -40
  60. package/dist/report/tree.js +252 -94
  61. package/dist/report/types.d.ts +247 -284
  62. package/dist/report/types.js +4 -3
  63. package/dist/report/web.d.ts +21 -5
  64. package/dist/report/web.js +42 -16
  65. package/dist/results/select.d.ts +38 -16
  66. package/dist/results/select.js +73 -25
  67. package/dist/results/types.d.ts +49 -14
  68. package/dist/runner/feedback/sink.d.ts +110 -0
  69. package/dist/runner/types.d.ts +513 -22
  70. package/dist/sandbox/docker.d.ts +23 -2
  71. package/dist/sandbox/e2b.d.ts +15 -1
  72. package/dist/sandbox/errors.d.ts +30 -3
  73. package/dist/sandbox/io-retry.d.ts +17 -0
  74. package/dist/sandbox/registry.d.ts +2 -0
  75. package/dist/sandbox/resolve.d.ts +18 -5
  76. package/dist/sandbox/retry.d.ts +11 -1
  77. package/dist/sandbox/types.d.ts +39 -5
  78. package/dist/sandbox/vercel.d.ts +7 -1
  79. package/dist/scoring/coverage.d.ts +30 -0
  80. package/dist/scoring/display.d.ts +21 -0
  81. package/dist/scoring/display.js +120 -0
  82. package/dist/scoring/types.d.ts +103 -20
  83. package/dist/shared/aggregate.d.ts +4 -2
  84. package/dist/shared/aggregate.js +8 -7
  85. package/dist/shared/types.d.ts +28 -0
  86. package/dist/tty-line.d.ts +0 -4
  87. package/dist/util.d.ts +23 -0
  88. package/docs-site/zh/README.md +44 -0
  89. package/docs-site/zh/examples/ai-agent-application.mdx +63 -0
  90. package/docs-site/zh/examples/coding-agent-extensions.mdx +57 -0
  91. package/docs-site/zh/examples/index.mdx +50 -0
  92. package/docs-site/zh/{concepts → explanation}/adapter.mdx +31 -13
  93. package/docs-site/zh/{concepts → explanation}/assert.mdx +7 -7
  94. package/docs-site/zh/{concepts → explanation}/drive.mdx +8 -8
  95. package/docs-site/zh/{concepts → explanation}/evals.mdx +4 -4
  96. package/docs-site/zh/{concepts → explanation}/experiment.mdx +8 -8
  97. package/docs-site/zh/{concepts → explanation}/hitl.mdx +8 -8
  98. package/docs-site/zh/{concepts → explanation}/judge.mdx +5 -5
  99. package/docs-site/zh/{concepts → explanation}/overview.mdx +11 -11
  100. package/docs-site/zh/{guides → explanation}/runner.mdx +18 -8
  101. package/docs-site/zh/{concepts → explanation}/tier.mdx +6 -6
  102. package/docs-site/zh/{guides → how-to}/agent-feedback-loop.mdx +35 -33
  103. package/docs-site/zh/{guides → how-to}/authoring.mdx +35 -2
  104. package/docs-site/zh/{guides → how-to}/ci-integration.mdx +23 -12
  105. package/docs-site/zh/{guides → how-to}/connect-otel.mdx +6 -6
  106. package/docs-site/zh/{guides → how-to}/connect-your-agent.mdx +47 -21
  107. package/docs-site/zh/{guides → how-to}/custom-reports.mdx +34 -39
  108. package/docs-site/zh/{guides → how-to}/dataset-fanout.mdx +25 -3
  109. package/docs-site/zh/{guides → how-to}/experiments.mdx +12 -5
  110. package/docs-site/zh/how-to/publish-report.mdx +105 -0
  111. package/docs-site/zh/{guides → how-to}/reporters.mdx +2 -2
  112. package/docs-site/zh/{guides → how-to}/sandbox-agent.mdx +56 -7
  113. package/docs-site/zh/how-to/sandbox-providers.mdx +350 -0
  114. package/docs-site/zh/{guides → how-to}/scoring-guide.mdx +4 -4
  115. package/docs-site/zh/{guides → how-to}/viewing-results.mdx +82 -39
  116. package/docs-site/zh/{guides → how-to}/write-experiment.mdx +6 -4
  117. package/docs-site/zh/{guides → how-to}/write-send.mdx +30 -14
  118. package/docs-site/zh/index.mdx +24 -26
  119. package/docs-site/zh/introduction.mdx +8 -8
  120. package/docs-site/zh/reference/builtin-agents.mdx +32 -5
  121. package/docs-site/zh/reference/capabilities.mdx +8 -8
  122. package/docs-site/zh/reference/cli.mdx +40 -12
  123. package/docs-site/zh/reference/define-agent.mdx +58 -5
  124. package/docs-site/zh/reference/define-config.mdx +1 -1
  125. package/docs-site/zh/reference/define-eval.mdx +42 -9
  126. package/docs-site/zh/reference/events.mdx +3 -3
  127. package/docs-site/zh/reference/expect.mdx +26 -1
  128. package/docs-site/zh/{guides → reference}/official-adapters.mdx +32 -8
  129. package/docs-site/zh/{guides → reference}/report-components.mdx +45 -33
  130. package/docs-site/zh/{guides → reference}/results-data.mdx +21 -13
  131. package/docs-site/zh/troubleshooting/debug-sandbox.mdx +57 -0
  132. package/docs-site/zh/troubleshooting/debugging.mdx +212 -0
  133. package/docs-site/zh/{quickstart.mdx → tutorials/quickstart.mdx} +5 -17
  134. package/package.json +10 -2
  135. package/src/agents/ai-sdk-otel.test.ts +1 -0
  136. package/src/agents/ai-sdk.test.ts +3 -0
  137. package/src/agents/ai-sdk.ts +3 -0
  138. package/src/agents/bub-install-spec.test.ts +34 -0
  139. package/src/agents/bub-install-spec.ts +32 -0
  140. package/src/agents/bub.ts +31 -32
  141. package/src/agents/claude-code.test.ts +130 -9
  142. package/src/agents/claude-code.ts +76 -4
  143. package/src/agents/codex.test.ts +189 -40
  144. package/src/agents/codex.ts +155 -14
  145. package/src/agents/coding-cli-versions.test.ts +15 -0
  146. package/src/agents/coding-cli-versions.ts +3 -0
  147. package/src/agents/index.ts +13 -2
  148. package/src/agents/langgraph.test.ts +204 -0
  149. package/src/agents/langgraph.ts +495 -0
  150. package/src/agents/marketplace.ts +85 -0
  151. package/src/agents/native-config.test.ts +179 -0
  152. package/src/agents/native-config.ts +267 -0
  153. package/src/agents/openai-compat.test.ts +1 -0
  154. package/src/agents/openai-compat.ts +1 -1
  155. package/src/agents/openclaw.test.ts +31 -0
  156. package/src/agents/openclaw.ts +171 -0
  157. package/src/agents/plugin-config.test.ts +1 -0
  158. package/src/agents/sdk-streams.test.ts +79 -0
  159. package/src/agents/sdk-streams.ts +55 -10
  160. package/src/agents/skills.test.ts +1 -0
  161. package/src/agents/streaming.test.ts +3 -9
  162. package/src/agents/streaming.ts +2 -2
  163. package/src/agents/types.ts +71 -8
  164. package/src/agents/ui-message-stream.test.ts +3 -0
  165. package/src/cli.ts +446 -124
  166. package/src/context/context.test.ts +51 -12
  167. package/src/context/context.ts +162 -30
  168. package/src/context/session.test.ts +2 -1
  169. package/src/context/session.ts +115 -7
  170. package/src/context/types.ts +30 -12
  171. package/src/define.test.ts +13 -8
  172. package/src/define.ts +25 -4
  173. package/src/expect/index.ts +53 -23
  174. package/src/i18n/en.ts +81 -17
  175. package/src/i18n/zh-CN.ts +80 -17
  176. package/src/o11y/cost.test.ts +1 -0
  177. package/src/o11y/execution-tree.test.ts +1 -20
  178. package/src/o11y/otlp/mappers/claude-code.test.ts +1 -0
  179. package/src/o11y/otlp/parse.test.ts +1 -0
  180. package/src/o11y/otlp/turn-otel.test.ts +1 -0
  181. package/src/o11y/parsers/bub.test.ts +1 -0
  182. package/src/o11y/parsers/claude-code.test.ts +1 -34
  183. package/src/o11y/parsers/openclaw.test.ts +154 -0
  184. package/src/o11y/parsers/openclaw.ts +310 -0
  185. package/src/o11y/prices.json +746 -311
  186. package/src/o11y/tool-names.test.ts +1 -0
  187. package/src/o11y/types.ts +16 -2
  188. package/src/report/aggregate.ts +178 -61
  189. package/src/report/built-in/index.tsx +9 -0
  190. package/src/report/components.tsx +625 -279
  191. package/src/report/compute.ts +723 -491
  192. package/src/report/dual-render.test.tsx +741 -1024
  193. package/src/report/flag.ts +104 -12
  194. package/src/report/format.ts +32 -12
  195. package/src/report/index.ts +119 -46
  196. package/src/report/load.ts +3 -2
  197. package/src/report/locale.ts +136 -65
  198. package/src/report/metrics.ts +108 -25
  199. package/src/report/primitives.tsx +196 -45
  200. package/src/report/react/AttemptList.tsx +30 -43
  201. package/src/report/react/DeltaTable.tsx +63 -45
  202. package/src/report/react/EvalList.tsx +0 -0
  203. package/src/report/react/ExperimentComparison.tsx +73 -0
  204. package/src/report/react/ExperimentList.tsx +50 -32
  205. package/src/report/react/MetricBars.tsx +5 -4
  206. package/src/report/react/MetricLine.tsx +13 -8
  207. package/src/report/react/MetricMatrix.tsx +2 -2
  208. package/src/report/react/MetricScatter.tsx +86 -34
  209. package/src/report/react/MetricTable.tsx +4 -76
  210. package/src/report/react/ScopeSummary.tsx +86 -0
  211. package/src/report/react/Scoreboard.tsx +28 -10
  212. package/src/report/react/cell.tsx +2 -2
  213. package/src/report/react/chart-math.test.ts +85 -0
  214. package/src/report/react/chart-math.ts +101 -22
  215. package/src/report/react/enhance.js +89 -5
  216. package/src/report/react/fixtures.ts +114 -154
  217. package/src/report/react/index.tsx +24 -39
  218. package/src/report/react/render.test.tsx +138 -158
  219. package/src/report/react/styles.css +243 -82
  220. package/src/report/report.test.ts +779 -841
  221. package/src/report/report.ts +423 -41
  222. package/src/report/text/faces.ts +290 -193
  223. package/src/report/text/plot.ts +1 -1
  224. package/src/report/text/table.ts +44 -7
  225. package/src/report/tree.ts +362 -104
  226. package/src/report/types.ts +261 -271
  227. package/src/report/web.ts +63 -20
  228. package/src/results/annotated-source.test.ts +62 -9
  229. package/src/results/annotated-source.ts +64 -6
  230. package/src/results/attempt-evidence.test.ts +13 -11
  231. package/src/results/attempt-evidence.ts +20 -13
  232. package/src/results/attempt-source.ts +6 -3
  233. package/src/results/copy.ts +150 -60
  234. package/src/results/host-equivalence.test.ts +34 -20
  235. package/src/results/index.ts +12 -4
  236. package/src/results/locator.test.ts +1 -22
  237. package/src/results/open.ts +15 -5
  238. package/src/results/publish.ts +149 -0
  239. package/src/results/results.test.ts +89 -54
  240. package/src/results/select.ts +104 -34
  241. package/src/results/truncate.ts +90 -0
  242. package/src/results/types.ts +43 -14
  243. package/src/results/writer.ts +31 -13
  244. package/src/runner/attempt.test.ts +138 -7
  245. package/src/runner/attempt.ts +603 -104
  246. package/src/runner/discover.test.ts +47 -0
  247. package/src/runner/discover.ts +36 -2
  248. package/src/runner/eval-source.test.ts +1 -27
  249. package/src/runner/feedback/agent.test.ts +504 -0
  250. package/src/runner/feedback/agent.ts +409 -0
  251. package/src/runner/feedback/ci.test.ts +562 -0
  252. package/src/runner/feedback/ci.ts +401 -0
  253. package/src/runner/feedback/coordinator.test.ts +317 -0
  254. package/src/runner/feedback/coordinator.ts +397 -0
  255. package/src/runner/feedback/failure.ts +40 -0
  256. package/src/runner/feedback/human.test.ts +616 -0
  257. package/src/runner/feedback/human.ts +535 -0
  258. package/src/runner/feedback/index.ts +66 -0
  259. package/src/runner/feedback/io.ts +78 -0
  260. package/src/runner/feedback/profile.test.ts +50 -0
  261. package/src/runner/feedback/profile.ts +58 -0
  262. package/src/runner/feedback/reducer.test.ts +395 -0
  263. package/src/runner/feedback/reducer.ts +260 -0
  264. package/src/runner/feedback/renderer.ts +82 -0
  265. package/src/runner/feedback/sink.ts +203 -0
  266. package/src/runner/feedback/testing.ts +106 -0
  267. package/src/runner/ledger.test.ts +230 -0
  268. package/src/runner/ledger.ts +329 -0
  269. package/src/runner/report.test.ts +128 -3
  270. package/src/runner/report.ts +33 -9
  271. package/src/runner/reporters/artifacts.ts +8 -2
  272. package/src/runner/reporters/braintrust.test.ts +8 -7
  273. package/src/runner/reporters/braintrust.ts +9 -2
  274. package/src/runner/reporters/index.ts +2 -2
  275. package/src/runner/reporters/json.test.ts +162 -0
  276. package/src/runner/reporters/json.ts +35 -8
  277. package/src/runner/reporters/shared.ts +1 -5
  278. package/src/runner/run.test.ts +760 -3
  279. package/src/runner/run.ts +243 -37
  280. package/src/runner/sandbox-prep.ts +3 -42
  281. package/src/runner/timing.ts +158 -0
  282. package/src/runner/types.ts +518 -22
  283. package/src/sandbox/checkpoint.test.ts +55 -0
  284. package/src/sandbox/checkpoint.ts +29 -8
  285. package/src/sandbox/cli-commands.ts +407 -0
  286. package/src/sandbox/docker.ts +115 -16
  287. package/src/sandbox/e2b-agent-template.test.ts +56 -0
  288. package/src/sandbox/e2b-agent-template.ts +94 -0
  289. package/src/sandbox/e2b.ts +74 -9
  290. package/src/sandbox/errors.ts +111 -4
  291. package/src/sandbox/index.ts +2 -0
  292. package/src/sandbox/io-retry.test.ts +58 -0
  293. package/src/sandbox/io-retry.ts +45 -0
  294. package/src/sandbox/keep-registry.test.ts +86 -0
  295. package/src/sandbox/keep-registry.ts +142 -0
  296. package/src/sandbox/keep.ts +178 -0
  297. package/src/sandbox/paths.test.ts +1 -0
  298. package/src/sandbox/paths.ts +19 -8
  299. package/src/sandbox/registry.ts +20 -3
  300. package/src/sandbox/resolve.ts +76 -11
  301. package/src/sandbox/retry.test.ts +70 -0
  302. package/src/sandbox/retry.ts +46 -4
  303. package/src/sandbox/types.ts +44 -6
  304. package/src/sandbox/vercel.ts +43 -20
  305. package/src/scoring/collector.ts +60 -17
  306. package/src/scoring/coverage.ts +95 -0
  307. package/src/scoring/diff.ts +81 -0
  308. package/src/scoring/display.test.ts +121 -0
  309. package/src/scoring/display.ts +133 -0
  310. package/src/scoring/evidence.test.ts +189 -0
  311. package/src/scoring/judge.test.ts +142 -0
  312. package/src/scoring/judge.ts +15 -18
  313. package/src/scoring/scoped.ts +217 -50
  314. package/src/scoring/types.ts +117 -20
  315. package/src/scoring/verdict.ts +16 -4
  316. package/src/shared/aggregate.ts +8 -6
  317. package/src/shared/types.ts +31 -0
  318. package/src/show/compose.ts +50 -67
  319. package/src/show/index.ts +127 -56
  320. package/src/show/render.ts +662 -131
  321. package/src/show/report-host.test.ts +188 -0
  322. package/src/show/report-host.ts +375 -0
  323. package/src/show/show.test.ts +320 -54
  324. package/src/tty-line.ts +8 -26
  325. package/src/util.test.ts +1 -0
  326. package/src/util.ts +41 -0
  327. package/src/view/app/App.test.tsx +69 -0
  328. package/src/view/app/App.tsx +144 -48
  329. package/src/view/app/components/AttemptModal.tsx +423 -11
  330. package/src/view/app/components/CodeView.tsx +41 -14
  331. package/src/view/app/components/CopyControls.tsx +2 -2
  332. package/src/view/app/i18n.ts +37 -17
  333. package/src/view/app/lib/attempt-route.test.ts +1 -0
  334. package/src/view/app/lib/verdict.ts +7 -9
  335. package/src/view/app/main.tsx +13 -8
  336. package/src/view/app/pages/{RunsPage.tsx → AttemptsPage.tsx} +6 -6
  337. package/src/view/app/types.ts +4 -1
  338. package/src/view/artifact-serving.test.ts +2 -1
  339. package/src/view/client-dist/app.css +1 -1
  340. package/src/view/client-dist/app.js +17 -17
  341. package/src/view/data.test.ts +10 -3
  342. package/src/view/data.ts +155 -49
  343. package/src/view/index.ts +56 -41
  344. package/src/view/server.ts +37 -15
  345. package/src/view/shared/types.ts +34 -5
  346. package/src/view/styles.css +227 -0
  347. package/src/view/view-report.test.ts +167 -62
  348. package/dist/report/built-ins/experiment-comparison.d.ts +0 -1
  349. package/dist/report/built-ins/experiment-comparison.js +0 -13
  350. package/dist/report/built-ins/index.d.ts +0 -1
  351. package/dist/report/built-ins/index.js +0 -2
  352. package/dist/report/react/GroupSummary.d.ts +0 -8
  353. package/dist/report/react/GroupSummary.js +0 -8
  354. package/dist/report/react/RunOverview.d.ts +0 -8
  355. package/dist/report/react/RunOverview.js +0 -12
  356. package/docs-site/zh/example/ai-agent-application.mdx +0 -152
  357. package/docs-site/zh/example/claude-code-codex-plugin.mdx +0 -167
  358. package/docs-site/zh/example/claude-code-codex-skill.mdx +0 -152
  359. package/docs-site/zh/example/showcase.mdx +0 -39
  360. package/docs-site/zh/guides/publish-report.mdx +0 -91
  361. package/docs-site/zh/guides/sandbox-providers.mdx +0 -102
  362. package/src/report/built-in-user-parity.test.tsx +0 -640
  363. package/src/report/built-ins/experiment-comparison.tsx +0 -19
  364. package/src/report/built-ins/index.ts +0 -2
  365. package/src/report/react/GroupSummary.tsx +0 -66
  366. package/src/report/react/RunOverview.tsx +0 -109
  367. package/src/runner/reporters/console.ts +0 -70
  368. package/src/runner/reporters/live.test.ts +0 -56
  369. package/src/runner/reporters/live.ts +0 -247
  370. package/src/runner/reporters/quiet.test.ts +0 -66
  371. package/src/runner/reporters/quiet.ts +0 -49
  372. package/src/runner/reporters/table.ts +0 -277
  373. /package/docs-site/zh/{example/tier1-ai-sdk-v7.mdx → examples/integrations/ai-sdk-v7.mdx} +0 -0
  374. /package/docs-site/zh/{example/tier1-claude-sdk.mdx → examples/integrations/claude-sdk.mdx} +0 -0
  375. /package/docs-site/zh/{example/tier1-codex-sdk.mdx → examples/integrations/codex-sdk.mdx} +0 -0
  376. /package/docs-site/zh/{example/tier1-langgraph.mdx → examples/integrations/langgraph.mdx} +0 -0
  377. /package/docs-site/zh/{example/tier1-pi-sdk.mdx → examples/integrations/pi-sdk.mdx} +0 -0
  378. /package/docs-site/zh/{guides → how-to}/fixtures.mdx +0 -0
@@ -1,14 +1,11 @@
1
- import type { AttemptHandle, Selection, SelectionWarning, Snapshot } from "../results/types.ts";
1
+ import type { AttemptHandle, ScopeWarning, Snapshot } from "../results/types.ts";
2
2
  import { type AttemptLocator } from "../results/locator.ts";
3
- import type { Aggregator, DimensionInput, Metric, MetricCell, MetricColumn, FlagRef } from "./types.ts";
3
+ import type { Aggregator, DimensionInput, Metric, MetricCell, MetricColumn, NumericAxis, ReportInput } from "./types.ts";
4
+ import { type LocalizedText } from "./locale.ts";
4
5
  import { evalPrefixPredicate } from "../shared/aggregate.ts";
5
- /** flag 未声明时的组名:不猜,如实归一组。 */
6
- export declare const FLAG_UNSET = "(unset)";
7
- /** 计算函数的第一参:Selection(warnings 随行)或手工挑的快照数组(没有挑选过程,自然无警告)。 */
8
- export type SnapshotsInput = Selection | Snapshot[];
9
- export declare function resolveInput(input: SnapshotsInput): {
10
- snapshots: Snapshot[];
11
- warnings: SelectionWarning[];
6
+ export declare function resolveInput(input: ReportInput): {
7
+ snapshots: readonly Snapshot[];
8
+ warnings: readonly ScopeWarning[];
12
9
  };
13
10
  /** 展平后的一条样本:attempt + 它所属的快照(维度解析与题级折叠都需要快照身份)。 */
14
11
  export interface Item {
@@ -17,13 +14,13 @@ export interface Item {
17
14
  }
18
15
  export declare function experimentIdOf(item: Item): string;
19
16
  export declare function evalIdOf(item: Item): string;
20
- /** 快照键:与 view Compare 同口径的 "<experimentId> @ <startedAt>"。 */
17
+ /** 快照键:"<experimentId> @ <startedAt>"("snapshot" 维度与手挑快照数组的对比用)。 */
21
18
  export declare function snapshotKeyOf(snapshot: Snapshot): string;
19
+ /** 一组 Item 的 eval 全身份键:experimentId + eval id(聚合中的题级身份始终是这一对)。 */
20
+ export declare function fullEvalKey(item: Item): string;
22
21
  /**
23
22
  * 一条 Item 的 AttemptLocator:真实读取路径(openResults() 产出的 handle)恒有
24
- * `attempt.locator`;手工构造的测试 fixture 若省略它,按当前身份元组兜底算一份
25
- * (与 `loadAttemptEvidence`、`open.ts` 的回填同一口径)——只服务这类场景,不改变真实
26
- * 读取路径的行为。
23
+ * `attempt.locator`;手工构造的测试 fixture 若省略它,按当前身份元组兜底算一份。
27
24
  */
28
25
  export declare function locatorOf(item: Item): AttemptLocator;
29
26
  /**
@@ -31,27 +28,38 @@ export declare function locatorOf(item: Item): AttemptLocator;
31
28
  * (experimentId, evalId, attempt, startedAt))。missing-startedAt 的警告不透出:
32
29
  * 官方产出永不缺 startedAt,缺失只可能来自 legacy 落盘,「不去重、如实保留重复」即终稿。
33
30
  */
34
- export declare function collectItems(snapshots: Snapshot[]): Item[];
31
+ export declare function collectItems(snapshots: readonly Snapshot[]): Item[];
35
32
  export { evalPrefixPredicate };
36
- export declare function filterItems(items: Item[], evals?: string | string[]): Item[];
33
+ export declare function filterItems(items: Item[], evals?: string | readonly string[]): Item[];
37
34
  export declare function dimensionName(dimension: DimensionInput): string;
38
- /** eval id 的第一段:"algebra/quadratic" → "algebra";没有 "/" 时就是 id 本身。 */
35
+ /** eval id 的完整父路径:"a/b/c" → "a/b";没有 "/" 时取完整 id(与可比组同一条派生规则)。 */
39
36
  export declare function evalGroupOf(id: string): string;
40
- /** flag 声明值 组标签:label 函数优先,其余 String();未声明 FLAG_UNSET。 */
41
- export declare function flagGroupKey(ref: FlagRef, item: Item): string;
42
- /** flag 作轴:要求数值;未声明或非数值 → null(点不画,注脚报数)。 */
43
- export declare function flagAxisValue(ref: FlagRef, item: Item): number | null;
37
+ /** flag / runConfig 未声明时的显示键:内置文案,en / zh-CN 同形。 */
38
+ export declare const MISSING_GROUP_KEY: string;
39
+ /**
40
+ * flag / runConfig 声明值 分组显示键(稳定 JSON 规则):字符串直接显示,其它值用对象键
41
+ * 递归排序后的 JSON,缺失值显示内置文案 `(missing)`。返回 [显示键, 冲突检测用的规范形]。
42
+ */
43
+ export declare function refDisplayKey(value: unknown): [display: string, canonical: string];
44
44
  export declare function dimensionKey(dimension: DimensionInput, item: Item): string;
45
- /** 按维度分组,保持首次出现顺序(无 sort 时表格行序即此序)。 */
45
+ /**
46
+ * 按维度分组;维度 domain 按稳定 key 字典序(Unicode)排列,不让文件扫描顺序渗进报告。
47
+ * flag / runConfig 维度做显示键冲突检测:不同原始值生成同一显示键时报错并要求改用
48
+ * CustomDimension,绝不静默合组。
49
+ */
46
50
  export declare function groupItems(items: Item[], dimension: DimensionInput): Map<string, Item[]>;
47
- export declare function applyAggregator(aggregator: Aggregator, values: number[]): number;
48
- /** where 不满足 → null,语义等价于 value 开头 return null */
51
+ export declare function applyAggregator(aggregator: Aggregator, values: readonly number[]): number;
52
+ /** where 不满足 → null,语义等价于 value 开头 return null;抛错与非有限数按完整用户反馈失败。 */
49
53
  export declare function evaluateMetric(metric: Metric, attempt: AttemptHandle): Promise<number | null>;
50
- export declare function displayValue(metric: Metric, value: number | null): string;
54
+ /** 单值 LocalizedText display:metric.display 覆盖内置 unit 格式化;null 的兜底归渲染面。 */
55
+ export declare function displayValue(metric: Metric, value: number | null): LocalizedText;
51
56
  /**
52
57
  * 一个格子:组内 attempt → 两级聚合 → 终值。
53
58
  * null 值不进聚合但计入 total(覆盖率经 samples/total 如实暴露);全 null → value null。
59
+ * refs 跟随覆盖范围(含值为 null 的 attempt),去重后按 locator 字典序。
54
60
  */
55
61
  export declare function computeCell(metric: Metric, items: Item[]): Promise<MetricCell>;
56
62
  export declare function toColumn(metric: Metric): MetricColumn;
57
63
  export declare function assertUniqueMetricNames(metrics: readonly Metric[], where: string): void;
64
+ /** NumericAxis 的取值,带完整反馈的非法值检查(NaN / Infinity 一律报错,不静默当缺失)。 */
65
+ export declare function axisValueOf(axis: NumericAxis, attempt: AttemptHandle): number | null;
@@ -1,20 +1,23 @@
1
- // 两级聚合引擎:去重 → 按维度分组 → 组内按 (eval × 快照) 折叠(perEval)→ 跨题折叠(across)→ MetricCell。
1
+ // 两级聚合引擎:去重 → 按维度分组 → 组内按 (experiment × eval) 折叠(perEval)→ 跨题折叠
2
+ // (acrossEvals)→ MetricCell(docs/feature/reports/architecture.md「指标聚合不变量」、
3
+ // docs/feature/reports/library/metrics.md「公开计算模型」)。
2
4
  //
3
5
  // 为什么是两级:earlyExit 默认开,失败的题天然比通过的题样本多;平铺求均值会把分数
4
6
  // 和重试策略纠缠在一起(eval A=[1]、eval B=[0,0,0] 平铺 = 0.25,两级宏平均 = 0.5)。
5
7
  // 自定义维度把同一道题的 attempt 分进不同组时,第一级折叠发生在各组内部。
6
8
  import { dedupeAttempts } from "../results/select.js";
7
9
  import { encodeAttemptLocator } from "../results/locator.js";
8
- import { formatMetricValue } from "./format.js";
10
+ import { flagValueOf, runConfigValueOf } from "./flag.js";
11
+ import { formatMetricValue, localizedDisplay } from "./format.js";
12
+ import { localeText } from "./locale.js";
9
13
  import { evalPrefixPredicate } from "../shared/aggregate.js";
10
14
  // 复合键分隔符:NUL 不会出现在 eval id / experimentId / ISO 时间里,拼接键不会串味
11
15
  const KEY_SEP = "\u0000";
12
- /** flag 未声明时的组名:不猜,如实归一组。 */
13
- export const FLAG_UNSET = "(unset)";
14
16
  export function resolveInput(input) {
15
17
  if (Array.isArray(input))
16
18
  return { snapshots: input, warnings: [] };
17
- return { snapshots: input.snapshots, warnings: input.warnings };
19
+ const scope = input;
20
+ return { snapshots: scope.snapshots, warnings: scope.warnings };
18
21
  }
19
22
  export function experimentIdOf(item) {
20
23
  return item.attempt.experimentId || item.snapshot.experimentId;
@@ -22,15 +25,17 @@ export function experimentIdOf(item) {
22
25
  export function evalIdOf(item) {
23
26
  return item.attempt.evalId || item.attempt.result.id;
24
27
  }
25
- /** 快照键:与 view Compare 同口径的 "<experimentId> @ <startedAt>"。 */
28
+ /** 快照键:"<experimentId> @ <startedAt>"("snapshot" 维度与手挑快照数组的对比用)。 */
26
29
  export function snapshotKeyOf(snapshot) {
27
30
  return `${snapshot.experimentId} @ ${snapshot.startedAt}`;
28
31
  }
32
+ /** 一组 Item 的 eval 全身份键:experimentId + eval id(聚合中的题级身份始终是这一对)。 */
33
+ export function fullEvalKey(item) {
34
+ return `${experimentIdOf(item)}${KEY_SEP}${evalIdOf(item)}`;
35
+ }
29
36
  /**
30
37
  * 一条 Item 的 AttemptLocator:真实读取路径(openResults() 产出的 handle)恒有
31
- * `attempt.locator`;手工构造的测试 fixture 若省略它,按当前身份元组兜底算一份
32
- * (与 `loadAttemptEvidence`、`open.ts` 的回填同一口径)——只服务这类场景,不改变真实
33
- * 读取路径的行为。
38
+ * `attempt.locator`;手工构造的测试 fixture 若省略它,按当前身份元组兜底算一份。
34
39
  */
35
40
  export function locatorOf(item) {
36
41
  return (item.attempt.locator ??
@@ -64,44 +69,55 @@ export { evalPrefixPredicate };
64
69
  export function filterItems(items, evals) {
65
70
  if (evals === undefined)
66
71
  return items;
67
- const match = evalPrefixPredicate(evals);
72
+ const match = evalPrefixPredicate(Array.isArray(evals) ? [...evals] : evals);
68
73
  return items.filter((item) => match(evalIdOf(item)));
69
74
  }
70
75
  // ───────────────────────── 维度 ─────────────────────────
71
76
  export function dimensionName(dimension) {
72
77
  return typeof dimension === "string" ? dimension : dimension.name;
73
78
  }
74
- /** eval id 的第一段:"algebra/quadratic" → "algebra";没有 "/" 时就是 id 本身。 */
79
+ /** eval id 的完整父路径:"a/b/c" → "a/b";没有 "/" 时取完整 id(与可比组同一条派生规则)。 */
75
80
  export function evalGroupOf(id) {
76
- const slash = id.indexOf("/");
81
+ const slash = id.lastIndexOf("/");
77
82
  return slash === -1 ? id : id.slice(0, slash);
78
83
  }
79
- function isFlagRef(dimension) {
80
- return typeof dimension === "object" && "kind" in dimension && dimension.kind === "flag";
84
+ function isDimensionRef(dimension) {
85
+ return (typeof dimension === "object" &&
86
+ "kind" in dimension &&
87
+ (dimension.kind === "flag" || dimension.kind === "runConfig"));
81
88
  }
82
- /** experiment 声明的 flags(经 runner 原样透传进持久化字段 ExperimentRunInfo.flags)。 */
83
- function flagsOf(attempt) {
84
- return attempt.result.experiment?.flags;
89
+ /** flag / runConfig 未声明时的显示键:内置文案,en / zh-CN 同形。 */
90
+ export const MISSING_GROUP_KEY = localeText("en", "cell.missingValue");
91
+ /** 对象键递归排序后的稳定 JSON(分组显示键与冲突检测共用)。 */
92
+ function canonicalJson(value) {
93
+ if (Array.isArray(value))
94
+ return `[${value.map(canonicalJson).join(",")}]`;
95
+ if (typeof value === "object" && value !== null) {
96
+ const keys = Object.keys(value).sort();
97
+ return `{${keys.map((k) => `${JSON.stringify(k)}:${canonicalJson(value[k])}`).join(",")}}`;
98
+ }
99
+ return JSON.stringify(value);
85
100
  }
86
- /** flag 声明值 → 组标签:label 函数优先,其余 String();未声明 → FLAG_UNSET。 */
87
- export function flagGroupKey(ref, item) {
88
- const value = flagsOf(item.attempt)?.[ref.name];
101
+ /**
102
+ * flag / runConfig 声明值 → 分组显示键(稳定 JSON 规则):字符串直接显示,其它值用对象键
103
+ * 递归排序后的 JSON,缺失值显示内置文案 `(missing)`。返回 [显示键, 冲突检测用的规范形]
104
+ */
105
+ export function refDisplayKey(value) {
89
106
  if (value === undefined)
90
- return FLAG_UNSET;
91
- // 持久化字段是 Record<string, unknown>;声明侧的合法值就是这三种标量
92
- if (typeof ref.label === "function")
93
- return ref.label(value);
94
- return String(value);
107
+ return [MISSING_GROUP_KEY, "undefined"];
108
+ if (typeof value === "string")
109
+ return [value, `string:${value}`];
110
+ return [canonicalJson(value), `json:${canonicalJson(value)}`];
95
111
  }
96
- /** flag 作轴:要求数值;未声明或非数值 → null(点不画,注脚报数) */
97
- export function flagAxisValue(ref, item) {
98
- const value = flagsOf(item.attempt)?.[ref.name];
99
- return typeof value === "number" ? value : null;
112
+ function refValueOf(ref, item) {
113
+ return ref.kind === "flag"
114
+ ? flagValueOf(item.attempt, ref.name)
115
+ : runConfigValueOf(item.attempt, ref.name);
100
116
  }
101
117
  export function dimensionKey(dimension, item) {
102
118
  if (typeof dimension !== "string") {
103
- if (isFlagRef(dimension))
104
- return flagGroupKey(dimension, item);
119
+ if (isDimensionRef(dimension))
120
+ return refDisplayKey(refValueOf(dimension, item))[0];
105
121
  return dimension.of(item.attempt);
106
122
  }
107
123
  const result = item.attempt.result;
@@ -125,18 +141,39 @@ export function dimensionKey(dimension, item) {
125
141
  }
126
142
  }
127
143
  }
128
- /** 按维度分组,保持首次出现顺序(无 sort 时表格行序即此序)。 */
144
+ /**
145
+ * 按维度分组;维度 domain 按稳定 key 字典序(Unicode)排列,不让文件扫描顺序渗进报告。
146
+ * flag / runConfig 维度做显示键冲突检测:不同原始值生成同一显示键时报错并要求改用
147
+ * CustomDimension,绝不静默合组。
148
+ */
129
149
  export function groupItems(items, dimension) {
130
150
  const groups = new Map();
151
+ const canonicalByDisplay = new Map();
152
+ const checkConflicts = typeof dimension !== "string" && isDimensionRef(dimension);
131
153
  for (const item of items) {
132
- const key = dimensionKey(dimension, item);
154
+ let key;
155
+ if (checkConflicts) {
156
+ const [display, canonical] = refDisplayKey(refValueOf(dimension, item));
157
+ const existing = canonicalByDisplay.get(display);
158
+ if (existing === undefined)
159
+ canonicalByDisplay.set(display, canonical);
160
+ else if (existing !== canonical) {
161
+ throw new Error(`Dimension "${dimensionName(dimension)}" maps two different raw values to the same display key "${display}" ` +
162
+ `(e.g. the string "5" and the number 5). Grouping them silently would merge distinct configurations; ` +
163
+ `use a CustomDimension ({ name, of(attempt) }) to define an unambiguous key.`);
164
+ }
165
+ key = display;
166
+ }
167
+ else {
168
+ key = dimensionKey(dimension, item);
169
+ }
133
170
  const list = groups.get(key);
134
171
  if (list)
135
172
  list.push(item);
136
173
  else
137
174
  groups.set(key, [item]);
138
175
  }
139
- return groups;
176
+ return new Map([...groups.entries()].sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)));
140
177
  }
141
178
  // ───────────────────────── 聚合 ─────────────────────────
142
179
  export function applyAggregator(aggregator, values) {
@@ -153,52 +190,109 @@ export function applyAggregator(aggregator, values) {
153
190
  return Math.max(...values);
154
191
  }
155
192
  }
156
- /** where 不满足 null,语义等价于 value 开头 return null。 */
193
+ function metricError(metric, step, cause, locator) {
194
+ const at = locator === undefined ? "" : ` at attempt ${locator}`;
195
+ return new Error(`Metric "${metric.name}" ${step} failed${at}: ${cause instanceof Error ? cause.message : String(cause)}. ` +
196
+ "Computation errors are not disguised as missing data — fix the metric (return null for expected gaps), then re-run.", { cause });
197
+ }
198
+ /** where 不满足 → null,语义等价于 value 开头 return null;抛错与非有限数按完整用户反馈失败。 */
157
199
  export async function evaluateMetric(metric, attempt) {
158
- if (metric.where && !metric.where(attempt))
159
- return null;
160
- return metric.value(attempt);
200
+ const locator = attempt.locator;
201
+ if (metric.where) {
202
+ let pass;
203
+ try {
204
+ pass = metric.where(attempt);
205
+ }
206
+ catch (e) {
207
+ throw metricError(metric, "where()", e, locator);
208
+ }
209
+ if (!pass)
210
+ return null;
211
+ }
212
+ let value;
213
+ try {
214
+ value = await metric.value(attempt);
215
+ }
216
+ catch (e) {
217
+ throw metricError(metric, "value()", e, locator);
218
+ }
219
+ if (value !== null && !Number.isFinite(value)) {
220
+ throw metricError(metric, "value()", `returned a non-finite number (${String(value)}); return null for "not measurable"`, locator);
221
+ }
222
+ return value;
161
223
  }
224
+ /** 单值 → LocalizedText display:metric.display 覆盖内置 unit 格式化;null 的兜底归渲染面。 */
162
225
  export function displayValue(metric, value) {
163
226
  // null 的纯文本兜底;组件把 null 渲染成「缺数据」,绝不画 0
164
227
  if (value === null)
165
228
  return "—";
166
- return metric.display ? metric.display(value) : formatMetricValue(value, metric.unit);
229
+ if (metric.display) {
230
+ const display = metric.display;
231
+ return localizedDisplay((locale) => {
232
+ try {
233
+ return display(value, locale);
234
+ }
235
+ catch (e) {
236
+ throw metricError(metric, "display()", e);
237
+ }
238
+ });
239
+ }
240
+ return formatMetricValue(value, metric.unit);
241
+ }
242
+ function foldAggregator(metric, step, values) {
243
+ const aggregator = metric.aggregate?.[step] ?? "mean";
244
+ let folded;
245
+ try {
246
+ folded = applyAggregator(aggregator, values);
247
+ }
248
+ catch (e) {
249
+ throw metricError(metric, `${step} aggregator`, e);
250
+ }
251
+ if (!Number.isFinite(folded)) {
252
+ throw metricError(metric, `${step} aggregator`, `returned a non-finite number (${String(folded)})`);
253
+ }
254
+ return folded;
167
255
  }
168
256
  /**
169
257
  * 一个格子:组内 attempt → 两级聚合 → 终值。
170
258
  * null 值不进聚合但计入 total(覆盖率经 samples/total 如实暴露);全 null → value null。
259
+ * refs 跟随覆盖范围(含值为 null 的 attempt),去重后按 locator 字典序。
171
260
  */
172
261
  export async function computeCell(metric, items) {
173
- // 第一级桶:同一 (eval × 快照) 的 attempt 折成一个题级值
262
+ // 第一级桶:同一 (experiment × eval × 快照) 的 attempt 折成一个题级值
174
263
  const buckets = new Map();
175
- const refs = [];
264
+ const refs = new Set();
176
265
  let samples = 0;
177
266
  for (const item of items) {
267
+ refs.add(locatorOf(item));
178
268
  const value = await evaluateMetric(metric, item.attempt);
179
269
  if (value === null)
180
270
  continue;
181
271
  samples += 1;
182
- refs.push(locatorOf(item)); // 证据引用由句柄直供,不反查下标
183
- const bucketKey = `${evalIdOf(item)}${KEY_SEP}${snapshotKeyOf(item.snapshot)}`;
272
+ const bucketKey = `${fullEvalKey(item)}${KEY_SEP}${snapshotKeyOf(item.snapshot)}`;
184
273
  const bucket = buckets.get(bucketKey);
185
274
  if (bucket)
186
275
  bucket.push(value);
187
276
  else
188
277
  buckets.set(bucketKey, [value]);
189
278
  }
190
- const perEval = metric.aggregate?.perEval ?? "mean";
191
- const across = metric.aggregate?.across ?? "mean";
192
- const evalValues = [...buckets.values()].map((values) => applyAggregator(perEval, values));
193
- const value = evalValues.length === 0 ? null : applyAggregator(across, evalValues);
194
- return { value, display: displayValue(metric, value), samples, total: items.length, refs };
279
+ const evalValues = [...buckets.values()].map((values) => foldAggregator(metric, "perEval", values));
280
+ const value = evalValues.length === 0 ? null : foldAggregator(metric, "acrossEvals", evalValues);
281
+ return {
282
+ value,
283
+ display: displayValue(metric, value),
284
+ samples,
285
+ total: items.length,
286
+ refs: [...refs].sort(),
287
+ };
195
288
  }
196
289
  export function toColumn(metric) {
197
290
  return {
198
291
  key: metric.name,
199
292
  label: metric.label ?? metric.name,
200
- unit: metric.unit,
201
- better: metric.better,
293
+ ...(metric.description !== undefined ? { description: metric.description } : {}),
294
+ ...(metric.unit !== undefined ? { unit: metric.unit } : {}),
295
+ ...(metric.better !== undefined ? { better: metric.better } : {}),
202
296
  };
203
297
  }
204
298
  export function assertUniqueMetricNames(metrics, where) {
@@ -210,3 +304,17 @@ export function assertUniqueMetricNames(metrics, where) {
210
304
  seen.add(metric.name);
211
305
  }
212
306
  }
307
+ /** NumericAxis 的取值,带完整反馈的非法值检查(NaN / Infinity 一律报错,不静默当缺失)。 */
308
+ export function axisValueOf(axis, attempt) {
309
+ let value;
310
+ try {
311
+ value = axis.of(attempt);
312
+ }
313
+ catch (e) {
314
+ throw new Error(`Numeric axis "${axis.name}" of() failed${attempt.locator ? ` at attempt ${attempt.locator}` : ""}: ${e instanceof Error ? e.message : String(e)}`, { cause: e });
315
+ }
316
+ if (value !== null && !Number.isFinite(value)) {
317
+ throw new Error(`Numeric axis "${axis.name}" of() returned a non-finite number (${String(value)}); return null for attempts without a plottable x value.`);
318
+ }
319
+ return value;
320
+ }
@@ -0,0 +1,2 @@
1
+ declare const _default: import("../report.ts").ReportDefinition;
2
+ export default _default;
@@ -0,0 +1,8 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // niceeval/report/built-in —— 包里自带的一个报告文件,没有任何私有钩子
3
+ // (docs/feature/reports/library/built-in.md)。裸 `niceeval show` 与 `niceeval view`
4
+ // 装载的就是这份默认导出:一份普通 defineReport,与用户的 --report 文件同层、
5
+ // 走同一条 装载 → resolve → validate → render 管线。「builtin」不是装载逻辑里的类别,
6
+ // 只是宿主默认拿哪个值的事实。
7
+ import { ExperimentComparison, defineReport } from "../index.js";
8
+ export default defineReport(_jsx(ExperimentComparison, {}));