niceeval 0.5.4 → 0.6.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 (342) hide show
  1. package/INDEX.md +55 -0
  2. package/dist/agents/types.d.ts +317 -0
  3. package/dist/context/types.d.ts +335 -0
  4. package/dist/i18n/core.d.ts +11 -0
  5. package/dist/i18n/en.d.ts +181 -0
  6. package/dist/i18n/index.d.ts +5 -0
  7. package/dist/i18n/zh-CN.d.ts +183 -0
  8. package/dist/o11y/execution-tree.d.ts +103 -0
  9. package/dist/o11y/otlp/parse.d.ts +3 -0
  10. package/dist/o11y/otlp/receiver.d.ts +21 -0
  11. package/dist/o11y/otlp/select.d.ts +22 -0
  12. package/dist/o11y/otlp/turn-otel.d.ts +43 -0
  13. package/dist/o11y/types.d.ts +190 -0
  14. package/dist/report/aggregate.d.ts +57 -0
  15. package/dist/report/aggregate.js +212 -0
  16. package/dist/report/built-ins/cost-pass-rate-comparison.d.ts +1 -0
  17. package/dist/report/built-ins/cost-pass-rate-comparison.js +17 -0
  18. package/dist/report/built-ins/index.d.ts +1 -0
  19. package/dist/report/built-ins/index.js +2 -0
  20. package/dist/report/components.d.ts +178 -0
  21. package/dist/report/components.js +132 -0
  22. package/dist/report/compute.d.ts +106 -0
  23. package/dist/report/compute.js +618 -0
  24. package/dist/report/flag.d.ts +6 -0
  25. package/dist/report/flag.js +8 -0
  26. package/dist/report/format.d.ts +32 -0
  27. package/dist/report/format.js +126 -0
  28. package/dist/report/index.d.ts +19 -0
  29. package/dist/report/index.js +30 -0
  30. package/dist/report/load.d.ts +12 -0
  31. package/dist/report/load.js +56 -0
  32. package/dist/report/locale.d.ts +83 -0
  33. package/dist/report/locale.js +160 -0
  34. package/dist/report/metrics.d.ts +21 -0
  35. package/dist/report/metrics.js +115 -0
  36. package/dist/report/primitives.d.ts +64 -0
  37. package/dist/report/primitives.js +113 -0
  38. package/dist/report/react/AttemptList.d.ts +23 -0
  39. package/dist/report/react/AttemptList.js +20 -0
  40. package/dist/report/react/DeltaTable.d.ts +8 -0
  41. package/dist/report/react/DeltaTable.js +24 -0
  42. package/dist/report/react/EvalList.d.ts +10 -0
  43. package/dist/report/react/EvalList.js +0 -0
  44. package/dist/report/react/ExperimentList.d.ts +10 -0
  45. package/dist/report/react/ExperimentList.js +15 -0
  46. package/dist/report/react/GroupSummary.d.ts +8 -0
  47. package/dist/report/react/GroupSummary.js +8 -0
  48. package/dist/report/react/MetricBars.d.ts +10 -0
  49. package/dist/report/react/MetricBars.js +48 -0
  50. package/dist/report/react/MetricLine.d.ts +9 -0
  51. package/dist/report/react/MetricLine.js +68 -0
  52. package/dist/report/react/MetricMatrix.d.ts +10 -0
  53. package/dist/report/react/MetricMatrix.js +23 -0
  54. package/dist/report/react/MetricScatter.d.ts +9 -0
  55. package/dist/report/react/MetricScatter.js +100 -0
  56. package/dist/report/react/MetricTable.d.ts +11 -0
  57. package/dist/report/react/MetricTable.js +26 -0
  58. package/dist/report/react/RunOverview.d.ts +8 -0
  59. package/dist/report/react/RunOverview.js +12 -0
  60. package/dist/report/react/Scoreboard.d.ts +8 -0
  61. package/dist/report/react/Scoreboard.js +26 -0
  62. package/dist/report/react/cell.d.ts +9 -0
  63. package/dist/report/react/cell.js +9 -0
  64. package/dist/report/react/chart-math.d.ts +14 -0
  65. package/dist/report/react/chart-math.js +68 -0
  66. package/dist/report/react/colors.d.ts +14 -0
  67. package/dist/report/react/colors.js +45 -0
  68. package/dist/report/react/fixtures.d.ts +19 -0
  69. package/dist/report/react/fixtures.js +393 -0
  70. package/dist/report/react/format.d.ts +3 -0
  71. package/dist/report/react/format.js +7 -0
  72. package/dist/report/react/index.d.ts +6 -0
  73. package/dist/report/react/index.js +14 -0
  74. package/dist/report/report.d.ts +24 -0
  75. package/dist/report/report.js +49 -0
  76. package/dist/report/text/faces.d.ts +29 -0
  77. package/dist/report/text/faces.js +503 -0
  78. package/dist/report/text/layout.d.ts +75 -0
  79. package/dist/report/text/layout.js +173 -0
  80. package/dist/report/text/plot.d.ts +37 -0
  81. package/dist/report/text/plot.js +81 -0
  82. package/dist/report/text/table.d.ts +8 -0
  83. package/dist/report/text/table.js +86 -0
  84. package/dist/report/tree.d.ts +99 -0
  85. package/dist/report/tree.js +228 -0
  86. package/dist/report/types.d.ts +420 -0
  87. package/dist/report/types.js +4 -0
  88. package/dist/report/web.d.ts +15 -0
  89. package/dist/report/web.js +37 -0
  90. package/dist/results/annotated-source.d.ts +61 -0
  91. package/dist/results/attempt-evidence.d.ts +69 -0
  92. package/dist/results/attempt-source.d.ts +15 -0
  93. package/dist/results/locator.d.ts +75 -0
  94. package/dist/results/locator.js +0 -0
  95. package/dist/results/select.d.ts +45 -0
  96. package/dist/results/select.js +230 -0
  97. package/dist/results/source-hash.d.ts +9 -0
  98. package/dist/results/types.d.ts +204 -0
  99. package/dist/runner/eval-source.d.ts +22 -0
  100. package/dist/runner/fingerprint.d.ts +23 -0
  101. package/dist/runner/types.d.ts +332 -0
  102. package/dist/sandbox/docker-stream.d.ts +24 -0
  103. package/dist/sandbox/docker.d.ts +84 -0
  104. package/dist/sandbox/e2b.d.ts +31 -0
  105. package/dist/sandbox/errors.d.ts +4 -0
  106. package/dist/sandbox/local-files.d.ts +6 -0
  107. package/dist/sandbox/paths.d.ts +4 -0
  108. package/dist/sandbox/registry.d.ts +13 -0
  109. package/dist/sandbox/resolve.d.ts +40 -0
  110. package/dist/sandbox/retry.d.ts +10 -0
  111. package/dist/sandbox/shell.d.ts +11 -0
  112. package/dist/sandbox/source-files.d.ts +13 -0
  113. package/dist/sandbox/types.d.ts +203 -0
  114. package/dist/sandbox/vercel.d.ts +37 -0
  115. package/dist/scoring/types.d.ts +70 -0
  116. package/dist/shared/aggregate.d.ts +29 -0
  117. package/dist/shared/aggregate.js +55 -0
  118. package/dist/shared/types.d.ts +27 -0
  119. package/dist/shared/verdict.d.ts +27 -0
  120. package/dist/shared/verdict.js +34 -0
  121. package/dist/tty-line.d.ts +6 -0
  122. package/dist/types.d.ts +7 -0
  123. package/dist/util.d.ts +22 -0
  124. package/docs-site/zh/concepts/adapter.mdx +5 -5
  125. package/docs-site/zh/concepts/assert.mdx +8 -8
  126. package/docs-site/zh/concepts/evals.mdx +5 -5
  127. package/docs-site/zh/concepts/experiment.mdx +2 -2
  128. package/docs-site/zh/concepts/judge.mdx +9 -9
  129. package/docs-site/zh/concepts/overview.mdx +2 -2
  130. package/docs-site/zh/concepts/tier.mdx +6 -6
  131. package/docs-site/zh/example/claude-code-codex-plugin.mdx +9 -5
  132. package/docs-site/zh/example/claude-code-codex-skill.mdx +12 -7
  133. package/docs-site/zh/guides/agent-feedback-loop.mdx +235 -78
  134. package/docs-site/zh/guides/connect-otel.mdx +3 -3
  135. package/docs-site/zh/guides/connect-your-agent.mdx +2 -2
  136. package/docs-site/zh/guides/custom-reports.mdx +196 -36
  137. package/docs-site/zh/guides/experiments.mdx +7 -1
  138. package/docs-site/zh/guides/fixtures.mdx +1 -1
  139. package/docs-site/zh/guides/official-adapters.mdx +31 -9
  140. package/docs-site/zh/guides/publish-report.mdx +3 -3
  141. package/docs-site/zh/guides/report-components.mdx +194 -55
  142. package/docs-site/zh/guides/reporters.mdx +2 -2
  143. package/docs-site/zh/guides/results-data.mdx +35 -34
  144. package/docs-site/zh/guides/runner.mdx +2 -2
  145. package/docs-site/zh/guides/sandbox-agent.mdx +2 -2
  146. package/docs-site/zh/guides/{sandbox-backends.mdx → sandbox-providers.mdx} +11 -11
  147. package/docs-site/zh/guides/viewing-results.mdx +199 -79
  148. package/docs-site/zh/guides/write-experiment.mdx +9 -1
  149. package/docs-site/zh/guides/write-send.mdx +2 -2
  150. package/docs-site/zh/index.mdx +4 -3
  151. package/docs-site/zh/introduction.mdx +2 -1
  152. package/docs-site/zh/quickstart.mdx +5 -3
  153. package/docs-site/zh/reference/builtin-agents.mdx +38 -13
  154. package/docs-site/zh/reference/capabilities.mdx +1 -1
  155. package/docs-site/zh/reference/cli.mdx +36 -13
  156. package/docs-site/zh/reference/define-agent.mdx +12 -12
  157. package/docs-site/zh/reference/define-config.mdx +1 -1
  158. package/docs-site/zh/reference/define-eval.mdx +3 -3
  159. package/docs-site/zh/reference/events.mdx +7 -1
  160. package/docs-site/zh/reference/expect.mdx +3 -3
  161. package/package.json +10 -6
  162. package/src/agents/bub.ts +129 -42
  163. package/src/agents/claude-code.test.ts +152 -0
  164. package/src/agents/claude-code.ts +127 -13
  165. package/src/agents/codex.test.ts +192 -0
  166. package/src/agents/codex.ts +151 -12
  167. package/src/agents/index.ts +9 -3
  168. package/src/agents/manifest.ts +41 -0
  169. package/src/agents/plugin-config.test.ts +94 -0
  170. package/src/agents/shared.ts +33 -2
  171. package/src/agents/skills.test.ts +214 -0
  172. package/src/agents/skills.ts +304 -0
  173. package/src/agents/streaming.ts +5 -5
  174. package/src/agents/types.ts +54 -2
  175. package/src/cli.ts +74 -47
  176. package/src/context/context.test.ts +20 -2
  177. package/src/context/control-flow.ts +1 -1
  178. package/src/context/session.test.ts +92 -1
  179. package/src/context/session.ts +23 -1
  180. package/src/context/types.ts +4 -4
  181. package/src/define.test.ts +8 -8
  182. package/src/define.ts +11 -11
  183. package/src/expect/index.ts +5 -5
  184. package/src/i18n/en.ts +34 -15
  185. package/src/i18n/zh-CN.ts +37 -21
  186. package/src/index.ts +1 -1
  187. package/src/o11y/execution-tree.test.ts +452 -0
  188. package/src/o11y/execution-tree.ts +367 -0
  189. package/src/o11y/otlp/receiver.ts +1 -1
  190. package/src/o11y/otlp/select.ts +7 -3
  191. package/src/o11y/parsers/claude-code.test.ts +175 -0
  192. package/src/o11y/parsers/claude-code.ts +32 -0
  193. package/src/o11y/types.ts +6 -1
  194. package/src/report/aggregate.ts +26 -20
  195. package/src/report/built-in-user-parity.test.tsx +643 -0
  196. package/src/report/built-ins/cost-pass-rate-comparison.tsx +23 -0
  197. package/src/report/built-ins/index.ts +2 -0
  198. package/src/report/components.tsx +223 -50
  199. package/src/report/compute.ts +358 -53
  200. package/src/report/dual-render.test.tsx +1125 -0
  201. package/src/report/flag.ts +1 -1
  202. package/src/report/format.ts +50 -2
  203. package/src/report/index.ts +48 -14
  204. package/src/report/load.ts +1 -1
  205. package/src/report/locale.ts +206 -0
  206. package/src/report/metrics.ts +39 -15
  207. package/src/report/primitives.tsx +100 -2
  208. package/src/report/react/AttemptList.tsx +115 -0
  209. package/src/report/react/DeltaTable.tsx +9 -6
  210. package/src/report/react/EvalList.tsx +0 -0
  211. package/src/report/react/ExperimentList.tsx +108 -0
  212. package/src/report/react/GroupSummary.tsx +66 -0
  213. package/src/report/react/MetricBars.tsx +24 -13
  214. package/src/report/react/MetricLine.tsx +21 -21
  215. package/src/report/react/MetricMatrix.tsx +8 -4
  216. package/src/report/react/MetricScatter.tsx +136 -96
  217. package/src/report/react/MetricTable.tsx +115 -15
  218. package/src/report/react/RunOverview.tsx +48 -26
  219. package/src/report/react/Scoreboard.tsx +15 -7
  220. package/src/report/react/cell.tsx +18 -9
  221. package/src/{view/app/lib/chart.ts → report/react/chart-math.ts} +2 -6
  222. package/src/report/react/colors.ts +24 -15
  223. package/src/report/react/enhance.js +129 -0
  224. package/src/report/react/fixtures.ts +163 -34
  225. package/src/report/react/format.ts +1 -1
  226. package/src/report/react/index.tsx +22 -6
  227. package/src/report/react/render.test.tsx +164 -31
  228. package/src/report/react/styles.css +405 -213
  229. package/src/report/report.test.ts +403 -94
  230. package/src/report/report.ts +29 -11
  231. package/src/report/text/faces.ts +303 -95
  232. package/src/report/text/layout.ts +76 -11
  233. package/src/report/text/table.ts +98 -0
  234. package/src/report/tree.ts +103 -20
  235. package/src/report/types.ts +196 -25
  236. package/src/report/web.ts +46 -19
  237. package/src/results/annotated-source.test.ts +146 -0
  238. package/src/results/annotated-source.ts +128 -0
  239. package/src/results/attempt-evidence.test.ts +158 -0
  240. package/src/results/attempt-evidence.ts +158 -0
  241. package/src/results/attempt-source.ts +48 -0
  242. package/src/results/copy.ts +154 -139
  243. package/src/results/format.ts +54 -47
  244. package/src/results/host-equivalence.test.ts +527 -0
  245. package/src/results/index.ts +56 -14
  246. package/src/results/locator.test.ts +198 -0
  247. package/src/results/locator.ts +0 -0
  248. package/src/results/open.ts +392 -175
  249. package/src/results/results.test.ts +943 -494
  250. package/src/results/select.ts +129 -23
  251. package/src/results/skipped-notice.ts +0 -0
  252. package/src/results/source-hash.ts +28 -0
  253. package/src/results/types.ts +98 -62
  254. package/src/results/writer.ts +304 -176
  255. package/src/runner/attempt.test.ts +170 -0
  256. package/src/runner/attempt.ts +48 -13
  257. package/src/runner/discover.test.ts +65 -0
  258. package/src/runner/discover.ts +6 -2
  259. package/src/runner/eval-source.test.ts +121 -0
  260. package/src/runner/eval-source.ts +45 -0
  261. package/src/runner/fingerprint.ts +54 -1
  262. package/src/runner/remote-sandbox.ts +1 -1
  263. package/src/runner/report.test.ts +6 -8
  264. package/src/runner/report.ts +5 -4
  265. package/src/runner/reporters/artifacts.ts +27 -41
  266. package/src/runner/reporters/braintrust.test.ts +3 -3
  267. package/src/runner/reporters/braintrust.ts +1 -1
  268. package/src/runner/reporters/console.ts +6 -6
  269. package/src/runner/reporters/json.ts +4 -4
  270. package/src/runner/reporters/live.test.ts +56 -0
  271. package/src/runner/reporters/live.ts +36 -10
  272. package/src/runner/reporters/quiet.test.ts +66 -0
  273. package/src/runner/reporters/quiet.ts +49 -0
  274. package/src/runner/reporters/shared.ts +5 -5
  275. package/src/runner/reporters/table.ts +17 -17
  276. package/src/runner/run.ts +61 -102
  277. package/src/runner/types.ts +43 -32
  278. package/src/sandbox/checkpoint.ts +1 -1
  279. package/src/sandbox/docker.ts +15 -2
  280. package/src/sandbox/e2b.ts +11 -5
  281. package/src/sandbox/errors.ts +13 -0
  282. package/src/sandbox/index.ts +3 -3
  283. package/src/sandbox/local-files.ts +1 -1
  284. package/src/sandbox/registry.ts +6 -5
  285. package/src/sandbox/resolve.ts +38 -23
  286. package/src/sandbox/retry.test.ts +68 -0
  287. package/src/sandbox/retry.ts +44 -0
  288. package/src/sandbox/types.ts +24 -24
  289. package/src/sandbox/vercel.ts +17 -3
  290. package/src/scoring/collector.ts +3 -3
  291. package/src/scoring/judge.ts +2 -2
  292. package/src/scoring/scoped.ts +13 -1
  293. package/src/scoring/types.ts +7 -7
  294. package/src/scoring/verdict.ts +4 -4
  295. package/src/shared/aggregate.ts +23 -3
  296. package/src/shared/types.ts +2 -2
  297. package/src/shared/{outcome.ts → verdict.ts} +15 -15
  298. package/src/show/compose.ts +12 -132
  299. package/src/show/index.ts +125 -94
  300. package/src/show/render.ts +660 -160
  301. package/src/show/show.test.ts +472 -253
  302. package/src/source-loc.ts +1 -1
  303. package/src/tty-line.ts +32 -0
  304. package/src/types.ts +2 -2
  305. package/src/util.test.ts +21 -1
  306. package/src/util.ts +5 -1
  307. package/src/view/app/App.tsx +33 -198
  308. package/src/view/app/components/AttemptModal.tsx +6 -6
  309. package/src/view/app/components/CodeView.tsx +2 -2
  310. package/src/view/app/components/CopyControls.tsx +32 -36
  311. package/src/view/app/components/SkippedRunsBanner.tsx +140 -0
  312. package/src/view/app/components/ui/badge.tsx +1 -1
  313. package/src/view/app/components/ui/dialog.tsx +2 -2
  314. package/src/view/app/i18n.ts +40 -31
  315. package/src/view/app/lib/artifact-url.ts +1 -1
  316. package/src/view/app/lib/attempt-route.test.ts +37 -47
  317. package/src/view/app/lib/attempt-route.ts +26 -26
  318. package/src/view/app/lib/rows.ts +9 -112
  319. package/src/view/app/lib/verdict.ts +25 -0
  320. package/src/view/app/main.tsx +9 -12
  321. package/src/view/app/pages/RunsPage.tsx +4 -4
  322. package/src/view/app/pages/TracesPage.tsx +3 -3
  323. package/src/view/app/shared.ts +0 -1
  324. package/src/view/app/types.ts +6 -51
  325. package/src/view/artifact-serving.test.ts +136 -0
  326. package/src/view/client-dist/app.css +1 -1
  327. package/src/view/client-dist/app.js +21 -21
  328. package/src/view/data.test.ts +214 -143
  329. package/src/view/data.ts +166 -120
  330. package/src/view/index.ts +23 -11
  331. package/src/view/server.ts +52 -17
  332. package/src/view/shared/types.ts +22 -31
  333. package/src/view/styles.css +117 -327
  334. package/src/view/view-report.test.ts +146 -98
  335. package/src/report/default-report.tsx +0 -222
  336. package/src/report/dual-face.test.tsx +0 -527
  337. package/src/report/react/CaseList.tsx +0 -70
  338. package/src/view/app/components/CostScoreChart.tsx +0 -147
  339. package/src/view/app/components/ExperimentTable.tsx +0 -284
  340. package/src/view/app/components/GroupSelector.tsx +0 -61
  341. package/src/view/app/components/primitives.tsx +0 -43
  342. package/src/view/app/lib/outcome.ts +0 -82
@@ -0,0 +1,212 @@
1
+ // 两级聚合引擎:去重 → 按维度分组 → 组内按 (eval × 快照) 折叠(perEval)→ 跨题折叠(across)→ MetricCell。
2
+ //
3
+ // 为什么是两级:earlyExit 默认开,失败的题天然比通过的题样本多;平铺求均值会把分数
4
+ // 和重试策略纠缠在一起(eval A=[1]、eval B=[0,0,0] 平铺 = 0.25,两级宏平均 = 0.5)。
5
+ // 自定义维度把同一道题的 attempt 分进不同组时,第一级折叠发生在各组内部。
6
+ import { dedupeAttempts } from "../results/select.js";
7
+ import { encodeAttemptLocator } from "../results/locator.js";
8
+ import { formatMetricValue } from "./format.js";
9
+ import { evalPrefixPredicate } from "../shared/aggregate.js";
10
+ // 复合键分隔符:NUL 不会出现在 eval id / experimentId / ISO 时间里,拼接键不会串味
11
+ const KEY_SEP = "\u0000";
12
+ /** flag 未声明时的组名:不猜,如实归一组。 */
13
+ export const FLAG_UNSET = "(unset)";
14
+ export function resolveInput(input) {
15
+ if (Array.isArray(input))
16
+ return { snapshots: input, warnings: [] };
17
+ return { snapshots: input.snapshots, warnings: input.warnings };
18
+ }
19
+ export function experimentIdOf(item) {
20
+ return item.attempt.experimentId || item.snapshot.experimentId;
21
+ }
22
+ export function evalIdOf(item) {
23
+ return item.attempt.evalId || item.attempt.result.id;
24
+ }
25
+ /** 快照键:与 view Compare 同口径的 "<experimentId> @ <startedAt>"。 */
26
+ export function snapshotKeyOf(snapshot) {
27
+ return `${snapshot.experimentId} @ ${snapshot.startedAt}`;
28
+ }
29
+ /**
30
+ * 一条 Item 的 AttemptLocator:真实读取路径(openResults() 产出的 handle)恒有
31
+ * `attempt.locator`;手工构造的测试 fixture 若省略它,按当前身份元组兜底算一份
32
+ * (与 `loadAttemptEvidence`、`open.ts` 的回填同一口径)——只服务这类场景,不改变真实
33
+ * 读取路径的行为。
34
+ */
35
+ export function locatorOf(item) {
36
+ return (item.attempt.locator ??
37
+ encodeAttemptLocator({
38
+ experimentId: experimentIdOf(item),
39
+ snapshotStartedAt: item.snapshot.startedAt,
40
+ evalId: evalIdOf(item),
41
+ attempt: item.attempt.result.attempt,
42
+ }));
43
+ }
44
+ /**
45
+ * 展平 + 聚合前去重(niceeval/results 的 dedupeAttempts,身份键
46
+ * (experimentId, evalId, attempt, startedAt))。missing-startedAt 的警告不透出:
47
+ * 官方产出永不缺 startedAt,缺失只可能来自 legacy 落盘,「不去重、如实保留重复」即终稿。
48
+ */
49
+ export function collectItems(snapshots) {
50
+ const snapshotByAttempt = new Map();
51
+ const flattened = [];
52
+ for (const snapshot of snapshots) {
53
+ for (const attempt of snapshot.attempts) {
54
+ // 同一 handle 对象出现在两个快照里时以首次归属为准(手工挑重叠快照的极端场景)
55
+ if (!snapshotByAttempt.has(attempt))
56
+ snapshotByAttempt.set(attempt, snapshot);
57
+ flattened.push(attempt);
58
+ }
59
+ }
60
+ const { attempts } = dedupeAttempts(flattened);
61
+ return attempts.map((attempt) => ({ attempt, snapshot: snapshotByAttempt.get(attempt) }));
62
+ }
63
+ export { evalPrefixPredicate };
64
+ export function filterItems(items, evals) {
65
+ if (evals === undefined)
66
+ return items;
67
+ const match = evalPrefixPredicate(evals);
68
+ return items.filter((item) => match(evalIdOf(item)));
69
+ }
70
+ // ───────────────────────── 维度 ─────────────────────────
71
+ export function dimensionName(dimension) {
72
+ return typeof dimension === "string" ? dimension : dimension.name;
73
+ }
74
+ /** eval id 的第一段:"algebra/quadratic" → "algebra";没有 "/" 时就是 id 本身。 */
75
+ export function evalGroupOf(id) {
76
+ const slash = id.indexOf("/");
77
+ return slash === -1 ? id : id.slice(0, slash);
78
+ }
79
+ function isFlagRef(dimension) {
80
+ return typeof dimension === "object" && "kind" in dimension && dimension.kind === "flag";
81
+ }
82
+ /** experiment 声明的 flags(经 runner 原样透传进持久化字段 ExperimentRunInfo.flags)。 */
83
+ function flagsOf(attempt) {
84
+ return attempt.result.experiment?.flags;
85
+ }
86
+ /** flag 声明值 → 组标签:label 函数优先,其余 String();未声明 → FLAG_UNSET。 */
87
+ export function flagGroupKey(ref, item) {
88
+ const value = flagsOf(item.attempt)?.[ref.name];
89
+ 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);
95
+ }
96
+ /** flag 作轴:要求数值;未声明或非数值 → null(点不画,注脚报数)。 */
97
+ export function flagAxisValue(ref, item) {
98
+ const value = flagsOf(item.attempt)?.[ref.name];
99
+ return typeof value === "number" ? value : null;
100
+ }
101
+ export function dimensionKey(dimension, item) {
102
+ if (typeof dimension !== "string") {
103
+ if (isFlagRef(dimension))
104
+ return flagGroupKey(dimension, item);
105
+ return dimension.of(item.attempt);
106
+ }
107
+ const result = item.attempt.result;
108
+ switch (dimension) {
109
+ case "agent":
110
+ return result.agent;
111
+ case "model":
112
+ return result.model ?? item.snapshot.model ?? "(none)";
113
+ case "experiment":
114
+ return experimentIdOf(item);
115
+ case "eval":
116
+ return evalIdOf(item);
117
+ case "evalGroup":
118
+ return evalGroupOf(evalIdOf(item));
119
+ case "snapshot":
120
+ return snapshotKeyOf(item.snapshot);
121
+ default: {
122
+ // 穷尽检查:新增内置维度而漏改这里时编译期报错
123
+ const exhausted = dimension;
124
+ throw new Error(`Unknown dimension: ${String(exhausted)}`);
125
+ }
126
+ }
127
+ }
128
+ /** 按维度分组,保持首次出现顺序(无 sort 时表格行序即此序)。 */
129
+ export function groupItems(items, dimension) {
130
+ const groups = new Map();
131
+ for (const item of items) {
132
+ const key = dimensionKey(dimension, item);
133
+ const list = groups.get(key);
134
+ if (list)
135
+ list.push(item);
136
+ else
137
+ groups.set(key, [item]);
138
+ }
139
+ return groups;
140
+ }
141
+ // ───────────────────────── 聚合 ─────────────────────────
142
+ export function applyAggregator(aggregator, values) {
143
+ if (typeof aggregator === "function")
144
+ return aggregator(values);
145
+ switch (aggregator) {
146
+ case "mean":
147
+ return values.reduce((a, b) => a + b, 0) / values.length;
148
+ case "sum":
149
+ return values.reduce((a, b) => a + b, 0);
150
+ case "min":
151
+ return Math.min(...values);
152
+ case "max":
153
+ return Math.max(...values);
154
+ }
155
+ }
156
+ /** where 不满足 → null,语义等价于 value 开头 return null。 */
157
+ export async function evaluateMetric(metric, attempt) {
158
+ if (metric.where && !metric.where(attempt))
159
+ return null;
160
+ return metric.value(attempt);
161
+ }
162
+ export function displayValue(metric, value) {
163
+ // null 的纯文本兜底;组件把 null 渲染成「缺数据」,绝不画 0
164
+ if (value === null)
165
+ return "—";
166
+ return metric.display ? metric.display(value) : formatMetricValue(value, metric.unit);
167
+ }
168
+ /**
169
+ * 一个格子:组内 attempt → 两级聚合 → 终值。
170
+ * null 值不进聚合但计入 total(覆盖率经 samples/total 如实暴露);全 null → value null。
171
+ */
172
+ export async function computeCell(metric, items) {
173
+ // 第一级桶:同一 (eval × 快照) 的 attempt 折成一个题级值
174
+ const buckets = new Map();
175
+ const refs = [];
176
+ let samples = 0;
177
+ for (const item of items) {
178
+ const value = await evaluateMetric(metric, item.attempt);
179
+ if (value === null)
180
+ continue;
181
+ samples += 1;
182
+ refs.push(locatorOf(item)); // 证据引用由句柄直供,不反查下标
183
+ const bucketKey = `${evalIdOf(item)}${KEY_SEP}${snapshotKeyOf(item.snapshot)}`;
184
+ const bucket = buckets.get(bucketKey);
185
+ if (bucket)
186
+ bucket.push(value);
187
+ else
188
+ buckets.set(bucketKey, [value]);
189
+ }
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 };
195
+ }
196
+ export function toColumn(metric) {
197
+ return {
198
+ key: metric.name,
199
+ label: metric.label ?? metric.name,
200
+ unit: metric.unit,
201
+ better: metric.better,
202
+ };
203
+ }
204
+ export function assertUniqueMetricNames(metrics, where) {
205
+ const seen = new Set();
206
+ for (const metric of metrics) {
207
+ if (seen.has(metric.name)) {
208
+ throw new Error(`Duplicate metric name "${metric.name}" in ${where}. Metric names must be unique within one computation; rename one via defineMetric.`);
209
+ }
210
+ seen.add(metric.name);
211
+ }
212
+ }
@@ -0,0 +1 @@
1
+ export declare const CostPassRateComparison: import("../report.ts").ReportDefinition;
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // CostPassRateComparison:show / view 裸跑时的出厂报告槽。
3
+ //
4
+ // 它是一份普通 ReportDefinition,零特权:只声明「摆什么」——成本 × 通过率的散点(比较
5
+ // experiments)和实验列表。散点走 selection-form(`MetricScatter` 声明了 `resolve`,宿主
6
+ // 渲染前的 resolveReportTree 会替它调 `.data`);`ExperimentList` 没有 selection-form,
7
+ // 报告函数体里直接 `await ExperimentList.data(selection)` 拿到普通数组再传 `items`——
8
+ // 这正是三个实体列表统一的用法,内置默认报告不搞一套特权写法。包外用户复制这段 TSX
9
+ // (只改 import 路径与 export 形式)会走完全相同的解析与渲染管线。
10
+ import { Col } from "../primitives.js";
11
+ import { ExperimentList, MetricScatter } from "../components.js";
12
+ import { costUSD, passRate } from "../metrics.js";
13
+ import { defineReport } from "../report.js";
14
+ export const CostPassRateComparison = defineReport(async ({ selection }) => {
15
+ const experiments = await ExperimentList.data(selection);
16
+ return (_jsxs(Col, { children: [_jsx(MetricScatter, { selection: selection, points: "experiment", series: "agent", x: costUSD, y: passRate }), _jsx(ExperimentList, { items: experiments })] }));
17
+ });
@@ -0,0 +1 @@
1
+ export { CostPassRateComparison } from "./cost-pass-rate-comparison.tsx";
@@ -0,0 +1,2 @@
1
+ // 内置报告目录:一个文件一个按展示内容命名的普通 ReportDefinition,显式导出值,不建字符串 registry。
2
+ export { CostPassRateComparison } from "./cost-pass-rate-comparison.js";
@@ -0,0 +1,178 @@
1
+ import type { ReportComponent } from "./tree.ts";
2
+ import type { ReportLocale } from "./locale.ts";
3
+ import type { AttemptLocator } from "../results/locator.ts";
4
+ import type { Selection } from "../results/types.ts";
5
+ import type { AttemptListItem, DeltaData, EvalListItem, ExperimentListItem, GroupSummaryData, LineData, MatrixData, OverviewData, ScatterData, ScoreboardData, TableData } from "./types.ts";
6
+ import { attemptListData, deltaData, evalListData, experimentListData, groupSummaryData, lineData, matrixData, overviewData, scatterData, scoreboardData, tableData } from "./compute.ts";
7
+ import type { ScatterDataOptions } from "./compute.ts";
8
+ export interface RunOverviewProps {
9
+ data: OverviewData;
10
+ /** chrome 文案 locale;省略时随宿主上下文(宿主外默认 "en")。 */
11
+ locale?: ReportLocale;
12
+ className?: string;
13
+ }
14
+ export interface GroupSummaryProps {
15
+ data: GroupSummaryData;
16
+ /** chrome 文案 locale;省略时随宿主上下文(宿主外默认 "en")。 */
17
+ locale?: ReportLocale;
18
+ className?: string;
19
+ }
20
+ /**
21
+ * 三个实体列表都只收算好的 `items`(`.data(selection)` 的产物,报告作者先 `.filter()`/
22
+ * `.slice()` 再传入)——没有 selection-form,组件不提供另一套过滤 DSL
23
+ * (docs/feature/reports/library.md「实体列表」)。
24
+ */
25
+ export interface ExperimentListProps {
26
+ items: ExperimentListItem[];
27
+ /** chrome 文案 locale;省略时随宿主上下文(宿主外默认 "en")。 */
28
+ locale?: ReportLocale;
29
+ className?: string;
30
+ }
31
+ export interface EvalListProps {
32
+ items: EvalListItem[];
33
+ /** chrome 文案 locale;省略时随宿主上下文(宿主外默认 "en")。 */
34
+ locale?: ReportLocale;
35
+ className?: string;
36
+ }
37
+ export interface AttemptListProps {
38
+ items: AttemptListItem[];
39
+ /** items 被 slice 时如实显示剩余数量;省略即 items.length(未截断)。 */
40
+ total?: number;
41
+ /** chrome 文案 locale;省略时随宿主上下文(宿主外默认 "en")。 */
42
+ locale?: ReportLocale;
43
+ className?: string;
44
+ }
45
+ export interface MetricTableProps {
46
+ data: TableData;
47
+ /** 传了,格子可点、下钻去处你定;不传,宿主里走证据室深链,宿主外纯展示。 */
48
+ attemptHref?: (locator: AttemptLocator) => string;
49
+ /**
50
+ * web 面在表格前渲染一个过滤输入框(`<input class="nre-filter" data-nre-filter>`),
51
+ * 由渐进增强 runtime(enhance.js)接管:输入过滤行 textContent。无 JS 时输入框
52
+ * 静默无功能,表格内容依旧完整可读。默认 false;text 面不受影响。
53
+ */
54
+ filter?: boolean;
55
+ /** chrome 文案 locale;省略时随宿主上下文(宿主外默认 "en")。 */
56
+ locale?: ReportLocale;
57
+ className?: string;
58
+ }
59
+ export interface MetricMatrixProps {
60
+ data: MatrixData;
61
+ attemptHref?: (locator: AttemptLocator) => string;
62
+ /** chrome 文案 locale;省略时随宿主上下文(宿主外默认 "en")。 */
63
+ locale?: ReportLocale;
64
+ className?: string;
65
+ }
66
+ export interface ScoreboardProps {
67
+ data: ScoreboardData;
68
+ /** chrome 文案 locale;省略时随宿主上下文(宿主外默认 "en")。 */
69
+ locale?: ReportLocale;
70
+ className?: string;
71
+ }
72
+ /** MetricScatter 两臂共享的纯渲染选项(不含数据来源)。 */
73
+ interface MetricScatterRenderProps {
74
+ /** 点一个点 → 该配置的下钻页。 */
75
+ pointHref?: (row: ScatterData["rows"][number]) => string;
76
+ /** chrome 文案 locale;省略时随宿主上下文(宿主外默认 "en")。 */
77
+ locale?: ReportLocale;
78
+ className?: string;
79
+ }
80
+ /** resolve 之后 web / text 面看到的形态:数据已备好,零 IO。 */
81
+ export interface MetricScatterResolvedProps extends MetricScatterRenderProps {
82
+ data: ScatterData;
83
+ }
84
+ /**
85
+ * 互斥两臂:要么直接给算好的 `data`(用于用户自己的 React 页面 / 预计算 / 跨边界序列化),
86
+ * 要么给 `selection` + 计算选项让宿主在渲染前解析(见 tree.ts 的 resolveReportTree)。
87
+ * 同时传 `data` 与 `selection`、或两者都不传,都在 typecheck 阶段失败。计算选项复用
88
+ * `ScatterDataOptions`,不手写会漂移的副本。
89
+ */
90
+ export type MetricScatterProps = (MetricScatterResolvedProps & {
91
+ selection?: never;
92
+ points?: never;
93
+ series?: never;
94
+ x?: never;
95
+ y?: never;
96
+ }) | ({
97
+ data?: never;
98
+ } & ScatterDataOptions & MetricScatterRenderProps & {
99
+ selection: Selection;
100
+ });
101
+ export interface MetricLineProps {
102
+ data: LineData;
103
+ pointHref?: (row: LineData["rows"][number]) => string;
104
+ /** chrome 文案 locale;省略时随宿主上下文(宿主外默认 "en")。 */
105
+ locale?: ReportLocale;
106
+ className?: string;
107
+ }
108
+ export interface DeltaTableProps {
109
+ data: DeltaData;
110
+ /** chrome 文案 locale;省略时随宿主上下文(宿主外默认 "en")。 */
111
+ locale?: ReportLocale;
112
+ className?: string;
113
+ }
114
+ /** 页头 KPI 条:何时跑的、几个配置、几道题、通过率、总成本;Selection 的警告随行显示在条内。 */
115
+ export declare const RunOverview: ReportComponent<RunOverviewProps> & {
116
+ data: typeof overviewData;
117
+ };
118
+ /**
119
+ * 组摘要:一组 experiment(典型用法是同一 `<Section>` 内的全部 experiment)的紧凑统计——
120
+ * 通过率(旧 GroupSelector 卡片口径)、experiment/eval/attempt 数、eval 级折叠计票、
121
+ * 总成本、最后运行时间。
122
+ */
123
+ export declare const GroupSummary: ReportComponent<GroupSummaryProps> & {
124
+ data: typeof groupSummaryData;
125
+ };
126
+ /**
127
+ * 实验列表:每项一个 experiment,固定展示身份、配置(flags)、Eval 判定构成和官方两级聚合
128
+ * 汇总指标;展开到这个 experiment 的 Eval,每道题内联该题全部 Attempt 的 locator 徽标。
129
+ * 没有 selection-form:`ExperimentList.data(selection)` 产出普通数组,过滤由报告作者对
130
+ * 数组调用 `.filter()`。
131
+ */
132
+ export declare const ExperimentList: ReportComponent<ExperimentListProps> & {
133
+ data: typeof experimentListData;
134
+ };
135
+ /**
136
+ * Eval 列表:每项一个 `experimentId + evalId`,固定展示判定、Attempt 数、分数、成本、耗时
137
+ * 与失败原因;展开到这道题的 Attempt(与 AttemptList 同一份 AttemptListItem)。
138
+ */
139
+ export declare const EvalList: ReportComponent<EvalListProps> & {
140
+ data: typeof evalListData;
141
+ };
142
+ /**
143
+ * Attempt 列表:实体列表的叶子层,每项一个 Attempt,固定展示判定、断言、error、Judge 评语
144
+ * (assertions 的 detail/evidence)与证据引用(locator + 证据能力标记)。它不预设只看失败;
145
+ * 报告作者过滤 `AttemptListItem[]`、用 `.slice()` 限量,`total` 让渲染面如实报告剩余数量。
146
+ */
147
+ export declare const AttemptList: ReportComponent<AttemptListProps> & {
148
+ data: typeof attemptListData;
149
+ };
150
+ /** 榜单:一行一个维度值、一列一个指标,回答「谁整体更好」。 */
151
+ export declare const MetricTable: ReportComponent<MetricTableProps> & {
152
+ data: typeof tableData;
153
+ };
154
+ /** 逐题格子:行 × 列两个维度、格子里一个指标,回答「哪道题谁挂了」。 */
155
+ export declare const MetricMatrix: ReportComponent<MetricMatrixProps> & {
156
+ data: typeof matrixData;
157
+ };
158
+ /** 分组条形:同一份矩阵数据的另一种摆法;MetricBars.data 就是 MetricMatrix.data 的别名。 */
159
+ export declare const MetricBars: ReportComponent<MetricMatrixProps> & {
160
+ data: typeof matrixData;
161
+ };
162
+ /** 考试成绩单:总分 + 分科小计,固定分母、missing 如实报。 */
163
+ export declare const Scoreboard: ReportComponent<ScoreboardProps> & {
164
+ data: typeof scoreboardData;
165
+ };
166
+ /** 质量 × 成本 frontier:每个点一个配置、两个指标各占一轴,「好」的角落恒在右上。 */
167
+ export declare const MetricScatter: ReportComponent<MetricScatterProps> & {
168
+ data: typeof scatterData;
169
+ };
170
+ /** 趋势线:x 是 experiment 声明的 flag(flag()),同系列按 x 排序连线。 */
171
+ export declare const MetricLine: ReportComponent<MetricLineProps> & {
172
+ data: typeof lineData;
173
+ };
174
+ /** 成对对比:每行一对配置、格子里 A、B、Δ 三个值,涨跌好坏由 better 判定。 */
175
+ export declare const DeltaTable: ReportComponent<DeltaTableProps> & {
176
+ data: typeof deltaData;
177
+ };
178
+ export {};
@@ -0,0 +1,132 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // 官方双面组件的装配点:web 面(./react/ 的纯 React 组件)+ text 面(./text/faces.ts)
3
+ // + 挂在组件上的 data 计算函数。faces 两键必填 —— 配对是结构义务,不是配对表;
4
+ // MetricBars.data 就是 MetricMatrix.data(同一份矩阵数据的另一种摆法),别名显式化。
5
+ //
6
+ // 官方组件在宿主里自动接上证据室:web 面的 attemptHref 缺省取 ctx.attemptHref
7
+ // (宿主注入的证据室深链);显式传 prop 可覆盖(嵌进自己应用时自定去处)。三个实体列表
8
+ // (ExperimentList / EvalList / AttemptList)没有这个覆盖口子——它们的 locator 徽标恒经
9
+ // ctx.attemptHref / ctx.attemptCommand,不接受 attemptHref/attemptCommand prop(docs/feature/reports/library.md
10
+ // 「嵌入自己的 React 页面」的函数签名没有这个参数),证据室深链在这三个组件上不是可选行为。
11
+ import { defineComponent, isHostWebContextActive } from "./tree.js";
12
+ import { attemptListData, deltaData, evalListData, experimentListData, groupSummaryData, lineData, matrixData, overviewData, scatterData, scoreboardData, tableData, } from "./compute.js";
13
+ import { attemptListText, barsText, deltaText, evalListText, experimentListText, groupSummaryText, lineText, matrixText, overviewText, scatterText, scoreboardText, tableText, } from "./text/faces.js";
14
+ import { RunOverview as RunOverviewWeb } from "./react/RunOverview.js";
15
+ import { GroupSummary as GroupSummaryWeb } from "./react/GroupSummary.js";
16
+ import { ExperimentList as ExperimentListWeb } from "./react/ExperimentList.js";
17
+ import { EvalList as EvalListWeb } from "./react/EvalList.js";
18
+ import { AttemptList as AttemptListWeb } from "./react/AttemptList.js";
19
+ import { MetricTable as MetricTableWeb } from "./react/MetricTable.js";
20
+ import { MetricMatrix as MetricMatrixWeb } from "./react/MetricMatrix.js";
21
+ import { MetricBars as MetricBarsWeb } from "./react/MetricBars.js";
22
+ import { Scoreboard as ScoreboardWeb } from "./react/Scoreboard.js";
23
+ import { MetricScatter as MetricScatterWeb } from "./react/MetricScatter.js";
24
+ import { MetricLine as MetricLineWeb } from "./react/MetricLine.js";
25
+ import { DeltaTable as DeltaTableWeb } from "./react/DeltaTable.js";
26
+ // ───────────────────────── 装配 ─────────────────────────
27
+ const SCATTER_UNRESOLVED_MESSAGE = "MetricScatter received unresolved (selection-form) props outside the report host pipeline. " +
28
+ "Render through defineReport + niceeval show/view (or renderReportToText/renderReportToStaticHtml), " +
29
+ "or precompute with `await MetricScatter.data(selection, options)` and pass the result as `data`.";
30
+ /** 渲染面(web / text)只吃已解析的数据形态;selection 形态漏解析时直说,而不是画一张空组件。 */
31
+ function requireScatterData(props) {
32
+ if (props.data === undefined)
33
+ throw new Error(SCATTER_UNRESOLVED_MESSAGE);
34
+ return props.data;
35
+ }
36
+ /** 页头 KPI 条:何时跑的、几个配置、几道题、通过率、总成本;Selection 的警告随行显示在条内。 */
37
+ export const RunOverview = Object.assign(defineComponent({
38
+ web: (props, ctx) => _jsx(RunOverviewWeb, { ...props, locale: props.locale ?? ctx.locale }),
39
+ text: ({ data }, ctx) => overviewText(data, ctx),
40
+ }), { data: overviewData });
41
+ RunOverview.displayName = "RunOverview";
42
+ /**
43
+ * 组摘要:一组 experiment(典型用法是同一 `<Section>` 内的全部 experiment)的紧凑统计——
44
+ * 通过率(旧 GroupSelector 卡片口径)、experiment/eval/attempt 数、eval 级折叠计票、
45
+ * 总成本、最后运行时间。
46
+ */
47
+ export const GroupSummary = Object.assign(defineComponent({
48
+ web: (props, ctx) => _jsx(GroupSummaryWeb, { ...props, locale: props.locale ?? ctx.locale }),
49
+ text: ({ data }, ctx) => groupSummaryText(data, ctx),
50
+ }), { data: groupSummaryData });
51
+ GroupSummary.displayName = "GroupSummary";
52
+ /**
53
+ * 实验列表:每项一个 experiment,固定展示身份、配置(flags)、Eval 判定构成和官方两级聚合
54
+ * 汇总指标;展开到这个 experiment 的 Eval,每道题内联该题全部 Attempt 的 locator 徽标。
55
+ * 没有 selection-form:`ExperimentList.data(selection)` 产出普通数组,过滤由报告作者对
56
+ * 数组调用 `.filter()`。
57
+ */
58
+ export const ExperimentList = Object.assign(defineComponent({
59
+ web: (props, ctx) => _jsx(ExperimentListWeb, { ...props, locale: props.locale ?? ctx.locale, attemptHref: ctx.attemptHref }),
60
+ text: ({ items }, ctx) => experimentListText(items, ctx),
61
+ }), { data: experimentListData });
62
+ ExperimentList.displayName = "ExperimentList";
63
+ /**
64
+ * Eval 列表:每项一个 `experimentId + evalId`,固定展示判定、Attempt 数、分数、成本、耗时
65
+ * 与失败原因;展开到这道题的 Attempt(与 AttemptList 同一份 AttemptListItem)。
66
+ */
67
+ export const EvalList = Object.assign(defineComponent({
68
+ web: (props, ctx) => _jsx(EvalListWeb, { ...props, locale: props.locale ?? ctx.locale, attemptHref: ctx.attemptHref }),
69
+ text: ({ items }, ctx) => evalListText(items, ctx),
70
+ }), { data: evalListData });
71
+ EvalList.displayName = "EvalList";
72
+ /**
73
+ * Attempt 列表:实体列表的叶子层,每项一个 Attempt,固定展示判定、断言、error、Judge 评语
74
+ * (assertions 的 detail/evidence)与证据引用(locator + 证据能力标记)。它不预设只看失败;
75
+ * 报告作者过滤 `AttemptListItem[]`、用 `.slice()` 限量,`total` 让渲染面如实报告剩余数量。
76
+ */
77
+ export const AttemptList = Object.assign(defineComponent({
78
+ web: (props, ctx) => _jsx(AttemptListWeb, { ...props, locale: props.locale ?? ctx.locale, attemptHref: ctx.attemptHref }),
79
+ text: ({ items, total }, ctx) => attemptListText(items, total, ctx),
80
+ }), { data: attemptListData });
81
+ AttemptList.displayName = "AttemptList";
82
+ /** 榜单:一行一个维度值、一列一个指标,回答「谁整体更好」。 */
83
+ export const MetricTable = Object.assign(defineComponent({
84
+ web: (props, ctx) => (_jsx(MetricTableWeb, { ...props, locale: props.locale ?? ctx.locale, attemptHref: props.attemptHref ?? (isHostWebContextActive() ? ctx.attemptHref : undefined) })),
85
+ text: ({ data }, ctx) => tableText(data, ctx),
86
+ }), { data: tableData });
87
+ MetricTable.displayName = "MetricTable";
88
+ /** 逐题格子:行 × 列两个维度、格子里一个指标,回答「哪道题谁挂了」。 */
89
+ export const MetricMatrix = Object.assign(defineComponent({
90
+ web: (props, ctx) => (_jsx(MetricMatrixWeb, { ...props, locale: props.locale ?? ctx.locale, attemptHref: props.attemptHref ?? (isHostWebContextActive() ? ctx.attemptHref : undefined) })),
91
+ text: ({ data }, ctx) => matrixText(data, ctx),
92
+ }), { data: matrixData });
93
+ MetricMatrix.displayName = "MetricMatrix";
94
+ /** 分组条形:同一份矩阵数据的另一种摆法;MetricBars.data 就是 MetricMatrix.data 的别名。 */
95
+ export const MetricBars = Object.assign(defineComponent({
96
+ web: (props, ctx) => (_jsx(MetricBarsWeb, { ...props, locale: props.locale ?? ctx.locale, attemptHref: props.attemptHref ?? (isHostWebContextActive() ? ctx.attemptHref : undefined) })),
97
+ text: ({ data }) => barsText(data),
98
+ }), { data: matrixData });
99
+ MetricBars.displayName = "MetricBars";
100
+ /** 考试成绩单:总分 + 分科小计,固定分母、missing 如实报。 */
101
+ export const Scoreboard = Object.assign(defineComponent({
102
+ web: (props, ctx) => _jsx(ScoreboardWeb, { ...props, locale: props.locale ?? ctx.locale }),
103
+ text: ({ data }, ctx) => scoreboardText(data, ctx),
104
+ }), { data: scoreboardData });
105
+ Scoreboard.displayName = "Scoreboard";
106
+ /** 质量 × 成本 frontier:每个点一个配置、两个指标各占一轴,「好」的角落恒在右上。 */
107
+ export const MetricScatter = Object.assign(defineComponent({
108
+ resolve: async (props) => {
109
+ if ("data" in props && props.data !== undefined)
110
+ return props;
111
+ const { selection, points, series, x, y, ...rest } = props;
112
+ return { ...rest, data: await scatterData(selection, { points, series, x, y }) };
113
+ },
114
+ web: (props, ctx) => {
115
+ requireScatterData(props);
116
+ return _jsx(MetricScatterWeb, { ...props, locale: props.locale ?? ctx.locale });
117
+ },
118
+ text: (props, ctx) => scatterText(requireScatterData(props), ctx),
119
+ }), { data: scatterData });
120
+ MetricScatter.displayName = "MetricScatter";
121
+ /** 趋势线:x 是 experiment 声明的 flag(flag()),同系列按 x 排序连线。 */
122
+ export const MetricLine = Object.assign(defineComponent({
123
+ web: (props, ctx) => _jsx(MetricLineWeb, { ...props, locale: props.locale ?? ctx.locale }),
124
+ text: ({ data }, ctx) => lineText(data, ctx),
125
+ }), { data: lineData });
126
+ MetricLine.displayName = "MetricLine";
127
+ /** 成对对比:每行一对配置、格子里 A、B、Δ 三个值,涨跌好坏由 better 判定。 */
128
+ export const DeltaTable = Object.assign(defineComponent({
129
+ web: (props, ctx) => _jsx(DeltaTableWeb, { ...props, locale: props.locale ?? ctx.locale }),
130
+ text: ({ data }, ctx) => deltaText(data, ctx),
131
+ }), { data: deltaData });
132
+ DeltaTable.displayName = "DeltaTable";
@@ -0,0 +1,106 @@
1
+ import type { AttemptListItem, DeltaData, DimensionInput, EvalListItem, ExperimentListItem, GroupSummaryData, LineData, MatrixData, Metric, OverviewData, FlagRef, ScatterData, ScoreboardData, TableData } from "./types.ts";
2
+ import type { AssertionResult, EvalResult } from "../types.ts";
3
+ import { type SnapshotsInput } from "./aggregate.ts";
4
+ export interface TableDataOptions<M extends readonly Metric[]> {
5
+ /** 行维度(内置 / 自定义 / flag())。 */
6
+ rows: DimensionInput;
7
+ /** 每列一个指标;列键 = metric.name 的字面量,拼错编译不过。 */
8
+ columns: M;
9
+ /** 构建时排序,方向随 better(higher 降序,「好」的一头在上);缺数据行沉底。两面同口径,预排即终排。 */
10
+ sort?: Metric;
11
+ /** eval id 前缀过滤,同 CLI 位置参数语义。 */
12
+ evals?: string | string[];
13
+ }
14
+ /**
15
+ * 一次 attempt 未通过的 gate 断言,原始声明顺序不变;soft 断言不参与判定,不算「失败原因」,
16
+ * 只影响得分,永不出现在这份列表里。`EvalList` / `ExperimentList` 的失败诊断与 `AttemptList`
17
+ * 的断言列表共用这同一份材料,保证同一个 attempt 在各处给出同一个原因。
18
+ */
19
+ export declare function failingGateAssertions(result: EvalResult): AssertionResult[];
20
+ /**
21
+ * 一次 attempt 的失败原因文案,按优先级取第一个在场的:
22
+ * `error` → `skipReason` → 未通过的 gate 断言(原始声明顺序,`name`,detail 在场则
23
+ * `"name: detail"`,多条用「, 」连接)→ 都缺席则无原因(如某道题恰好没有失败信号)。
24
+ * soft 断言永不进入这份原因文案,soft 得分是独立概念,不与 reason 混用同一个字段。
25
+ */
26
+ export declare function reasonFor(result: EvalResult): string | undefined;
27
+ export declare function tableData<const M extends readonly Metric[]>(input: SnapshotsInput, opts: TableDataOptions<M>): Promise<TableData<M[number]["name"]>>;
28
+ export interface AttemptListDataOptions {
29
+ /** 发布消毒:error / 断言 detail / evidence 经这个钩子;身份字段(experimentId/evalId/locator…)不经它。 */
30
+ redact?: (text: string) => string;
31
+ }
32
+ /** `AttemptList.data(selection)`:每个 Attempt 一项,顺序取自 Selection 展平顺序(不重排)。 */
33
+ export declare function attemptListData(input: SnapshotsInput, opts?: AttemptListDataOptions): Promise<AttemptListItem[]>;
34
+ /** `EvalList.data(selection)`:每个 `experimentId + evalId` 一项,按 evalId 再按 experimentId 升序。 */
35
+ export declare function evalListData(input: SnapshotsInput): Promise<EvalListItem[]>;
36
+ /** `ExperimentList.data(selection)`:每个 experiment 一项,按 experimentId 升序;展开到每道 Eval。 */
37
+ export declare function experimentListData(input: SnapshotsInput): Promise<ExperimentListItem[]>;
38
+ export interface MatrixDataOptions {
39
+ rows: DimensionInput;
40
+ columns: DimensionInput;
41
+ cell: Metric;
42
+ /** eval id 前缀过滤,同 CLI 位置参数语义。 */
43
+ evals?: string | string[];
44
+ }
45
+ export declare function matrixData(input: SnapshotsInput, opts: MatrixDataOptions): Promise<MatrixData>;
46
+ export interface ScoreboardDataOptions {
47
+ /** 给谁打分(被打分的维度);维度槽与 MetricTable.data 统一叫 rows。 */
48
+ rows: DimensionInput;
49
+ /** 按什么分科;默认 "evalGroup"(考试里的「科目」)。 */
50
+ subjects?: DimensionInput;
51
+ /** eval id 前缀 → 每题分值;未列默认 1;前缀重叠时最长的生效。 */
52
+ weights?: Record<string, number>;
53
+ /** 折算满分;默认 100。 */
54
+ fullMarks?: number;
55
+ /** 每题得分指标;缺省即 examScore,可换自定义(如「答对但超预算扣分」)。 */
56
+ score?: Metric;
57
+ /** 选中范围:eval id 前缀过滤;题集(分母)只遍历这个范围。 */
58
+ evals?: string | string[];
59
+ }
60
+ /**
61
+ * 逐题分值制,分母对所有被打分者恒定:
62
+ * 题分值 = 命中的权重(默认 1) 题得分 = score 指标的题级值(perEval 折叠后)
63
+ * 总分 = fullMarks × Σ(题得分 × 题分值) / Σ(题分值) Σ 遍历选中范围内全部题
64
+ * 没跑到的题挣 0 分但留在分母里,missing 如实报 —— 这是显式的考试契约,不是「null ≠ 0」的例外。
65
+ */
66
+ export declare function scoreboardData(input: SnapshotsInput, opts: ScoreboardDataOptions): Promise<ScoreboardData>;
67
+ export interface ScatterDataOptions {
68
+ /** 点维度:每个点 = 该组 attempt 的聚合。 */
69
+ points: DimensionInput;
70
+ /** 可选:同系列的点连成线;省略 = 纯散点。 */
71
+ series?: DimensionInput;
72
+ x: Metric;
73
+ y: Metric;
74
+ }
75
+ export declare function scatterData(input: SnapshotsInput, opts: ScatterDataOptions): Promise<ScatterData>;
76
+ export interface LineDataOptions {
77
+ /** x 轴:experiment 声明的 flag(数值),不解析 experiment 命名。 */
78
+ x: FlagRef;
79
+ y: Metric;
80
+ /** 可选:每个系列一条线(flag 或普通维度);省略 = 单系列。 */
81
+ series?: DimensionInput;
82
+ }
83
+ /** 每个点 = 一个 experiment 的聚合;同系列的点按 x 排序连线(排序在组件面,数据保持分组序)。 */
84
+ export declare function lineData(input: SnapshotsInput, opts: LineDataOptions): Promise<LineData>;
85
+ /** Selection 的 warnings 随行进 OverviewData,RunOverview 直接渲染 —— 诚实不靠使用者记得接线。 */
86
+ export declare function overviewData(input: SnapshotsInput): Promise<OverviewData>;
87
+ /**
88
+ * 一组 experiment 的摘要:experiment/eval/attempt 数量、eval 级折叠计票、通过率(旧
89
+ * `GroupSelector` 卡片口径,见 summarizeItems)、总成本(null-safe 求和)、最后运行时间
90
+ * (组内快照 startedAt 最大值)。`input` 就是调用方已经收窄好的组 Selection(如自定义报告
91
+ * 按 experiment 组前缀 filter 出来的那份)——本函数不再自己分组。
92
+ */
93
+ export declare function groupSummaryData(input: SnapshotsInput): Promise<GroupSummaryData>;
94
+ export interface DeltaPair {
95
+ /** 基线侧:experiment id,或快照键 "<experimentId> @ <startedAt>"(时间轴对比用手挑的快照数组)。 */
96
+ a: string;
97
+ /** 对比侧,同上。 */
98
+ b: string;
99
+ label?: string;
100
+ }
101
+ export interface DeltaDataOptions<M extends readonly Metric[]> {
102
+ /** 每行一对:B 相对 A。 */
103
+ pairs: DeltaPair[];
104
+ metrics: M;
105
+ }
106
+ export declare function deltaData<const M extends readonly Metric[]>(input: SnapshotsInput, opts: DeltaDataOptions<M>): Promise<DeltaData<M[number]["name"]>>;