niceeval 0.5.4 → 0.6.1
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.
- package/INDEX.md +55 -0
- package/dist/agents/types.d.ts +321 -0
- package/dist/context/types.d.ts +335 -0
- package/dist/i18n/core.d.ts +11 -0
- package/dist/i18n/en.d.ts +181 -0
- package/dist/i18n/index.d.ts +5 -0
- package/dist/i18n/zh-CN.d.ts +183 -0
- package/dist/o11y/otlp/parse.d.ts +3 -0
- package/dist/o11y/otlp/receiver.d.ts +21 -0
- package/dist/o11y/otlp/turn-otel.d.ts +43 -0
- package/dist/o11y/types.d.ts +190 -0
- package/dist/report/aggregate.d.ts +57 -0
- package/dist/report/aggregate.js +212 -0
- package/dist/report/built-ins/experiment-comparison.d.ts +1 -0
- package/dist/report/built-ins/experiment-comparison.js +13 -0
- package/dist/report/built-ins/index.d.ts +1 -0
- package/dist/report/built-ins/index.js +2 -0
- package/dist/report/components.d.ts +180 -0
- package/dist/report/components.js +132 -0
- package/dist/report/compute.d.ts +106 -0
- package/dist/report/compute.js +600 -0
- package/dist/report/flag.d.ts +6 -0
- package/dist/report/flag.js +8 -0
- package/dist/report/format.d.ts +26 -0
- package/dist/report/format.js +109 -0
- package/dist/report/index.d.ts +19 -0
- package/dist/report/index.js +30 -0
- package/dist/report/load.d.ts +12 -0
- package/dist/report/load.js +56 -0
- package/dist/report/locale.d.ts +94 -0
- package/dist/report/locale.js +183 -0
- package/dist/report/metrics.d.ts +21 -0
- package/dist/report/metrics.js +115 -0
- package/dist/report/primitives.d.ts +64 -0
- package/dist/report/primitives.js +113 -0
- package/dist/report/react/AttemptList.d.ts +23 -0
- package/dist/report/react/AttemptList.js +19 -0
- package/dist/report/react/DeltaTable.d.ts +8 -0
- package/dist/report/react/DeltaTable.js +24 -0
- package/dist/report/react/EvalList.d.ts +10 -0
- package/dist/report/react/EvalList.js +0 -0
- package/dist/report/react/ExperimentList.d.ts +11 -0
- package/dist/report/react/ExperimentList.js +67 -0
- package/dist/report/react/GroupSummary.d.ts +8 -0
- package/dist/report/react/GroupSummary.js +8 -0
- package/dist/report/react/MetricBars.d.ts +10 -0
- package/dist/report/react/MetricBars.js +48 -0
- package/dist/report/react/MetricLine.d.ts +9 -0
- package/dist/report/react/MetricLine.js +68 -0
- package/dist/report/react/MetricMatrix.d.ts +10 -0
- package/dist/report/react/MetricMatrix.js +23 -0
- package/dist/report/react/MetricScatter.d.ts +9 -0
- package/dist/report/react/MetricScatter.js +96 -0
- package/dist/report/react/MetricTable.d.ts +11 -0
- package/dist/report/react/MetricTable.js +26 -0
- package/dist/report/react/RunOverview.d.ts +8 -0
- package/dist/report/react/RunOverview.js +12 -0
- package/dist/report/react/Scoreboard.d.ts +8 -0
- package/dist/report/react/Scoreboard.js +26 -0
- package/dist/report/react/cell.d.ts +9 -0
- package/dist/report/react/cell.js +9 -0
- package/dist/report/react/chart-math.d.ts +14 -0
- package/dist/report/react/chart-math.js +68 -0
- package/dist/report/react/colors.d.ts +14 -0
- package/dist/report/react/colors.js +45 -0
- package/dist/report/react/fixtures.d.ts +19 -0
- package/dist/report/react/fixtures.js +398 -0
- package/dist/report/react/format.d.ts +3 -0
- package/dist/report/react/format.js +7 -0
- package/dist/report/react/index.d.ts +6 -0
- package/dist/report/react/index.js +14 -0
- package/dist/report/report.d.ts +24 -0
- package/dist/report/report.js +49 -0
- package/dist/report/text/faces.d.ts +29 -0
- package/dist/report/text/faces.js +541 -0
- package/dist/report/text/layout.d.ts +75 -0
- package/dist/report/text/layout.js +173 -0
- package/dist/report/text/plot.d.ts +37 -0
- package/dist/report/text/plot.js +81 -0
- package/dist/report/text/table.d.ts +8 -0
- package/dist/report/text/table.js +86 -0
- package/dist/report/tree.d.ts +99 -0
- package/dist/report/tree.js +228 -0
- package/dist/report/types.d.ts +408 -0
- package/dist/report/types.js +4 -0
- package/dist/report/web.d.ts +15 -0
- package/dist/report/web.js +37 -0
- package/dist/results/locator.d.ts +75 -0
- package/dist/results/locator.js +0 -0
- package/dist/results/select.d.ts +45 -0
- package/dist/results/select.js +230 -0
- package/dist/results/source-hash.d.ts +9 -0
- package/dist/results/types.d.ts +204 -0
- package/dist/runner/eval-source.d.ts +22 -0
- package/dist/runner/fingerprint.d.ts +23 -0
- package/dist/runner/types.d.ts +332 -0
- package/dist/sandbox/docker-stream.d.ts +24 -0
- package/dist/sandbox/docker.d.ts +84 -0
- package/dist/sandbox/e2b.d.ts +31 -0
- package/dist/sandbox/errors.d.ts +4 -0
- package/dist/sandbox/local-files.d.ts +6 -0
- package/dist/sandbox/paths.d.ts +4 -0
- package/dist/sandbox/registry.d.ts +13 -0
- package/dist/sandbox/resolve.d.ts +40 -0
- package/dist/sandbox/retry.d.ts +10 -0
- package/dist/sandbox/shell.d.ts +11 -0
- package/dist/sandbox/source-files.d.ts +13 -0
- package/dist/sandbox/types.d.ts +203 -0
- package/dist/sandbox/vercel.d.ts +37 -0
- package/dist/scoring/types.d.ts +70 -0
- package/dist/shared/aggregate.d.ts +29 -0
- package/dist/shared/aggregate.js +55 -0
- package/dist/shared/types.d.ts +27 -0
- package/dist/shared/verdict.d.ts +27 -0
- package/dist/shared/verdict.js +34 -0
- package/dist/tty-line.d.ts +6 -0
- package/dist/types.d.ts +7 -0
- package/dist/util.d.ts +22 -0
- package/docs-site/zh/concepts/adapter.mdx +7 -7
- package/docs-site/zh/concepts/assert.mdx +19 -18
- package/docs-site/zh/concepts/evals.mdx +12 -11
- package/docs-site/zh/concepts/experiment.mdx +2 -2
- package/docs-site/zh/concepts/judge.mdx +9 -9
- package/docs-site/zh/concepts/overview.mdx +3 -3
- package/docs-site/zh/concepts/tier.mdx +6 -6
- package/docs-site/zh/example/claude-code-codex-plugin.mdx +9 -5
- package/docs-site/zh/example/claude-code-codex-skill.mdx +12 -7
- package/docs-site/zh/guides/agent-feedback-loop.mdx +237 -78
- package/docs-site/zh/guides/connect-otel.mdx +3 -3
- package/docs-site/zh/guides/connect-your-agent.mdx +2 -2
- package/docs-site/zh/guides/custom-reports.mdx +196 -36
- package/docs-site/zh/guides/experiments.mdx +7 -1
- package/docs-site/zh/guides/fixtures.mdx +4 -2
- package/docs-site/zh/guides/official-adapters.mdx +31 -9
- package/docs-site/zh/guides/publish-report.mdx +4 -4
- package/docs-site/zh/guides/report-components.mdx +194 -55
- package/docs-site/zh/guides/reporters.mdx +2 -2
- package/docs-site/zh/guides/results-data.mdx +61 -34
- package/docs-site/zh/guides/runner.mdx +2 -2
- package/docs-site/zh/guides/sandbox-agent.mdx +3 -2
- package/docs-site/zh/guides/{sandbox-backends.mdx → sandbox-providers.mdx} +12 -12
- package/docs-site/zh/guides/viewing-results.mdx +200 -79
- package/docs-site/zh/guides/write-experiment.mdx +9 -1
- package/docs-site/zh/guides/write-send.mdx +4 -3
- package/docs-site/zh/index.mdx +4 -3
- package/docs-site/zh/introduction.mdx +2 -1
- package/docs-site/zh/quickstart.mdx +5 -3
- package/docs-site/zh/reference/builtin-agents.mdx +38 -13
- package/docs-site/zh/reference/capabilities.mdx +1 -1
- package/docs-site/zh/reference/cli.mdx +36 -13
- package/docs-site/zh/reference/define-agent.mdx +15 -13
- package/docs-site/zh/reference/define-config.mdx +1 -1
- package/docs-site/zh/reference/define-eval.mdx +3 -3
- package/docs-site/zh/reference/events.mdx +9 -3
- package/docs-site/zh/reference/expect.mdx +13 -8
- package/package.json +10 -6
- package/src/agents/bub.ts +129 -42
- package/src/agents/claude-code.test.ts +152 -0
- package/src/agents/claude-code.ts +127 -13
- package/src/agents/codex.test.ts +192 -0
- package/src/agents/codex.ts +151 -12
- package/src/agents/index.ts +9 -3
- package/src/agents/manifest.ts +41 -0
- package/src/agents/plugin-config.test.ts +94 -0
- package/src/agents/shared.ts +33 -2
- package/src/agents/skills.test.ts +214 -0
- package/src/agents/skills.ts +304 -0
- package/src/agents/streaming.ts +5 -5
- package/src/agents/types.ts +59 -3
- package/src/cli.ts +74 -47
- package/src/context/context.test.ts +20 -2
- package/src/context/control-flow.ts +1 -1
- package/src/context/session.test.ts +92 -1
- package/src/context/session.ts +23 -1
- package/src/context/types.ts +4 -4
- package/src/define.test.ts +8 -8
- package/src/define.ts +11 -11
- package/src/expect/index.ts +5 -5
- package/src/i18n/en.ts +33 -15
- package/src/i18n/zh-CN.ts +37 -21
- package/src/index.ts +1 -1
- package/src/o11y/execution-tree.test.ts +452 -0
- package/src/o11y/execution-tree.ts +367 -0
- package/src/o11y/otlp/receiver.ts +1 -1
- package/src/o11y/otlp/select.ts +7 -3
- package/src/o11y/parsers/claude-code.test.ts +175 -0
- package/src/o11y/parsers/claude-code.ts +32 -0
- package/src/o11y/types.ts +6 -1
- package/src/report/aggregate.ts +26 -20
- package/src/report/built-in-user-parity.test.tsx +640 -0
- package/src/report/built-ins/experiment-comparison.tsx +19 -0
- package/src/report/built-ins/index.ts +2 -0
- package/src/report/components.tsx +225 -50
- package/src/report/compute.ts +335 -54
- package/src/report/dual-render.test.tsx +1129 -0
- package/src/report/flag.ts +1 -1
- package/src/report/format.ts +35 -2
- package/src/report/index.ts +47 -14
- package/src/report/load.ts +1 -1
- package/src/report/locale.ts +229 -0
- package/src/report/metrics.ts +39 -15
- package/src/report/primitives.tsx +100 -2
- package/src/report/react/AttemptList.tsx +113 -0
- package/src/report/react/DeltaTable.tsx +9 -6
- package/src/report/react/EvalList.tsx +0 -0
- package/src/report/react/ExperimentList.tsx +220 -0
- package/src/report/react/GroupSummary.tsx +66 -0
- package/src/report/react/MetricBars.tsx +24 -13
- package/src/report/react/MetricLine.tsx +21 -21
- package/src/report/react/MetricMatrix.tsx +8 -4
- package/src/report/react/MetricScatter.tsx +126 -96
- package/src/report/react/MetricTable.tsx +115 -15
- package/src/report/react/RunOverview.tsx +48 -26
- package/src/report/react/Scoreboard.tsx +15 -7
- package/src/report/react/cell.tsx +18 -9
- package/src/{view/app/lib/chart.ts → report/react/chart-math.ts} +2 -6
- package/src/report/react/colors.ts +24 -15
- package/src/report/react/enhance.js +168 -0
- package/src/report/react/fixtures.ts +169 -34
- package/src/report/react/format.ts +1 -1
- package/src/report/react/index.tsx +21 -6
- package/src/report/react/render.test.tsx +180 -31
- package/src/report/react/styles.css +432 -213
- package/src/report/report.test.ts +398 -94
- package/src/report/report.ts +29 -11
- package/src/report/text/faces.ts +344 -96
- package/src/report/text/layout.ts +76 -11
- package/src/report/text/table.ts +98 -0
- package/src/report/tree.ts +103 -20
- package/src/report/types.ts +184 -25
- package/src/report/web.ts +46 -19
- package/src/results/annotated-source.test.ts +146 -0
- package/src/results/annotated-source.ts +128 -0
- package/src/results/attempt-evidence.test.ts +158 -0
- package/src/results/attempt-evidence.ts +158 -0
- package/src/results/attempt-source.ts +48 -0
- package/src/results/copy.ts +154 -139
- package/src/results/format.ts +54 -47
- package/src/results/host-equivalence.test.ts +527 -0
- package/src/results/index.ts +56 -14
- package/src/results/locator.test.ts +198 -0
- package/src/results/locator.ts +0 -0
- package/src/results/open.ts +392 -175
- package/src/results/results.test.ts +943 -494
- package/src/results/select.ts +129 -23
- package/src/results/skipped-notice.ts +0 -0
- package/src/results/source-hash.ts +28 -0
- package/src/results/types.ts +98 -62
- package/src/results/writer.ts +304 -176
- package/src/runner/attempt.test.ts +170 -0
- package/src/runner/attempt.ts +48 -13
- package/src/runner/discover.test.ts +65 -0
- package/src/runner/discover.ts +6 -2
- package/src/runner/eval-source.test.ts +121 -0
- package/src/runner/eval-source.ts +45 -0
- package/src/runner/fingerprint.ts +54 -1
- package/src/runner/remote-sandbox.ts +1 -1
- package/src/runner/report.test.ts +6 -8
- package/src/runner/report.ts +5 -4
- package/src/runner/reporters/artifacts.ts +27 -41
- package/src/runner/reporters/braintrust.test.ts +3 -3
- package/src/runner/reporters/braintrust.ts +1 -1
- package/src/runner/reporters/console.ts +6 -6
- package/src/runner/reporters/json.ts +4 -4
- package/src/runner/reporters/live.test.ts +56 -0
- package/src/runner/reporters/live.ts +36 -10
- package/src/runner/reporters/quiet.test.ts +66 -0
- package/src/runner/reporters/quiet.ts +49 -0
- package/src/runner/reporters/shared.ts +5 -5
- package/src/runner/reporters/table.ts +17 -17
- package/src/runner/run.ts +61 -102
- package/src/runner/types.ts +43 -32
- package/src/sandbox/checkpoint.ts +1 -1
- package/src/sandbox/docker.ts +15 -2
- package/src/sandbox/e2b.ts +11 -5
- package/src/sandbox/errors.ts +13 -0
- package/src/sandbox/index.ts +3 -3
- package/src/sandbox/local-files.ts +1 -1
- package/src/sandbox/registry.ts +6 -5
- package/src/sandbox/resolve.ts +38 -23
- package/src/sandbox/retry.test.ts +68 -0
- package/src/sandbox/retry.ts +44 -0
- package/src/sandbox/types.ts +24 -24
- package/src/sandbox/vercel.ts +17 -3
- package/src/scoring/collector.ts +3 -3
- package/src/scoring/judge.ts +2 -2
- package/src/scoring/scoped.ts +13 -1
- package/src/scoring/types.ts +7 -7
- package/src/scoring/verdict.ts +4 -4
- package/src/shared/aggregate.ts +23 -3
- package/src/shared/types.ts +2 -2
- package/src/shared/{outcome.ts → verdict.ts} +15 -15
- package/src/show/compose.ts +12 -132
- package/src/show/index.ts +119 -95
- package/src/show/render.ts +462 -159
- package/src/show/show.test.ts +471 -253
- package/src/source-loc.ts +1 -1
- package/src/tty-line.ts +32 -0
- package/src/types.ts +2 -2
- package/src/util.test.ts +21 -1
- package/src/util.ts +5 -1
- package/src/view/app/App.tsx +33 -198
- package/src/view/app/components/AttemptModal.tsx +6 -6
- package/src/view/app/components/CodeView.tsx +2 -2
- package/src/view/app/components/CopyControls.tsx +32 -36
- package/src/view/app/components/SkippedRunsBanner.tsx +140 -0
- package/src/view/app/components/ui/badge.tsx +1 -1
- package/src/view/app/components/ui/dialog.tsx +2 -2
- package/src/view/app/i18n.ts +40 -31
- package/src/view/app/lib/artifact-url.ts +1 -1
- package/src/view/app/lib/attempt-route.test.ts +37 -47
- package/src/view/app/lib/attempt-route.ts +26 -26
- package/src/view/app/lib/rows.ts +9 -112
- package/src/view/app/lib/verdict.ts +25 -0
- package/src/view/app/main.tsx +9 -12
- package/src/view/app/pages/RunsPage.tsx +4 -4
- package/src/view/app/pages/TracesPage.tsx +3 -3
- package/src/view/app/shared.ts +0 -1
- package/src/view/app/types.ts +6 -51
- package/src/view/artifact-serving.test.ts +136 -0
- package/src/view/client-dist/app.css +1 -1
- package/src/view/client-dist/app.js +21 -21
- package/src/view/data.test.ts +214 -143
- package/src/view/data.ts +166 -120
- package/src/view/index.ts +23 -11
- package/src/view/server.ts +52 -17
- package/src/view/shared/types.ts +22 -31
- package/src/view/styles.css +117 -327
- package/src/view/view-report.test.ts +146 -98
- package/src/report/default-report.tsx +0 -222
- package/src/report/dual-face.test.tsx +0 -527
- package/src/report/react/CaseList.tsx +0 -70
- package/src/view/app/components/CostScoreChart.tsx +0 -147
- package/src/view/app/components/ExperimentTable.tsx +0 -284
- package/src/view/app/components/GroupSelector.tsx +0 -61
- package/src/view/app/components/primitives.tsx +0 -43
- package/src/view/app/lib/outcome.ts +0 -82
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// unit 驱动的内置格式化(docs/feature/reports/library.md「指标」):
|
|
2
|
+
// "%" → 87% "ms" → 1.2s "$" → $0.31 其余 → 1.2k 缩写(带 unit 后缀)
|
|
3
|
+
// metric.display 可整体覆盖;这里只负责默认。
|
|
4
|
+
/** 一位小数、去掉无意义的 ".0" 尾巴。 */
|
|
5
|
+
function trimmed(n) {
|
|
6
|
+
const s = n.toFixed(1);
|
|
7
|
+
return s.endsWith(".0") ? s.slice(0, -2) : s;
|
|
8
|
+
}
|
|
9
|
+
/** 1.2k / 3.4M / 5.6B 式缩写(输入为非负数)。 */
|
|
10
|
+
function abbreviate(abs) {
|
|
11
|
+
if (abs >= 1e9)
|
|
12
|
+
return `${trimmed(abs / 1e9)}B`;
|
|
13
|
+
if (abs >= 1e6)
|
|
14
|
+
return `${trimmed(abs / 1e6)}M`;
|
|
15
|
+
if (abs >= 1e3)
|
|
16
|
+
return `${trimmed(abs / 1e3)}k`;
|
|
17
|
+
return Number.isInteger(abs) ? String(abs) : trimmed(abs);
|
|
18
|
+
}
|
|
19
|
+
function formatDuration(absMs) {
|
|
20
|
+
if (absMs < 1000)
|
|
21
|
+
return `${Math.round(absMs)}ms`;
|
|
22
|
+
if (absMs < 60_000)
|
|
23
|
+
return `${trimmed(absMs / 1000)}s`;
|
|
24
|
+
const totalSeconds = Math.round(absMs / 1000);
|
|
25
|
+
return `${Math.floor(totalSeconds / 60)}m ${totalSeconds % 60}s`;
|
|
26
|
+
}
|
|
27
|
+
function formatDollars(abs) {
|
|
28
|
+
if (abs >= 1000)
|
|
29
|
+
return abbreviate(abs);
|
|
30
|
+
if (abs >= 0.01 || abs === 0)
|
|
31
|
+
return abs.toFixed(2);
|
|
32
|
+
// 小额成本保留有效位,不四舍成 "$0.00" 假零
|
|
33
|
+
return abs.toFixed(4);
|
|
34
|
+
}
|
|
35
|
+
export function formatMetricValue(value, unit) {
|
|
36
|
+
const sign = value < 0 ? "-" : "";
|
|
37
|
+
const abs = Math.abs(value);
|
|
38
|
+
if (unit === "%")
|
|
39
|
+
return `${sign}${trimmed(Math.round(abs * 1000) / 10)}%`;
|
|
40
|
+
if (unit === "ms")
|
|
41
|
+
return sign + formatDuration(abs);
|
|
42
|
+
if (unit === "$")
|
|
43
|
+
return `${sign}$${formatDollars(abs)}`;
|
|
44
|
+
const n = abbreviate(abs);
|
|
45
|
+
return unit ? `${sign}${n} ${unit}` : `${sign}${n}`;
|
|
46
|
+
}
|
|
47
|
+
/** 无单位纯数字(scoreboard 总分等):一位小数,去尾零。 */
|
|
48
|
+
export function formatPlainNumber(value) {
|
|
49
|
+
const sign = value < 0 ? "-" : "";
|
|
50
|
+
return sign + trimmed(Math.round(Math.abs(value) * 10) / 10);
|
|
51
|
+
}
|
|
52
|
+
// ── 以下是两个渲染面共用的展示格式化:MetricCell 一律自带 display(格式化发生在
|
|
53
|
+
// 计算侧),渲染面不重算;这里只服务 OverviewData 这类携带裸数字的字段。──
|
|
54
|
+
/** 全 null / 无样本的统一文案。绝不画 0(docs/feature/reports/architecture.md「指标聚合不变量」)。 */
|
|
55
|
+
export const MISSING_TEXT = "no data";
|
|
56
|
+
/** 毫秒 → 人读耗时("850ms" / "1.2s" / "4m 20s" / "1h 4m")。 */
|
|
57
|
+
export function formatDurationMs(ms) {
|
|
58
|
+
if (ms < 1000)
|
|
59
|
+
return `${Math.round(ms)}ms`;
|
|
60
|
+
const s = ms / 1000;
|
|
61
|
+
if (s < 60)
|
|
62
|
+
return `${s.toFixed(1)}s`;
|
|
63
|
+
const m = Math.floor(s / 60);
|
|
64
|
+
if (m < 60)
|
|
65
|
+
return `${m}m ${Math.round(s % 60)}s`;
|
|
66
|
+
const h = Math.floor(m / 60);
|
|
67
|
+
return `${h}h ${m % 60}m`;
|
|
68
|
+
}
|
|
69
|
+
/** 美元金额;小额保留更多位数,不四舍五入成 $0.00 的假零。 */
|
|
70
|
+
export function formatUSD(usd) {
|
|
71
|
+
if (usd === 0)
|
|
72
|
+
return "$0";
|
|
73
|
+
const digits = Math.abs(usd) >= 0.01 ? 2 : 4;
|
|
74
|
+
return `$${usd.toFixed(digits)}`;
|
|
75
|
+
}
|
|
76
|
+
/** 0..1 的比率 → 整数百分比。 */
|
|
77
|
+
export function formatPercent(ratio) {
|
|
78
|
+
return `${Math.round(ratio * 100)}%`;
|
|
79
|
+
}
|
|
80
|
+
// ── 实体列表(ExperimentList / EvalList / AttemptList)共用的判定符 ──
|
|
81
|
+
/** passed / failed / errored / skipped 的判定符。 */
|
|
82
|
+
export function verdictMark(verdict) {
|
|
83
|
+
switch (verdict) {
|
|
84
|
+
case "passed":
|
|
85
|
+
return "✓";
|
|
86
|
+
case "failed":
|
|
87
|
+
return "✗";
|
|
88
|
+
case "errored":
|
|
89
|
+
return "!";
|
|
90
|
+
case "skipped":
|
|
91
|
+
return "–";
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* 一个 AttemptListItem 的失败原因摘要,按优先级取第一个在场的:`error` → 未通过的 gate 断言
|
|
96
|
+
* (原始声明顺序,`name`,detail 在场则 `"name: detail"`,多条用「, 」连接)→ 缺席。与
|
|
97
|
+
* compute.ts 的 `reasonFor(EvalResult)` 同一口径,只是输入换成瘦身后的 AttemptListItem——
|
|
98
|
+
* 没有 `skipReason` 字段(AttemptListItem 不携带),skipped 的 attempt 因此没有原因摘要,
|
|
99
|
+
* 这与它们本来就不该出现在「为什么失败」列表里的事实一致。EvalList / ExperimentList 的
|
|
100
|
+
* 逐 attempt 徽标行用它给每个 attempt 自己的原因(而不是复用整道题的代表原因)。
|
|
101
|
+
*/
|
|
102
|
+
export function attemptItemReason(item) {
|
|
103
|
+
if (item.error !== undefined)
|
|
104
|
+
return item.error;
|
|
105
|
+
const gates = item.assertions.filter((a) => !a.passed && a.severity === "gate");
|
|
106
|
+
if (gates.length === 0)
|
|
107
|
+
return undefined;
|
|
108
|
+
return gates.map((a) => (a.detail ? `${a.name}: ${a.detail}` : a.name)).join(", ");
|
|
109
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export { defineMetric, passRate, examScore, durationMs, tokens, costUSD, turns } from "./metrics.ts";
|
|
2
|
+
export { flag } from "./flag.ts";
|
|
3
|
+
export { defineReport, isReportDefinition, renderReportToText } from "./report.ts";
|
|
4
|
+
export type { ReportContext, ReportDefinition } from "./report.ts";
|
|
5
|
+
export { defineComponent } from "./tree.ts";
|
|
6
|
+
export type { ComponentFaces, ReportComponent, ReportElement, ReportNode, TextContext, TextRenderOptions, WebContext, } from "./tree.ts";
|
|
7
|
+
export { Row, Col, Section, Text, Style, Table } from "./primitives.tsx";
|
|
8
|
+
export type { LayoutProps, SectionProps, StyleProps, TableColumn, TableProps, TableRow } from "./primitives.tsx";
|
|
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
|
+
export type { ColumnAlign } from "./text/layout.ts";
|
|
11
|
+
export { ExperimentComparison } from "./built-ins/index.ts";
|
|
12
|
+
export { DEFAULT_REPORT_LOCALE, resolveMetricLabel } from "./locale.ts";
|
|
13
|
+
export type { LocalizedLabel, ReportLocale } from "./locale.ts";
|
|
14
|
+
export { AttemptList, DeltaTable, EvalList, ExperimentList, GroupSummary, MetricBars, MetricLine, MetricMatrix, MetricScatter, MetricTable, RunOverview, Scoreboard, } from "./components.tsx";
|
|
15
|
+
export type { AttemptListProps, DeltaTableProps, EvalListProps, ExperimentListProps, GroupSummaryProps, MetricLineProps, MetricMatrixProps, MetricScatterProps, MetricTableProps, RunOverviewProps, ScoreboardProps, } from "./components.tsx";
|
|
16
|
+
export type { AttemptListDataOptions, DeltaDataOptions, DeltaPair, LineDataOptions, MatrixDataOptions, ScatterDataOptions, ScoreboardDataOptions, TableDataOptions, } from "./compute.ts";
|
|
17
|
+
export type { Aggregator, 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 { AttemptHandle, Results, Selection, Snapshot } from "../results/types.ts";
|
|
19
|
+
export { experimentGroupOf } from "../shared/aggregate.ts";
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// niceeval/report —— 报告积木:指标 × 计算函数 × 双面组件 × defineReport。
|
|
2
|
+
// 契约见 docs/feature/reports/README.md;公开叙事的准绳是 docs-site/zh/guides/custom-reports.mdx
|
|
3
|
+
// 与 report-components.mdx。
|
|
4
|
+
//
|
|
5
|
+
// import 边界即运行时边界:计算函数(挂在组件上的 .data)会经句柄触碰文件系统
|
|
6
|
+
// (懒加载 artifact),只能进服务端 / 脚本;组件的渲染面纯同步零 IO。text 宿主遍历渲染
|
|
7
|
+
// 不需要 react-dom(renderReportToText);web 宿主的 renderReportToStaticHtml 在
|
|
8
|
+
// ./web.ts,只有那一侧 import react-dom。写报告文件的项目要装 react(.tsx 编译产物
|
|
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";
|
|
13
|
+
// 报告基座与双面组件基座
|
|
14
|
+
export { defineReport, isReportDefinition, renderReportToText } from "./report.js";
|
|
15
|
+
export { defineComponent } from "./tree.js";
|
|
16
|
+
// 排版原语(六个内置双面组件)
|
|
17
|
+
export { Row, Col, Section, Text, Style, Table } from "./primitives.js";
|
|
18
|
+
// 文本排版工具箱:自定义组件的 text 面用的就是官方组件那把尺子。
|
|
19
|
+
// 表格有 <Table> 承担,这里只给表以外的形态用 —— 尤其别拿 String.prototype.padEnd 对齐:
|
|
20
|
+
// 它数 UTF-16 码元不数显示列宽,一带中文列就撕歪。renderAlignedRows 刻意不导出。
|
|
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 { ExperimentComparison } from "./built-ins/index.js";
|
|
24
|
+
// locale:官方组件 chrome 文案的语言(en / zh-CN);指标 label 可按 locale 给字典
|
|
25
|
+
export { DEFAULT_REPORT_LOCALE, resolveMetricLabel } from "./locale.js";
|
|
26
|
+
// 官方双面组件(各自带 data 计算函数;MetricBars.data = MetricMatrix.data)
|
|
27
|
+
export { AttemptList, DeltaTable, EvalList, ExperimentList, GroupSummary, MetricBars, MetricLine, MetricMatrix, MetricScatter, MetricTable, RunOverview, Scoreboard, } from "./components.js";
|
|
28
|
+
// experiment id 的组键推导(id 的目录前缀,如 `compare/bub-low` 的 `compare`)。
|
|
29
|
+
// 重新导出,让自定义报告能按同一份口径把 experiment 分组,不必自己重写这两行逻辑。
|
|
30
|
+
export { experimentGroupOf } from "../shared/aggregate.js";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type ReportDefinition } from "./report.ts";
|
|
2
|
+
/** 可预期的装载错误:宿主打一句英文直说问题与下一步,不抛堆栈。 */
|
|
3
|
+
export declare class ReportLoadError extends Error {
|
|
4
|
+
}
|
|
5
|
+
export interface LoadReportOptions {
|
|
6
|
+
/**
|
|
7
|
+
* 按文件 mtime 追加 query 绕开 ESM 模块缓存:同一文件未变时命中缓存,
|
|
8
|
+
* 变更后重新装载。dev server(view)传 true;一次性进程(show / --out)不需要。
|
|
9
|
+
*/
|
|
10
|
+
freshImport?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare function loadReportFile(cwd: string, path: string, options?: LoadReportOptions): Promise<ReportDefinition>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// --report 的装载:两个宿主(show / view)共用的中性入口。复用跑用户 .ts 配置的
|
|
2
|
+
// 同一 tsx 加载机制(bin 里已 register)。show 一进程一次装载;view 的 dev server
|
|
3
|
+
// 每次请求现读现渲染 —— ESM 模块缓存永不失效,所以按文件 mtime 做 cache-busting
|
|
4
|
+
// (freshImport):报告文件变更 → 下次请求拿到新模块,整页重算(docs/feature/reports/view.md
|
|
5
|
+
// 裁决记录 6)。装载环境坑见 memory/tsx-dynamic-import-require-cycle.md。
|
|
6
|
+
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
7
|
+
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
8
|
+
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
9
|
+
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
return path;
|
|
13
|
+
};
|
|
14
|
+
import { existsSync, statSync } from "node:fs";
|
|
15
|
+
import { resolve } from "node:path";
|
|
16
|
+
import { pathToFileURL } from "node:url";
|
|
17
|
+
import { isReportDefinition } from "./report.js";
|
|
18
|
+
/** 可预期的装载错误:宿主打一句英文直说问题与下一步,不抛堆栈。 */
|
|
19
|
+
export class ReportLoadError extends Error {
|
|
20
|
+
}
|
|
21
|
+
export async function loadReportFile(cwd, path, options) {
|
|
22
|
+
const abs = resolve(cwd, path);
|
|
23
|
+
if (!existsSync(abs)) {
|
|
24
|
+
throw new ReportLoadError(`Report file not found: ${abs}. Pass --report an explicit path to a module whose default export is defineReport(...).`);
|
|
25
|
+
}
|
|
26
|
+
const plain = pathToFileURL(abs);
|
|
27
|
+
const url = new URL(plain.href);
|
|
28
|
+
if (options?.freshImport) {
|
|
29
|
+
// 只 bust 报告文件本体:它 import 的模块仍走缓存,与「报告文件变更整页重算」的
|
|
30
|
+
// 装载语义一致(依赖变更不追踪)。tsx 与 Node 原生 ESM 都认 file URL 的 query。
|
|
31
|
+
url.searchParams.set("mtime", String(statSync(abs).mtimeMs));
|
|
32
|
+
}
|
|
33
|
+
let mod;
|
|
34
|
+
try {
|
|
35
|
+
mod = (await import(__rewriteRelativeImportExtension(url.href)));
|
|
36
|
+
}
|
|
37
|
+
catch (e) {
|
|
38
|
+
// 个别装载环境(如 vitest 的 vite-node)按「扩展名 + query」误判文件类型;
|
|
39
|
+
// 退化为普通 import(失去变更重载,不失去功能)。仍失败才是真错误。
|
|
40
|
+
if (url.href !== plain.href) {
|
|
41
|
+
try {
|
|
42
|
+
mod = (await import(__rewriteRelativeImportExtension(plain.href)));
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
throw new ReportLoadError(`Cannot load report file ${abs}: ${e instanceof Error ? e.message : String(e)}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
throw new ReportLoadError(`Cannot load report file ${abs}: ${e instanceof Error ? e.message : String(e)}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (!isReportDefinition(mod.default)) {
|
|
53
|
+
throw new ReportLoadError(`${path} does not default-export a report. Export default defineReport(async ({ selection, results }) => ...) from "niceeval/report".`);
|
|
54
|
+
}
|
|
55
|
+
return mod.default;
|
|
56
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/** 报告渲染的 locale;默认 "en"(text 面缺省即 en,`niceeval show` 输出不变)。 */
|
|
2
|
+
export type ReportLocale = "en" | "zh-CN";
|
|
3
|
+
export declare const DEFAULT_REPORT_LOCALE: ReportLocale;
|
|
4
|
+
declare const en: {
|
|
5
|
+
readonly "verdict.passed": "passed";
|
|
6
|
+
readonly "verdict.failed": "failed";
|
|
7
|
+
readonly "verdict.errored": "errored";
|
|
8
|
+
readonly "verdict.skipped": "skipped";
|
|
9
|
+
/** 全 null / 无样本的统一文案,绝不画 0。 */
|
|
10
|
+
readonly "cell.missing": "no data";
|
|
11
|
+
readonly "cell.measuredTitle": "{samples}/{total} attempts measured";
|
|
12
|
+
readonly "cell.noneMeasurableTitle": "0/{total} attempts measurable";
|
|
13
|
+
readonly "cell.coverageTitle": "coverage {samples}/{total}: this metric is null for the remaining attempts";
|
|
14
|
+
readonly "table.higherBetter": "higher is better";
|
|
15
|
+
readonly "table.lowerBetter": "lower is better";
|
|
16
|
+
readonly "table.model": "Model";
|
|
17
|
+
readonly "table.agent": "Agent";
|
|
18
|
+
readonly "table.verdicts": "Verdicts";
|
|
19
|
+
readonly "table.filterPlaceholder": "Filter rows…";
|
|
20
|
+
readonly "table.eval": "Eval";
|
|
21
|
+
readonly "table.reason": "Reason";
|
|
22
|
+
readonly "table.viewBreakdown": "Per-eval breakdown";
|
|
23
|
+
/** <Table> 的 locator 列表头(行带 locator 时自动追加)。 */
|
|
24
|
+
readonly "table.attempt": "attempt";
|
|
25
|
+
readonly "experimentList.experiment": "Experiment";
|
|
26
|
+
readonly "experimentList.avgDuration": "Avg duration";
|
|
27
|
+
readonly "experimentList.passRate": "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
|
+
/** <Table> 压到下限仍放不下时,从右侧丢列并如实报数。 */
|
|
39
|
+
readonly "table.columnsHidden.one": "({n} more column not shown)";
|
|
40
|
+
readonly "table.columnsHidden.other": "({n} more columns not shown)";
|
|
41
|
+
readonly "overview.snapshots": "Snapshots";
|
|
42
|
+
readonly "overview.evals": "Evals";
|
|
43
|
+
readonly "overview.attempts": "attempts";
|
|
44
|
+
readonly "overview.passRate": "Pass rate";
|
|
45
|
+
readonly "overview.totalCost": "Total cost";
|
|
46
|
+
readonly "overview.totalDuration": "Total duration";
|
|
47
|
+
readonly "overview.source": "Source: {n} snapshots";
|
|
48
|
+
readonly "overview.experiments.one": "{n} experiment";
|
|
49
|
+
readonly "overview.experiments.other": "{n} experiments";
|
|
50
|
+
readonly "overview.evalsCount": "{n} evals";
|
|
51
|
+
readonly "overview.attemptsCount": "{n} attempts";
|
|
52
|
+
/** GroupSummary 的 KPI dt 标签(其余字段复用 verdict.* / overview.* 已有 key)。 */
|
|
53
|
+
readonly "groupSummary.experiments": "Experiments";
|
|
54
|
+
readonly "composedFrom.one": "composed from {n} run";
|
|
55
|
+
readonly "composedFrom.other": "composed from {n} runs";
|
|
56
|
+
readonly latestRun: "latest {run}";
|
|
57
|
+
readonly "scatter.betterHint": "better ↗";
|
|
58
|
+
readonly "scatter.betterUpperRight": "better → upper right";
|
|
59
|
+
readonly "scatter.axisReversed": "(axis reversed: right = better)";
|
|
60
|
+
/** 0 个可画点:x/y 指标没有可用数据。 */
|
|
61
|
+
readonly "scatter.noData": "No data to plot {x} × {y}";
|
|
62
|
+
readonly "pointsMissing.one": "{n} point missing data";
|
|
63
|
+
readonly "pointsMissing.other": "{n} points missing data";
|
|
64
|
+
readonly "attemptList.empty": "No attempts";
|
|
65
|
+
readonly "attemptList.truncated": "and {n} more not shown";
|
|
66
|
+
readonly "attemptList.truncatedText": "({n} more not shown)";
|
|
67
|
+
readonly "attemptList.score": "score {score}";
|
|
68
|
+
readonly "attemptList.details": "details";
|
|
69
|
+
readonly "scoreboard.total": "Total";
|
|
70
|
+
readonly "scoreboard.totalText": "total";
|
|
71
|
+
readonly "scoreboard.missing.one": "{n} eval missing, scored 0";
|
|
72
|
+
readonly "scoreboard.missing.other": "{n} evals missing, scored 0";
|
|
73
|
+
readonly "scoreboard.missingText": "({n} missing)";
|
|
74
|
+
readonly "scoreboard.weights": "weights:";
|
|
75
|
+
readonly "scoreboard.allWeights": "all evals ×1";
|
|
76
|
+
readonly "scoreboard.othersWeight": "others ×1";
|
|
77
|
+
readonly "scoreboard.subjectTitle": "{evals} evals, weighted {earned} of {possible}";
|
|
78
|
+
readonly "delta.pairHeader": "pair (A → B)";
|
|
79
|
+
readonly "board.currentVerdicts": "Current verdicts";
|
|
80
|
+
readonly "board.failing": "Failing:";
|
|
81
|
+
};
|
|
82
|
+
export type ReportMessageKey = keyof typeof en;
|
|
83
|
+
/** 查字典 + 简单插值({name} 占位符)。缺 locale 回退 en。 */
|
|
84
|
+
export declare function localeText(locale: ReportLocale, key: ReportMessageKey, vars?: Record<string, string | number>): string;
|
|
85
|
+
/** 带单复数的计数文案:n === 1 用 `<base>.one`,其余 `<base>.other`(zh-CN 两键同值)。 */
|
|
86
|
+
export declare function countText(locale: ReportLocale, base: "overview.experiments" | "composedFrom" | "pointsMissing" | "scoreboard.missing" | "table.columnsHidden", n: number): string;
|
|
87
|
+
/** 指标 label 的可本地化形态:字符串,或按 locale 给的字典(缺项回退 en,再回退任一非空值)。 */
|
|
88
|
+
export type LocalizedLabel = string | Partial<Record<ReportLocale, string>>;
|
|
89
|
+
/**
|
|
90
|
+
* 按 locale 解析指标 label:字符串原样;字典取当前 locale,缺项回退 en,
|
|
91
|
+
* 再回退字典里任一值;全空回退 fallback(= metric.name)。渲染面(web / text)共用。
|
|
92
|
+
*/
|
|
93
|
+
export declare function resolveMetricLabel(label: LocalizedLabel | undefined, locale: ReportLocale, fallback: string): string;
|
|
94
|
+
export {};
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
// 官方组件 chrome 文案的 locale 字典(en / zh-CN 两份)。
|
|
2
|
+
// 只覆盖组件自带的固定文案(verdict 词、缺数据、覆盖率角标、注脚、占位符等);
|
|
3
|
+
// 数据本身(display、维度键、warnings 的 message)不经这里。
|
|
4
|
+
// 刻意不 import src/i18n/(CLI 专用字典,locale 来源与 key 面完全不同);
|
|
5
|
+
// zh-CN 译法与 src/view/app/i18n.ts 现有词保持一致(成功率/平均耗时/预估成本…)。
|
|
6
|
+
export const DEFAULT_REPORT_LOCALE = "en";
|
|
7
|
+
const en = {
|
|
8
|
+
"verdict.passed": "passed",
|
|
9
|
+
"verdict.failed": "failed",
|
|
10
|
+
"verdict.errored": "errored",
|
|
11
|
+
"verdict.skipped": "skipped",
|
|
12
|
+
/** 全 null / 无样本的统一文案,绝不画 0。 */
|
|
13
|
+
"cell.missing": "no data",
|
|
14
|
+
"cell.measuredTitle": "{samples}/{total} attempts measured",
|
|
15
|
+
"cell.noneMeasurableTitle": "0/{total} attempts measurable",
|
|
16
|
+
"cell.coverageTitle": "coverage {samples}/{total}: this metric is null for the remaining attempts",
|
|
17
|
+
"table.higherBetter": "higher is better",
|
|
18
|
+
"table.lowerBetter": "lower is better",
|
|
19
|
+
"table.model": "Model",
|
|
20
|
+
"table.agent": "Agent",
|
|
21
|
+
"table.verdicts": "Verdicts",
|
|
22
|
+
"table.filterPlaceholder": "Filter rows…",
|
|
23
|
+
"table.eval": "Eval",
|
|
24
|
+
"table.reason": "Reason",
|
|
25
|
+
"table.viewBreakdown": "Per-eval breakdown",
|
|
26
|
+
/** <Table> 的 locator 列表头(行带 locator 时自动追加)。 */
|
|
27
|
+
"table.attempt": "attempt",
|
|
28
|
+
"experimentList.experiment": "Experiment",
|
|
29
|
+
"experimentList.avgDuration": "Avg duration",
|
|
30
|
+
"experimentList.passRate": "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
|
+
/** <Table> 压到下限仍放不下时,从右侧丢列并如实报数。 */
|
|
42
|
+
"table.columnsHidden.one": "({n} more column not shown)",
|
|
43
|
+
"table.columnsHidden.other": "({n} more columns not shown)",
|
|
44
|
+
"overview.snapshots": "Snapshots",
|
|
45
|
+
"overview.evals": "Evals",
|
|
46
|
+
"overview.attempts": "attempts",
|
|
47
|
+
"overview.passRate": "Pass rate",
|
|
48
|
+
"overview.totalCost": "Total cost",
|
|
49
|
+
"overview.totalDuration": "Total duration",
|
|
50
|
+
"overview.source": "Source: {n} snapshots",
|
|
51
|
+
"overview.experiments.one": "{n} experiment",
|
|
52
|
+
"overview.experiments.other": "{n} experiments",
|
|
53
|
+
"overview.evalsCount": "{n} evals",
|
|
54
|
+
"overview.attemptsCount": "{n} attempts",
|
|
55
|
+
/** GroupSummary 的 KPI dt 标签(其余字段复用 verdict.* / overview.* 已有 key)。 */
|
|
56
|
+
"groupSummary.experiments": "Experiments",
|
|
57
|
+
"composedFrom.one": "composed from {n} run",
|
|
58
|
+
"composedFrom.other": "composed from {n} runs",
|
|
59
|
+
"latestRun": "latest {run}",
|
|
60
|
+
"scatter.betterHint": "better ↗",
|
|
61
|
+
"scatter.betterUpperRight": "better → upper right",
|
|
62
|
+
"scatter.axisReversed": "(axis reversed: right = better)",
|
|
63
|
+
/** 0 个可画点:x/y 指标没有可用数据。 */
|
|
64
|
+
"scatter.noData": "No data to plot {x} × {y}",
|
|
65
|
+
"pointsMissing.one": "{n} point missing data",
|
|
66
|
+
"pointsMissing.other": "{n} points missing data",
|
|
67
|
+
"attemptList.empty": "No attempts",
|
|
68
|
+
"attemptList.truncated": "and {n} more not shown",
|
|
69
|
+
"attemptList.truncatedText": "({n} more not shown)",
|
|
70
|
+
"attemptList.score": "score {score}",
|
|
71
|
+
"attemptList.details": "details",
|
|
72
|
+
"scoreboard.total": "Total",
|
|
73
|
+
"scoreboard.totalText": "total",
|
|
74
|
+
"scoreboard.missing.one": "{n} eval missing, scored 0",
|
|
75
|
+
"scoreboard.missing.other": "{n} evals missing, scored 0",
|
|
76
|
+
"scoreboard.missingText": "({n} missing)",
|
|
77
|
+
"scoreboard.weights": "weights:",
|
|
78
|
+
"scoreboard.allWeights": "all evals ×1",
|
|
79
|
+
"scoreboard.othersWeight": "others ×1",
|
|
80
|
+
"scoreboard.subjectTitle": "{evals} evals, weighted {earned} of {possible}",
|
|
81
|
+
"delta.pairHeader": "pair (A → B)",
|
|
82
|
+
"board.currentVerdicts": "Current verdicts",
|
|
83
|
+
"board.failing": "Failing:",
|
|
84
|
+
};
|
|
85
|
+
const zhCN = {
|
|
86
|
+
"verdict.passed": "通过",
|
|
87
|
+
"verdict.failed": "失败",
|
|
88
|
+
"verdict.errored": "错误",
|
|
89
|
+
"verdict.skipped": "跳过",
|
|
90
|
+
"cell.missing": "无数据",
|
|
91
|
+
"cell.measuredTitle": "{samples}/{total} 次 attempt 测得",
|
|
92
|
+
"cell.noneMeasurableTitle": "0/{total} 次 attempt 可测",
|
|
93
|
+
"cell.coverageTitle": "覆盖率 {samples}/{total}:其余 attempt 测不了这个指标",
|
|
94
|
+
"table.higherBetter": "越高越好",
|
|
95
|
+
"table.lowerBetter": "越低越好",
|
|
96
|
+
"table.model": "模型",
|
|
97
|
+
"table.agent": "Agent",
|
|
98
|
+
"table.verdicts": "结果",
|
|
99
|
+
"table.filterPlaceholder": "筛选行…",
|
|
100
|
+
"table.eval": "题目",
|
|
101
|
+
"table.reason": "原因",
|
|
102
|
+
"table.viewBreakdown": "逐题明细",
|
|
103
|
+
"table.attempt": "Attempt",
|
|
104
|
+
"experimentList.experiment": "实验",
|
|
105
|
+
"experimentList.avgDuration": "平均耗时",
|
|
106
|
+
"experimentList.passRate": "成功率",
|
|
107
|
+
"experimentList.tokens": "Tokens",
|
|
108
|
+
"experimentList.estimatedCost": "预估成本",
|
|
109
|
+
"experimentList.result": "结果",
|
|
110
|
+
"experimentList.status": "状态",
|
|
111
|
+
"experimentList.evalAttempt": "题目 / Attempt",
|
|
112
|
+
"experimentList.duration": "耗时",
|
|
113
|
+
"experimentList.cost": "成本",
|
|
114
|
+
"experimentList.filterPlaceholder": "筛选实验…",
|
|
115
|
+
"experimentList.defaultModel": "默认",
|
|
116
|
+
"experimentList.flags": "Flags",
|
|
117
|
+
"table.columnsHidden.one": "(还有 {n} 列未列出)",
|
|
118
|
+
"table.columnsHidden.other": "(还有 {n} 列未列出)",
|
|
119
|
+
"overview.snapshots": "快照",
|
|
120
|
+
"overview.evals": "题目",
|
|
121
|
+
"overview.attempts": "尝试",
|
|
122
|
+
"overview.passRate": "通过率",
|
|
123
|
+
"overview.totalCost": "总成本",
|
|
124
|
+
"overview.totalDuration": "总耗时",
|
|
125
|
+
"overview.source": "数据来源:{n} 个快照",
|
|
126
|
+
"overview.experiments.one": "{n} 个实验",
|
|
127
|
+
"overview.experiments.other": "{n} 个实验",
|
|
128
|
+
"overview.evalsCount": "{n} 道题",
|
|
129
|
+
"overview.attemptsCount": "{n} 次 attempt",
|
|
130
|
+
"groupSummary.experiments": "实验数",
|
|
131
|
+
"composedFrom.one": "合成自 {n} 个 run",
|
|
132
|
+
"composedFrom.other": "合成自 {n} 个 run",
|
|
133
|
+
"latestRun": "最新 {run}",
|
|
134
|
+
"scatter.betterHint": "更好 ↗",
|
|
135
|
+
"scatter.betterUpperRight": "更好 → 右上",
|
|
136
|
+
"scatter.axisReversed": "(轴反向:右 = 更好)",
|
|
137
|
+
"scatter.noData": "{x} × {y} 没有可绘制的数据",
|
|
138
|
+
"pointsMissing.one": "{n} 个点缺数据",
|
|
139
|
+
"pointsMissing.other": "{n} 个点缺数据",
|
|
140
|
+
"attemptList.empty": "没有 attempt",
|
|
141
|
+
"attemptList.truncated": "还有 {n} 条未列出",
|
|
142
|
+
"attemptList.truncatedText": "(还有 {n} 条未列出)",
|
|
143
|
+
"attemptList.score": "得分 {score}",
|
|
144
|
+
"attemptList.details": "详情",
|
|
145
|
+
"scoreboard.total": "总分",
|
|
146
|
+
"scoreboard.totalText": "总分",
|
|
147
|
+
"scoreboard.missing.one": "{n} 道题缺席,按 0 计",
|
|
148
|
+
"scoreboard.missing.other": "{n} 道题缺席,按 0 计",
|
|
149
|
+
"scoreboard.missingText": "(缺 {n} 题)",
|
|
150
|
+
"scoreboard.weights": "权重:",
|
|
151
|
+
"scoreboard.allWeights": "全部题 ×1",
|
|
152
|
+
"scoreboard.othersWeight": "其余 ×1",
|
|
153
|
+
"scoreboard.subjectTitle": "{evals} 道题,加权得分 {earned}/{possible}",
|
|
154
|
+
"delta.pairHeader": "对比 (A → B)",
|
|
155
|
+
"board.currentVerdicts": "现刻判定",
|
|
156
|
+
"board.failing": "失败清单:",
|
|
157
|
+
};
|
|
158
|
+
const dictionaries = {
|
|
159
|
+
en,
|
|
160
|
+
"zh-CN": zhCN,
|
|
161
|
+
};
|
|
162
|
+
/** 查字典 + 简单插值({name} 占位符)。缺 locale 回退 en。 */
|
|
163
|
+
export function localeText(locale, key, vars) {
|
|
164
|
+
const template = dictionaries[locale]?.[key] ?? dictionaries.en[key];
|
|
165
|
+
if (!vars)
|
|
166
|
+
return template;
|
|
167
|
+
return template.replace(/\{(\w+)\}/g, (m, name) => name in vars ? String(vars[name]) : m);
|
|
168
|
+
}
|
|
169
|
+
/** 带单复数的计数文案:n === 1 用 `<base>.one`,其余 `<base>.other`(zh-CN 两键同值)。 */
|
|
170
|
+
export function countText(locale, base, n) {
|
|
171
|
+
return localeText(locale, `${base}.${n === 1 ? "one" : "other"}`, { n });
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* 按 locale 解析指标 label:字符串原样;字典取当前 locale,缺项回退 en,
|
|
175
|
+
* 再回退字典里任一值;全空回退 fallback(= metric.name)。渲染面(web / text)共用。
|
|
176
|
+
*/
|
|
177
|
+
export function resolveMetricLabel(label, locale, fallback) {
|
|
178
|
+
if (label === undefined)
|
|
179
|
+
return fallback;
|
|
180
|
+
if (typeof label === "string")
|
|
181
|
+
return label;
|
|
182
|
+
return label[locale] ?? label.en ?? Object.values(label).find((v) => v !== undefined) ?? fallback;
|
|
183
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { EvalResult } from "../types.ts";
|
|
2
|
+
import type { Metric } from "./types.ts";
|
|
3
|
+
/**
|
|
4
|
+
* 定义一个指标。内置指标与自定义指标是同一个类型,没有特权;
|
|
5
|
+
* 校验只管「能进计算」的最低要求,聚合语义见 docs/feature/reports/architecture.md「指标聚合不变量」。
|
|
6
|
+
* name 是 const 泛型:产物的 name 保持字面量,`row.cells[metric.name]` 才有编译期列键。
|
|
7
|
+
*/
|
|
8
|
+
export declare function defineMetric<const Name extends string>(def: Metric<Name>): Metric<Name>;
|
|
9
|
+
/** 单 attempt 成本:网关实测(usage.costUSD)优先于价格表估算(estimatedCostUSD);都缺 → null,不编 0。 */
|
|
10
|
+
export declare function attemptCostUSD(result: EvalResult): number | null;
|
|
11
|
+
export declare const passRate: Metric<"pass-rate">;
|
|
12
|
+
export declare const examScore: Metric<"exam-score">;
|
|
13
|
+
export declare const durationMs: Metric<"duration">;
|
|
14
|
+
export declare const tokens: Metric<"tokens">;
|
|
15
|
+
export declare const costUSD: Metric<"cost">;
|
|
16
|
+
/**
|
|
17
|
+
* 唯一读 artifact(o11y,懒加载)的内置指标——其余五个只读瘦身字段。
|
|
18
|
+
* 发布时若该 attempt 没带 o11y(如 copySnapshots 的 artifacts 选项漏了它),
|
|
19
|
+
* value 如实返回 null,渲染成「—」,不冒充 0——见 docs/feature/reports/library.md「内置指标」。
|
|
20
|
+
*/
|
|
21
|
+
export declare const turns: Metric<"turns">;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// defineMetric 与内置指标。
|
|
2
|
+
//
|
|
3
|
+
// null ≠ 0:null = 此 attempt 测不了这个指标(不进聚合);0 = 测了,结果是零(照常进)。
|
|
4
|
+
// 哪个 verdict 落哪边必须显式表态,内置指标按 docs/feature/reports/library.md「内置指标」的表格:
|
|
5
|
+
//
|
|
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
|
|
13
|
+
//
|
|
14
|
+
// 两档指标(docs/feature/reports/library.md「内置指标」):以上除 turns 外全部只读 attempt.result
|
|
15
|
+
// 的瘦身字段——任何 producer、任何 copySnapshots artifacts 选择都算得出,内置报告
|
|
16
|
+
// ExperimentComparison 只用这一档。turns 读 attempt.o11y()(懒加载 artifact),发布时若
|
|
17
|
+
// o11y 没随行就诚实渲染缺数据「—」,不算 0——报告作者自己摆时心里要有这根弦,内置报告不用它。
|
|
18
|
+
/**
|
|
19
|
+
* 定义一个指标。内置指标与自定义指标是同一个类型,没有特权;
|
|
20
|
+
* 校验只管「能进计算」的最低要求,聚合语义见 docs/feature/reports/architecture.md「指标聚合不变量」。
|
|
21
|
+
* name 是 const 泛型:产物的 name 保持字面量,`row.cells[metric.name]` 才有编译期列键。
|
|
22
|
+
*/
|
|
23
|
+
export function defineMetric(def) {
|
|
24
|
+
if (typeof def.name !== "string" || def.name.length === 0) {
|
|
25
|
+
throw new Error("defineMetric: metric name must be a non-empty string.");
|
|
26
|
+
}
|
|
27
|
+
if (typeof def.value !== "function") {
|
|
28
|
+
throw new Error(`defineMetric: metric "${def.name}" must provide a value(attempt) function.`);
|
|
29
|
+
}
|
|
30
|
+
return { ...def };
|
|
31
|
+
}
|
|
32
|
+
/** 单 attempt 成本:网关实测(usage.costUSD)优先于价格表估算(estimatedCostUSD);都缺 → null,不编 0。 */
|
|
33
|
+
export function attemptCostUSD(result) {
|
|
34
|
+
return result.usage?.costUSD ?? result.estimatedCostUSD ?? null;
|
|
35
|
+
}
|
|
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).",
|
|
40
|
+
better: "higher",
|
|
41
|
+
unit: "%",
|
|
42
|
+
value: (a) => a.result.verdict === "skipped" ? null : a.result.verdict === "passed" ? 1 : 0,
|
|
43
|
+
});
|
|
44
|
+
export const examScore = defineMetric({
|
|
45
|
+
name: "exam-score",
|
|
46
|
+
label: { en: "Exam score", "zh-CN": "考试得分" },
|
|
47
|
+
description: "Per-eval score: gates decide pass, soft assertions grade quality.",
|
|
48
|
+
better: "higher",
|
|
49
|
+
unit: "%",
|
|
50
|
+
value(a) {
|
|
51
|
+
const { verdict, assertions } = a.result;
|
|
52
|
+
if (verdict === "skipped")
|
|
53
|
+
return null;
|
|
54
|
+
// 先按 verdict 分派,再看断言:errored 的断言是空数组,「gate 全过才得分」的
|
|
55
|
+
// 字面实现会让条件空真成立、崩溃反而得满分 —— 交白卷是 0 分,不是缺数据,更不是满分。
|
|
56
|
+
// failed 同理得 0:--strict 下被翻成 failed 的哪怕 soft 分不低也是 0(报告不重新判卷)。
|
|
57
|
+
if (verdict !== "passed")
|
|
58
|
+
return 0;
|
|
59
|
+
const soft = assertions.filter((x) => x.severity === "soft");
|
|
60
|
+
if (soft.length === 0)
|
|
61
|
+
return 1;
|
|
62
|
+
return soft.reduce((sum, x) => sum + x.score, 0) / soft.length;
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
export const durationMs = defineMetric({
|
|
66
|
+
name: "duration",
|
|
67
|
+
label: { en: "Duration", "zh-CN": "平均耗时" },
|
|
68
|
+
description: "Wall-clock duration of the attempt.",
|
|
69
|
+
better: "lower",
|
|
70
|
+
unit: "ms",
|
|
71
|
+
value: (a) => (a.result.verdict === "skipped" ? null : a.result.durationMs),
|
|
72
|
+
});
|
|
73
|
+
export const tokens = defineMetric({
|
|
74
|
+
name: "tokens",
|
|
75
|
+
label: { en: "Tokens", "zh-CN": "Tokens" },
|
|
76
|
+
description: "Input + output tokens (cache reads/writes excluded).",
|
|
77
|
+
better: "lower",
|
|
78
|
+
unit: "tokens",
|
|
79
|
+
value(a) {
|
|
80
|
+
if (a.result.verdict === "skipped")
|
|
81
|
+
return null;
|
|
82
|
+
const usage = a.result.usage;
|
|
83
|
+
if (!usage)
|
|
84
|
+
return null;
|
|
85
|
+
// 只加 input + output:缓存读写量大但便宜,计进去会把缓存热的 agent 画成 token 大户;
|
|
86
|
+
// 花钱多少本来就有 costUSD 负责。
|
|
87
|
+
return usage.inputTokens + usage.outputTokens;
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
export const costUSD = defineMetric({
|
|
91
|
+
name: "cost",
|
|
92
|
+
label: { en: "Cost", "zh-CN": "预估成本" },
|
|
93
|
+
description: "USD cost per attempt (gateway-measured beats estimated).",
|
|
94
|
+
better: "lower",
|
|
95
|
+
unit: "$",
|
|
96
|
+
value: (a) => (a.result.verdict === "skipped" ? null : attemptCostUSD(a.result)),
|
|
97
|
+
});
|
|
98
|
+
/**
|
|
99
|
+
* 唯一读 artifact(o11y,懒加载)的内置指标——其余五个只读瘦身字段。
|
|
100
|
+
* 发布时若该 attempt 没带 o11y(如 copySnapshots 的 artifacts 选项漏了它),
|
|
101
|
+
* value 如实返回 null,渲染成「—」,不冒充 0——见 docs/feature/reports/library.md「内置指标」。
|
|
102
|
+
*/
|
|
103
|
+
export const turns = defineMetric({
|
|
104
|
+
name: "turns",
|
|
105
|
+
label: { en: "Turns", "zh-CN": "轮次" },
|
|
106
|
+
description: "Total agent turns (assistant replies) per attempt. Reads o11y — “—” if not published alongside this attempt.",
|
|
107
|
+
better: "lower",
|
|
108
|
+
unit: "turns",
|
|
109
|
+
async value(a) {
|
|
110
|
+
if (a.result.verdict === "skipped")
|
|
111
|
+
return null;
|
|
112
|
+
const o11y = await a.o11y();
|
|
113
|
+
return o11y?.totalTurns ?? null;
|
|
114
|
+
},
|
|
115
|
+
});
|