niceeval 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (428) hide show
  1. package/INDEX.md +31 -33
  2. package/dist/agents/types.d.ts +20 -17
  3. package/dist/context/types.d.ts +21 -21
  4. package/dist/i18n/en.d.ts +10 -2
  5. package/dist/i18n/zh-CN.d.ts +15 -7
  6. package/dist/o11y/execution-tree.d.ts +103 -0
  7. package/dist/o11y/otlp/select.d.ts +22 -0
  8. package/dist/report/{react → assets}/colors.d.ts +8 -0
  9. package/dist/report/{react → assets}/colors.js +23 -0
  10. package/dist/report/built-in/index.d.ts +2 -2
  11. package/dist/report/built-in/index.js +2 -2
  12. package/dist/report/built-in/standard.d.ts +8 -1
  13. package/dist/report/built-in/standard.js +17 -7
  14. package/dist/report/components/attempt-detail/AttemptAssertions.d.ts +6 -0
  15. package/dist/report/components/attempt-detail/AttemptAssertions.js +17 -0
  16. package/dist/report/components/attempt-detail/AttemptConversation.d.ts +6 -0
  17. package/dist/report/components/attempt-detail/AttemptConversation.js +34 -0
  18. package/dist/report/components/attempt-detail/AttemptDiagnostics.d.ts +6 -0
  19. package/dist/report/components/attempt-detail/AttemptDiagnostics.js +7 -0
  20. package/dist/report/components/attempt-detail/AttemptDiff.d.ts +6 -0
  21. package/dist/report/components/attempt-detail/AttemptDiff.js +11 -0
  22. package/dist/report/components/attempt-detail/AttemptError.d.ts +6 -0
  23. package/dist/report/components/attempt-detail/AttemptError.js +16 -0
  24. package/dist/report/components/attempt-detail/AttemptFixPrompt.d.ts +6 -0
  25. package/dist/report/components/attempt-detail/AttemptFixPrompt.js +7 -0
  26. package/dist/report/components/attempt-detail/AttemptSource.d.ts +6 -0
  27. package/dist/report/components/attempt-detail/AttemptSource.js +27 -0
  28. package/dist/report/components/attempt-detail/AttemptSummary.d.ts +8 -0
  29. package/dist/report/components/attempt-detail/AttemptSummary.js +20 -0
  30. package/dist/report/components/attempt-detail/AttemptTimeline.d.ts +6 -0
  31. package/dist/report/components/attempt-detail/AttemptTimeline.js +28 -0
  32. package/dist/report/components/attempt-detail/AttemptTrace.d.ts +6 -0
  33. package/dist/report/components/attempt-detail/AttemptTrace.js +29 -0
  34. package/dist/report/components/attempt-detail/AttemptUsage.d.ts +6 -0
  35. package/dist/report/components/attempt-detail/AttemptUsage.js +20 -0
  36. package/dist/report/components/attempt-detail/compute.d.ts +24 -0
  37. package/dist/report/components/attempt-detail/compute.js +254 -0
  38. package/dist/report/components/attempt-detail/faces.d.ts +14 -0
  39. package/dist/report/components/attempt-detail/faces.js +235 -0
  40. package/dist/report/components/attempt-detail/index.d.ts +38 -0
  41. package/dist/report/components/attempt-detail/index.js +527 -0
  42. package/dist/report/{react → components}/cell.d.ts +5 -3
  43. package/dist/report/{react → components}/cell.js +3 -3
  44. package/dist/report/{react → components/entity-lists}/AttemptList.d.ts +8 -4
  45. package/dist/report/{react → components/entity-lists}/AttemptList.js +16 -10
  46. package/dist/report/{react → components/entity-lists}/EvalList.d.ts +3 -3
  47. package/dist/report/{react → components/entity-lists}/EvalList.js +0 -0
  48. package/dist/report/{react → components/entity-lists}/ExperimentList.d.ts +3 -3
  49. package/dist/report/{react → components/entity-lists}/ExperimentList.js +10 -10
  50. package/dist/report/components/entity-lists/compute.d.ts +23 -0
  51. package/dist/report/components/entity-lists/compute.js +171 -0
  52. package/dist/report/components/entity-lists/faces.d.ts +5 -0
  53. package/dist/report/components/entity-lists/faces.js +174 -0
  54. package/dist/report/components/entity-lists/index.d.ts +51 -0
  55. package/dist/report/components/entity-lists/index.js +165 -0
  56. package/dist/report/{react → components}/fixtures.d.ts +2 -2
  57. package/dist/report/{react → components}/fixtures.js +1 -1
  58. package/dist/report/{react → components/metric-views}/DeltaTable.d.ts +2 -2
  59. package/dist/report/{react → components/metric-views}/DeltaTable.js +4 -4
  60. package/dist/report/{react → components/metric-views}/MetricBars.d.ts +3 -3
  61. package/dist/report/{react → components/metric-views}/MetricBars.js +3 -3
  62. package/dist/report/{react → components/metric-views}/MetricLine.d.ts +2 -2
  63. package/dist/report/{react → components/metric-views}/MetricLine.js +8 -5
  64. package/dist/report/{react → components/metric-views}/MetricMatrix.d.ts +3 -3
  65. package/dist/report/{react → components/metric-views}/MetricMatrix.js +4 -4
  66. package/dist/report/components/metric-views/MetricScatter.d.ts +11 -0
  67. package/dist/report/{react → components/metric-views}/MetricScatter.js +26 -23
  68. package/dist/report/{react → components/metric-views}/MetricTable.d.ts +3 -3
  69. package/dist/report/{react → components/metric-views}/MetricTable.js +4 -4
  70. package/dist/report/{react → components/metric-views}/Scoreboard.d.ts +2 -2
  71. package/dist/report/{react → components/metric-views}/Scoreboard.js +3 -3
  72. package/dist/report/components/metric-views/compute.d.ts +89 -0
  73. package/dist/report/{compute.js → components/metric-views/compute.js} +32 -415
  74. package/dist/report/components/metric-views/faces.d.ts +13 -0
  75. package/dist/report/components/metric-views/faces.js +381 -0
  76. package/dist/report/components/metric-views/index.d.ts +50 -0
  77. package/dist/report/components/metric-views/index.js +273 -0
  78. package/dist/report/{text → components/metric-views}/plot.js +1 -1
  79. package/dist/report/components/shared-compute.d.ts +22 -0
  80. package/dist/report/components/shared-compute.js +47 -0
  81. package/dist/report/components/shared-faces.d.ts +9 -0
  82. package/dist/report/components/shared-faces.js +26 -0
  83. package/dist/report/components/shared.d.ts +68 -0
  84. package/dist/report/components/shared.js +125 -0
  85. package/dist/report/{react → components/site-components}/CopyFixPrompt.d.ts +2 -2
  86. package/dist/report/{react → components/site-components}/CopyFixPrompt.js +2 -2
  87. package/dist/report/{react → components/site-components}/HeroCard.d.ts +2 -2
  88. package/dist/report/{react → components/site-components}/HeroCard.js +2 -2
  89. package/dist/report/{react → components/site-components}/ScopeWarnings.d.ts +2 -2
  90. package/dist/report/{react → components/site-components}/ScopeWarnings.js +3 -3
  91. package/dist/report/{react → components/site-components}/TraceWaterfall.d.ts +3 -3
  92. package/dist/report/{react → components/site-components}/TraceWaterfall.js +5 -5
  93. package/dist/report/components/site-components/compute.d.ts +28 -0
  94. package/dist/report/components/site-components/compute.js +132 -0
  95. package/dist/report/components/site-components/faces.d.ts +21 -0
  96. package/dist/report/components/site-components/faces.js +75 -0
  97. package/dist/report/components/site-components/index.d.ts +74 -0
  98. package/dist/report/components/site-components/index.js +220 -0
  99. package/dist/report/{scope-warnings.d.ts → components/site-components/scope-warnings.d.ts} +2 -2
  100. package/dist/report/{scope-warnings.js → components/site-components/scope-warnings.js} +2 -2
  101. package/dist/report/{react → components/summaries}/ScopeSummary.d.ts +2 -2
  102. package/dist/report/{react → components/summaries}/ScopeSummary.js +17 -8
  103. package/dist/report/components/summaries/compute.d.ts +7 -0
  104. package/dist/report/components/summaries/compute.js +51 -0
  105. package/dist/report/components/summaries/faces.d.ts +7 -0
  106. package/dist/report/components/summaries/faces.js +38 -0
  107. package/dist/report/components/summaries/index.d.ts +27 -0
  108. package/dist/report/components/summaries/index.js +78 -0
  109. package/dist/report/definition/grid-layout.d.ts +50 -0
  110. package/dist/report/definition/grid-layout.js +89 -0
  111. package/dist/report/{primitives.d.ts → definition/primitives.d.ts} +34 -4
  112. package/dist/report/{primitives.js → definition/primitives.js} +101 -10
  113. package/dist/report/{report.d.ts → definition/report.d.ts} +36 -65
  114. package/dist/report/{report.js → definition/report.js} +58 -90
  115. package/dist/report/{text/table.d.ts → definition/table-text.d.ts} +2 -2
  116. package/dist/report/{text/table.js → definition/table-text.js} +2 -2
  117. package/dist/report/{tree.d.ts → definition/tree.d.ts} +44 -12
  118. package/dist/report/{tree.js → definition/tree.js} +10 -12
  119. package/dist/report/index.d.ts +33 -18
  120. package/dist/report/index.js +23 -14
  121. package/dist/report/{aggregate.d.ts → model/aggregate.d.ts} +12 -4
  122. package/dist/report/{aggregate.js → model/aggregate.js} +28 -8
  123. package/dist/report/{flag.d.ts → model/flag.d.ts} +14 -1
  124. package/dist/report/{flag.js → model/flag.js} +35 -0
  125. package/dist/report/{format.d.ts → model/format.d.ts} +11 -5
  126. package/dist/report/{format.js → model/format.js} +48 -4
  127. package/dist/report/{locale.d.ts → model/locale.d.ts} +17 -17
  128. package/dist/report/{locale.js → model/locale.js} +31 -33
  129. package/dist/report/{metrics.d.ts → model/metrics.d.ts} +2 -2
  130. package/dist/report/{metrics.js → model/metrics.js} +2 -2
  131. package/dist/report/{types.d.ts → model/types.d.ts} +140 -20
  132. package/dist/report/react/index.d.ts +32 -21
  133. package/dist/report/react/index.js +33 -21
  134. package/dist/report/{load.d.ts → runtime/load.d.ts} +1 -1
  135. package/dist/report/{load.js → runtime/load.js} +1 -1
  136. package/dist/report/runtime/text.d.ts +67 -0
  137. package/dist/report/runtime/text.js +114 -0
  138. package/dist/report/runtime/web.d.ts +35 -0
  139. package/dist/report/runtime/web.js +70 -0
  140. package/dist/results/annotated-source.d.ts +87 -0
  141. package/dist/results/attempt-evidence.d.ts +74 -0
  142. package/dist/results/attempt-source.d.ts +15 -0
  143. package/dist/results/select.d.ts +12 -2
  144. package/dist/results/select.js +27 -5
  145. package/dist/runner/eval-selection.d.ts +26 -0
  146. package/dist/runner/feedback/sink.d.ts +26 -1
  147. package/dist/runner/types.d.ts +149 -23
  148. package/dist/sandbox/docker.d.ts +6 -0
  149. package/dist/sandbox/types.d.ts +23 -21
  150. package/dist/scoring/display.d.ts +7 -2
  151. package/dist/scoring/display.js +42 -4
  152. package/dist/scoring/types.d.ts +3 -3
  153. package/dist/shared/aggregate.d.ts +15 -1
  154. package/dist/shared/aggregate.js +32 -1
  155. package/dist/shared/types.d.ts +6 -1
  156. package/docs-site/images/logo-dark.svg +7 -0
  157. package/docs-site/images/logo.svg +6 -5
  158. package/docs-site/zh/README.md +8 -7
  159. package/docs-site/zh/examples/ai-agent-application.mdx +5 -5
  160. package/docs-site/zh/examples/coding-agent-extensions.mdx +4 -4
  161. package/docs-site/zh/examples/index.mdx +3 -3
  162. package/docs-site/zh/examples/integrations/ai-sdk-v7.mdx +1 -1
  163. package/docs-site/zh/examples/integrations/claude-sdk.mdx +3 -3
  164. package/docs-site/zh/examples/integrations/codex-sdk.mdx +3 -3
  165. package/docs-site/zh/examples/integrations/langgraph.mdx +3 -3
  166. package/docs-site/zh/examples/integrations/pi-sdk.mdx +2 -2
  167. package/docs-site/zh/explanation/adapter.mdx +19 -19
  168. package/docs-site/zh/explanation/assert.mdx +9 -9
  169. package/docs-site/zh/explanation/drive.mdx +4 -4
  170. package/docs-site/zh/explanation/evals.mdx +8 -8
  171. package/docs-site/zh/explanation/experiment.mdx +8 -8
  172. package/docs-site/zh/explanation/hitl.mdx +13 -13
  173. package/docs-site/zh/explanation/judge.mdx +3 -3
  174. package/docs-site/zh/explanation/overview.mdx +7 -7
  175. package/docs-site/zh/explanation/runner.mdx +11 -11
  176. package/docs-site/zh/explanation/tier.mdx +6 -6
  177. package/docs-site/zh/index.mdx +14 -14
  178. package/docs-site/zh/introduction.mdx +13 -16
  179. package/docs-site/zh/reference/builtin-agents.mdx +61 -28
  180. package/docs-site/zh/reference/capabilities.mdx +7 -27
  181. package/docs-site/zh/reference/cli.mdx +35 -32
  182. package/docs-site/zh/reference/define-agent.mdx +32 -31
  183. package/docs-site/zh/reference/define-config.mdx +6 -6
  184. package/docs-site/zh/reference/define-eval.mdx +33 -20
  185. package/docs-site/zh/reference/events.mdx +4 -4
  186. package/docs-site/zh/reference/expect.mdx +3 -3
  187. package/docs-site/zh/reference/official-adapters.mdx +17 -17
  188. package/docs-site/zh/reference/report-components.mdx +193 -121
  189. package/docs-site/zh/reference/results-data.mdx +13 -13
  190. package/docs-site/zh/troubleshooting/debug-sandbox.mdx +11 -11
  191. package/docs-site/zh/troubleshooting/debugging.mdx +43 -19
  192. package/docs-site/zh/{how-to → tutorials}/agent-feedback-loop.mdx +38 -38
  193. package/docs-site/zh/tutorials/agent-onboarding.mdx +100 -0
  194. package/docs-site/zh/tutorials/authoring.mdx +211 -0
  195. package/docs-site/zh/{how-to → tutorials}/ci-integration.mdx +8 -8
  196. package/docs-site/zh/{how-to → tutorials}/connect-otel.mdx +22 -22
  197. package/docs-site/zh/{how-to → tutorials}/connect-your-agent.mdx +58 -63
  198. package/docs-site/zh/tutorials/custom-reports.mdx +453 -0
  199. package/docs-site/zh/{how-to → tutorials}/dataset-fanout.mdx +4 -4
  200. package/docs-site/zh/tutorials/experiments.mdx +103 -0
  201. package/docs-site/zh/{how-to → tutorials}/fixtures.mdx +9 -9
  202. package/docs-site/zh/{how-to → tutorials}/publish-report.mdx +11 -5
  203. package/docs-site/zh/tutorials/quickstart.mdx +18 -18
  204. package/docs-site/zh/{how-to → tutorials}/reporters.mdx +6 -6
  205. package/docs-site/zh/{how-to → tutorials}/sandbox-agent.mdx +6 -6
  206. package/docs-site/zh/{how-to → tutorials}/sandbox-providers.mdx +41 -19
  207. package/docs-site/zh/{how-to → tutorials}/scoring-guide.mdx +4 -4
  208. package/docs-site/zh/{how-to → tutorials}/viewing-results.mdx +59 -50
  209. package/docs-site/zh/tutorials/write-experiment.mdx +355 -0
  210. package/docs-site/zh/{how-to → tutorials}/write-send.mdx +40 -40
  211. package/package.json +8 -4
  212. package/src/agents/bub.ts +21 -8
  213. package/src/agents/claude-code.ts +23 -10
  214. package/src/agents/codex.test.ts +22 -7
  215. package/src/agents/codex.ts +22 -9
  216. package/src/agents/index.ts +2 -2
  217. package/src/agents/openai-compat.ts +1 -1
  218. package/src/agents/post-setup.ts +45 -22
  219. package/src/agents/streaming.ts +1 -1
  220. package/src/agents/types.ts +20 -17
  221. package/src/agents/ui-message-stream.test.ts +10 -0
  222. package/src/agents/ui-message-stream.ts +12 -1
  223. package/src/cli.ts +77 -64
  224. package/src/context/types.ts +21 -21
  225. package/src/define.ts +13 -0
  226. package/src/i18n/en.ts +30 -15
  227. package/src/i18n/zh-CN.ts +30 -15
  228. package/src/index.ts +2 -0
  229. package/src/report/{react → assets}/colors.ts +22 -0
  230. package/src/report/{react → assets}/enhance.js +1 -33
  231. package/src/report/{react → assets}/styles.css +136 -112
  232. package/src/report/built-in/index.tsx +2 -2
  233. package/src/report/built-in/standard.tsx +18 -6
  234. package/src/report/components/attempt-detail/AttemptAssertions.tsx +67 -0
  235. package/src/report/components/attempt-detail/AttemptConversation.tsx +110 -0
  236. package/src/report/components/attempt-detail/AttemptDiagnostics.tsx +38 -0
  237. package/src/report/components/attempt-detail/AttemptDiff.tsx +35 -0
  238. package/src/report/components/attempt-detail/AttemptError.tsx +31 -0
  239. package/src/report/components/attempt-detail/AttemptFixPrompt.tsx +26 -0
  240. package/src/report/components/attempt-detail/AttemptSource.tsx +80 -0
  241. package/src/report/components/attempt-detail/AttemptSummary.tsx +74 -0
  242. package/src/report/components/attempt-detail/AttemptTimeline.tsx +108 -0
  243. package/src/report/components/attempt-detail/AttemptTrace.tsx +63 -0
  244. package/src/report/components/attempt-detail/AttemptUsage.tsx +29 -0
  245. package/src/report/components/attempt-detail/attempt-components.test.tsx +680 -0
  246. package/src/report/components/attempt-detail/compute.ts +287 -0
  247. package/src/report/components/attempt-detail/faces.ts +257 -0
  248. package/src/report/components/attempt-detail/index.tsx +583 -0
  249. package/src/report/components/attempt-detail/validate.test.ts +235 -0
  250. package/src/report/{react → components}/cell.tsx +6 -3
  251. package/src/report/{report.test.ts → components/compute.test.ts} +316 -46
  252. package/src/report/{react → components/entity-lists}/AttemptList.tsx +24 -16
  253. package/src/report/{react → components/entity-lists}/EvalList.tsx +0 -0
  254. package/src/report/{react → components/entity-lists}/ExperimentList.tsx +23 -20
  255. package/src/report/components/entity-lists/compute.ts +196 -0
  256. package/src/report/components/entity-lists/faces.ts +207 -0
  257. package/src/report/components/entity-lists/index.tsx +241 -0
  258. package/src/report/components/entity-lists/validate.test.ts +114 -0
  259. package/src/report/{react → components}/fixtures.ts +2 -2
  260. package/src/report/{react → components/metric-views}/DeltaTable.tsx +5 -5
  261. package/src/report/{react → components/metric-views}/MetricBars.tsx +5 -5
  262. package/src/report/{react → components/metric-views}/MetricLine.tsx +9 -6
  263. package/src/report/{react → components/metric-views}/MetricMatrix.tsx +6 -6
  264. package/src/report/{react → components/metric-views}/MetricScatter.tsx +41 -33
  265. package/src/report/{react → components/metric-views}/MetricTable.tsx +6 -6
  266. package/src/report/{react → components/metric-views}/Scoreboard.tsx +4 -4
  267. package/src/report/{compute.ts → components/metric-views/compute.ts} +51 -477
  268. package/src/report/components/metric-views/faces.ts +423 -0
  269. package/src/report/components/metric-views/index.tsx +352 -0
  270. package/src/report/{text → components/metric-views}/plot.ts +1 -1
  271. package/src/report/components/metric-views/validate.test.ts +211 -0
  272. package/src/report/{react → components}/render.test.tsx +47 -17
  273. package/src/report/components/shared-compute.ts +59 -0
  274. package/src/report/components/shared-faces.ts +30 -0
  275. package/src/report/components/shared.ts +192 -0
  276. package/src/report/{react → components/site-components}/CopyFixPrompt.tsx +3 -3
  277. package/src/report/{react → components/site-components}/HeroCard.tsx +3 -3
  278. package/src/report/{react → components/site-components}/ScopeWarnings.tsx +4 -4
  279. package/src/report/{react → components/site-components}/TraceWaterfall.tsx +13 -10
  280. package/src/report/components/site-components/compute.ts +144 -0
  281. package/src/report/components/site-components/faces.ts +80 -0
  282. package/src/report/components/site-components/index.tsx +265 -0
  283. package/src/report/{scope-warnings.ts → components/site-components/scope-warnings.ts} +3 -3
  284. package/src/report/{site-components.test.tsx → components/site-components/site-components.test.tsx} +23 -21
  285. package/src/report/components/site-components/validate.test.ts +89 -0
  286. package/src/report/{react → components/summaries}/ScopeSummary.tsx +26 -11
  287. package/src/report/components/summaries/compute.ts +56 -0
  288. package/src/report/components/summaries/faces.ts +48 -0
  289. package/src/report/components/summaries/index.tsx +127 -0
  290. package/src/report/components/summaries/validate.test.ts +49 -0
  291. package/src/report/definition/grid-layout.test.ts +124 -0
  292. package/src/report/definition/grid-layout.ts +146 -0
  293. package/src/report/{primitives.tsx → definition/primitives.tsx} +168 -18
  294. package/src/report/{report.ts → definition/report.ts} +98 -165
  295. package/src/report/{shell-head.test.ts → definition/shell-head.test.ts} +2 -2
  296. package/src/report/{text/table.ts → definition/table-text.ts} +4 -4
  297. package/src/report/{tree.ts → definition/tree.ts} +58 -22
  298. package/src/report/index.ts +126 -66
  299. package/src/report/{aggregate.ts → model/aggregate.ts} +31 -9
  300. package/src/report/{flag.ts → model/flag.ts} +39 -1
  301. package/src/report/{format.ts → model/format.ts} +59 -5
  302. package/src/report/{locale.ts → model/locale.ts} +34 -36
  303. package/src/report/{metrics.ts → model/metrics.ts} +3 -3
  304. package/src/report/{types.ts → model/types.ts} +143 -22
  305. package/src/report/react/index.tsx +52 -26
  306. package/src/report/{dual-render.test.tsx → runtime/dual-render.test.tsx} +564 -49
  307. package/src/report/runtime/host.test.ts +44 -0
  308. package/src/report/runtime/host.ts +138 -0
  309. package/src/report/{load.ts → runtime/load.ts} +1 -1
  310. package/src/report/runtime/text.ts +192 -0
  311. package/src/report/runtime/web.ts +106 -0
  312. package/src/results/attempt-evidence.ts +5 -1
  313. package/src/results/host-equivalence.test.ts +54 -3
  314. package/src/results/select.ts +30 -6
  315. package/src/runner/attempt.test.ts +48 -1
  316. package/src/runner/attempt.ts +62 -42
  317. package/src/runner/cleanup-timeout.test.ts +16 -0
  318. package/src/runner/cleanup-timeout.ts +23 -0
  319. package/src/runner/discover.ts +1 -2
  320. package/src/runner/eval-selection.test.ts +187 -0
  321. package/src/runner/eval-selection.ts +109 -0
  322. package/src/runner/experiment-cleanup-registry.test.ts +89 -0
  323. package/src/runner/experiment-cleanup-registry.ts +39 -0
  324. package/src/runner/experiment-labels.test.ts +71 -0
  325. package/src/runner/feedback/agent.test.ts +33 -1
  326. package/src/runner/feedback/agent.ts +12 -16
  327. package/src/runner/feedback/ci.test.ts +35 -1
  328. package/src/runner/feedback/ci.ts +16 -18
  329. package/src/runner/feedback/coordinator.ts +28 -0
  330. package/src/runner/feedback/human.test.ts +108 -1
  331. package/src/runner/feedback/human.ts +71 -21
  332. package/src/runner/feedback/reducer.test.ts +64 -0
  333. package/src/runner/feedback/reducer.ts +26 -0
  334. package/src/runner/feedback/sink.ts +40 -1
  335. package/src/runner/fingerprint.ts +2 -1
  336. package/src/runner/report.test.ts +14 -0
  337. package/src/runner/report.ts +3 -1
  338. package/src/runner/run.test.ts +390 -12
  339. package/src/runner/run.ts +259 -26
  340. package/src/runner/sandbox-selection.test.ts +13 -3
  341. package/src/runner/sandbox-selection.ts +4 -2
  342. package/src/runner/types.ts +158 -23
  343. package/src/sandbox/docker.ts +7 -0
  344. package/src/sandbox/e2b.ts +12 -13
  345. package/src/sandbox/types.ts +23 -21
  346. package/src/scoring/display.test.ts +28 -2
  347. package/src/scoring/display.ts +38 -5
  348. package/src/scoring/types.ts +3 -3
  349. package/src/shared/aggregate.test.ts +52 -0
  350. package/src/shared/aggregate.ts +30 -1
  351. package/src/shared/types.ts +6 -1
  352. package/src/show/command.test.ts +34 -0
  353. package/src/show/command.ts +16 -0
  354. package/src/show/compose.ts +1 -1
  355. package/src/show/index.ts +58 -22
  356. package/src/show/render.ts +16 -240
  357. package/src/show/show.test.ts +128 -35
  358. package/src/view/app/App.test.tsx +8 -7
  359. package/src/view/app/App.tsx +135 -51
  360. package/src/view/app/i18n.ts +7 -147
  361. package/src/view/app/lib/attempt-dialog.test.ts +65 -0
  362. package/src/view/app/lib/attempt-dialog.ts +69 -0
  363. package/src/view/app/main.tsx +0 -1
  364. package/src/view/app/types.ts +3 -86
  365. package/src/view/artifact-serving.test.ts +22 -38
  366. package/src/view/client-dist/app.css +1 -1
  367. package/src/view/client-dist/app.js +15 -22
  368. package/src/view/data.test.ts +48 -36
  369. package/src/view/data.ts +122 -89
  370. package/src/view/index.ts +3 -13
  371. package/src/view/server.ts +36 -6
  372. package/src/view/shared/types.ts +6 -31
  373. package/src/view/site-parity.test.ts +24 -1
  374. package/src/view/site.ts +144 -17
  375. package/src/view/styles.css +18 -806
  376. package/src/view/view-report.test.ts +182 -34
  377. package/dist/report/components.d.ts +0 -179
  378. package/dist/report/components.js +0 -544
  379. package/dist/report/compute.d.ts +0 -139
  380. package/dist/report/react/ExperimentComparison.d.ts +0 -10
  381. package/dist/report/react/ExperimentComparison.js +0 -12
  382. package/dist/report/react/MetricScatter.d.ts +0 -9
  383. package/dist/report/react/format.d.ts +0 -3
  384. package/dist/report/react/format.js +0 -7
  385. package/dist/report/text/faces.d.ts +0 -45
  386. package/dist/report/text/faces.js +0 -671
  387. package/dist/report/web.d.ts +0 -32
  388. package/dist/report/web.js +0 -48
  389. package/docs-site/zh/how-to/authoring.mdx +0 -162
  390. package/docs-site/zh/how-to/custom-reports.mdx +0 -414
  391. package/docs-site/zh/how-to/experiments.mdx +0 -86
  392. package/docs-site/zh/how-to/write-experiment.mdx +0 -164
  393. package/src/report/components.tsx +0 -925
  394. package/src/report/react/ExperimentComparison.tsx +0 -73
  395. package/src/report/react/format.ts +0 -9
  396. package/src/report/text/faces.ts +0 -767
  397. package/src/report/web.ts +0 -77
  398. package/src/show/report-host.test.ts +0 -205
  399. package/src/show/report-host.ts +0 -389
  400. package/src/view/app/components/AttemptModal.tsx +0 -496
  401. package/src/view/app/components/CodeView.test.tsx +0 -142
  402. package/src/view/app/components/CodeView.tsx +0 -310
  403. package/src/view/app/components/CopyControls.tsx +0 -106
  404. package/src/view/app/components/Trace.tsx +0 -100
  405. package/src/view/app/components/Transcript.tsx +0 -157
  406. package/src/view/app/components/ui/badge.tsx +0 -21
  407. package/src/view/app/lib/artifact-url.ts +0 -17
  408. package/src/view/app/lib/attempt-route.test.ts +0 -80
  409. package/src/view/app/lib/attempt-route.ts +0 -52
  410. package/src/view/app/lib/format.ts +0 -70
  411. package/src/view/app/lib/guards.test.ts +0 -108
  412. package/src/view/app/lib/guards.ts +0 -71
  413. package/src/view/app/lib/rows.ts +0 -22
  414. package/src/view/app/lib/transcript-data.tsx +0 -151
  415. package/src/view/app/lib/verdict.ts +0 -23
  416. package/src/view/app/shared.ts +0 -8
  417. /package/dist/report/{react → components/metric-views}/chart-math.d.ts +0 -0
  418. /package/dist/report/{react → components/metric-views}/chart-math.js +0 -0
  419. /package/dist/report/{text → components/metric-views}/plot.d.ts +0 -0
  420. /package/dist/report/{react → components/site-components}/PoweredBy.d.ts +0 -0
  421. /package/dist/report/{react → components/site-components}/PoweredBy.js +0 -0
  422. /package/dist/report/{text/layout.d.ts → model/text-layout.d.ts} +0 -0
  423. /package/dist/report/{text/layout.js → model/text-layout.js} +0 -0
  424. /package/dist/report/{types.js → model/types.js} +0 -0
  425. /package/src/report/{react → components/metric-views}/chart-math.test.ts +0 -0
  426. /package/src/report/{react → components/metric-views}/chart-math.ts +0 -0
  427. /package/src/report/{react → components/site-components}/PoweredBy.tsx +0 -0
  428. /package/src/report/{text/layout.ts → model/text-layout.ts} +0 -0
@@ -1,925 +0,0 @@
1
- // 官方双面组件的装配点:web 面(./react/ 的纯 React 组件)+ text 面(./text/faces.ts)
2
- // + resolve 解析面(spec 形态由管线代调配套 *Data 函数)。faces 两键必填 —— 配对是结构
3
- // 义务;MetricBars 与 MetricMatrix 消费同一份矩阵数据(同一个 metricMatrixData)。
4
- //
5
- // props 双形态以 `data` 判别(docs/feature/reports/library/metric-views.md):
6
- // spec 形态 = 计算选项平铺 + 可选 `input`(默认宿主注入的 Scope),管线在 resolve 阶段
7
- // 代调同名 *Data,与手工计算严格等价;data 形态接收算好的可序列化数据,跳过取数。
8
- // 同一组件同时给出 `data` 与 spec 字段按完整用户反馈报错,不静默取一边。
9
- // 组件消费 `data` 时校验结构,不符合当前形状按完整用户反馈报错并提示可能的版本漂移。
10
- //
11
- // 官方组件在宿主里自动接上证据室:web 面的 attemptHref 缺省取 ctx.attemptHref
12
- // (宿主注入的证据室深链);显式传 prop 可覆盖(嵌进自己应用时自定去处)。
13
-
14
- import type { ReactNode } from "react";
15
- import {
16
- defineComponent,
17
- isHostWebContextActive,
18
- memoFetchOf,
19
- type ReportComponent,
20
- type ResolveContext,
21
- type TextContext,
22
- type WebContext,
23
- } from "./tree.ts";
24
- import type { LocalizedText, ReportLocale } from "./locale.ts";
25
- import type { AttemptLocator } from "../results/locator.ts";
26
- import type {
27
- AttemptListItem,
28
- CopyFixPromptData,
29
- DeltaData,
30
- EvalListItem,
31
- ExperimentComparisonData,
32
- ExperimentListItem,
33
- HeroData,
34
- LineData,
35
- MatrixData,
36
- ReportInput,
37
- ScatterData,
38
- ScopeSummaryData,
39
- ScopeWarning,
40
- ScoreboardData,
41
- TableData,
42
- TraceWaterfallRow,
43
- } from "./types.ts";
44
- import {
45
- attemptListData,
46
- copyFixPromptData,
47
- deltaTableData,
48
- evalListData,
49
- experimentComparisonData,
50
- experimentListData,
51
- heroData,
52
- metricLineData,
53
- metricMatrixData,
54
- metricScatterData,
55
- metricTableData,
56
- scopeSummaryData,
57
- scopeWarningsData,
58
- scoreboardData,
59
- traceWaterfallData,
60
- type DeltaTableOptions,
61
- type MetricLineOptions,
62
- type MetricMatrixOptions,
63
- type MetricScatterOptions,
64
- type MetricTableOptions,
65
- type ScoreboardOptions,
66
- } from "./compute.ts";
67
- import { collectItems, locatorOf, resolveInput } from "./aggregate.ts";
68
- import {
69
- attemptListText,
70
- deltaText,
71
- evalListText,
72
- experimentComparisonText,
73
- experimentListText,
74
- barsText,
75
- heroCardText,
76
- lineText,
77
- matrixText,
78
- scatterText,
79
- scopeWarningsText,
80
- scoreboardText,
81
- scopeSummaryText,
82
- tableText,
83
- traceWaterfallText,
84
- } from "./text/faces.ts";
85
- import { ScopeSummary as ScopeSummaryWeb } from "./react/ScopeSummary.tsx";
86
- import { ExperimentComparisonView } from "./react/ExperimentComparison.tsx";
87
- import { ExperimentList as ExperimentListWeb } from "./react/ExperimentList.tsx";
88
- import { EvalList as EvalListWeb } from "./react/EvalList.tsx";
89
- import { AttemptList as AttemptListWeb } from "./react/AttemptList.tsx";
90
- import { HeroCard as HeroCardWeb } from "./react/HeroCard.tsx";
91
- import { PoweredBy as PoweredByWeb } from "./react/PoweredBy.tsx";
92
- import { ScopeWarnings as ScopeWarningsWeb } from "./react/ScopeWarnings.tsx";
93
- import { CopyFixPrompt as CopyFixPromptWeb } from "./react/CopyFixPrompt.tsx";
94
- import { TraceWaterfall as TraceWaterfallWeb } from "./react/TraceWaterfall.tsx";
95
- import { MetricTable as MetricTableWeb } from "./react/MetricTable.tsx";
96
- import { MetricMatrix as MetricMatrixWeb } from "./react/MetricMatrix.tsx";
97
- import { MetricBars as MetricBarsWeb } from "./react/MetricBars.tsx";
98
- import { Scoreboard as ScoreboardWeb } from "./react/Scoreboard.tsx";
99
- import { MetricScatter as MetricScatterWeb } from "./react/MetricScatter.tsx";
100
- import { MetricLine as MetricLineWeb } from "./react/MetricLine.tsx";
101
- import { DeltaTable as DeltaTableWeb } from "./react/DeltaTable.tsx";
102
-
103
- // ───────────────────────── DataProps 组合规则 ─────────────────────────
104
-
105
- type Never<T> = { [K in keyof T]?: never };
106
-
107
- /**
108
- * 官方数据组件的统一 props 组合(docs/feature/reports/library/metric-views.md):
109
- * data 形态(接收配套 *Data 的产物)或 spec 形态(Options 平铺 + 可选 input)。
110
- */
111
- export type DataProps<Data, Options, Presentation> =
112
- | ({ data: Data; input?: never } & Never<Options> & Presentation)
113
- | ({ data?: never; input?: ReportInput } & Options & Presentation);
114
-
115
- // ───────────────────────── data 结构校验(版本漂移防线)─────────────────────────
116
-
117
- function isObject(value: unknown): value is Record<string, unknown> {
118
- return typeof value === "object" && value !== null && !Array.isArray(value);
119
- }
120
-
121
- function isCell(value: unknown): boolean {
122
- return (
123
- isObject(value) &&
124
- "value" in value &&
125
- "display" in value &&
126
- typeof value.samples === "number" &&
127
- typeof value.total === "number" &&
128
- Array.isArray(value.refs)
129
- );
130
- }
131
-
132
- function isTally(value: unknown): boolean {
133
- return (
134
- isObject(value) &&
135
- typeof value.passed === "number" &&
136
- typeof value.failed === "number" &&
137
- typeof value.errored === "number" &&
138
- typeof value.skipped === "number"
139
- );
140
- }
141
-
142
- type Validator = (data: unknown) => string | null;
143
-
144
- function dataShapeError(component: string, dataFnName: string, shape: string, problem: string): Error {
145
- return new Error(
146
- `<${component}> received data that does not match the current ${shape} shape: ${problem}. ` +
147
- `It may have been computed by a different niceeval version (component data carries no schemaVersion; the support window is same-version write and read). ` +
148
- `Recompute it with ${dataFnName}() from this niceeval version, then re-render.`,
149
- );
150
- }
151
-
152
- const validateTableData: Validator = (data) => {
153
- if (!isObject(data)) return "expected an object";
154
- if (typeof data.rowDimension !== "string") return 'missing "rowDimension" (string)';
155
- if (!Array.isArray(data.columns)) return 'missing "columns" (array)';
156
- if (!Array.isArray(data.rows)) return 'missing "rows" (array)';
157
- for (const row of data.rows as unknown[]) {
158
- if (!isObject(row) || typeof row.key !== "string" || !isObject(row.cells)) {
159
- return 'each row needs { key, cells }';
160
- }
161
- }
162
- return null;
163
- };
164
-
165
- const validateMatrixData: Validator = (data) => {
166
- if (!isObject(data)) return "expected an object";
167
- if (typeof data.rowDimension !== "string" || typeof data.columnDimension !== "string") {
168
- return 'missing "rowDimension" / "columnDimension" (string)';
169
- }
170
- if (!isObject(data.metric)) return 'missing "metric" (MetricColumn)';
171
- if (!Array.isArray(data.cells)) return 'missing "cells" (array)';
172
- return null;
173
- };
174
-
175
- const validateScatterData: Validator = (data) => {
176
- if (!isObject(data)) return "expected an object";
177
- if (typeof data.pointDimension !== "string") return 'missing "pointDimension" (string)';
178
- if (!isObject(data.x) || !isObject(data.y)) return 'missing "x" / "y" (MetricColumn)';
179
- if (!Array.isArray(data.rows)) return 'missing "rows" (array)';
180
- for (const row of data.rows as unknown[]) {
181
- if (!isObject(row) || typeof row.key !== "string" || !isCell(row.x) || !isCell(row.y)) {
182
- return "each row needs { key, x: MetricCell, y: MetricCell }";
183
- }
184
- }
185
- return null;
186
- };
187
-
188
- const validateLineData: Validator = (data) => {
189
- if (!isObject(data)) return "expected an object";
190
- if (!isObject(data.x) || typeof (data.x as Record<string, unknown>).key !== "string") return 'missing "x" axis descriptor';
191
- if (!isObject(data.y)) return 'missing "y" (MetricColumn)';
192
- if (!Array.isArray(data.rows)) return 'missing "rows" (array)';
193
- return null;
194
- };
195
-
196
- const validateScoreboardData: Validator = (data) => {
197
- if (!isObject(data)) return "expected an object";
198
- if (typeof data.rowDimension !== "string") return 'missing "rowDimension" (string)';
199
- if (!Array.isArray(data.questions)) return 'missing "questions" (array)';
200
- if (typeof data.fullMarks !== "number") return 'missing "fullMarks" (number)';
201
- if (typeof data.ignoredEvals !== "number") return 'missing "ignoredEvals" (number)';
202
- if (!Array.isArray(data.rows)) return 'missing "rows" (array)';
203
- for (const row of data.rows as unknown[]) {
204
- if (!isObject(row) || !isObject(row.total) || typeof (row.total as Record<string, unknown>).notRun !== "number") {
205
- return 'each row needs { key, total: { value, display, notRun, unscorable, refs }, subjects }';
206
- }
207
- }
208
- return null;
209
- };
210
-
211
- const validateDeltaData: Validator = (data) => {
212
- if (!isObject(data)) return "expected an object";
213
- if (typeof data.byDimension !== "string") return 'missing "byDimension" (string)';
214
- if (!Array.isArray(data.columns) || !Array.isArray(data.rows)) return 'missing "columns" / "rows" (array)';
215
- for (const row of data.rows as unknown[]) {
216
- if (!isObject(row) || row.label === undefined || !isObject(row.a) || !isObject(row.b)) {
217
- return "each row needs { key, label, a, b, cells }";
218
- }
219
- }
220
- return null;
221
- };
222
-
223
- const validateScopeSummaryData: Validator = (data) => {
224
- if (!isObject(data)) return "expected an object";
225
- if (!isObject(data.range)) return 'missing "range" ({ earliestStartedAt, latestStartedAt })';
226
- if (!isTally(data.evalVerdicts) || !isTally(data.attemptVerdicts)) {
227
- return 'missing "evalVerdicts" / "attemptVerdicts" tallies';
228
- }
229
- if (!isCell(data.endToEndPassRate) || !isCell(data.totalCostUSD)) {
230
- return 'missing "endToEndPassRate" / "totalCostUSD" (MetricCell)';
231
- }
232
- return null;
233
- };
234
-
235
- const validateComparisonData: Validator = (data) => {
236
- if (!isObject(data)) return "expected an object";
237
- if (!Array.isArray(data.groups)) return 'missing "groups" (array)';
238
- for (const group of data.groups as unknown[]) {
239
- if (!isObject(group) || typeof group.key !== "string" || validateScopeSummaryData(group.summary) !== null) {
240
- return "each group needs { key, summary, scatter, experiments }";
241
- }
242
- }
243
- return null;
244
- };
245
-
246
- const validateExperimentListData: Validator = (data) => {
247
- if (!Array.isArray(data)) return "expected an array of ExperimentListItem";
248
- for (const item of data as unknown[]) {
249
- if (!isObject(item) || typeof item.experimentId !== "string" || !isTally(item.evalVerdicts) || !isCell(item.endToEndPassRate)) {
250
- return "each item needs { experimentId, evalVerdicts, endToEndPassRate, costUSD, durationMs, tokens, evalRows, … }";
251
- }
252
- }
253
- return null;
254
- };
255
-
256
- const validateEvalListData: Validator = (data) => {
257
- if (!Array.isArray(data)) return "expected an array of EvalListItem";
258
- for (const item of data as unknown[]) {
259
- if (!isObject(item) || typeof item.evalId !== "string" || !isCell(item.examScore) || !Array.isArray(item.attempts)) {
260
- return "each item needs { experimentId, evalId, verdict, examScore, durationMs, costUSD, attempts }";
261
- }
262
- }
263
- return null;
264
- };
265
-
266
- const validateAttemptListData: Validator = (data) => {
267
- if (!Array.isArray(data)) return "expected an array of AttemptListItem";
268
- for (const item of data as unknown[]) {
269
- if (!isObject(item) || typeof item.evalId !== "string" || !("failureSummary" in item) || !("costUSD" in item)) {
270
- return "each item needs { experimentId, evalId, verdict, failureSummary, moreFailures, examScore, durationMs, costUSD, locator }";
271
- }
272
- }
273
- return null;
274
- };
275
-
276
- const validateHeroData: Validator = (data) => {
277
- if (!isObject(data)) return "expected an object";
278
- if (!("latestStartedAt" in data) || (data.latestStartedAt !== null && typeof data.latestStartedAt !== "string")) {
279
- return 'missing "latestStartedAt" (string | null)';
280
- }
281
- if (typeof data.snapshots !== "number") return 'missing "snapshots" (number)';
282
- return null;
283
- };
284
-
285
- const validateScopeWarningsData: Validator = (data) => {
286
- if (!Array.isArray(data)) return "expected an array of ScopeWarning";
287
- for (const item of data as unknown[]) {
288
- if (!isObject(item) || typeof item.kind !== "string" || typeof item.message !== "string") {
289
- return "each warning needs { kind, message, … }";
290
- }
291
- }
292
- return null;
293
- };
294
-
295
- const validateCopyFixPromptData: Validator = (data) => {
296
- if (!isObject(data)) return "expected an object";
297
- if (typeof data.prompt !== "string") return 'missing "prompt" (string)';
298
- if (typeof data.failures !== "number") return 'missing "failures" (number)';
299
- return null;
300
- };
301
-
302
- const validateTraceWaterfallData: Validator = (data) => {
303
- if (!Array.isArray(data)) return "expected an array of TraceWaterfallRow";
304
- for (const row of data as unknown[]) {
305
- if (
306
- !isObject(row) ||
307
- typeof row.experimentId !== "string" ||
308
- typeof row.evalId !== "string" ||
309
- typeof row.locator !== "string" ||
310
- !("durationMs" in row) ||
311
- (row.durationMs !== null && typeof row.durationMs !== "number") ||
312
- !Array.isArray(row.spans)
313
- ) {
314
- return "each row needs { experimentId, evalId, locator, durationMs: number | null, spans }";
315
- }
316
- for (const span of row.spans as unknown[]) {
317
- if (!isObject(span) || typeof span.name !== "string" || typeof span.startOffsetMs !== "number") {
318
- return "each span needs { name, kind, startOffsetMs, durationMs, failed }";
319
- }
320
- }
321
- }
322
- return null;
323
- };
324
-
325
- // ───────────────────────── spec / data 双形态的通用装配 ─────────────────────────
326
-
327
- interface DataComponentDef<Data, Options, Presentation> {
328
- name: string;
329
- dataFnName: string;
330
- shapeName: string;
331
- dataFn: (input: ReportInput, options: Options) => Promise<Data>;
332
- /** spec 形态的计算选项 prop 名(不含 input);未列出的 props 视为呈现选项原样保留。 */
333
- specKeys: readonly string[];
334
- validate: Validator;
335
- web(props: { data: Data } & Presentation, ctx: WebContext): ReactNode;
336
- text(props: { data: Data } & Presentation, ctx: TextContext): string;
337
- }
338
-
339
- function makeDataComponent<Data, Options, Presentation>(
340
- def: DataComponentDef<Data, Options, Presentation>,
341
- ): ReportComponent<DataProps<Data, Options, Presentation>> {
342
- type Props = Record<string, unknown>;
343
- type Resolved = { data: Data } & Presentation;
344
-
345
- const assertData = (data: unknown): Data => {
346
- const problem = def.validate(data);
347
- if (problem !== null) throw dataShapeError(def.name, def.dataFnName, def.shapeName, problem);
348
- return data as Data;
349
- };
350
-
351
- const resolve = async (props: Props, ctx: ResolveContext): Promise<Resolved> => {
352
- const givenSpec = def.specKeys.filter((key) => props[key] !== undefined);
353
- if (props.data !== undefined) {
354
- if (givenSpec.length > 0 || props.input !== undefined) {
355
- const extras = [...givenSpec, ...(props.input !== undefined ? ["input"] : [])];
356
- throw new Error(
357
- `<${def.name}> got both \`data\` and spec field${extras.length > 1 ? "s" : ""} (${extras.join(", ")}) — the two data sources are exclusive and niceeval will not silently pick one. ` +
358
- `Keep \`data\` (precomputed with ${def.dataFnName}()) and drop the spec fields, or drop \`data\` and let the pipeline compute from the spec.`,
359
- );
360
- }
361
- assertData(props.data);
362
- return props as unknown as Resolved;
363
- }
364
- const options: Record<string, unknown> = {};
365
- for (const key of givenSpec) options[key] = props[key];
366
- const input = (props.input as ReportInput | undefined) ?? ctx.input;
367
- const data = await memoFetchOf(ctx)(def.dataFn, input, options, () =>
368
- def.dataFn(input, options as Options),
369
- );
370
- const rest: Record<string, unknown> = { ...props };
371
- delete rest.input;
372
- for (const key of def.specKeys) delete rest[key];
373
- return { ...rest, data } as unknown as Resolved;
374
- };
375
-
376
- const component = defineComponent<Props, Resolved>({
377
- resolve,
378
- web: (props, ctx) => {
379
- assertData((props as { data?: unknown }).data);
380
- return def.web(props, ctx);
381
- },
382
- text: (props, ctx) => {
383
- assertData((props as { data?: unknown }).data);
384
- return def.text(props, ctx);
385
- },
386
- }) as unknown as ReportComponent<DataProps<Data, Options, Presentation>>;
387
- component.displayName = def.name;
388
- return component;
389
- }
390
-
391
- /** 宿主内缺省接证据室,显式 prop 覆盖;宿主外不传 attemptHref 就是纯展示。 */
392
- function hrefOf(
393
- props: { attemptHref?: (locator: AttemptLocator) => string },
394
- ctx: WebContext,
395
- ): ((locator: AttemptLocator) => string) | undefined {
396
- return props.attemptHref ?? (isHostWebContextActive() ? ctx.attemptHref : undefined);
397
- }
398
-
399
- // ───────────────────────── 呈现选项类型 ─────────────────────────
400
-
401
- interface ChromeProps {
402
- /** chrome 文案 locale;省略时随宿主上下文(宿主外默认 "en")。 */
403
- locale?: ReportLocale;
404
- className?: string;
405
- }
406
-
407
- // ───────────────────────── 概览组件 ─────────────────────────
408
-
409
- export type ScopeSummaryProps = DataProps<
410
- ScopeSummaryData,
411
- Record<never, never>,
412
- ChromeProps & {
413
- /** 显示哪一级计票;默认 "eval"。data 恒携带两级,votes 只选择呈现。 */
414
- votes?: "eval" | "attempt";
415
- }
416
- >;
417
-
418
- /** 范围摘要卡:时间窗、数量、两级计票、端到端成功率与总成本。 */
419
- export const ScopeSummary = makeDataComponent<
420
- ScopeSummaryData,
421
- Record<never, never>,
422
- ChromeProps & { votes?: "eval" | "attempt" }
423
- >({
424
- name: "ScopeSummary",
425
- dataFnName: "scopeSummaryData",
426
- shapeName: "ScopeSummaryData",
427
- dataFn: (input) => scopeSummaryData(input),
428
- specKeys: [],
429
- validate: validateScopeSummaryData,
430
- web: (props, ctx) => <ScopeSummaryWeb {...props} locale={props.locale ?? ctx.locale} />,
431
- text: (props, ctx) => scopeSummaryText(props.data, props.votes ?? "eval", ctx),
432
- }) as unknown as ReportComponent<ScopeSummaryProps>;
433
-
434
- export type ExperimentComparisonProps = DataProps<ExperimentComparisonData, Record<never, never>, ChromeProps>;
435
-
436
- /**
437
- * 内建报告的默认组合件:先把 input 按可比组分区,再为每组分别计算 ScopeSummary、
438
- * 成本 × 端到端成功率散点和 ExperimentList。web 面持有完整组索引并一次聚焦一组;
439
- * text 面命中多个组时只显示组索引与可执行的单组查看命令,命中单组时才输出完整散点与列表。
440
- */
441
- export const ExperimentComparison = makeDataComponent<ExperimentComparisonData, Record<never, never>, ChromeProps>({
442
- name: "ExperimentComparison",
443
- dataFnName: "experimentComparisonData",
444
- shapeName: "ExperimentComparisonData",
445
- dataFn: (input) => experimentComparisonData(input),
446
- specKeys: [],
447
- validate: validateComparisonData,
448
- web: (props, ctx) => (
449
- <ExperimentComparisonView
450
- data={props.data}
451
- locale={props.locale ?? ctx.locale}
452
- className={props.className}
453
- attemptHref={isHostWebContextActive() ? ctx.attemptHref : undefined}
454
- />
455
- ),
456
- text: (props, ctx) => experimentComparisonText(props.data, props.className, ctx),
457
- }) as unknown as ReportComponent<ExperimentComparisonProps>;
458
-
459
- // ───────────────────────── 实体列表 ─────────────────────────
460
-
461
- interface EntityListChrome extends ChromeProps {
462
- attemptHref?: (locator: AttemptLocator) => string;
463
- }
464
-
465
- export type ExperimentListProps = DataProps<
466
- readonly ExperimentListItem[],
467
- Record<never, never>,
468
- EntityListChrome & {
469
- /** web 面在比较表前显示实验过滤框;text 面忽略。 */
470
- filter?: boolean;
471
- /**
472
- * 可选父路径:两面的行标签去掉与它相同的前缀,只显示 experiment id 末段(默认
473
- * `ExperimentComparison` 给每组传组键)。完整 id 仍是排序 / 着色 / 过滤 / 折叠的键。
474
- */
475
- relativeTo?: string;
476
- }
477
- >;
478
-
479
- /** 实验列表:每项一个 experiment,固定八列比较表 + 展开到 Eval / Attempt。 */
480
- export const ExperimentList = makeDataComponent<
481
- readonly ExperimentListItem[],
482
- Record<never, never>,
483
- EntityListChrome & { filter?: boolean; relativeTo?: string }
484
- >({
485
- name: "ExperimentList",
486
- dataFnName: "experimentListData",
487
- shapeName: "ExperimentListItem[]",
488
- dataFn: (input) => experimentListData(input),
489
- specKeys: [],
490
- validate: validateExperimentListData,
491
- web: (props, ctx) => (
492
- <ExperimentListWeb
493
- data={props.data}
494
- filter={props.filter}
495
- relativeTo={props.relativeTo}
496
- locale={props.locale ?? ctx.locale}
497
- attemptHref={hrefOf(props, ctx) ?? ctx.attemptHref}
498
- className={props.className}
499
- />
500
- ),
501
- text: (props, ctx) => experimentListText(props.data, ctx, props.relativeTo),
502
- }) as unknown as ReportComponent<ExperimentListProps>;
503
-
504
- export type EvalListProps = DataProps<readonly EvalListItem[], Record<never, never>, EntityListChrome>;
505
-
506
- /** Eval 列表:每项一个 experimentId + evalId,展开到这道题的 Attempt。 */
507
- export const EvalList = makeDataComponent<readonly EvalListItem[], Record<never, never>, EntityListChrome>({
508
- name: "EvalList",
509
- dataFnName: "evalListData",
510
- shapeName: "EvalListItem[]",
511
- dataFn: (input) => evalListData(input),
512
- specKeys: [],
513
- validate: validateEvalListData,
514
- web: (props, ctx) => (
515
- <EvalListWeb
516
- data={props.data}
517
- locale={props.locale ?? ctx.locale}
518
- attemptHref={hrefOf(props, ctx) ?? ctx.attemptHref}
519
- className={props.className}
520
- />
521
- ),
522
- text: (props, ctx) => evalListText(props.data, ctx),
523
- }) as unknown as ReportComponent<EvalListProps>;
524
-
525
- export type AttemptListProps = DataProps<
526
- readonly AttemptListItem[],
527
- Record<never, never>,
528
- EntityListChrome & {
529
- /** 过滤 / 截断前的总数;省略时等于 data 长度。 */
530
- total?: number;
531
- /** web 面加过滤输入框(按 experiment、eval、agent、verdict 或摘要文本收窄行);渐进增强,不改变数据与 text 面。 */
532
- filter?: boolean;
533
- }
534
- >;
535
-
536
- /** Attempt 列表:实体列表的叶子层,每项一次 attempt 的判定、单行摘要与 locator。 */
537
- export const AttemptList = makeDataComponent<
538
- readonly AttemptListItem[],
539
- Record<never, never>,
540
- EntityListChrome & { total?: number; filter?: boolean }
541
- >({
542
- name: "AttemptList",
543
- dataFnName: "attemptListData",
544
- shapeName: "AttemptListItem[]",
545
- dataFn: (input) => attemptListData(input),
546
- specKeys: [],
547
- validate: validateAttemptListData,
548
- web: (props, ctx) => (
549
- <AttemptListWeb
550
- data={props.data}
551
- total={props.total}
552
- filter={props.filter}
553
- locale={props.locale ?? ctx.locale}
554
- attemptHref={hrefOf(props, ctx) ?? ctx.attemptHref}
555
- className={props.className}
556
- />
557
- ),
558
- text: (props, ctx) => attemptListText(props.data, props.total, ctx),
559
- }) as unknown as ReportComponent<AttemptListProps>;
560
-
561
- // ───────────────────────── FailureList(官方组合件)─────────────────────────
562
-
563
- export interface FailureListProps {
564
- /** 显示的最大条数;默认 20。 */
565
- limit?: number;
566
- /** 默认宿主注入的 Scope。 */
567
- input?: ReportInput;
568
- attemptHref?: (locator: AttemptLocator) => string;
569
- locale?: ReportLocale;
570
- className?: string;
571
- }
572
-
573
- /**
574
- * 「现在有哪些失败要处理」的成品组合件:内部就是 attemptListData → 过滤 → AttemptList
575
- * data 形态,与手写组合严格等价、没有私有能力(docs/feature/reports/library/entity-lists.md)。
576
- * verdict ∈ failed / errored,按 attempt 开始时间降序(同刻按 locator 字典序),
577
- * 截断到 limit(默认 20),total 报告截断前总数。
578
- */
579
- export const FailureList = defineComponent<FailureListProps>(async (props, ctx) => {
580
- const input = props.input ?? ctx.scope;
581
- const all = await attemptListData(input);
582
- // attempt 开始时间不在列表条目里(它不是列表展示字段);从同一 input 的读取面按 locator 对回。
583
- const startedAtByLocator = new Map<string, string>();
584
- for (const item of collectItems(resolveInput(input).snapshots)) {
585
- startedAtByLocator.set(locatorOf(item), item.attempt.result.startedAt ?? "");
586
- }
587
- const failures = all
588
- .filter((item) => item.verdict === "failed" || item.verdict === "errored")
589
- .sort((a, b) => {
590
- const ta = startedAtByLocator.get(a.locator) ?? "";
591
- const tb = startedAtByLocator.get(b.locator) ?? "";
592
- if (ta !== tb) return ta < tb ? 1 : -1; // 最近的失败在前
593
- return a.locator < b.locator ? -1 : a.locator > b.locator ? 1 : 0;
594
- });
595
- const limit = props.limit ?? 20;
596
- return (
597
- <AttemptList
598
- data={failures.slice(0, limit)}
599
- total={failures.length}
600
- attemptHref={props.attemptHref}
601
- locale={props.locale}
602
- className={props.className}
603
- />
604
- );
605
- });
606
- FailureList.displayName = "FailureList";
607
-
608
- // ───────────────────────── 站点组件(Hero / PoweredBy / ScopeWarnings / CopyFixPrompt / TraceWaterfall)─────────────────────────
609
-
610
- /** `Hero` 的 props:标题缺省取 `ctx.report.title`(回退链后的站点标题)。 */
611
- export interface HeroProps {
612
- /** 覆盖标题;省略时取 ctx.report.title(回退链后的站点标题)。 */
613
- title?: LocalizedText;
614
- className?: string;
615
- }
616
-
617
- /** HeroCard 的 data 校验入口(它不经 makeDataComponent,数据形态是唯一形态)。 */
618
- const assertHeroData = (data: unknown): HeroData => {
619
- const problem = validateHeroData(data);
620
- if (problem !== null) throw dataShapeError("HeroCard", "heroData", "HeroData", problem);
621
- return data as HeroData;
622
- };
623
-
624
- /**
625
- * `HeroCard`:Hero 的渲染件,双面组件,只收 data 形态——标题输入是站点声明与 Scope 的
626
- * 合成物,没有单独的 spec 等价形。web 面渲染 hero 标题(h1)、按渲染 locale 格式化的运行
627
- * meta(latestStartedAt 为 null 时内置「暂无运行」文案)与品牌行(等同 PoweredBy,恒含、
628
- * 无拆除 prop);text 面输出标题行与 meta 行,不含品牌行
629
- * (docs/feature/reports/library/site-components.md「HeroCard」)。
630
- */
631
- export const HeroCard = defineComponent<HeroCardProps>({
632
- web: (props, ctx) => {
633
- assertHeroData(props.data);
634
- return <HeroCardWeb title={props.title} data={props.data} className={props.className} locale={ctx.locale} />;
635
- },
636
- text: (props, ctx) => {
637
- assertHeroData(props.data);
638
- return heroCardText(props.title, props.data, ctx);
639
- },
640
- });
641
- HeroCard.displayName = "HeroCard";
642
-
643
- /** `HeroCard` 的 props:标题 + `heroData()` 的产物,只有 data 形态。 */
644
- export interface HeroCardProps {
645
- title: LocalizedText;
646
- data: HeroData;
647
- className?: string;
648
- }
649
-
650
- /**
651
- * `Hero`:页首的站点标题区——标题、最后运行时间、快照合成来源,恒含品牌行。官方组合组件,
652
- * 与手写 `<HeroCard title={title ?? ctx.report.title} data={await heroData(ctx.scope)} />`
653
- * 严格等价、没有私有能力;读 `ctx.report` 意味着输出跟随站点,要站点无关的标题区直接用
654
- * `HeroCard` 显式传值(docs/feature/reports/library/site-components.md「Hero」)。
655
- */
656
- export const Hero = defineComponent<HeroProps>(async ({ title, className }, ctx) => (
657
- <HeroCard title={title ?? ctx.report.title} data={await heroData(ctx.scope)} className={className} />
658
- ));
659
- Hero.displayName = "Hero";
660
-
661
- /**
662
- * `PoweredBy`:唯一的品牌件,无 props 双面组件。web 面渲染指向 niceeval 官网的一行品牌色
663
- * 小字(`utm_source=report&utm_medium=powered-by`,`rel` 仅 `noopener` 以保留 Referer);
664
- * text 面零输出。没有任何配置——品牌契约是「提供一个组件,不给开关」:不想要品牌就不用
665
- * 这些组件、自己写替代组件(docs/feature/reports/library/site-components.md「PoweredBy」)。
666
- */
667
- export const PoweredBy = defineComponent<Record<never, never>>({
668
- web: () => <PoweredByWeb />,
669
- text: () => "",
670
- });
671
- PoweredBy.displayName = "PoweredBy";
672
-
673
- /** `ScopeWarnings` 的 props:spec 形态取宿主 Scope 的 warnings,data 形态收 `ScopeWarning[]`。 */
674
- export type ScopeWarningsProps = DataProps<readonly ScopeWarning[], Record<never, never>, ChromeProps>;
675
-
676
- /**
677
- * `ScopeWarnings`:选择警告区,警告的唯一呈现组件。把 Scope 携带的 `ScopeWarning[]`
678
- * 按「下一步动作」聚合渲染(带 experimentId 的按实验聚合、非实验作用域按 kind 聚合;
679
- * integrity 组在前);web 面组头带去重后的可复制命令、明细收原生 `<details>`(总条数 ≤ 3
680
- * 默认展开),text 面同构但不折叠。空警告集与裸 `Snapshot[]` 输入两面零输出
681
- * (docs/feature/reports/library/site-components.md「ScopeWarnings」)。
682
- */
683
- export const ScopeWarnings = makeDataComponent<readonly ScopeWarning[], Record<never, never>, ChromeProps>({
684
- name: "ScopeWarnings",
685
- dataFnName: "scopeWarningsData",
686
- shapeName: "ScopeWarning[]",
687
- dataFn: (input) => scopeWarningsData(input),
688
- specKeys: [],
689
- validate: validateScopeWarningsData,
690
- web: (props, ctx) =>
691
- props.data.length === 0 ? null : (
692
- <ScopeWarningsWeb data={props.data} locale={props.locale ?? ctx.locale} className={props.className} />
693
- ),
694
- text: (props, ctx) => scopeWarningsText(props.data, ctx),
695
- }) as unknown as ReportComponent<ScopeWarningsProps>;
696
-
697
- /** `CopyFixPrompt` 的 props:spec 形态无选项,data 形态收 `copyFixPromptData()` 的产物。 */
698
- export type CopyFixPromptProps = DataProps<CopyFixPromptData, Record<never, never>, ChromeProps>;
699
-
700
- /**
701
- * `CopyFixPrompt`:把当前范围的全部失败整理成一段可交给 coding agent 的修复 prompt。
702
- * prompt 在 resolve 阶段算好、烘进静态 HTML,无 JS 时在折叠块里完整可读,「复制」是增强层
703
- * 行为;`failures` 为 0 时两面零输出;text 面恒零输出——终端里的等价能力是 `show` 的
704
- * attempt 下钻命令本身(docs/feature/reports/library/site-components.md「CopyFixPrompt」)。
705
- */
706
- export const CopyFixPrompt = makeDataComponent<CopyFixPromptData, Record<never, never>, ChromeProps>({
707
- name: "CopyFixPrompt",
708
- dataFnName: "copyFixPromptData",
709
- shapeName: "CopyFixPromptData",
710
- dataFn: (input) => copyFixPromptData(input),
711
- specKeys: [],
712
- validate: validateCopyFixPromptData,
713
- web: (props, ctx) =>
714
- props.data.failures === 0 ? null : (
715
- <CopyFixPromptWeb data={props.data} locale={props.locale ?? ctx.locale} className={props.className} />
716
- ),
717
- text: () => "",
718
- }) as unknown as ReportComponent<CopyFixPromptProps>;
719
-
720
- /** `TraceWaterfall` 的 props:spec 形态无选项,data 形态收 `traceWaterfallData()` 的产物。 */
721
- export type TraceWaterfallProps = DataProps<
722
- readonly TraceWaterfallRow[],
723
- Record<never, never>,
724
- ChromeProps & { attemptHref?: (locator: AttemptLocator) => string }
725
- >;
726
-
727
- /**
728
- * `TraceWaterfall`:每个 attempt 一行的执行时间瀑布,用 canonical OTel 字段显示被测 agent
729
- * 的原始 span(agent / model / tool)。web 面静态渲染顶层 span 分解条(失败 span 带失败
730
- * 标记),行链接 attempt 详情;text 面每 attempt 一行(locator、总耗时、span 计数与失败
731
- * 标记)+ 可复制的 `--timing` 下钻命令。trace 缺失的行照常出现并如实显示缺失;runner
732
- * 生命周期节点不进瀑布(docs/feature/reports/library/site-components.md「TraceWaterfall」)。
733
- */
734
- export const TraceWaterfall = makeDataComponent<
735
- readonly TraceWaterfallRow[],
736
- Record<never, never>,
737
- ChromeProps & { attemptHref?: (locator: AttemptLocator) => string }
738
- >({
739
- name: "TraceWaterfall",
740
- dataFnName: "traceWaterfallData",
741
- shapeName: "TraceWaterfallRow[]",
742
- dataFn: (input) => traceWaterfallData(input),
743
- specKeys: [],
744
- validate: validateTraceWaterfallData,
745
- web: (props, ctx) => (
746
- <TraceWaterfallWeb
747
- data={props.data}
748
- attemptHref={hrefOf(props, ctx) ?? ctx.attemptHref}
749
- locale={props.locale ?? ctx.locale}
750
- className={props.className}
751
- />
752
- ),
753
- text: (props, ctx) => traceWaterfallText(props.data, ctx),
754
- }) as unknown as ReportComponent<TraceWaterfallProps>;
755
-
756
- // ───────────────────────── 指标组件 ─────────────────────────
757
-
758
- export type MetricTableProps = DataProps<
759
- TableData,
760
- MetricTableOptions,
761
- ChromeProps & {
762
- /** web 面在表格前渲染过滤输入框(enhance.js 接管);无 JS 时表格内容依旧完整。 */
763
- filter?: boolean;
764
- attemptHref?: (locator: AttemptLocator) => string;
765
- }
766
- >;
767
-
768
- /** 榜单:一行一个维度值、一列一个指标,回答「谁整体更好」。 */
769
- export const MetricTable = makeDataComponent<
770
- TableData,
771
- MetricTableOptions,
772
- ChromeProps & { filter?: boolean; attemptHref?: (locator: AttemptLocator) => string }
773
- >({
774
- name: "MetricTable",
775
- dataFnName: "metricTableData",
776
- shapeName: "TableData",
777
- dataFn: metricTableData,
778
- specKeys: ["rows", "columns", "sort", "evals"],
779
- validate: validateTableData,
780
- web: (props, ctx) => (
781
- <MetricTableWeb
782
- data={props.data}
783
- filter={props.filter}
784
- locale={props.locale ?? ctx.locale}
785
- attemptHref={hrefOf(props, ctx)}
786
- className={props.className}
787
- />
788
- ),
789
- text: (props, ctx) => tableText(props.data, ctx),
790
- }) as unknown as ReportComponent<MetricTableProps>;
791
-
792
- export type MetricMatrixProps = DataProps<
793
- MatrixData,
794
- MetricMatrixOptions,
795
- ChromeProps & { attemptHref?: (locator: AttemptLocator) => string }
796
- >;
797
- export type MetricBarsProps = MetricMatrixProps;
798
-
799
- /** 逐题格子:行 × 列两个维度、格子里一个指标,回答「哪道题谁挂了」。 */
800
- export const MetricMatrix = makeDataComponent<
801
- MatrixData,
802
- MetricMatrixOptions,
803
- ChromeProps & { attemptHref?: (locator: AttemptLocator) => string }
804
- >({
805
- name: "MetricMatrix",
806
- dataFnName: "metricMatrixData",
807
- shapeName: "MatrixData",
808
- dataFn: metricMatrixData,
809
- specKeys: ["rows", "columns", "cell", "evals"],
810
- validate: validateMatrixData,
811
- web: (props, ctx) => (
812
- <MetricMatrixWeb data={props.data} locale={props.locale ?? ctx.locale} attemptHref={hrefOf(props, ctx)} className={props.className} />
813
- ),
814
- text: (props, ctx) => matrixText(props.data, ctx),
815
- }) as unknown as ReportComponent<MetricMatrixProps>;
816
-
817
- /** 分组条形:同一份矩阵数据的另一种摆法;与 MetricMatrix 写同一份 spec 时只计算一次。 */
818
- export const MetricBars = makeDataComponent<
819
- MatrixData,
820
- MetricMatrixOptions,
821
- ChromeProps & { attemptHref?: (locator: AttemptLocator) => string }
822
- >({
823
- name: "MetricBars",
824
- dataFnName: "metricMatrixData",
825
- shapeName: "MatrixData",
826
- dataFn: metricMatrixData,
827
- specKeys: ["rows", "columns", "cell", "evals"],
828
- validate: validateMatrixData,
829
- web: (props, ctx) => (
830
- <MetricBarsWeb data={props.data} locale={props.locale ?? ctx.locale} attemptHref={hrefOf(props, ctx)} className={props.className} />
831
- ),
832
- text: (props, ctx) => barsText(props.data, ctx),
833
- }) as unknown as ReportComponent<MetricBarsProps>;
834
-
835
- export type ScoreboardProps = DataProps<
836
- ScoreboardData,
837
- ScoreboardOptions,
838
- ChromeProps & { attemptHref?: (locator: AttemptLocator) => string }
839
- >;
840
-
841
- /** 考试成绩单:总分 + 分科小计,固定分母、notRun / unscorable 分开如实报。 */
842
- export const Scoreboard = makeDataComponent<
843
- ScoreboardData,
844
- ScoreboardOptions,
845
- ChromeProps & { attemptHref?: (locator: AttemptLocator) => string }
846
- >({
847
- name: "Scoreboard",
848
- dataFnName: "scoreboardData",
849
- shapeName: "ScoreboardData",
850
- dataFn: scoreboardData,
851
- specKeys: ["rows", "questions", "subject", "weights", "fullMarks", "score"],
852
- validate: validateScoreboardData,
853
- web: (props, ctx) => <ScoreboardWeb data={props.data} locale={props.locale ?? ctx.locale} className={props.className} />,
854
- text: (props, ctx) => scoreboardText(props.data, ctx),
855
- }) as unknown as ReportComponent<ScoreboardProps>;
856
-
857
- export type MetricScatterProps = DataProps<
858
- ScatterData,
859
- MetricScatterOptions,
860
- ChromeProps & { pointHref?: (row: ScatterData["rows"][number]) => string }
861
- >;
862
-
863
- /** 两个指标之间的取舍:每个点一个维度值,x / y 各一个指标,series 只决定颜色和分组。 */
864
- export const MetricScatter = makeDataComponent<
865
- ScatterData,
866
- MetricScatterOptions,
867
- ChromeProps & { pointHref?: (row: ScatterData["rows"][number]) => string }
868
- >({
869
- name: "MetricScatter",
870
- dataFnName: "metricScatterData",
871
- shapeName: "ScatterData",
872
- dataFn: metricScatterData,
873
- specKeys: ["points", "series", "x", "y", "evals"],
874
- validate: validateScatterData,
875
- web: (props, ctx) => (
876
- <MetricScatterWeb data={props.data} pointHref={props.pointHref} locale={props.locale ?? ctx.locale} className={props.className} />
877
- ),
878
- text: (props, ctx) => scatterText(props.data, ctx),
879
- }) as unknown as ReportComponent<MetricScatterProps>;
880
-
881
- export type MetricLineProps = DataProps<
882
- LineData,
883
- MetricLineOptions,
884
- ChromeProps & { pointHref?: (row: LineData["rows"][number]) => string }
885
- >;
886
-
887
- /** 趋势线:x 是 NumericAxis(参数轴),点身份 = (series, x)。 */
888
- export const MetricLine = makeDataComponent<
889
- LineData,
890
- MetricLineOptions,
891
- ChromeProps & { pointHref?: (row: LineData["rows"][number]) => string }
892
- >({
893
- name: "MetricLine",
894
- dataFnName: "metricLineData",
895
- shapeName: "LineData",
896
- dataFn: metricLineData,
897
- specKeys: ["x", "series", "y", "evals"],
898
- validate: validateLineData,
899
- web: (props, ctx) => (
900
- <MetricLineWeb data={props.data} pointHref={props.pointHref} locale={props.locale ?? ctx.locale} className={props.className} />
901
- ),
902
- text: (props, ctx) => lineText(props.data, ctx),
903
- }) as unknown as ReportComponent<MetricLineProps>;
904
-
905
- export type DeltaTableProps = DataProps<
906
- DeltaData,
907
- DeltaTableOptions,
908
- ChromeProps & { attemptHref?: (locator: AttemptLocator) => string }
909
- >;
910
-
911
- /** 成对对比:每行一对(字面声明或 pairsByFlag 派生),格子里 A、B、Δ 三个值。 */
912
- export const DeltaTable = makeDataComponent<
913
- DeltaData,
914
- DeltaTableOptions,
915
- ChromeProps & { attemptHref?: (locator: AttemptLocator) => string }
916
- >({
917
- name: "DeltaTable",
918
- dataFnName: "deltaTableData",
919
- shapeName: "DeltaData",
920
- dataFn: deltaTableData,
921
- specKeys: ["by", "pairs", "metrics", "evals"],
922
- validate: validateDeltaData,
923
- web: (props, ctx) => <DeltaTableWeb data={props.data} locale={props.locale ?? ctx.locale} className={props.className} />,
924
- text: (props, ctx) => deltaText(props.data, ctx),
925
- }) as unknown as ReportComponent<DeltaTableProps>;