niceeval 0.6.0 → 0.6.2

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 (314) hide show
  1. package/dist/agents/types.d.ts +72 -6
  2. package/dist/context/types.d.ts +32 -12
  3. package/dist/i18n/en.d.ts +54 -0
  4. package/dist/i18n/zh-CN.d.ts +55 -1
  5. package/dist/o11y/types.d.ts +16 -2
  6. package/dist/report/aggregate.d.ts +5 -3
  7. package/dist/report/aggregate.js +32 -5
  8. package/dist/report/built-ins/experiment-comparison.d.ts +39 -0
  9. package/dist/report/built-ins/experiment-comparison.js +119 -0
  10. package/dist/report/built-ins/index.d.ts +2 -1
  11. package/dist/report/built-ins/index.js +2 -2
  12. package/dist/report/components.d.ts +10 -2
  13. package/dist/report/components.js +3 -3
  14. package/dist/report/compute.d.ts +11 -18
  15. package/dist/report/compute.js +68 -66
  16. package/dist/report/flag.d.ts +16 -1
  17. package/dist/report/flag.js +19 -1
  18. package/dist/report/format.d.ts +16 -14
  19. package/dist/report/format.js +28 -30
  20. package/dist/report/index.d.ts +5 -4
  21. package/dist/report/index.js +6 -5
  22. package/dist/report/locale.d.ts +23 -3
  23. package/dist/report/locale.js +47 -6
  24. package/dist/report/metrics.d.ts +13 -1
  25. package/dist/report/metrics.js +66 -15
  26. package/dist/report/primitives.d.ts +6 -0
  27. package/dist/report/react/AttemptList.d.ts +4 -4
  28. package/dist/report/react/AttemptList.js +8 -10
  29. package/dist/report/react/EvalList.d.ts +1 -1
  30. package/dist/report/react/EvalList.js +0 -0
  31. package/dist/report/react/ExperimentComparison.d.ts +8 -0
  32. package/dist/report/react/ExperimentComparison.js +11 -0
  33. package/dist/report/react/ExperimentList.d.ts +4 -2
  34. package/dist/report/react/ExperimentList.js +57 -7
  35. package/dist/report/react/MetricScatter.js +6 -16
  36. package/dist/report/react/chart-math.d.ts +23 -6
  37. package/dist/report/react/chart-math.js +71 -19
  38. package/dist/report/react/fixtures.d.ts +3 -3
  39. package/dist/report/react/fixtures.js +30 -18
  40. package/dist/report/react/format.d.ts +1 -1
  41. package/dist/report/react/format.js +1 -1
  42. package/dist/report/react/index.d.ts +1 -1
  43. package/dist/report/report.d.ts +5 -1
  44. package/dist/report/report.js +6 -2
  45. package/dist/report/text/faces.d.ts +1 -1
  46. package/dist/report/text/faces.js +100 -61
  47. package/dist/report/text/table.js +36 -5
  48. package/dist/report/types.d.ts +40 -34
  49. package/dist/results/types.d.ts +11 -0
  50. package/dist/runner/feedback/sink.d.ts +110 -0
  51. package/dist/runner/types.d.ts +513 -22
  52. package/dist/sandbox/docker.d.ts +23 -2
  53. package/dist/sandbox/e2b.d.ts +15 -1
  54. package/dist/sandbox/errors.d.ts +30 -3
  55. package/dist/sandbox/io-retry.d.ts +17 -0
  56. package/dist/sandbox/registry.d.ts +2 -0
  57. package/dist/sandbox/resolve.d.ts +18 -5
  58. package/dist/sandbox/retry.d.ts +11 -1
  59. package/dist/sandbox/types.d.ts +39 -5
  60. package/dist/sandbox/vercel.d.ts +7 -1
  61. package/dist/scoring/coverage.d.ts +30 -0
  62. package/dist/scoring/display.d.ts +21 -0
  63. package/dist/scoring/display.js +120 -0
  64. package/dist/scoring/types.d.ts +103 -20
  65. package/dist/shared/aggregate.d.ts +1 -0
  66. package/dist/shared/aggregate.js +3 -3
  67. package/dist/shared/types.d.ts +28 -0
  68. package/dist/tty-line.d.ts +0 -4
  69. package/dist/util.d.ts +23 -0
  70. package/docs-site/zh/concepts/adapter.mdx +24 -6
  71. package/docs-site/zh/concepts/assert.mdx +11 -10
  72. package/docs-site/zh/concepts/evals.mdx +7 -6
  73. package/docs-site/zh/concepts/experiment.mdx +1 -1
  74. package/docs-site/zh/concepts/overview.mdx +7 -7
  75. package/docs-site/zh/guides/agent-feedback-loop.mdx +35 -31
  76. package/docs-site/zh/guides/authoring.mdx +33 -0
  77. package/docs-site/zh/guides/ci-integration.mdx +23 -12
  78. package/docs-site/zh/guides/connect-your-agent.mdx +29 -3
  79. package/docs-site/zh/guides/custom-reports.mdx +29 -34
  80. package/docs-site/zh/guides/dataset-fanout.mdx +25 -3
  81. package/docs-site/zh/guides/debug-sandbox.mdx +57 -0
  82. package/docs-site/zh/guides/debugging.mdx +210 -0
  83. package/docs-site/zh/guides/experiments.mdx +10 -3
  84. package/docs-site/zh/guides/fixtures.mdx +3 -1
  85. package/docs-site/zh/guides/official-adapters.mdx +27 -3
  86. package/docs-site/zh/guides/publish-report.mdx +30 -16
  87. package/docs-site/zh/guides/report-components.mdx +49 -37
  88. package/docs-site/zh/guides/reporters.mdx +2 -2
  89. package/docs-site/zh/guides/results-data.mdx +42 -8
  90. package/docs-site/zh/guides/runner.mdx +17 -7
  91. package/docs-site/zh/guides/sandbox-agent.mdx +57 -7
  92. package/docs-site/zh/guides/sandbox-providers.mdx +258 -10
  93. package/docs-site/zh/guides/scoring-guide.mdx +4 -4
  94. package/docs-site/zh/guides/viewing-results.mdx +85 -41
  95. package/docs-site/zh/guides/write-experiment.mdx +5 -3
  96. package/docs-site/zh/guides/write-send.mdx +19 -2
  97. package/docs-site/zh/index.mdx +1 -1
  98. package/docs-site/zh/reference/builtin-agents.mdx +27 -0
  99. package/docs-site/zh/reference/capabilities.mdx +2 -2
  100. package/docs-site/zh/reference/cli.mdx +35 -9
  101. package/docs-site/zh/reference/define-agent.mdx +60 -5
  102. package/docs-site/zh/reference/define-config.mdx +1 -1
  103. package/docs-site/zh/reference/define-eval.mdx +42 -9
  104. package/docs-site/zh/reference/events.mdx +2 -2
  105. package/docs-site/zh/reference/expect.mdx +36 -6
  106. package/package.json +5 -1
  107. package/src/agents/ai-sdk-otel.test.ts +1 -0
  108. package/src/agents/ai-sdk.test.ts +3 -0
  109. package/src/agents/ai-sdk.ts +3 -0
  110. package/src/agents/bub-install-spec.test.ts +34 -0
  111. package/src/agents/bub-install-spec.ts +32 -0
  112. package/src/agents/bub.ts +31 -32
  113. package/src/agents/claude-code.test.ts +130 -9
  114. package/src/agents/claude-code.ts +76 -4
  115. package/src/agents/codex.test.ts +189 -40
  116. package/src/agents/codex.ts +155 -14
  117. package/src/agents/coding-cli-versions.test.ts +15 -0
  118. package/src/agents/coding-cli-versions.ts +3 -0
  119. package/src/agents/index.ts +11 -0
  120. package/src/agents/langgraph.test.ts +204 -0
  121. package/src/agents/langgraph.ts +495 -0
  122. package/src/agents/marketplace.ts +85 -0
  123. package/src/agents/native-config.test.ts +179 -0
  124. package/src/agents/native-config.ts +267 -0
  125. package/src/agents/openai-compat.test.ts +1 -0
  126. package/src/agents/openclaw.test.ts +31 -0
  127. package/src/agents/openclaw.ts +171 -0
  128. package/src/agents/plugin-config.test.ts +1 -0
  129. package/src/agents/sdk-streams.test.ts +79 -0
  130. package/src/agents/sdk-streams.ts +55 -10
  131. package/src/agents/skills.test.ts +1 -0
  132. package/src/agents/streaming.test.ts +3 -9
  133. package/src/agents/types.ts +73 -6
  134. package/src/agents/ui-message-stream.test.ts +3 -0
  135. package/src/cli.ts +411 -108
  136. package/src/context/context.test.ts +51 -12
  137. package/src/context/context.ts +161 -29
  138. package/src/context/session.test.ts +1 -0
  139. package/src/context/session.ts +114 -6
  140. package/src/context/types.ts +30 -12
  141. package/src/define.test.ts +13 -8
  142. package/src/define.ts +25 -4
  143. package/src/expect/index.ts +53 -23
  144. package/src/i18n/en.ts +65 -4
  145. package/src/i18n/zh-CN.ts +66 -4
  146. package/src/o11y/cost.test.ts +1 -0
  147. package/src/o11y/execution-tree.test.ts +1 -20
  148. package/src/o11y/otlp/mappers/claude-code.test.ts +1 -0
  149. package/src/o11y/otlp/parse.test.ts +1 -0
  150. package/src/o11y/otlp/turn-otel.test.ts +1 -0
  151. package/src/o11y/parsers/bub.test.ts +1 -0
  152. package/src/o11y/parsers/claude-code.test.ts +1 -34
  153. package/src/o11y/parsers/openclaw.test.ts +154 -0
  154. package/src/o11y/parsers/openclaw.ts +310 -0
  155. package/src/o11y/prices.json +746 -311
  156. package/src/o11y/tool-names.test.ts +1 -0
  157. package/src/o11y/types.ts +16 -2
  158. package/src/report/aggregate.ts +34 -5
  159. package/src/report/built-in-user-parity.test.tsx +127 -173
  160. package/src/report/built-ins/experiment-comparison.tsx +179 -0
  161. package/src/report/built-ins/index.ts +7 -2
  162. package/src/report/components.tsx +11 -3
  163. package/src/report/compute.ts +80 -74
  164. package/src/report/dual-render.test.tsx +222 -91
  165. package/src/report/flag.ts +30 -2
  166. package/src/report/format.ts +36 -27
  167. package/src/report/index.ts +23 -6
  168. package/src/report/locale.ts +49 -6
  169. package/src/report/metrics.ts +68 -15
  170. package/src/report/primitives.tsx +6 -0
  171. package/src/report/react/AttemptList.tsx +9 -36
  172. package/src/report/react/EvalList.tsx +0 -0
  173. package/src/report/react/ExperimentComparison.tsx +68 -0
  174. package/src/report/react/ExperimentList.tsx +173 -55
  175. package/src/report/react/MetricScatter.tsx +13 -25
  176. package/src/report/react/chart-math.test.ts +85 -0
  177. package/src/report/react/chart-math.ts +101 -22
  178. package/src/report/react/enhance.js +72 -1
  179. package/src/report/react/fixtures.ts +34 -21
  180. package/src/report/react/format.ts +1 -1
  181. package/src/report/react/index.tsx +0 -1
  182. package/src/report/react/render.test.tsx +30 -69
  183. package/src/report/react/styles.css +112 -14
  184. package/src/report/report.test.ts +308 -105
  185. package/src/report/report.ts +6 -2
  186. package/src/report/text/faces.ts +111 -67
  187. package/src/report/text/table.ts +42 -5
  188. package/src/report/types.ts +42 -34
  189. package/src/results/annotated-source.test.ts +62 -9
  190. package/src/results/annotated-source.ts +64 -6
  191. package/src/results/attempt-evidence.test.ts +9 -7
  192. package/src/results/attempt-evidence.ts +15 -8
  193. package/src/results/attempt-source.ts +6 -3
  194. package/src/results/copy.ts +145 -55
  195. package/src/results/host-equivalence.test.ts +11 -9
  196. package/src/results/index.ts +2 -0
  197. package/src/results/locator.test.ts +1 -22
  198. package/src/results/open.ts +7 -1
  199. package/src/results/publish.ts +149 -0
  200. package/src/results/results.test.ts +85 -51
  201. package/src/results/truncate.ts +90 -0
  202. package/src/results/types.ts +7 -0
  203. package/src/results/writer.ts +31 -13
  204. package/src/runner/attempt.test.ts +138 -7
  205. package/src/runner/attempt.ts +603 -104
  206. package/src/runner/discover.test.ts +47 -0
  207. package/src/runner/discover.ts +36 -2
  208. package/src/runner/eval-source.test.ts +1 -27
  209. package/src/runner/feedback/agent.test.ts +504 -0
  210. package/src/runner/feedback/agent.ts +409 -0
  211. package/src/runner/feedback/ci.test.ts +562 -0
  212. package/src/runner/feedback/ci.ts +401 -0
  213. package/src/runner/feedback/coordinator.test.ts +317 -0
  214. package/src/runner/feedback/coordinator.ts +397 -0
  215. package/src/runner/feedback/failure.ts +40 -0
  216. package/src/runner/feedback/human.test.ts +616 -0
  217. package/src/runner/feedback/human.ts +535 -0
  218. package/src/runner/feedback/index.ts +66 -0
  219. package/src/runner/feedback/io.ts +78 -0
  220. package/src/runner/feedback/profile.test.ts +50 -0
  221. package/src/runner/feedback/profile.ts +58 -0
  222. package/src/runner/feedback/reducer.test.ts +395 -0
  223. package/src/runner/feedback/reducer.ts +260 -0
  224. package/src/runner/feedback/renderer.ts +82 -0
  225. package/src/runner/feedback/sink.ts +203 -0
  226. package/src/runner/feedback/testing.ts +106 -0
  227. package/src/runner/ledger.test.ts +230 -0
  228. package/src/runner/ledger.ts +329 -0
  229. package/src/runner/report.test.ts +128 -3
  230. package/src/runner/report.ts +33 -9
  231. package/src/runner/reporters/artifacts.ts +8 -2
  232. package/src/runner/reporters/braintrust.test.ts +8 -7
  233. package/src/runner/reporters/braintrust.ts +9 -2
  234. package/src/runner/reporters/index.ts +2 -2
  235. package/src/runner/reporters/json.test.ts +162 -0
  236. package/src/runner/reporters/json.ts +35 -8
  237. package/src/runner/reporters/shared.ts +1 -5
  238. package/src/runner/run.test.ts +760 -3
  239. package/src/runner/run.ts +242 -36
  240. package/src/runner/sandbox-prep.ts +3 -42
  241. package/src/runner/timing.ts +158 -0
  242. package/src/runner/types.ts +518 -22
  243. package/src/sandbox/checkpoint.test.ts +55 -0
  244. package/src/sandbox/checkpoint.ts +29 -8
  245. package/src/sandbox/cli-commands.ts +407 -0
  246. package/src/sandbox/docker.ts +115 -16
  247. package/src/sandbox/e2b-agent-template.test.ts +56 -0
  248. package/src/sandbox/e2b-agent-template.ts +94 -0
  249. package/src/sandbox/e2b.ts +74 -9
  250. package/src/sandbox/errors.ts +111 -4
  251. package/src/sandbox/index.ts +2 -0
  252. package/src/sandbox/io-retry.test.ts +58 -0
  253. package/src/sandbox/io-retry.ts +45 -0
  254. package/src/sandbox/keep-registry.test.ts +86 -0
  255. package/src/sandbox/keep-registry.ts +142 -0
  256. package/src/sandbox/keep.ts +178 -0
  257. package/src/sandbox/paths.test.ts +1 -0
  258. package/src/sandbox/paths.ts +19 -8
  259. package/src/sandbox/registry.ts +20 -3
  260. package/src/sandbox/resolve.ts +76 -11
  261. package/src/sandbox/retry.test.ts +70 -0
  262. package/src/sandbox/retry.ts +46 -4
  263. package/src/sandbox/types.ts +44 -6
  264. package/src/sandbox/vercel.ts +43 -20
  265. package/src/scoring/collector.ts +60 -17
  266. package/src/scoring/coverage.ts +95 -0
  267. package/src/scoring/diff.ts +81 -0
  268. package/src/scoring/display.test.ts +121 -0
  269. package/src/scoring/display.ts +133 -0
  270. package/src/scoring/evidence.test.ts +189 -0
  271. package/src/scoring/judge.test.ts +142 -0
  272. package/src/scoring/judge.ts +15 -18
  273. package/src/scoring/scoped.ts +217 -50
  274. package/src/scoring/types.ts +117 -20
  275. package/src/scoring/verdict.ts +16 -4
  276. package/src/shared/aggregate.ts +3 -2
  277. package/src/shared/types.ts +31 -0
  278. package/src/show/compose.ts +2 -2
  279. package/src/show/index.ts +29 -16
  280. package/src/show/render.ts +626 -308
  281. package/src/show/show.test.ts +251 -36
  282. package/src/tty-line.ts +8 -26
  283. package/src/util.test.ts +1 -0
  284. package/src/util.ts +41 -0
  285. package/src/view/app/components/AttemptModal.tsx +153 -2
  286. package/src/view/app/components/CodeView.tsx +32 -11
  287. package/src/view/app/components/CopyControls.tsx +2 -2
  288. package/src/view/app/i18n.ts +6 -0
  289. package/src/view/app/lib/attempt-route.test.ts +1 -0
  290. package/src/view/app/lib/verdict.ts +7 -9
  291. package/src/view/artifact-serving.test.ts +2 -1
  292. package/src/view/client-dist/app.css +1 -1
  293. package/src/view/client-dist/app.js +17 -17
  294. package/src/view/data.test.ts +2 -1
  295. package/src/view/data.ts +17 -7
  296. package/src/view/index.ts +12 -1
  297. package/src/view/server.ts +2 -0
  298. package/src/view/shared/types.ts +1 -1
  299. package/src/view/styles.css +3 -0
  300. package/src/view/view-report.test.ts +11 -10
  301. package/dist/o11y/execution-tree.d.ts +0 -103
  302. package/dist/o11y/otlp/select.d.ts +0 -22
  303. package/dist/report/built-ins/cost-pass-rate-comparison.d.ts +0 -1
  304. package/dist/report/built-ins/cost-pass-rate-comparison.js +0 -17
  305. package/dist/results/annotated-source.d.ts +0 -61
  306. package/dist/results/attempt-evidence.d.ts +0 -69
  307. package/dist/results/attempt-source.d.ts +0 -15
  308. package/src/report/built-ins/cost-pass-rate-comparison.tsx +0 -23
  309. package/src/runner/reporters/console.ts +0 -70
  310. package/src/runner/reporters/live.test.ts +0 -56
  311. package/src/runner/reporters/live.ts +0 -247
  312. package/src/runner/reporters/quiet.test.ts +0 -66
  313. package/src/runner/reporters/quiet.ts +0 -49
  314. package/src/runner/reporters/table.ts +0 -277
@@ -1,5 +1,5 @@
1
- export { defineMetric, passRate, examScore, durationMs, tokens, costUSD, turns } from "./metrics.ts";
2
- export { flag } from "./flag.ts";
1
+ export { defineMetric, taskPassRate, executionReliability, endToEndPassRate, examScore, durationMs, tokens, costUSD, turns, } from "./metrics.ts";
2
+ export { flag, config } from "./flag.ts";
3
3
  export { defineReport, isReportDefinition, renderReportToText } from "./report.ts";
4
4
  export type { ReportContext, ReportDefinition } from "./report.ts";
5
5
  export { defineComponent } from "./tree.ts";
@@ -8,12 +8,13 @@ export { Row, Col, Section, Text, Style, Table } from "./primitives.tsx";
8
8
  export type { LayoutProps, SectionProps, StyleProps, TableColumn, TableProps, TableRow } from "./primitives.tsx";
9
9
  export { stringWidth, padDisplay as padEnd, padStartDisplay as padStart, wrapDisplay as wrapText, indentBlock as indent, textBar as bar, joinColumns as columns, } from "./text/layout.ts";
10
10
  export type { ColumnAlign } from "./text/layout.ts";
11
- export { CostPassRateComparison } from "./built-ins/index.ts";
11
+ export { ExperimentComparison } from "./built-ins/index.ts";
12
+ export type { ExperimentComparisonData, ExperimentComparisonGroupData, ExperimentComparisonProps, } from "./built-ins/index.ts";
12
13
  export { DEFAULT_REPORT_LOCALE, resolveMetricLabel } from "./locale.ts";
13
14
  export type { LocalizedLabel, ReportLocale } from "./locale.ts";
14
15
  export { AttemptList, DeltaTable, EvalList, ExperimentList, GroupSummary, MetricBars, MetricLine, MetricMatrix, MetricScatter, MetricTable, RunOverview, Scoreboard, } from "./components.tsx";
15
16
  export type { AttemptListProps, DeltaTableProps, EvalListProps, ExperimentListProps, GroupSummaryProps, MetricLineProps, MetricMatrixProps, MetricScatterProps, MetricTableProps, RunOverviewProps, ScoreboardProps, } from "./components.tsx";
16
17
  export type { AttemptListDataOptions, DeltaDataOptions, DeltaPair, LineDataOptions, MatrixDataOptions, ScatterDataOptions, ScoreboardDataOptions, TableDataOptions, } from "./compute.ts";
17
- export type { Aggregator, AttemptEvidenceCapabilities, AttemptListItem, AttemptLocator, DeltaData, Dimension, DimensionInput, EvalListItem, ExperimentListEvalRow, ExperimentListItem, GroupSummaryData, LineAxis, LineData, MatrixData, Metric, MetricAggregate, MetricCell, MetricColumn, OverviewData, FlagRef, ScatterData, ScoreboardData, SelectionWarning, TableData, TableRowMeta, } from "./types.ts";
18
+ export type { Aggregator, AttemptListItem, AttemptLocator, AxisInput, ConfigRef, DeltaData, Dimension, DimensionInput, EvalListItem, ExperimentListEvalRow, ExperimentListItem, GroupSummaryData, LineAxis, LineData, MatrixData, Metric, MetricAggregate, MetricCell, MetricColumn, OverviewData, FlagRef, ScatterData, ScoreboardData, SelectionWarning, TableData, TableRowMeta, } from "./types.ts";
18
19
  export type { AttemptHandle, Results, Selection, Snapshot } from "../results/types.ts";
19
20
  export { experimentGroupOf } from "../shared/aggregate.ts";
@@ -7,9 +7,9 @@
7
7
  // 不需要 react-dom(renderReportToText);web 宿主的 renderReportToStaticHtml 在
8
8
  // ./web.ts,只有那一侧 import react-dom。写报告文件的项目要装 react(.tsx 编译产物
9
9
  // import react/jsx-runtime)。
10
- // 指标与 flag
11
- export { defineMetric, passRate, examScore, durationMs, tokens, costUSD, turns } from "./metrics.js";
12
- export { flag } from "./flag.js";
10
+ // 指标与维度读取器(flag / config)
11
+ export { defineMetric, taskPassRate, executionReliability, endToEndPassRate, examScore, durationMs, tokens, costUSD, turns, } from "./metrics.js";
12
+ export { flag, config } from "./flag.js";
13
13
  // 报告基座与双面组件基座
14
14
  export { defineReport, isReportDefinition, renderReportToText } from "./report.js";
15
15
  export { defineComponent } from "./tree.js";
@@ -19,8 +19,9 @@ export { Row, Col, Section, Text, Style, Table } from "./primitives.js";
19
19
  // 表格有 <Table> 承担,这里只给表以外的形态用 —— 尤其别拿 String.prototype.padEnd 对齐:
20
20
  // 它数 UTF-16 码元不数显示列宽,一带中文列就撕歪。renderAlignedRows 刻意不导出。
21
21
  export { stringWidth, padDisplay as padEnd, padStartDisplay as padStart, wrapDisplay as wrapText, indentBlock as indent, textBar as bar, joinColumns as columns, } from "./text/layout.js";
22
- // 内置报告(show / view 裸跑时报告槽的出厂填充,一份普通 ReportDefinition,无 renderer 特权)
23
- export { CostPassRateComparison } from "./built-ins/index.js";
22
+ // 内置报告兼组合件(show / view 裸跑时报告槽的出厂填充;也可作组件整体引用,
23
+ // `<ExperimentComparison data={await ExperimentComparison.data(selection)} />`),无 renderer 特权
24
+ export { ExperimentComparison } from "./built-ins/index.js";
24
25
  // locale:官方组件 chrome 文案的语言(en / zh-CN);指标 label 可按 locale 给字典
25
26
  export { DEFAULT_REPORT_LOCALE, resolveMetricLabel } from "./locale.js";
26
27
  // 官方双面组件(各自带 data 计算函数;MetricBars.data = MetricMatrix.data)
@@ -22,13 +22,27 @@ declare const en: {
22
22
  readonly "table.viewBreakdown": "Per-eval breakdown";
23
23
  /** <Table> 的 locator 列表头(行带 locator 时自动追加)。 */
24
24
  readonly "table.attempt": "attempt";
25
+ readonly "experimentList.experiment": "Experiment";
26
+ readonly "experimentList.avgDuration": "Avg duration";
27
+ readonly "experimentList.passRate": "End-to-end pass rate";
28
+ readonly "experimentList.tokens": "Tokens";
29
+ readonly "experimentList.estimatedCost": "Est. cost";
30
+ readonly "experimentList.result": "Result";
31
+ readonly "experimentList.status": "Status";
32
+ readonly "experimentList.evalAttempt": "Eval / Attempt";
33
+ readonly "experimentList.duration": "Duration";
34
+ readonly "experimentList.cost": "Cost";
35
+ readonly "experimentList.filterPlaceholder": "Filter experiments…";
36
+ readonly "experimentList.defaultModel": "default";
37
+ readonly "experimentList.flags": "Flags";
38
+ readonly "entityList.average": "{value} avg";
25
39
  /** <Table> 压到下限仍放不下时,从右侧丢列并如实报数。 */
26
40
  readonly "table.columnsHidden.one": "({n} more column not shown)";
27
41
  readonly "table.columnsHidden.other": "({n} more columns not shown)";
28
42
  readonly "overview.snapshots": "Snapshots";
29
43
  readonly "overview.evals": "Evals";
30
44
  readonly "overview.attempts": "attempts";
31
- readonly "overview.passRate": "Pass rate";
45
+ readonly "overview.passRate": "End-to-end pass rate";
32
46
  readonly "overview.totalCost": "Total cost";
33
47
  readonly "overview.totalDuration": "Total duration";
34
48
  readonly "overview.source": "Source: {n} snapshots";
@@ -38,6 +52,13 @@ declare const en: {
38
52
  readonly "overview.attemptsCount": "{n} attempts";
39
53
  /** GroupSummary 的 KPI dt 标签(其余字段复用 verdict.* / overview.* 已有 key)。 */
40
54
  readonly "groupSummary.experiments": "Experiments";
55
+ readonly "experimentComparison.groups": "Experiment groups";
56
+ readonly "experimentComparison.group": "Experiment group";
57
+ readonly "experimentComparison.results": "Results";
58
+ readonly "experimentComparison.lastRun": "Last run";
59
+ readonly "experimentComparison.selectGroup": "Select experiment group {group}";
60
+ readonly "experimentComparison.command": "Details: {command}";
61
+ readonly "experimentComparison.empty": "No experiment groups";
41
62
  readonly "composedFrom.one": "composed from {n} run";
42
63
  readonly "composedFrom.other": "composed from {n} runs";
43
64
  readonly latestRun: "latest {run}";
@@ -46,14 +67,13 @@ declare const en: {
46
67
  readonly "scatter.axisReversed": "(axis reversed: right = better)";
47
68
  /** 0 个可画点:x/y 指标没有可用数据。 */
48
69
  readonly "scatter.noData": "No data to plot {x} × {y}";
49
- /** 恰好 1 个可画点:比较至少要两个实验。 */
50
- readonly "scatter.needTwo": "At least 2 experiments needed to compare {x} × {y}";
51
70
  readonly "pointsMissing.one": "{n} point missing data";
52
71
  readonly "pointsMissing.other": "{n} points missing data";
53
72
  readonly "attemptList.empty": "No attempts";
54
73
  readonly "attemptList.truncated": "and {n} more not shown";
55
74
  readonly "attemptList.truncatedText": "({n} more not shown)";
56
75
  readonly "attemptList.score": "score {score}";
76
+ readonly "attemptList.unavailable": "unavailable";
57
77
  readonly "attemptList.details": "details";
58
78
  readonly "scoreboard.total": "Total";
59
79
  readonly "scoreboard.totalText": "total";
@@ -2,7 +2,7 @@
2
2
  // 只覆盖组件自带的固定文案(verdict 词、缺数据、覆盖率角标、注脚、占位符等);
3
3
  // 数据本身(display、维度键、warnings 的 message)不经这里。
4
4
  // 刻意不 import src/i18n/(CLI 专用字典,locale 来源与 key 面完全不同);
5
- // zh-CN 译法与 src/view/app/i18n.ts 现有词保持一致(成功率/平均耗时/预估成本…)。
5
+ // zh-CN 译法与默认 Reports 契约保持一致(端到端成功率/平均耗时/预估成本…)。
6
6
  export const DEFAULT_REPORT_LOCALE = "en";
7
7
  const en = {
8
8
  "verdict.passed": "passed",
@@ -25,13 +25,27 @@ const en = {
25
25
  "table.viewBreakdown": "Per-eval breakdown",
26
26
  /** <Table> 的 locator 列表头(行带 locator 时自动追加)。 */
27
27
  "table.attempt": "attempt",
28
+ "experimentList.experiment": "Experiment",
29
+ "experimentList.avgDuration": "Avg duration",
30
+ "experimentList.passRate": "End-to-end pass rate",
31
+ "experimentList.tokens": "Tokens",
32
+ "experimentList.estimatedCost": "Est. cost",
33
+ "experimentList.result": "Result",
34
+ "experimentList.status": "Status",
35
+ "experimentList.evalAttempt": "Eval / Attempt",
36
+ "experimentList.duration": "Duration",
37
+ "experimentList.cost": "Cost",
38
+ "experimentList.filterPlaceholder": "Filter experiments…",
39
+ "experimentList.defaultModel": "default",
40
+ "experimentList.flags": "Flags",
41
+ "entityList.average": "{value} avg",
28
42
  /** <Table> 压到下限仍放不下时,从右侧丢列并如实报数。 */
29
43
  "table.columnsHidden.one": "({n} more column not shown)",
30
44
  "table.columnsHidden.other": "({n} more columns not shown)",
31
45
  "overview.snapshots": "Snapshots",
32
46
  "overview.evals": "Evals",
33
47
  "overview.attempts": "attempts",
34
- "overview.passRate": "Pass rate",
48
+ "overview.passRate": "End-to-end pass rate",
35
49
  "overview.totalCost": "Total cost",
36
50
  "overview.totalDuration": "Total duration",
37
51
  "overview.source": "Source: {n} snapshots",
@@ -41,6 +55,13 @@ const en = {
41
55
  "overview.attemptsCount": "{n} attempts",
42
56
  /** GroupSummary 的 KPI dt 标签(其余字段复用 verdict.* / overview.* 已有 key)。 */
43
57
  "groupSummary.experiments": "Experiments",
58
+ "experimentComparison.groups": "Experiment groups",
59
+ "experimentComparison.group": "Experiment group",
60
+ "experimentComparison.results": "Results",
61
+ "experimentComparison.lastRun": "Last run",
62
+ "experimentComparison.selectGroup": "Select experiment group {group}",
63
+ "experimentComparison.command": "Details: {command}",
64
+ "experimentComparison.empty": "No experiment groups",
44
65
  "composedFrom.one": "composed from {n} run",
45
66
  "composedFrom.other": "composed from {n} runs",
46
67
  "latestRun": "latest {run}",
@@ -49,14 +70,13 @@ const en = {
49
70
  "scatter.axisReversed": "(axis reversed: right = better)",
50
71
  /** 0 个可画点:x/y 指标没有可用数据。 */
51
72
  "scatter.noData": "No data to plot {x} × {y}",
52
- /** 恰好 1 个可画点:比较至少要两个实验。 */
53
- "scatter.needTwo": "At least 2 experiments needed to compare {x} × {y}",
54
73
  "pointsMissing.one": "{n} point missing data",
55
74
  "pointsMissing.other": "{n} points missing data",
56
75
  "attemptList.empty": "No attempts",
57
76
  "attemptList.truncated": "and {n} more not shown",
58
77
  "attemptList.truncatedText": "({n} more not shown)",
59
78
  "attemptList.score": "score {score}",
79
+ "attemptList.unavailable": "unavailable",
60
80
  "attemptList.details": "details",
61
81
  "scoreboard.total": "Total",
62
82
  "scoreboard.totalText": "total",
@@ -90,12 +110,26 @@ const zhCN = {
90
110
  "table.reason": "原因",
91
111
  "table.viewBreakdown": "逐题明细",
92
112
  "table.attempt": "Attempt",
113
+ "experimentList.experiment": "实验",
114
+ "experimentList.avgDuration": "平均耗时",
115
+ "experimentList.passRate": "端到端成功率",
116
+ "experimentList.tokens": "Tokens",
117
+ "experimentList.estimatedCost": "预估成本",
118
+ "experimentList.result": "结果",
119
+ "experimentList.status": "状态",
120
+ "experimentList.evalAttempt": "题目 / Attempt",
121
+ "experimentList.duration": "耗时",
122
+ "experimentList.cost": "成本",
123
+ "experimentList.filterPlaceholder": "筛选实验…",
124
+ "experimentList.defaultModel": "默认",
125
+ "experimentList.flags": "Flags",
126
+ "entityList.average": "平均 {value}",
93
127
  "table.columnsHidden.one": "(还有 {n} 列未列出)",
94
128
  "table.columnsHidden.other": "(还有 {n} 列未列出)",
95
129
  "overview.snapshots": "快照",
96
130
  "overview.evals": "题目",
97
131
  "overview.attempts": "尝试",
98
- "overview.passRate": "通过率",
132
+ "overview.passRate": "端到端成功率",
99
133
  "overview.totalCost": "总成本",
100
134
  "overview.totalDuration": "总耗时",
101
135
  "overview.source": "数据来源:{n} 个快照",
@@ -104,6 +138,13 @@ const zhCN = {
104
138
  "overview.evalsCount": "{n} 道题",
105
139
  "overview.attemptsCount": "{n} 次 attempt",
106
140
  "groupSummary.experiments": "实验数",
141
+ "experimentComparison.groups": "实验组",
142
+ "experimentComparison.group": "实验组",
143
+ "experimentComparison.results": "结果",
144
+ "experimentComparison.lastRun": "最后运行",
145
+ "experimentComparison.selectGroup": "选择实验组 {group}",
146
+ "experimentComparison.command": "查看:{command}",
147
+ "experimentComparison.empty": "没有实验组",
107
148
  "composedFrom.one": "合成自 {n} 个 run",
108
149
  "composedFrom.other": "合成自 {n} 个 run",
109
150
  "latestRun": "最新 {run}",
@@ -111,13 +152,13 @@ const zhCN = {
111
152
  "scatter.betterUpperRight": "更好 → 右上",
112
153
  "scatter.axisReversed": "(轴反向:右 = 更好)",
113
154
  "scatter.noData": "{x} × {y} 没有可绘制的数据",
114
- "scatter.needTwo": "至少需要 2 个实验才能比较 {x} × {y}",
115
155
  "pointsMissing.one": "{n} 个点缺数据",
116
156
  "pointsMissing.other": "{n} 个点缺数据",
117
157
  "attemptList.empty": "没有 attempt",
118
158
  "attemptList.truncated": "还有 {n} 条未列出",
119
159
  "attemptList.truncatedText": "(还有 {n} 条未列出)",
120
160
  "attemptList.score": "得分 {score}",
161
+ "attemptList.unavailable": "评不了",
121
162
  "attemptList.details": "详情",
122
163
  "scoreboard.total": "总分",
123
164
  "scoreboard.totalText": "总分",
@@ -8,7 +8,19 @@ import type { Metric } from "./types.ts";
8
8
  export declare function defineMetric<const Name extends string>(def: Metric<Name>): Metric<Name>;
9
9
  /** 单 attempt 成本:网关实测(usage.costUSD)优先于价格表估算(estimatedCostUSD);都缺 → null,不编 0。 */
10
10
  export declare function attemptCostUSD(result: EvalResult): number | null;
11
- export declare const passRate: Metric<"pass-rate">;
11
+ /**
12
+ * 条件答题质量:passed = 1,failed = 0,errored 记 null 不进分母。
13
+ * 这是「已形成可信判定」条件下的诊断指标,不能简称默认成功率
14
+ * (docs/feature/reports/library.md「内置指标」)。
15
+ */
16
+ export declare const taskPassRate: Metric<"task-pass-rate">;
17
+ /** 执行可靠性:跑到可判定(passed / failed)= 1,errored = 0;skipped → null。 */
18
+ export declare const executionReliability: Metric<"execution-reliability">;
19
+ /**
20
+ * 端到端合成:passed = 1,failed / errored = 0;哪边拖累用
21
+ * taskPassRate / executionReliability 拆开看。
22
+ */
23
+ export declare const endToEndPassRate: Metric<"end-to-end-pass-rate">;
12
24
  export declare const examScore: Metric<"exam-score">;
13
25
  export declare const durationMs: Metric<"duration">;
14
26
  export declare const tokens: Metric<"tokens">;
@@ -1,19 +1,22 @@
1
1
  // defineMetric 与内置指标。
2
2
  //
3
3
  // null ≠ 0:null = 此 attempt 测不了这个指标(不进聚合);0 = 测了,结果是零(照常进)。
4
- // 哪个 verdict 落哪边必须显式表态,内置指标按 docs/feature/reports/library.md「内置指标」的表格:
4
+ // 哪个 verdict 落哪边必须显式表态,内置指标按 docs/feature/reports/library.md「内置指标」的表格。
5
+ // 三个通过率指标把「Agent 答错」与「基建没跑起来」拆开,不互相伪装:
5
6
  //
6
- // 指标(name) skipped errored failed passed better
7
- // passRate(pass-rate) null 0 0 1 higher
8
- // examScore(exam-score) null 0 0 soft 均分 higher
9
- // durationMs(duration) null 实测 实测 实测 lower
10
- // tokens(tokens) null 实测;无 usage→null 同左 同左 lower
11
- // costUSD(cost) null 同上 同左 同左 lower
12
- // turns(turns) null 实测;o11y 缺失→null 同左 同左 lower
7
+ // 指标(name) skipped errored failed passed better
8
+ // taskPassRate(task-pass-rate) null null 0 1 higher
9
+ // executionReliability(execution-reliability) null 0 1 1 higher
10
+ // endToEndPassRate(end-to-end-pass-rate) null 0 0 1 higher
11
+ // examScore(exam-score) null 0 0 soft 均分 higher
12
+ // durationMs(duration) null 实测 实测 实测 lower
13
+ // tokens(tokens) null 实测;无 usage→null 同左 同左 lower
14
+ // costUSD(cost) null 同上 同左 同左 lower
15
+ // turns(turns) null 实测;o11y 缺失→null 同左 同左 lower
13
16
  //
14
17
  // 两档指标(docs/feature/reports/library.md「内置指标」):以上除 turns 外全部只读 attempt.result
15
18
  // 的瘦身字段——任何 producer、任何 copySnapshots artifacts 选择都算得出,内置报告
16
- // CostPassRateComparison 只用这一档。turns 读 attempt.o11y()(懒加载 artifact),发布时若
19
+ // ExperimentComparison 只用这一档。turns 读 attempt.o11y()(懒加载 artifact),发布时若
17
20
  // o11y 没随行就诚实渲染缺数据「—」,不算 0——报告作者自己摆时心里要有这根弦,内置报告不用它。
18
21
  /**
19
22
  * 定义一个指标。内置指标与自定义指标是同一个类型,没有特权;
@@ -33,10 +36,56 @@ export function defineMetric(def) {
33
36
  export function attemptCostUSD(result) {
34
37
  return result.usage?.costUSD ?? result.estimatedCostUSD ?? null;
35
38
  }
36
- export const passRate = defineMetric({
37
- name: "pass-rate",
38
- label: { en: "Pass rate", "zh-CN": "成功率" },
39
- description: "Share of evals that passed (skipped attempts excluded).",
39
+ /**
40
+ * 条件答题质量:passed = 1,failed = 0,errored 记 null 不进分母。
41
+ * 这是「已形成可信判定」条件下的诊断指标,不能简称默认成功率
42
+ * (docs/feature/reports/library.md「内置指标」)
43
+ */
44
+ export const taskPassRate = defineMetric({
45
+ name: "task-pass-rate",
46
+ label: { en: "Task pass rate", "zh-CN": "可判定任务通过率" },
47
+ description: "Conditional task quality among attempts that formed a trustworthy verdict: passed = 1, failed = 0; errored is null.",
48
+ better: "higher",
49
+ unit: "%",
50
+ value(a) {
51
+ switch (a.result.verdict) {
52
+ case "passed":
53
+ return 1;
54
+ case "failed":
55
+ return 0;
56
+ default:
57
+ // errored = 没形成可信判定 → null 不进这个条件指标;skipped 同为 null。
58
+ return null;
59
+ }
60
+ },
61
+ });
62
+ /** 执行可靠性:跑到可判定(passed / failed)= 1,errored = 0;skipped → null。 */
63
+ export const executionReliability = defineMetric({
64
+ name: "execution-reliability",
65
+ label: { en: "Execution reliability", "zh-CN": "执行可靠性" },
66
+ description: "Execution reliability: reached a trustworthy verdict (passed / failed) = 1, errored = 0.",
67
+ better: "higher",
68
+ unit: "%",
69
+ value(a) {
70
+ switch (a.result.verdict) {
71
+ case "passed":
72
+ case "failed":
73
+ return 1;
74
+ case "errored":
75
+ return 0;
76
+ default:
77
+ return null; // skipped
78
+ }
79
+ },
80
+ });
81
+ /**
82
+ * 端到端合成:passed = 1,failed / errored = 0;哪边拖累用
83
+ * taskPassRate / executionReliability 拆开看。
84
+ */
85
+ export const endToEndPassRate = defineMetric({
86
+ name: "end-to-end-pass-rate",
87
+ label: { en: "End-to-end pass rate", "zh-CN": "端到端成功率" },
88
+ description: "End-to-end composite: passed = 1, failed / errored = 0. Split blame with taskPassRate and executionReliability.",
40
89
  better: "higher",
41
90
  unit: "%",
42
91
  value: (a) => a.result.verdict === "skipped" ? null : a.result.verdict === "passed" ? 1 : 0,
@@ -56,10 +105,12 @@ export const examScore = defineMetric({
56
105
  // failed 同理得 0:--strict 下被翻成 failed 的哪怕 soft 分不低也是 0(报告不重新判卷)。
57
106
  if (verdict !== "passed")
58
107
  return 0;
59
- const soft = assertions.filter((x) => x.severity === "soft");
108
+ // unavailable 没有分数:不计入均分分母(评不了 0 分;非 optional 的 unavailable
109
+ // 早已把 verdict 拖成 errored,走不到这个分支)。
110
+ const soft = assertions.filter((x) => x.severity === "soft" && x.outcome !== "unavailable");
60
111
  if (soft.length === 0)
61
112
  return 1;
62
- return soft.reduce((sum, x) => sum + x.score, 0) / soft.length;
113
+ return soft.reduce((sum, x) => sum + (x.outcome === "unavailable" ? 0 : x.score), 0) / soft.length;
63
114
  },
64
115
  });
65
116
  export const durationMs = defineMetric({
@@ -30,6 +30,12 @@ export interface TableColumn {
30
30
  header: string;
31
31
  /** 对齐方向,默认 `"left"`;`"right"` 按显示宽度右对齐,数字列用。 */
32
32
  align?: ColumnAlign;
33
+ /**
34
+ * text 面:单元格折行后的最大物理行数,放不下的部分以 `…` 收口;省略则不限行数。
35
+ * 摘要类列(如比较列表的 Result)用它保证「格子是可扫读的预览」;完整值在下钻面。
36
+ * web 面不消费——网页的高度约束是组件自己的 CSS 决定。
37
+ */
38
+ maxLines?: number;
33
39
  }
34
40
  /** 一行的数据:身份键、已格式化的格子、可选的 attempt locator。 */
35
41
  export interface TableRow {
@@ -2,13 +2,13 @@ import type { ReactElement } from "react";
2
2
  import type { AttemptListItem } from "../types.ts";
3
3
  import type { AttemptLocator } from "../../results/locator.ts";
4
4
  import { type ReportLocale } from "../locale.ts";
5
- /** locator + 判定符 + 证据能力标记的普通 <a>,AttemptList/EvalList/ExperimentList 共用。 */
5
+ /** locator + 判定符的普通 <a>,AttemptList/EvalList/ExperimentList 共用。 */
6
6
  export declare function AttemptLocatorBadge({ item, attemptHref, }: {
7
- item: Pick<AttemptListItem, "locator" | "verdict" | "capabilities">;
7
+ item: Pick<AttemptListItem, "locator" | "verdict">;
8
8
  attemptHref: (locator: AttemptLocator) => string;
9
9
  }): ReactElement;
10
- /** 一条 Attempt 的完整卡片:AttemptList 自己的一项,也是 EvalList 展开区里的一行。 */
11
- export declare function AttemptRow({ item, attemptHref, locale, }: {
10
+ /** 一条 Attempt 的比较卡片;完整 assertions 通过 locator 下钻,不在列表内展开。 */
11
+ export declare function AttemptRow({ item, attemptHref, }: {
12
12
  item: AttemptListItem;
13
13
  attemptHref: (locator: AttemptLocator) => string;
14
14
  locale?: ReportLocale;
@@ -1,18 +1,16 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { DEFAULT_REPORT_LOCALE, localeText } from "../locale.js";
3
+ import { attemptItemReason } from "../format.js";
3
4
  import { colorClassForKey } from "./colors.js";
4
- import { capabilityBadge, cx, formatDurationMs, formatUSD, verdictMark } from "./format.js";
5
- /** locator + 判定符 + 证据能力标记的普通 <a>,AttemptList/EvalList/ExperimentList 共用。 */
5
+ import { cx, formatDurationMs, formatUSD, verdictMark } from "./format.js";
6
+ /** locator + 判定符的普通 <a>,AttemptList/EvalList/ExperimentList 共用。 */
6
7
  export function AttemptLocatorBadge({ item, attemptHref, }) {
7
- const caps = capabilityBadge(item.capabilities);
8
- return (_jsxs("a", { className: cx("nre-locator", `nre-verdict-${item.verdict}`), href: attemptHref(item.locator), children: [item.locator, _jsx("span", { className: "nre-locator-mark", children: verdictMark(item.verdict) }), caps && _jsx("span", { className: "nre-locator-caps", children: caps })] }));
8
+ return (_jsxs("a", { className: cx("nre-locator", `nre-verdict-${item.verdict}`), href: attemptHref(item.locator), children: [item.locator, _jsx("span", { className: "nre-locator-mark", children: verdictMark(item.verdict) })] }));
9
9
  }
10
- function AssertionRow({ assertion, locale }) {
11
- return (_jsxs("li", { className: cx("nre-assertion", `nre-assertion-${assertion.severity}`, !assertion.passed && "nre-assertion-failed"), children: [_jsx("span", { className: "nre-assertion-severity", children: assertion.severity }), _jsx("span", { className: "nre-assertion-name", children: assertion.name }), _jsx("span", { className: "nre-assertion-score", children: localeText(locale, "attemptList.score", { score: assertion.score }) }), (assertion.detail || assertion.evidence) && (_jsxs("details", { className: "nre-assertion-more", children: [_jsx("summary", { children: localeText(locale, "attemptList.details") }), assertion.detail && _jsx("p", { className: "nre-assertion-detail", children: assertion.detail }), assertion.evidence && _jsx("blockquote", { className: "nre-assertion-evidence", children: assertion.evidence })] }))] }));
12
- }
13
- /** 一条 Attempt 的完整卡片:AttemptList 自己的一项,也是 EvalList 展开区里的一行。 */
14
- export function AttemptRow({ item, attemptHref, locale = DEFAULT_REPORT_LOCALE, }) {
15
- return (_jsxs("li", { className: cx("nre-attempt", `nre-attempt-${item.verdict}`), children: [_jsxs("div", { className: "nre-attempt-head", children: [_jsx(AttemptLocatorBadge, { item: item, attemptHref: attemptHref }), _jsx("span", { className: "nre-attempt-eval", children: item.evalId }), _jsx("span", { className: "nre-attempt-experiment", children: item.experimentId }), _jsx("span", { className: cx("nre-attempt-agent", "nre-key", colorClassForKey(item.agent)), children: item.agent }), _jsx("span", { className: "nre-attempt-duration", children: formatDurationMs(item.durationMs) }), item.costUSD !== undefined && _jsx("span", { className: "nre-attempt-cost", children: formatUSD(item.costUSD) })] }), item.error && _jsx("p", { className: "nre-attempt-error", children: item.error }), item.assertions.length > 0 && (_jsx("ul", { className: "nre-assertions", children: item.assertions.map((a, i) => (_jsx(AssertionRow, { assertion: a, locale: locale }, i))) }))] }));
10
+ /** 一条 Attempt 的比较卡片;完整 assertions 通过 locator 下钻,不在列表内展开。 */
11
+ export function AttemptRow({ item, attemptHref, }) {
12
+ const reason = attemptItemReason(item);
13
+ return (_jsxs("li", { className: cx("nre-attempt", `nre-attempt-${item.verdict}`), children: [_jsxs("div", { className: "nre-attempt-head", children: [_jsx(AttemptLocatorBadge, { item: item, attemptHref: attemptHref }), _jsx("span", { className: "nre-attempt-eval", children: item.evalId }), _jsx("span", { className: "nre-attempt-experiment", children: item.experimentId }), _jsx("span", { className: cx("nre-attempt-agent", "nre-key", colorClassForKey(item.agent)), children: item.agent }), _jsx("span", { className: "nre-attempt-duration", children: formatDurationMs(item.durationMs) }), item.costUSD !== undefined && _jsx("span", { className: "nre-attempt-cost", children: formatUSD(item.costUSD) })] }), reason && _jsx("p", { className: "nre-attempt-result", children: reason })] }));
16
14
  }
17
15
  export function AttemptList({ items, total, attemptHref, className, locale = DEFAULT_REPORT_LOCALE, }) {
18
16
  const remaining = (total ?? items.length) - items.length;
@@ -1,6 +1,6 @@
1
1
  import type { ReactElement } from "react";
2
- import type { EvalListItem } from "../types.ts";
3
2
  import type { AttemptLocator } from "../../results/locator.ts";
3
+ import type { EvalListItem } from "../types.ts";
4
4
  import { type ReportLocale } from "../locale.ts";
5
5
  export declare function EvalList({ items, attemptHref, className, locale, }: {
6
6
  items: EvalListItem[];
Binary file
@@ -0,0 +1,8 @@
1
+ import type { ReactElement } from "react";
2
+ import type { ExperimentComparisonData } from "../built-ins/experiment-comparison.tsx";
3
+ import { type ReportLocale } from "../locale.ts";
4
+ export declare function ExperimentComparisonView({ data, className, locale, }: {
5
+ data: ExperimentComparisonData;
6
+ className?: string;
7
+ locale?: ReportLocale;
8
+ }): ReactElement;
@@ -0,0 +1,11 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { DEFAULT_REPORT_LOCALE, localeText } from "../locale.js";
3
+ import { ExperimentList, MetricScatter } from "../components.js";
4
+ import { GroupSummary } from "./GroupSummary.js";
5
+ import { cx } from "./format.js";
6
+ export function ExperimentComparisonView({ data, className, locale = DEFAULT_REPORT_LOCALE, }) {
7
+ if (data.groups.length === 0) {
8
+ return (_jsx("div", { className: cx("nre", "nre-experiment-comparison", className), children: _jsx("p", { className: "nre-experiment-groups-empty", children: localeText(locale, "experimentComparison.empty") }) }));
9
+ }
10
+ return (_jsxs("div", { className: cx("nre", "nre-experiment-comparison", className), "data-nre-experiment-groups": true, children: [_jsx("div", { className: "nre-experiment-group-tabs", role: "tablist", "aria-label": localeText(locale, "experimentComparison.groups"), children: data.groups.map((group, index) => (_jsxs("div", { className: "nre-experiment-group-tab", role: "tab", tabIndex: index === 0 ? 0 : -1, "aria-selected": index === 0 ? "true" : "false", "aria-label": localeText(locale, "experimentComparison.selectGroup", { group: group.key }), "data-nre-experiment-group-select": index, children: [_jsx("strong", { className: "nre-experiment-group-name", children: group.key }), _jsx(GroupSummary, { data: group.summary, locale: locale })] }, group.key))) }), _jsx("div", { className: "nre-experiment-group-panels", children: data.groups.map((group, index) => (_jsxs("details", { className: "nre-experiment-group-panel", "data-nre-experiment-group-panel": index, role: "tabpanel", open: index === 0, children: [_jsx("summary", { children: group.key }), _jsx(MetricScatter, { data: group.scatter, locale: locale }), _jsx(ExperimentList, { items: group.experiments, filter: true, locale: locale, relativeTo: group.key })] }, group.key))) })] }));
11
+ }
@@ -1,10 +1,12 @@
1
1
  import type { ReactElement } from "react";
2
- import type { ExperimentListItem } from "../types.ts";
3
2
  import type { AttemptLocator } from "../../results/locator.ts";
3
+ import type { ExperimentListItem } from "../types.ts";
4
4
  import { type ReportLocale } from "../locale.ts";
5
- export declare function ExperimentList({ items, attemptHref, className, locale, }: {
5
+ export declare function ExperimentList({ items, attemptHref, filter, className, locale, relativeTo, }: {
6
6
  items: ExperimentListItem[];
7
7
  attemptHref: (locator: AttemptLocator) => string;
8
+ filter?: boolean;
8
9
  className?: string;
9
10
  locale?: ReportLocale;
11
+ relativeTo?: string;
10
12
  }): ReactElement;
@@ -1,15 +1,65 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { attemptItemReason, experimentDisplayName } from "../format.js";
2
3
  import { DEFAULT_REPORT_LOCALE, localeText } from "../locale.js";
3
- import { MetricCellView } from "./cell.js";
4
4
  import { AttemptLocatorBadge } from "./AttemptList.js";
5
+ import { MetricCellView } from "./cell.js";
5
6
  import { colorClassForKey } from "./colors.js";
6
7
  import { cx, formatDurationMs, formatUSD, verdictMark } from "./format.js";
7
- function EvalRow({ row, attemptHref, }) {
8
- return (_jsxs("li", { className: cx("nre-experiment-eval-row", `nre-eval-${row.verdict}`), children: [_jsx("span", { className: cx("nre-eval-verdict", `nre-verdict-${row.verdict}`), children: verdictMark(row.verdict) }), _jsx("span", { className: "nre-eval-id", children: row.evalId }), _jsx("span", { className: "nre-eval-attempt-badges", children: row.attempts.map((attempt) => (_jsx(AttemptLocatorBadge, { item: attempt, attemptHref: attemptHref }, attempt.locator))) }), row.verdict === "passed" ? (_jsxs("span", { className: "nre-eval-avg", children: [formatDurationMs(row.duration.value ?? 0), row.cost.value !== null && _jsxs(_Fragment, { children: [" \u00B7 ", formatUSD(row.cost.value)] })] })) : (_jsx("span", { className: "nre-eval-reason", children: row.reason }))] }));
8
+ const verdictOrder = ["passed", "failed", "errored", "skipped"];
9
+ function passRateTone(value) {
10
+ if (value === null)
11
+ return undefined;
12
+ if (value >= 0.8)
13
+ return "nre-good";
14
+ if (value >= 0.5)
15
+ return "nre-warn";
16
+ return "nre-bad";
17
+ }
18
+ function formatDate(value, locale) {
19
+ const date = new Date(value);
20
+ if (Number.isNaN(date.valueOf()))
21
+ return value;
22
+ return new Intl.DateTimeFormat(locale === "zh-CN" ? "zh-CN" : "en", {
23
+ month: "short",
24
+ day: "numeric",
25
+ hour: "2-digit",
26
+ minute: "2-digit",
27
+ }).format(date);
28
+ }
29
+ function VerdictSummary({ item, locale }) {
30
+ const parts = verdictOrder
31
+ .filter((verdict) => item.verdicts[verdict] > 0)
32
+ .map((verdict) => `${item.verdicts[verdict]} ${localeText(locale, `verdict.${verdict}`)}`);
33
+ return _jsx("span", { className: "nre-experiment-pill", children: parts.join(" / ") || "—" });
34
+ }
35
+ function ExperimentAttemptRow({ attempt, last, attemptHref, }) {
36
+ const reason = attemptItemReason(attempt);
37
+ return (_jsxs("li", { className: cx("nre-experiment-attempt-row", `nre-eval-${attempt.verdict}`), children: [_jsx("span", { className: "nre-attempt-branch", "aria-hidden": "true", children: last ? "└─" : "├─" }), _jsx("span", { className: "nre-eval-attempt-badges", children: _jsx(AttemptLocatorBadge, { item: attempt, attemptHref: attemptHref }) }), _jsxs("span", { className: "nre-eval-attempt-metrics", children: [formatDurationMs(attempt.durationMs), attempt.costUSD !== undefined && _jsxs(_Fragment, { children: [" \u00B7 ", formatUSD(attempt.costUSD)] })] }), _jsx("span", { className: "nre-eval-reason", children: reason ?? "—" })] }));
38
+ }
39
+ function EvalAttempts({ row, attemptHref, locale, }) {
40
+ const duration = row.duration.value === null ? localeText(locale, "cell.missing") : formatDurationMs(row.duration.value);
41
+ const cost = row.cost.value === null ? localeText(locale, "cell.missing") : formatUSD(row.cost.value);
42
+ return (_jsxs("li", { className: "nre-experiment-eval", children: [_jsxs("div", { className: cx("nre-experiment-eval-header", `nre-eval-${row.verdict}`), children: [_jsx("span", { className: cx("nre-eval-verdict", `nre-verdict-${row.verdict}`), children: verdictMark(row.verdict) }), _jsx("span", { className: "nre-eval-id", children: row.evalId }), _jsx("span", { className: "nre-eval-attempt-count", children: localeText(locale, "overview.attemptsCount", { n: row.attempts.length }) }), _jsxs("span", { className: "nre-eval-rollup", children: [localeText(locale, "entityList.average", { value: duration }), " · ", localeText(locale, "entityList.average", { value: cost })] })] }), _jsx("ul", { className: "nre-experiment-attempts", children: row.attempts.map((attempt, index) => (_jsx(ExperimentAttemptRow, { attempt: attempt, last: index === row.attempts.length - 1, attemptHref: attemptHref }, attempt.locator))) })] }));
43
+ }
44
+ function Flags({ flags, locale }) {
45
+ if (!flags || Object.keys(flags).length === 0)
46
+ return null;
47
+ return (_jsxs("div", { className: "nre-experiment-flags", children: [_jsx("span", { children: localeText(locale, "experimentList.flags") }), Object.entries(flags).map(([key, value]) => (_jsxs("b", { children: [key, "=", String(value)] }, key)))] }));
9
48
  }
10
- function ExperimentCard({ item, attemptHref, locale, }) {
11
- return (_jsx("li", { className: "nre-experiment-entry", children: _jsxs("details", { className: "nre-experiment-details", children: [_jsxs("summary", { className: "nre-experiment-summary", children: [_jsx("span", { className: cx("nre-experiment-id", "nre-key", colorClassForKey(item.experimentId)), children: item.experimentId }), _jsx("span", { className: "nre-experiment-agent", children: item.agent }), item.model && _jsx("span", { className: "nre-experiment-model", children: item.model }), _jsx("span", { className: "nre-experiment-cell", children: _jsx(MetricCellView, { cell: item.passRate, attemptHref: attemptHref, locale: locale }) }), _jsx("span", { className: "nre-experiment-cell", children: _jsx(MetricCellView, { cell: item.cost, attemptHref: attemptHref, locale: locale }) }), _jsx("span", { className: "nre-experiment-cell", children: _jsx(MetricCellView, { cell: item.duration, attemptHref: attemptHref, locale: locale }) }), _jsxs("span", { className: "nre-experiment-meta-sub", children: [localeText(locale, "overview.evalsCount", { n: item.evals }), item.attempts > item.evals ? ` · ${localeText(locale, "overview.attemptsCount", { n: item.attempts })}` : "", ` · ${localeText(locale, "latestRun", { run: item.lastRunAt })}`] })] }), _jsx("ul", { className: "nre-experiment-evals", children: item.evalRows.map((row) => (_jsx(EvalRow, { row: row, attemptHref: attemptHref }, row.evalId))) })] }) }));
49
+ function ExperimentRow({ item, attemptHref, locale, relativeTo, }) {
50
+ return (_jsxs("details", { className: "nre-experiment-entry", children: [_jsxs("summary", { className: "nre-experiment-summary", children: [_jsxs("span", { className: "nre-experiment-name", "data-sort-value": item.experimentId, children: [_jsx("b", { className: cx("nre-experiment-id", "nre-key", colorClassForKey(item.experimentId)), children: experimentDisplayName(item.experimentId, relativeTo) }), _jsxs("small", { children: [localeText(locale, "overview.evalsCount", { n: item.evals }), item.attempts > item.evals ? ` · ${localeText(locale, "overview.attemptsCount", { n: item.attempts })}` : "", ` · ${formatDate(item.lastRunAt, locale)}`] })] }), _jsx("span", { "data-sort-value": item.model ?? "", children: item.model ?? localeText(locale, "experimentList.defaultModel") }), _jsx("span", { "data-sort-value": item.agent, children: item.agent }), _jsx("span", { className: "nre-num", "data-sort-value": item.duration.value ?? "", children: _jsx(MetricCellView, { cell: item.duration, locale: locale }) }), _jsx("span", { className: cx("nre-num", passRateTone(item.passRate.value)), "data-sort-value": item.passRate.value ?? "", children: _jsx(MetricCellView, { cell: item.passRate, locale: locale }) }), _jsx("span", { className: "nre-num", "data-sort-value": item.tokens.value ?? "", children: _jsx(MetricCellView, { cell: item.tokens, locale: locale }) }), _jsx("span", { className: "nre-num", "data-sort-value": item.cost.value ?? "", children: _jsx(MetricCellView, { cell: item.cost, locale: locale }) }), _jsx("span", { "data-sort-value": item.verdicts.passed, children: _jsx(VerdictSummary, { item: item, locale: locale }) })] }), _jsxs("div", { className: "nre-experiment-detail", children: [_jsx(Flags, { flags: item.flags, locale: locale }), _jsx("ul", { className: "nre-experiment-evals", children: item.evalRows.map((row) => (_jsx(EvalAttempts, { row: row, attemptHref: attemptHref, locale: locale }, row.evalId))) })] })] }));
12
51
  }
13
- export function ExperimentList({ items, attemptHref, className, locale = DEFAULT_REPORT_LOCALE, }) {
14
- return (_jsxs("div", { className: cx("nre", "nre-experiment-list", className), children: [items.length === 0 && _jsx("p", { className: "nre-experiment-list-empty", children: localeText(locale, "attemptList.empty") }), _jsx("ul", { className: "nre-experiments", children: items.map((item) => (_jsx(ExperimentCard, { item: item, attemptHref: attemptHref, locale: locale }, item.experimentId))) })] }));
52
+ export function ExperimentList({ items, attemptHref, filter = false, className, locale = DEFAULT_REPORT_LOCALE, relativeTo, }) {
53
+ const labels = [
54
+ localeText(locale, "experimentList.experiment"),
55
+ localeText(locale, "table.model"),
56
+ localeText(locale, "table.agent"),
57
+ localeText(locale, "experimentList.avgDuration"),
58
+ localeText(locale, "experimentList.passRate"),
59
+ localeText(locale, "experimentList.tokens"),
60
+ localeText(locale, "experimentList.estimatedCost"),
61
+ localeText(locale, "experimentList.result"),
62
+ ];
63
+ const board = (_jsxs("div", { className: "nre-experiment-table", children: [_jsx("div", { className: "nre-experiment-head", children: labels.map((label, index) => (_jsx("button", { type: "button", "data-nre-experiment-sort": index, className: index === 4 ? "nre-sort-desc" : undefined, children: label }, label))) }), items.length === 0 && _jsx("p", { className: "nre-experiment-list-empty", children: localeText(locale, "attemptList.empty") }), items.map((item) => (_jsx(ExperimentRow, { item: item, attemptHref: attemptHref, locale: locale, relativeTo: relativeTo }, item.experimentId)))] }));
64
+ return (_jsxs("div", { className: cx("nre", "nre-experiment-list", filter && "nre-experiment-table-wrap", className), children: [filter && (_jsx("input", { className: "nre-filter", "data-nre-experiment-filter": "", type: "search", placeholder: localeText(locale, "experimentList.filterPlaceholder") })), board] }));
15
65
  }