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.
- package/dist/agents/types.d.ts +72 -6
- package/dist/context/types.d.ts +32 -12
- package/dist/i18n/en.d.ts +54 -0
- package/dist/i18n/zh-CN.d.ts +55 -1
- package/dist/o11y/types.d.ts +16 -2
- package/dist/report/aggregate.d.ts +5 -3
- package/dist/report/aggregate.js +32 -5
- package/dist/report/built-ins/experiment-comparison.d.ts +39 -0
- package/dist/report/built-ins/experiment-comparison.js +119 -0
- package/dist/report/built-ins/index.d.ts +2 -1
- package/dist/report/built-ins/index.js +2 -2
- package/dist/report/components.d.ts +10 -2
- package/dist/report/components.js +3 -3
- package/dist/report/compute.d.ts +11 -18
- package/dist/report/compute.js +68 -66
- package/dist/report/flag.d.ts +16 -1
- package/dist/report/flag.js +19 -1
- package/dist/report/format.d.ts +16 -14
- package/dist/report/format.js +28 -30
- package/dist/report/index.d.ts +5 -4
- package/dist/report/index.js +6 -5
- package/dist/report/locale.d.ts +23 -3
- package/dist/report/locale.js +47 -6
- package/dist/report/metrics.d.ts +13 -1
- package/dist/report/metrics.js +66 -15
- package/dist/report/primitives.d.ts +6 -0
- package/dist/report/react/AttemptList.d.ts +4 -4
- package/dist/report/react/AttemptList.js +8 -10
- package/dist/report/react/EvalList.d.ts +1 -1
- package/dist/report/react/EvalList.js +0 -0
- package/dist/report/react/ExperimentComparison.d.ts +8 -0
- package/dist/report/react/ExperimentComparison.js +11 -0
- package/dist/report/react/ExperimentList.d.ts +4 -2
- package/dist/report/react/ExperimentList.js +57 -7
- package/dist/report/react/MetricScatter.js +6 -16
- package/dist/report/react/chart-math.d.ts +23 -6
- package/dist/report/react/chart-math.js +71 -19
- package/dist/report/react/fixtures.d.ts +3 -3
- package/dist/report/react/fixtures.js +30 -18
- package/dist/report/react/format.d.ts +1 -1
- package/dist/report/react/format.js +1 -1
- package/dist/report/react/index.d.ts +1 -1
- package/dist/report/report.d.ts +5 -1
- package/dist/report/report.js +6 -2
- package/dist/report/text/faces.d.ts +1 -1
- package/dist/report/text/faces.js +100 -61
- package/dist/report/text/table.js +36 -5
- package/dist/report/types.d.ts +40 -34
- package/dist/results/types.d.ts +11 -0
- package/dist/runner/feedback/sink.d.ts +110 -0
- package/dist/runner/types.d.ts +513 -22
- package/dist/sandbox/docker.d.ts +23 -2
- package/dist/sandbox/e2b.d.ts +15 -1
- package/dist/sandbox/errors.d.ts +30 -3
- package/dist/sandbox/io-retry.d.ts +17 -0
- package/dist/sandbox/registry.d.ts +2 -0
- package/dist/sandbox/resolve.d.ts +18 -5
- package/dist/sandbox/retry.d.ts +11 -1
- package/dist/sandbox/types.d.ts +39 -5
- package/dist/sandbox/vercel.d.ts +7 -1
- package/dist/scoring/coverage.d.ts +30 -0
- package/dist/scoring/display.d.ts +21 -0
- package/dist/scoring/display.js +120 -0
- package/dist/scoring/types.d.ts +103 -20
- package/dist/shared/aggregate.d.ts +1 -0
- package/dist/shared/aggregate.js +3 -3
- package/dist/shared/types.d.ts +28 -0
- package/dist/tty-line.d.ts +0 -4
- package/dist/util.d.ts +23 -0
- package/docs-site/zh/concepts/adapter.mdx +24 -6
- package/docs-site/zh/concepts/assert.mdx +11 -10
- package/docs-site/zh/concepts/evals.mdx +7 -6
- package/docs-site/zh/concepts/experiment.mdx +1 -1
- package/docs-site/zh/concepts/overview.mdx +7 -7
- package/docs-site/zh/guides/agent-feedback-loop.mdx +35 -31
- package/docs-site/zh/guides/authoring.mdx +33 -0
- package/docs-site/zh/guides/ci-integration.mdx +23 -12
- package/docs-site/zh/guides/connect-your-agent.mdx +29 -3
- package/docs-site/zh/guides/custom-reports.mdx +29 -34
- package/docs-site/zh/guides/dataset-fanout.mdx +25 -3
- package/docs-site/zh/guides/debug-sandbox.mdx +57 -0
- package/docs-site/zh/guides/debugging.mdx +210 -0
- package/docs-site/zh/guides/experiments.mdx +10 -3
- package/docs-site/zh/guides/fixtures.mdx +3 -1
- package/docs-site/zh/guides/official-adapters.mdx +27 -3
- package/docs-site/zh/guides/publish-report.mdx +30 -16
- package/docs-site/zh/guides/report-components.mdx +49 -37
- package/docs-site/zh/guides/reporters.mdx +2 -2
- package/docs-site/zh/guides/results-data.mdx +42 -8
- package/docs-site/zh/guides/runner.mdx +17 -7
- package/docs-site/zh/guides/sandbox-agent.mdx +57 -7
- package/docs-site/zh/guides/sandbox-providers.mdx +258 -10
- package/docs-site/zh/guides/scoring-guide.mdx +4 -4
- package/docs-site/zh/guides/viewing-results.mdx +85 -41
- package/docs-site/zh/guides/write-experiment.mdx +5 -3
- package/docs-site/zh/guides/write-send.mdx +19 -2
- package/docs-site/zh/index.mdx +1 -1
- package/docs-site/zh/reference/builtin-agents.mdx +27 -0
- package/docs-site/zh/reference/capabilities.mdx +2 -2
- package/docs-site/zh/reference/cli.mdx +35 -9
- package/docs-site/zh/reference/define-agent.mdx +60 -5
- package/docs-site/zh/reference/define-config.mdx +1 -1
- package/docs-site/zh/reference/define-eval.mdx +42 -9
- package/docs-site/zh/reference/events.mdx +2 -2
- package/docs-site/zh/reference/expect.mdx +36 -6
- package/package.json +5 -1
- package/src/agents/ai-sdk-otel.test.ts +1 -0
- package/src/agents/ai-sdk.test.ts +3 -0
- package/src/agents/ai-sdk.ts +3 -0
- package/src/agents/bub-install-spec.test.ts +34 -0
- package/src/agents/bub-install-spec.ts +32 -0
- package/src/agents/bub.ts +31 -32
- package/src/agents/claude-code.test.ts +130 -9
- package/src/agents/claude-code.ts +76 -4
- package/src/agents/codex.test.ts +189 -40
- package/src/agents/codex.ts +155 -14
- package/src/agents/coding-cli-versions.test.ts +15 -0
- package/src/agents/coding-cli-versions.ts +3 -0
- package/src/agents/index.ts +11 -0
- package/src/agents/langgraph.test.ts +204 -0
- package/src/agents/langgraph.ts +495 -0
- package/src/agents/marketplace.ts +85 -0
- package/src/agents/native-config.test.ts +179 -0
- package/src/agents/native-config.ts +267 -0
- package/src/agents/openai-compat.test.ts +1 -0
- package/src/agents/openclaw.test.ts +31 -0
- package/src/agents/openclaw.ts +171 -0
- package/src/agents/plugin-config.test.ts +1 -0
- package/src/agents/sdk-streams.test.ts +79 -0
- package/src/agents/sdk-streams.ts +55 -10
- package/src/agents/skills.test.ts +1 -0
- package/src/agents/streaming.test.ts +3 -9
- package/src/agents/types.ts +73 -6
- package/src/agents/ui-message-stream.test.ts +3 -0
- package/src/cli.ts +411 -108
- package/src/context/context.test.ts +51 -12
- package/src/context/context.ts +161 -29
- package/src/context/session.test.ts +1 -0
- package/src/context/session.ts +114 -6
- package/src/context/types.ts +30 -12
- package/src/define.test.ts +13 -8
- package/src/define.ts +25 -4
- package/src/expect/index.ts +53 -23
- package/src/i18n/en.ts +65 -4
- package/src/i18n/zh-CN.ts +66 -4
- package/src/o11y/cost.test.ts +1 -0
- package/src/o11y/execution-tree.test.ts +1 -20
- package/src/o11y/otlp/mappers/claude-code.test.ts +1 -0
- package/src/o11y/otlp/parse.test.ts +1 -0
- package/src/o11y/otlp/turn-otel.test.ts +1 -0
- package/src/o11y/parsers/bub.test.ts +1 -0
- package/src/o11y/parsers/claude-code.test.ts +1 -34
- package/src/o11y/parsers/openclaw.test.ts +154 -0
- package/src/o11y/parsers/openclaw.ts +310 -0
- package/src/o11y/prices.json +746 -311
- package/src/o11y/tool-names.test.ts +1 -0
- package/src/o11y/types.ts +16 -2
- package/src/report/aggregate.ts +34 -5
- package/src/report/built-in-user-parity.test.tsx +127 -173
- package/src/report/built-ins/experiment-comparison.tsx +179 -0
- package/src/report/built-ins/index.ts +7 -2
- package/src/report/components.tsx +11 -3
- package/src/report/compute.ts +80 -74
- package/src/report/dual-render.test.tsx +222 -91
- package/src/report/flag.ts +30 -2
- package/src/report/format.ts +36 -27
- package/src/report/index.ts +23 -6
- package/src/report/locale.ts +49 -6
- package/src/report/metrics.ts +68 -15
- package/src/report/primitives.tsx +6 -0
- package/src/report/react/AttemptList.tsx +9 -36
- package/src/report/react/EvalList.tsx +0 -0
- package/src/report/react/ExperimentComparison.tsx +68 -0
- package/src/report/react/ExperimentList.tsx +173 -55
- package/src/report/react/MetricScatter.tsx +13 -25
- package/src/report/react/chart-math.test.ts +85 -0
- package/src/report/react/chart-math.ts +101 -22
- package/src/report/react/enhance.js +72 -1
- package/src/report/react/fixtures.ts +34 -21
- package/src/report/react/format.ts +1 -1
- package/src/report/react/index.tsx +0 -1
- package/src/report/react/render.test.tsx +30 -69
- package/src/report/react/styles.css +112 -14
- package/src/report/report.test.ts +308 -105
- package/src/report/report.ts +6 -2
- package/src/report/text/faces.ts +111 -67
- package/src/report/text/table.ts +42 -5
- package/src/report/types.ts +42 -34
- package/src/results/annotated-source.test.ts +62 -9
- package/src/results/annotated-source.ts +64 -6
- package/src/results/attempt-evidence.test.ts +9 -7
- package/src/results/attempt-evidence.ts +15 -8
- package/src/results/attempt-source.ts +6 -3
- package/src/results/copy.ts +145 -55
- package/src/results/host-equivalence.test.ts +11 -9
- package/src/results/index.ts +2 -0
- package/src/results/locator.test.ts +1 -22
- package/src/results/open.ts +7 -1
- package/src/results/publish.ts +149 -0
- package/src/results/results.test.ts +85 -51
- package/src/results/truncate.ts +90 -0
- package/src/results/types.ts +7 -0
- package/src/results/writer.ts +31 -13
- package/src/runner/attempt.test.ts +138 -7
- package/src/runner/attempt.ts +603 -104
- package/src/runner/discover.test.ts +47 -0
- package/src/runner/discover.ts +36 -2
- package/src/runner/eval-source.test.ts +1 -27
- package/src/runner/feedback/agent.test.ts +504 -0
- package/src/runner/feedback/agent.ts +409 -0
- package/src/runner/feedback/ci.test.ts +562 -0
- package/src/runner/feedback/ci.ts +401 -0
- package/src/runner/feedback/coordinator.test.ts +317 -0
- package/src/runner/feedback/coordinator.ts +397 -0
- package/src/runner/feedback/failure.ts +40 -0
- package/src/runner/feedback/human.test.ts +616 -0
- package/src/runner/feedback/human.ts +535 -0
- package/src/runner/feedback/index.ts +66 -0
- package/src/runner/feedback/io.ts +78 -0
- package/src/runner/feedback/profile.test.ts +50 -0
- package/src/runner/feedback/profile.ts +58 -0
- package/src/runner/feedback/reducer.test.ts +395 -0
- package/src/runner/feedback/reducer.ts +260 -0
- package/src/runner/feedback/renderer.ts +82 -0
- package/src/runner/feedback/sink.ts +203 -0
- package/src/runner/feedback/testing.ts +106 -0
- package/src/runner/ledger.test.ts +230 -0
- package/src/runner/ledger.ts +329 -0
- package/src/runner/report.test.ts +128 -3
- package/src/runner/report.ts +33 -9
- package/src/runner/reporters/artifacts.ts +8 -2
- package/src/runner/reporters/braintrust.test.ts +8 -7
- package/src/runner/reporters/braintrust.ts +9 -2
- package/src/runner/reporters/index.ts +2 -2
- package/src/runner/reporters/json.test.ts +162 -0
- package/src/runner/reporters/json.ts +35 -8
- package/src/runner/reporters/shared.ts +1 -5
- package/src/runner/run.test.ts +760 -3
- package/src/runner/run.ts +242 -36
- package/src/runner/sandbox-prep.ts +3 -42
- package/src/runner/timing.ts +158 -0
- package/src/runner/types.ts +518 -22
- package/src/sandbox/checkpoint.test.ts +55 -0
- package/src/sandbox/checkpoint.ts +29 -8
- package/src/sandbox/cli-commands.ts +407 -0
- package/src/sandbox/docker.ts +115 -16
- package/src/sandbox/e2b-agent-template.test.ts +56 -0
- package/src/sandbox/e2b-agent-template.ts +94 -0
- package/src/sandbox/e2b.ts +74 -9
- package/src/sandbox/errors.ts +111 -4
- package/src/sandbox/index.ts +2 -0
- package/src/sandbox/io-retry.test.ts +58 -0
- package/src/sandbox/io-retry.ts +45 -0
- package/src/sandbox/keep-registry.test.ts +86 -0
- package/src/sandbox/keep-registry.ts +142 -0
- package/src/sandbox/keep.ts +178 -0
- package/src/sandbox/paths.test.ts +1 -0
- package/src/sandbox/paths.ts +19 -8
- package/src/sandbox/registry.ts +20 -3
- package/src/sandbox/resolve.ts +76 -11
- package/src/sandbox/retry.test.ts +70 -0
- package/src/sandbox/retry.ts +46 -4
- package/src/sandbox/types.ts +44 -6
- package/src/sandbox/vercel.ts +43 -20
- package/src/scoring/collector.ts +60 -17
- package/src/scoring/coverage.ts +95 -0
- package/src/scoring/diff.ts +81 -0
- package/src/scoring/display.test.ts +121 -0
- package/src/scoring/display.ts +133 -0
- package/src/scoring/evidence.test.ts +189 -0
- package/src/scoring/judge.test.ts +142 -0
- package/src/scoring/judge.ts +15 -18
- package/src/scoring/scoped.ts +217 -50
- package/src/scoring/types.ts +117 -20
- package/src/scoring/verdict.ts +16 -4
- package/src/shared/aggregate.ts +3 -2
- package/src/shared/types.ts +31 -0
- package/src/show/compose.ts +2 -2
- package/src/show/index.ts +29 -16
- package/src/show/render.ts +626 -308
- package/src/show/show.test.ts +251 -36
- package/src/tty-line.ts +8 -26
- package/src/util.test.ts +1 -0
- package/src/util.ts +41 -0
- package/src/view/app/components/AttemptModal.tsx +153 -2
- package/src/view/app/components/CodeView.tsx +32 -11
- package/src/view/app/components/CopyControls.tsx +2 -2
- package/src/view/app/i18n.ts +6 -0
- package/src/view/app/lib/attempt-route.test.ts +1 -0
- package/src/view/app/lib/verdict.ts +7 -9
- package/src/view/artifact-serving.test.ts +2 -1
- package/src/view/client-dist/app.css +1 -1
- package/src/view/client-dist/app.js +17 -17
- package/src/view/data.test.ts +2 -1
- package/src/view/data.ts +17 -7
- package/src/view/index.ts +12 -1
- package/src/view/server.ts +2 -0
- package/src/view/shared/types.ts +1 -1
- package/src/view/styles.css +3 -0
- package/src/view/view-report.test.ts +11 -10
- package/dist/o11y/execution-tree.d.ts +0 -103
- package/dist/o11y/otlp/select.d.ts +0 -22
- package/dist/report/built-ins/cost-pass-rate-comparison.d.ts +0 -1
- package/dist/report/built-ins/cost-pass-rate-comparison.js +0 -17
- package/dist/results/annotated-source.d.ts +0 -61
- package/dist/results/attempt-evidence.d.ts +0 -69
- package/dist/results/attempt-source.d.ts +0 -15
- package/src/report/built-ins/cost-pass-rate-comparison.tsx +0 -23
- package/src/runner/reporters/console.ts +0 -70
- package/src/runner/reporters/live.test.ts +0 -56
- package/src/runner/reporters/live.ts +0 -247
- package/src/runner/reporters/quiet.test.ts +0 -66
- package/src/runner/reporters/quiet.ts +0 -49
- package/src/runner/reporters/table.ts +0 -277
package/src/report/format.ts
CHANGED
|
@@ -2,8 +2,22 @@
|
|
|
2
2
|
// "%" → 87% "ms" → 1.2s "$" → $0.31 其余 → 1.2k 缩写(带 unit 后缀)
|
|
3
3
|
// metric.display 可整体覆盖;这里只负责默认。
|
|
4
4
|
|
|
5
|
-
import type { AttemptEvidenceCapabilities } from "../results/attempt-evidence.ts";
|
|
6
5
|
import type { AssertionResult, Verdict } from "../types.ts";
|
|
6
|
+
import { compactAssertionSummary, fitCompactAssertionSummary, primaryAssertionSummary } from "../scoring/display.ts";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* experiment 行的显示名:给了父路径 `relativeTo` 且它确是前缀,就去掉 `relativeTo + "/"`,
|
|
10
|
+
* 只留 id 末段——用在已经以组为标题的上下文(如默认 `ExperimentComparison` 的每组面板)里,
|
|
11
|
+
* 避免每行重复文件夹名。组键就是 experiment id 的父目录,因此这里的末段与 `MetricScatter`
|
|
12
|
+
* 点标签取的末段同源。不给 `relativeTo`、或它不是前缀(如根目录单例组)时原样返回完整 id。
|
|
13
|
+
* 完整 id 仍是排序 / 着色 / 折叠的键,调用方不要拿这个显示名当身份用。
|
|
14
|
+
*/
|
|
15
|
+
export function experimentDisplayName(experimentId: string, relativeTo?: string): string {
|
|
16
|
+
if (relativeTo && experimentId.startsWith(`${relativeTo}/`)) {
|
|
17
|
+
return experimentId.slice(relativeTo.length + 1);
|
|
18
|
+
}
|
|
19
|
+
return experimentId;
|
|
20
|
+
}
|
|
7
21
|
|
|
8
22
|
/** 一位小数、去掉无意义的 ".0" 尾巴。 */
|
|
9
23
|
function trimmed(n: number): string {
|
|
@@ -78,8 +92,7 @@ export function formatPercent(ratio: number): string {
|
|
|
78
92
|
return `${Math.round(ratio * 100)}%`;
|
|
79
93
|
}
|
|
80
94
|
|
|
81
|
-
// ── 实体列表(ExperimentList / EvalList / AttemptList)
|
|
82
|
-
// web / text 两面对同一个 AttemptListItem 给出同一个标记(docs-site「终端输出形成反馈闭环」)。
|
|
95
|
+
// ── 实体列表(ExperimentList / EvalList / AttemptList)共用的判定符 ──
|
|
83
96
|
|
|
84
97
|
/** passed / failed / errored / skipped 的判定符。 */
|
|
85
98
|
export function verdictMark(verdict: Verdict): string {
|
|
@@ -96,29 +109,25 @@ export function verdictMark(verdict: Verdict): string {
|
|
|
96
109
|
}
|
|
97
110
|
|
|
98
111
|
/**
|
|
99
|
-
*
|
|
100
|
-
*
|
|
112
|
+
* Attempt 比较项的一层结果摘要;完整 assertions 只在 locator 详情里展开。
|
|
113
|
+
* maxChars(可选)是渲染面的宽度收口预算(如两行单元格 = 2 × 列宽):断言摘要按
|
|
114
|
+
* fitCompactAssertionSummary 的优先级让位,error 摘要折单行后尾截。
|
|
101
115
|
*/
|
|
102
|
-
export function
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
export function attemptItemReason(item: { error?: string; assertions: AssertionResult[] }): string | undefined {
|
|
120
|
-
if (item.error !== undefined) return item.error;
|
|
121
|
-
const gates = item.assertions.filter((a) => !a.passed && a.severity === "gate");
|
|
122
|
-
if (gates.length === 0) return undefined;
|
|
123
|
-
return gates.map((a) => (a.detail ? `${a.name}: ${a.detail}` : a.name)).join(", ");
|
|
116
|
+
export function attemptItemReason(
|
|
117
|
+
item: {
|
|
118
|
+
verdict: Verdict;
|
|
119
|
+
error?: { message: string };
|
|
120
|
+
assertions: AssertionResult[];
|
|
121
|
+
},
|
|
122
|
+
maxChars?: number,
|
|
123
|
+
): string | undefined {
|
|
124
|
+
if (item.error !== undefined) {
|
|
125
|
+
const message = item.error.message.replace(/\s+/g, " ").trim();
|
|
126
|
+
return maxChars !== undefined && message.length > maxChars
|
|
127
|
+
? `${message.slice(0, Math.max(0, maxChars - 1))}…`
|
|
128
|
+
: message;
|
|
129
|
+
}
|
|
130
|
+
const summary = primaryAssertionSummary(item.assertions, item.verdict);
|
|
131
|
+
if (summary === undefined) return undefined;
|
|
132
|
+
return maxChars === undefined ? compactAssertionSummary(summary) : fitCompactAssertionSummary(summary, maxChars);
|
|
124
133
|
}
|
package/src/report/index.ts
CHANGED
|
@@ -8,9 +8,19 @@
|
|
|
8
8
|
// ./web.ts,只有那一侧 import react-dom。写报告文件的项目要装 react(.tsx 编译产物
|
|
9
9
|
// import react/jsx-runtime)。
|
|
10
10
|
|
|
11
|
-
//
|
|
12
|
-
export {
|
|
13
|
-
|
|
11
|
+
// 指标与维度读取器(flag / config)
|
|
12
|
+
export {
|
|
13
|
+
defineMetric,
|
|
14
|
+
taskPassRate,
|
|
15
|
+
executionReliability,
|
|
16
|
+
endToEndPassRate,
|
|
17
|
+
examScore,
|
|
18
|
+
durationMs,
|
|
19
|
+
tokens,
|
|
20
|
+
costUSD,
|
|
21
|
+
turns,
|
|
22
|
+
} from "./metrics.ts";
|
|
23
|
+
export { flag, config } from "./flag.ts";
|
|
14
24
|
|
|
15
25
|
// 报告基座与双面组件基座
|
|
16
26
|
export { defineReport, isReportDefinition, renderReportToText } from "./report.ts";
|
|
@@ -44,8 +54,14 @@ export {
|
|
|
44
54
|
} from "./text/layout.ts";
|
|
45
55
|
export type { ColumnAlign } from "./text/layout.ts";
|
|
46
56
|
|
|
47
|
-
//
|
|
48
|
-
|
|
57
|
+
// 内置报告兼组合件(show / view 裸跑时报告槽的出厂填充;也可作组件整体引用,
|
|
58
|
+
// `<ExperimentComparison data={await ExperimentComparison.data(selection)} />`),无 renderer 特权
|
|
59
|
+
export { ExperimentComparison } from "./built-ins/index.ts";
|
|
60
|
+
export type {
|
|
61
|
+
ExperimentComparisonData,
|
|
62
|
+
ExperimentComparisonGroupData,
|
|
63
|
+
ExperimentComparisonProps,
|
|
64
|
+
} from "./built-ins/index.ts";
|
|
49
65
|
|
|
50
66
|
// locale:官方组件 chrome 文案的语言(en / zh-CN);指标 label 可按 locale 给字典
|
|
51
67
|
export { DEFAULT_REPORT_LOCALE, resolveMetricLabel } from "./locale.ts";
|
|
@@ -95,9 +111,10 @@ export type {
|
|
|
95
111
|
// 数据契约(组件的 data props)
|
|
96
112
|
export type {
|
|
97
113
|
Aggregator,
|
|
98
|
-
AttemptEvidenceCapabilities,
|
|
99
114
|
AttemptListItem,
|
|
100
115
|
AttemptLocator,
|
|
116
|
+
AxisInput,
|
|
117
|
+
ConfigRef,
|
|
101
118
|
DeltaData,
|
|
102
119
|
Dimension,
|
|
103
120
|
DimensionInput,
|
package/src/report/locale.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// 只覆盖组件自带的固定文案(verdict 词、缺数据、覆盖率角标、注脚、占位符等);
|
|
3
3
|
// 数据本身(display、维度键、warnings 的 message)不经这里。
|
|
4
4
|
// 刻意不 import src/i18n/(CLI 专用字典,locale 来源与 key 面完全不同);
|
|
5
|
-
// zh-CN
|
|
5
|
+
// zh-CN 译法与默认 Reports 契约保持一致(端到端成功率/平均耗时/预估成本…)。
|
|
6
6
|
|
|
7
7
|
/** 报告渲染的 locale;默认 "en"(text 面缺省即 en,`niceeval show` 输出不变)。 */
|
|
8
8
|
export type ReportLocale = "en" | "zh-CN";
|
|
@@ -32,6 +32,20 @@ const en = {
|
|
|
32
32
|
"table.viewBreakdown": "Per-eval breakdown",
|
|
33
33
|
/** <Table> 的 locator 列表头(行带 locator 时自动追加)。 */
|
|
34
34
|
"table.attempt": "attempt",
|
|
35
|
+
"experimentList.experiment": "Experiment",
|
|
36
|
+
"experimentList.avgDuration": "Avg duration",
|
|
37
|
+
"experimentList.passRate": "End-to-end pass rate",
|
|
38
|
+
"experimentList.tokens": "Tokens",
|
|
39
|
+
"experimentList.estimatedCost": "Est. cost",
|
|
40
|
+
"experimentList.result": "Result",
|
|
41
|
+
"experimentList.status": "Status",
|
|
42
|
+
"experimentList.evalAttempt": "Eval / Attempt",
|
|
43
|
+
"experimentList.duration": "Duration",
|
|
44
|
+
"experimentList.cost": "Cost",
|
|
45
|
+
"experimentList.filterPlaceholder": "Filter experiments…",
|
|
46
|
+
"experimentList.defaultModel": "default",
|
|
47
|
+
"experimentList.flags": "Flags",
|
|
48
|
+
"entityList.average": "{value} avg",
|
|
35
49
|
/** <Table> 压到下限仍放不下时,从右侧丢列并如实报数。 */
|
|
36
50
|
"table.columnsHidden.one": "({n} more column not shown)",
|
|
37
51
|
"table.columnsHidden.other": "({n} more columns not shown)",
|
|
@@ -39,7 +53,7 @@ const en = {
|
|
|
39
53
|
"overview.snapshots": "Snapshots",
|
|
40
54
|
"overview.evals": "Evals",
|
|
41
55
|
"overview.attempts": "attempts",
|
|
42
|
-
"overview.passRate": "
|
|
56
|
+
"overview.passRate": "End-to-end pass rate",
|
|
43
57
|
"overview.totalCost": "Total cost",
|
|
44
58
|
"overview.totalDuration": "Total duration",
|
|
45
59
|
"overview.source": "Source: {n} snapshots",
|
|
@@ -51,6 +65,14 @@ const en = {
|
|
|
51
65
|
/** GroupSummary 的 KPI dt 标签(其余字段复用 verdict.* / overview.* 已有 key)。 */
|
|
52
66
|
"groupSummary.experiments": "Experiments",
|
|
53
67
|
|
|
68
|
+
"experimentComparison.groups": "Experiment groups",
|
|
69
|
+
"experimentComparison.group": "Experiment group",
|
|
70
|
+
"experimentComparison.results": "Results",
|
|
71
|
+
"experimentComparison.lastRun": "Last run",
|
|
72
|
+
"experimentComparison.selectGroup": "Select experiment group {group}",
|
|
73
|
+
"experimentComparison.command": "Details: {command}",
|
|
74
|
+
"experimentComparison.empty": "No experiment groups",
|
|
75
|
+
|
|
54
76
|
"composedFrom.one": "composed from {n} run",
|
|
55
77
|
"composedFrom.other": "composed from {n} runs",
|
|
56
78
|
"latestRun": "latest {run}",
|
|
@@ -60,8 +82,6 @@ const en = {
|
|
|
60
82
|
"scatter.axisReversed": "(axis reversed: right = better)",
|
|
61
83
|
/** 0 个可画点:x/y 指标没有可用数据。 */
|
|
62
84
|
"scatter.noData": "No data to plot {x} × {y}",
|
|
63
|
-
/** 恰好 1 个可画点:比较至少要两个实验。 */
|
|
64
|
-
"scatter.needTwo": "At least 2 experiments needed to compare {x} × {y}",
|
|
65
85
|
"pointsMissing.one": "{n} point missing data",
|
|
66
86
|
"pointsMissing.other": "{n} points missing data",
|
|
67
87
|
|
|
@@ -69,6 +89,7 @@ const en = {
|
|
|
69
89
|
"attemptList.truncated": "and {n} more not shown",
|
|
70
90
|
"attemptList.truncatedText": "({n} more not shown)",
|
|
71
91
|
"attemptList.score": "score {score}",
|
|
92
|
+
"attemptList.unavailable": "unavailable",
|
|
72
93
|
"attemptList.details": "details",
|
|
73
94
|
|
|
74
95
|
"scoreboard.total": "Total",
|
|
@@ -110,13 +131,27 @@ const zhCN: Record<ReportMessageKey, string> = {
|
|
|
110
131
|
"table.reason": "原因",
|
|
111
132
|
"table.viewBreakdown": "逐题明细",
|
|
112
133
|
"table.attempt": "Attempt",
|
|
134
|
+
"experimentList.experiment": "实验",
|
|
135
|
+
"experimentList.avgDuration": "平均耗时",
|
|
136
|
+
"experimentList.passRate": "端到端成功率",
|
|
137
|
+
"experimentList.tokens": "Tokens",
|
|
138
|
+
"experimentList.estimatedCost": "预估成本",
|
|
139
|
+
"experimentList.result": "结果",
|
|
140
|
+
"experimentList.status": "状态",
|
|
141
|
+
"experimentList.evalAttempt": "题目 / Attempt",
|
|
142
|
+
"experimentList.duration": "耗时",
|
|
143
|
+
"experimentList.cost": "成本",
|
|
144
|
+
"experimentList.filterPlaceholder": "筛选实验…",
|
|
145
|
+
"experimentList.defaultModel": "默认",
|
|
146
|
+
"experimentList.flags": "Flags",
|
|
147
|
+
"entityList.average": "平均 {value}",
|
|
113
148
|
"table.columnsHidden.one": "(还有 {n} 列未列出)",
|
|
114
149
|
"table.columnsHidden.other": "(还有 {n} 列未列出)",
|
|
115
150
|
|
|
116
151
|
"overview.snapshots": "快照",
|
|
117
152
|
"overview.evals": "题目",
|
|
118
153
|
"overview.attempts": "尝试",
|
|
119
|
-
"overview.passRate": "
|
|
154
|
+
"overview.passRate": "端到端成功率",
|
|
120
155
|
"overview.totalCost": "总成本",
|
|
121
156
|
"overview.totalDuration": "总耗时",
|
|
122
157
|
"overview.source": "数据来源:{n} 个快照",
|
|
@@ -127,6 +162,14 @@ const zhCN: Record<ReportMessageKey, string> = {
|
|
|
127
162
|
|
|
128
163
|
"groupSummary.experiments": "实验数",
|
|
129
164
|
|
|
165
|
+
"experimentComparison.groups": "实验组",
|
|
166
|
+
"experimentComparison.group": "实验组",
|
|
167
|
+
"experimentComparison.results": "结果",
|
|
168
|
+
"experimentComparison.lastRun": "最后运行",
|
|
169
|
+
"experimentComparison.selectGroup": "选择实验组 {group}",
|
|
170
|
+
"experimentComparison.command": "查看:{command}",
|
|
171
|
+
"experimentComparison.empty": "没有实验组",
|
|
172
|
+
|
|
130
173
|
"composedFrom.one": "合成自 {n} 个 run",
|
|
131
174
|
"composedFrom.other": "合成自 {n} 个 run",
|
|
132
175
|
"latestRun": "最新 {run}",
|
|
@@ -135,7 +178,6 @@ const zhCN: Record<ReportMessageKey, string> = {
|
|
|
135
178
|
"scatter.betterUpperRight": "更好 → 右上",
|
|
136
179
|
"scatter.axisReversed": "(轴反向:右 = 更好)",
|
|
137
180
|
"scatter.noData": "{x} × {y} 没有可绘制的数据",
|
|
138
|
-
"scatter.needTwo": "至少需要 2 个实验才能比较 {x} × {y}",
|
|
139
181
|
"pointsMissing.one": "{n} 个点缺数据",
|
|
140
182
|
"pointsMissing.other": "{n} 个点缺数据",
|
|
141
183
|
|
|
@@ -143,6 +185,7 @@ const zhCN: Record<ReportMessageKey, string> = {
|
|
|
143
185
|
"attemptList.truncated": "还有 {n} 条未列出",
|
|
144
186
|
"attemptList.truncatedText": "(还有 {n} 条未列出)",
|
|
145
187
|
"attemptList.score": "得分 {score}",
|
|
188
|
+
"attemptList.unavailable": "评不了",
|
|
146
189
|
"attemptList.details": "详情",
|
|
147
190
|
|
|
148
191
|
"scoreboard.total": "总分",
|
package/src/report/metrics.ts
CHANGED
|
@@ -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)
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
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
|
-
//
|
|
19
|
+
// ExperimentComparison 只用这一档。turns 读 attempt.o11y()(懒加载 artifact),发布时若
|
|
17
20
|
// o11y 没随行就诚实渲染缺数据「—」,不算 0——报告作者自己摆时心里要有这根弦,内置报告不用它。
|
|
18
21
|
|
|
19
22
|
import type { EvalResult } from "../types.ts";
|
|
@@ -39,10 +42,58 @@ export function attemptCostUSD(result: EvalResult): number | null {
|
|
|
39
42
|
return result.usage?.costUSD ?? result.estimatedCostUSD ?? null;
|
|
40
43
|
}
|
|
41
44
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
/**
|
|
46
|
+
* 条件答题质量:passed = 1,failed = 0,errored 记 null 不进分母。
|
|
47
|
+
* 这是「已形成可信判定」条件下的诊断指标,不能简称默认成功率
|
|
48
|
+
* (docs/feature/reports/library.md「内置指标」)。
|
|
49
|
+
*/
|
|
50
|
+
export const taskPassRate = defineMetric({
|
|
51
|
+
name: "task-pass-rate",
|
|
52
|
+
label: { en: "Task pass rate", "zh-CN": "可判定任务通过率" },
|
|
53
|
+
description: "Conditional task quality among attempts that formed a trustworthy verdict: passed = 1, failed = 0; errored is null.",
|
|
54
|
+
better: "higher",
|
|
55
|
+
unit: "%",
|
|
56
|
+
value(a) {
|
|
57
|
+
switch (a.result.verdict) {
|
|
58
|
+
case "passed":
|
|
59
|
+
return 1;
|
|
60
|
+
case "failed":
|
|
61
|
+
return 0;
|
|
62
|
+
default:
|
|
63
|
+
// errored = 没形成可信判定 → null 不进这个条件指标;skipped 同为 null。
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
/** 执行可靠性:跑到可判定(passed / failed)= 1,errored = 0;skipped → null。 */
|
|
70
|
+
export const executionReliability = defineMetric({
|
|
71
|
+
name: "execution-reliability",
|
|
72
|
+
label: { en: "Execution reliability", "zh-CN": "执行可靠性" },
|
|
73
|
+
description: "Execution reliability: reached a trustworthy verdict (passed / failed) = 1, errored = 0.",
|
|
74
|
+
better: "higher",
|
|
75
|
+
unit: "%",
|
|
76
|
+
value(a) {
|
|
77
|
+
switch (a.result.verdict) {
|
|
78
|
+
case "passed":
|
|
79
|
+
case "failed":
|
|
80
|
+
return 1;
|
|
81
|
+
case "errored":
|
|
82
|
+
return 0;
|
|
83
|
+
default:
|
|
84
|
+
return null; // skipped
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* 端到端合成:passed = 1,failed / errored = 0;哪边拖累用
|
|
91
|
+
* taskPassRate / executionReliability 拆开看。
|
|
92
|
+
*/
|
|
93
|
+
export const endToEndPassRate = defineMetric({
|
|
94
|
+
name: "end-to-end-pass-rate",
|
|
95
|
+
label: { en: "End-to-end pass rate", "zh-CN": "端到端成功率" },
|
|
96
|
+
description: "End-to-end composite: passed = 1, failed / errored = 0. Split blame with taskPassRate and executionReliability.",
|
|
46
97
|
better: "higher",
|
|
47
98
|
unit: "%",
|
|
48
99
|
value: (a) =>
|
|
@@ -62,9 +113,11 @@ export const examScore = defineMetric({
|
|
|
62
113
|
// 字面实现会让条件空真成立、崩溃反而得满分 —— 交白卷是 0 分,不是缺数据,更不是满分。
|
|
63
114
|
// failed 同理得 0:--strict 下被翻成 failed 的哪怕 soft 分不低也是 0(报告不重新判卷)。
|
|
64
115
|
if (verdict !== "passed") return 0;
|
|
65
|
-
|
|
116
|
+
// unavailable 没有分数:不计入均分分母(评不了 ≠ 0 分;非 optional 的 unavailable
|
|
117
|
+
// 早已把 verdict 拖成 errored,走不到这个分支)。
|
|
118
|
+
const soft = assertions.filter((x) => x.severity === "soft" && x.outcome !== "unavailable");
|
|
66
119
|
if (soft.length === 0) return 1;
|
|
67
|
-
return soft.reduce((sum, x) => sum + x.score, 0) / soft.length;
|
|
120
|
+
return soft.reduce((sum, x) => sum + (x.outcome === "unavailable" ? 0 : x.score), 0) / soft.length;
|
|
68
121
|
},
|
|
69
122
|
});
|
|
70
123
|
|
|
@@ -128,6 +128,12 @@ export interface TableColumn {
|
|
|
128
128
|
header: string;
|
|
129
129
|
/** 对齐方向,默认 `"left"`;`"right"` 按显示宽度右对齐,数字列用。 */
|
|
130
130
|
align?: ColumnAlign;
|
|
131
|
+
/**
|
|
132
|
+
* text 面:单元格折行后的最大物理行数,放不下的部分以 `…` 收口;省略则不限行数。
|
|
133
|
+
* 摘要类列(如比较列表的 Result)用它保证「格子是可扫读的预览」;完整值在下钻面。
|
|
134
|
+
* web 面不消费——网页的高度约束是组件自己的 CSS 决定。
|
|
135
|
+
*/
|
|
136
|
+
maxLines?: number;
|
|
131
137
|
}
|
|
132
138
|
|
|
133
139
|
/** 一行的数据:身份键、已格式化的格子、可选的 attempt locator。 */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
// AttemptList:实体列表的叶子层——每项一个 Attempt
|
|
2
|
-
// (assertions
|
|
1
|
+
// AttemptList:实体列表的叶子层——每项一个 Attempt,固定展示判定、一条主失败摘要或 error
|
|
2
|
+
// 摘要与证据引用(locator)。完整 assertions / evidence 只经 locator 下钻。它不预设只看失败,
|
|
3
3
|
// 报告作者过滤 items、用 .slice() 限量,total 让渲染面如实报告剩余数量,不静默截断。
|
|
4
4
|
// ExperimentList / EvalList 的下钻数组是同一个 AttemptListItem[],这里的渲染逻辑因此
|
|
5
5
|
// 也是它们展开区里"逐条 attempt"那一层的唯一实现(通过 AttemptRow 导出复用,不重写一遍)。
|
|
@@ -8,18 +8,18 @@ import type { ReactElement } from "react";
|
|
|
8
8
|
import type { AttemptListItem } from "../types.ts";
|
|
9
9
|
import type { AttemptLocator } from "../../results/locator.ts";
|
|
10
10
|
import { DEFAULT_REPORT_LOCALE, localeText, type ReportLocale } from "../locale.ts";
|
|
11
|
+
import { attemptItemReason } from "../format.ts";
|
|
11
12
|
import { colorClassForKey } from "./colors.ts";
|
|
12
|
-
import {
|
|
13
|
+
import { cx, formatDurationMs, formatUSD, verdictMark } from "./format.ts";
|
|
13
14
|
|
|
14
|
-
/** locator +
|
|
15
|
+
/** locator + 判定符的普通 <a>,AttemptList/EvalList/ExperimentList 共用。 */
|
|
15
16
|
export function AttemptLocatorBadge({
|
|
16
17
|
item,
|
|
17
18
|
attemptHref,
|
|
18
19
|
}: {
|
|
19
|
-
item: Pick<AttemptListItem, "locator" | "verdict"
|
|
20
|
+
item: Pick<AttemptListItem, "locator" | "verdict">;
|
|
20
21
|
attemptHref: (locator: AttemptLocator) => string;
|
|
21
22
|
}): ReactElement {
|
|
22
|
-
const caps = capabilityBadge(item.capabilities);
|
|
23
23
|
return (
|
|
24
24
|
<a
|
|
25
25
|
className={cx("nre-locator", `nre-verdict-${item.verdict}`)}
|
|
@@ -27,40 +27,20 @@ export function AttemptLocatorBadge({
|
|
|
27
27
|
>
|
|
28
28
|
{item.locator}
|
|
29
29
|
<span className="nre-locator-mark">{verdictMark(item.verdict)}</span>
|
|
30
|
-
{caps && <span className="nre-locator-caps">{caps}</span>}
|
|
31
30
|
</a>
|
|
32
31
|
);
|
|
33
32
|
}
|
|
34
33
|
|
|
35
|
-
|
|
36
|
-
return (
|
|
37
|
-
<li className={cx("nre-assertion", `nre-assertion-${assertion.severity}`, !assertion.passed && "nre-assertion-failed")}>
|
|
38
|
-
<span className="nre-assertion-severity">{assertion.severity}</span>
|
|
39
|
-
<span className="nre-assertion-name">{assertion.name}</span>
|
|
40
|
-
<span className="nre-assertion-score">
|
|
41
|
-
{localeText(locale, "attemptList.score", { score: assertion.score })}
|
|
42
|
-
</span>
|
|
43
|
-
{(assertion.detail || assertion.evidence) && (
|
|
44
|
-
<details className="nre-assertion-more">
|
|
45
|
-
<summary>{localeText(locale, "attemptList.details")}</summary>
|
|
46
|
-
{assertion.detail && <p className="nre-assertion-detail">{assertion.detail}</p>}
|
|
47
|
-
{assertion.evidence && <blockquote className="nre-assertion-evidence">{assertion.evidence}</blockquote>}
|
|
48
|
-
</details>
|
|
49
|
-
)}
|
|
50
|
-
</li>
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/** 一条 Attempt 的完整卡片:AttemptList 自己的一项,也是 EvalList 展开区里的一行。 */
|
|
34
|
+
/** 一条 Attempt 的比较卡片;完整 assertions 通过 locator 下钻,不在列表内展开。 */
|
|
55
35
|
export function AttemptRow({
|
|
56
36
|
item,
|
|
57
37
|
attemptHref,
|
|
58
|
-
locale = DEFAULT_REPORT_LOCALE,
|
|
59
38
|
}: {
|
|
60
39
|
item: AttemptListItem;
|
|
61
40
|
attemptHref: (locator: AttemptLocator) => string;
|
|
62
41
|
locale?: ReportLocale;
|
|
63
42
|
}): ReactElement {
|
|
43
|
+
const reason = attemptItemReason(item);
|
|
64
44
|
return (
|
|
65
45
|
<li className={cx("nre-attempt", `nre-attempt-${item.verdict}`)}>
|
|
66
46
|
<div className="nre-attempt-head">
|
|
@@ -72,14 +52,7 @@ export function AttemptRow({
|
|
|
72
52
|
<span className="nre-attempt-duration">{formatDurationMs(item.durationMs)}</span>
|
|
73
53
|
{item.costUSD !== undefined && <span className="nre-attempt-cost">{formatUSD(item.costUSD)}</span>}
|
|
74
54
|
</div>
|
|
75
|
-
{
|
|
76
|
-
{item.assertions.length > 0 && (
|
|
77
|
-
<ul className="nre-assertions">
|
|
78
|
-
{item.assertions.map((a, i) => (
|
|
79
|
-
<AssertionRow key={i} assertion={a} locale={locale} />
|
|
80
|
-
))}
|
|
81
|
-
</ul>
|
|
82
|
-
)}
|
|
55
|
+
{reason && <p className="nre-attempt-result">{reason}</p>}
|
|
83
56
|
</li>
|
|
84
57
|
);
|
|
85
58
|
}
|
|
Binary file
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// ExperimentComparison 的 web 面:完整持有所有可比组,组选择只切换已经独立计算好的 panel。
|
|
2
|
+
// 静态 HTML 用原生 <details> 保留每组完整内容;enhance.js 再把上方摘要卡变成单选切换。
|
|
3
|
+
|
|
4
|
+
import type { ReactElement } from "react";
|
|
5
|
+
import type { ExperimentComparisonData } from "../built-ins/experiment-comparison.tsx";
|
|
6
|
+
import { DEFAULT_REPORT_LOCALE, localeText, type ReportLocale } from "../locale.ts";
|
|
7
|
+
import { ExperimentList, MetricScatter } from "../components.tsx";
|
|
8
|
+
import { GroupSummary } from "./GroupSummary.tsx";
|
|
9
|
+
import { cx } from "./format.ts";
|
|
10
|
+
|
|
11
|
+
export function ExperimentComparisonView({
|
|
12
|
+
data,
|
|
13
|
+
className,
|
|
14
|
+
locale = DEFAULT_REPORT_LOCALE,
|
|
15
|
+
}: {
|
|
16
|
+
data: ExperimentComparisonData;
|
|
17
|
+
className?: string;
|
|
18
|
+
locale?: ReportLocale;
|
|
19
|
+
}): ReactElement {
|
|
20
|
+
if (data.groups.length === 0) {
|
|
21
|
+
return (
|
|
22
|
+
<div className={cx("nre", "nre-experiment-comparison", className)}>
|
|
23
|
+
<p className="nre-experiment-groups-empty">{localeText(locale, "experimentComparison.empty")}</p>
|
|
24
|
+
</div>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<div className={cx("nre", "nre-experiment-comparison", className)} data-nre-experiment-groups>
|
|
30
|
+
<div
|
|
31
|
+
className="nre-experiment-group-tabs"
|
|
32
|
+
role="tablist"
|
|
33
|
+
aria-label={localeText(locale, "experimentComparison.groups")}
|
|
34
|
+
>
|
|
35
|
+
{data.groups.map((group, index) => (
|
|
36
|
+
<div
|
|
37
|
+
key={group.key}
|
|
38
|
+
className="nre-experiment-group-tab"
|
|
39
|
+
role="tab"
|
|
40
|
+
tabIndex={index === 0 ? 0 : -1}
|
|
41
|
+
aria-selected={index === 0 ? "true" : "false"}
|
|
42
|
+
aria-label={localeText(locale, "experimentComparison.selectGroup", { group: group.key })}
|
|
43
|
+
data-nre-experiment-group-select={index}
|
|
44
|
+
>
|
|
45
|
+
<strong className="nre-experiment-group-name">{group.key}</strong>
|
|
46
|
+
<GroupSummary data={group.summary} locale={locale} />
|
|
47
|
+
</div>
|
|
48
|
+
))}
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<div className="nre-experiment-group-panels">
|
|
52
|
+
{data.groups.map((group, index) => (
|
|
53
|
+
<details
|
|
54
|
+
key={group.key}
|
|
55
|
+
className="nre-experiment-group-panel"
|
|
56
|
+
data-nre-experiment-group-panel={index}
|
|
57
|
+
role="tabpanel"
|
|
58
|
+
open={index === 0}
|
|
59
|
+
>
|
|
60
|
+
<summary>{group.key}</summary>
|
|
61
|
+
<MetricScatter data={group.scatter} locale={locale} />
|
|
62
|
+
<ExperimentList items={group.experiments} filter locale={locale} relativeTo={group.key} />
|
|
63
|
+
</details>
|
|
64
|
+
))}
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
}
|