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
|
@@ -1,527 +0,0 @@
|
|
|
1
|
-
// 双面验收:每个官方组件对同一份数据,web 面(renderToStaticMarkup)与 text 面
|
|
2
|
-
// 给出一致判读 —— 排序方向随 better、samples < total 角标、缺数据 — 不补 0、
|
|
3
|
-
// 截断如实报剩余;text 面形态以内联快照锁定(照 report-components.mdx 的示例形态)。
|
|
4
|
-
// 另验收:排版原语的分栏与降级、渲染前树校验、defineComponent 自定义组件、
|
|
5
|
-
// defineReport + renderReportToText / renderReportToStaticHtml 两个宿主入口。
|
|
6
|
-
|
|
7
|
-
import { renderToStaticMarkup } from "react-dom/server";
|
|
8
|
-
import { describe, expect, it } from "vitest";
|
|
9
|
-
|
|
10
|
-
import type { Results, Selection, Snapshot } from "../results/index.ts";
|
|
11
|
-
import {
|
|
12
|
-
CaseList,
|
|
13
|
-
Col,
|
|
14
|
-
DefaultReport,
|
|
15
|
-
DeltaTable,
|
|
16
|
-
MetricBars,
|
|
17
|
-
MetricLine,
|
|
18
|
-
MetricMatrix,
|
|
19
|
-
MetricScatter,
|
|
20
|
-
MetricTable,
|
|
21
|
-
Row,
|
|
22
|
-
RunOverview,
|
|
23
|
-
Scoreboard,
|
|
24
|
-
Section,
|
|
25
|
-
Style,
|
|
26
|
-
Text,
|
|
27
|
-
defineComponent,
|
|
28
|
-
defineReport,
|
|
29
|
-
isReportDefinition,
|
|
30
|
-
renderReportToText,
|
|
31
|
-
} from "./index.ts";
|
|
32
|
-
import { renderReportToStaticHtml } from "./web.ts";
|
|
33
|
-
import { createTextContext, renderNodeToText, validateReportTree } from "./tree.ts";
|
|
34
|
-
import {
|
|
35
|
-
caseListData,
|
|
36
|
-
deltaData,
|
|
37
|
-
lineData,
|
|
38
|
-
matrixData,
|
|
39
|
-
overviewData,
|
|
40
|
-
scatterData,
|
|
41
|
-
scoreboardData,
|
|
42
|
-
tableData,
|
|
43
|
-
} from "./react/fixtures.ts";
|
|
44
|
-
|
|
45
|
-
const ctx = createTextContext({ width: 80 });
|
|
46
|
-
const text = (node: Parameters<typeof renderNodeToText>[0]) => renderNodeToText(node, ctx);
|
|
47
|
-
|
|
48
|
-
// ───────────────────────── 每组件双面一致 ─────────────────────────
|
|
49
|
-
|
|
50
|
-
describe("RunOverview 双面", () => {
|
|
51
|
-
const html = renderToStaticMarkup(<RunOverview data={overviewData} />);
|
|
52
|
-
const term = text(<RunOverview data={overviewData} />);
|
|
53
|
-
|
|
54
|
-
it("text 面形态:头行 + 判决行 + 警告行", () => {
|
|
55
|
-
expect(term).toMatchInlineSnapshot(`
|
|
56
|
-
"2 experiments · 12 evals · 48 attempts · composed from 2 runs · latest 2026-07-01T11:30:00Z
|
|
57
|
-
passed 36 · failed 8 · errored 2 · skipped 2 · no data · 4m 21s
|
|
58
|
-
! snapshot covers 9 of 12 evals seen in history; re-run \`niceeval exp compare/bub\` for a full snapshot"
|
|
59
|
-
`);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it("两面同口径:成本全缺都是 no data 不编 $0;警告都在", () => {
|
|
63
|
-
for (const face of [html, term]) {
|
|
64
|
-
expect(face).toContain("no data");
|
|
65
|
-
expect(face).not.toContain("$0");
|
|
66
|
-
expect(face).toContain("snapshot covers 9 of 12 evals");
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
describe("MetricTable 双面", () => {
|
|
72
|
-
const html = renderToStaticMarkup(<MetricTable data={tableData} />);
|
|
73
|
-
const term = text(<MetricTable data={tableData} />);
|
|
74
|
-
|
|
75
|
-
it("text 面形态:对齐列 + 覆盖率角标 + 缺数据 —", () => {
|
|
76
|
-
expect(term).toMatchInlineSnapshot(`
|
|
77
|
-
"agent pass rate code lines
|
|
78
|
-
codex 50% —
|
|
79
|
-
bub 87% 120 lines 5/6"
|
|
80
|
-
`);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it("两面同口径:行序一致(预排即终排)、角标一致、缺数据不补 0", () => {
|
|
84
|
-
// 行序:codex 在 bub 前(数据侧顺序,两面都不重排)
|
|
85
|
-
expect(html.indexOf(">codex<")).toBeLessThan(html.indexOf(">bub<"));
|
|
86
|
-
expect(term.indexOf("codex")).toBeLessThan(term.indexOf("bub"));
|
|
87
|
-
// 覆盖率角标 5/6 两面都在
|
|
88
|
-
expect(html).toContain("5/6");
|
|
89
|
-
expect(term).toContain("5/6");
|
|
90
|
-
// 全 null 格子:web 是 no data 文案,text 是 —,都绝不画 0
|
|
91
|
-
expect(html).toContain("no data");
|
|
92
|
-
expect(term).toContain("—");
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
describe("MetricMatrix 双面", () => {
|
|
97
|
-
const html = renderToStaticMarkup(<MetricMatrix data={matrixData} />);
|
|
98
|
-
const term = text(<MetricMatrix data={matrixData} />);
|
|
99
|
-
|
|
100
|
-
it("text 面形态:稀疏格子 — + 下钻命令指向最值得看的一行", () => {
|
|
101
|
-
expect(term).toMatchInlineSnapshot(`
|
|
102
|
-
"eval bub codex
|
|
103
|
-
algebra/quadratic 100% 0%
|
|
104
|
-
geometry/angles 50% —
|
|
105
|
-
|
|
106
|
-
next: niceeval show geometry/angles"
|
|
107
|
-
`);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it("两面同口径:数字一致,稀疏格子不编数", () => {
|
|
111
|
-
for (const value of ["100%", "0%", "50%"]) {
|
|
112
|
-
expect(html).toContain(value);
|
|
113
|
-
expect(term).toContain(value);
|
|
114
|
-
}
|
|
115
|
-
expect(html.match(/nre-td-empty/g)).toHaveLength(1); // web 空格子
|
|
116
|
-
});
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
describe("MetricBars 双面", () => {
|
|
120
|
-
const html = renderToStaticMarkup(<MetricBars data={matrixData} />);
|
|
121
|
-
const term = text(<MetricBars data={matrixData} />);
|
|
122
|
-
|
|
123
|
-
it("text 面形态:一组条 = 一个 row 键,条长即刻度,缺数据 —", () => {
|
|
124
|
-
expect(term).toMatchInlineSnapshot(`
|
|
125
|
-
"algebra/quadratic
|
|
126
|
-
bub ████████████████████ 100%
|
|
127
|
-
codex ░░░░░░░░░░░░░░░░░░░░ 0%
|
|
128
|
-
geometry/angles
|
|
129
|
-
bub ██████████░░░░░░░░░░ 50%
|
|
130
|
-
codex —"
|
|
131
|
-
`);
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it("两面同口径:组内按值排序(better higher 降序),缺数据不出柱", () => {
|
|
135
|
-
expect(html).toContain("100%");
|
|
136
|
-
expect(html).toContain("50%");
|
|
137
|
-
// web 面:codex 在 geometry/angles 组没有柱(缺数据),柱数 = 3
|
|
138
|
-
expect(html.match(/class="nre-bar"/g)).toHaveLength(3);
|
|
139
|
-
// text 面:codex 行是 —
|
|
140
|
-
expect(term).toContain("codex —");
|
|
141
|
-
});
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
describe("Scoreboard 双面", () => {
|
|
145
|
-
const html = renderToStaticMarkup(<Scoreboard data={scoreboardData} />);
|
|
146
|
-
const term = text(<Scoreboard data={scoreboardData} />);
|
|
147
|
-
|
|
148
|
-
it("text 面形态:总分/满分 + 分科 + missing 注脚 + 权重表", () => {
|
|
149
|
-
expect(term).toMatchInlineSnapshot(`
|
|
150
|
-
"agent total algebra geometry
|
|
151
|
-
bub 78.5/100 14/16 (1 missing) 3/4
|
|
152
|
-
codex 52/100 9/16 1.4/4 (2 missing)
|
|
153
|
-
weights: algebra/ ×2 · others ×1"
|
|
154
|
-
`);
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
it("两面同口径:missing 与权重都如实", () => {
|
|
158
|
-
expect(html).toContain("1 eval missing, scored 0");
|
|
159
|
-
expect(term).toContain("(1 missing)");
|
|
160
|
-
expect(html).toContain("algebra/ ×2");
|
|
161
|
-
expect(term).toContain("algebra/ ×2");
|
|
162
|
-
});
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
describe("MetricScatter 双面", () => {
|
|
166
|
-
const html = renderToStaticMarkup(<MetricScatter data={scatterData} />);
|
|
167
|
-
const term = text(<MetricScatter data={scatterData} />);
|
|
168
|
-
|
|
169
|
-
it("text 面形态:字符坐标图 + 图例 + 缺数据注脚", () => {
|
|
170
|
-
expect(term).toMatchInlineSnapshot(`
|
|
171
|
-
" pass rate ↑
|
|
172
|
-
90%│B····
|
|
173
|
-
│ ·········
|
|
174
|
-
│ ··········
|
|
175
|
-
│ ·········
|
|
176
|
-
│ ·········
|
|
177
|
-
│ ·········
|
|
178
|
-
│ C ··········
|
|
179
|
-
│ ·········
|
|
180
|
-
50%│ ····A
|
|
181
|
-
└───────────────────────────────────────────────────────────────────────────→ cost (axis reversed: right = better)
|
|
182
|
-
$10.00 $5.00
|
|
183
|
-
|
|
184
|
-
better → upper right
|
|
185
|
-
A compare/bub-low B compare/bub-high C compare/codex-mid
|
|
186
|
-
1 point missing data"
|
|
187
|
-
`);
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
it("两面同口径:x 轴反向(lower 好的一端在右)、缺数据点都不画、注脚同数", () => {
|
|
191
|
-
// web:$5(便宜)在 $10 右边(cx 更大)——render.test.tsx 已细测,这里只对注脚
|
|
192
|
-
expect(html).toContain("1 point missing data");
|
|
193
|
-
expect(term).toContain("1 point missing data");
|
|
194
|
-
expect(html).not.toContain('data-key="compare/codex-broken"'); // 注脚 title 如实列缺数据的点,但不画
|
|
195
|
-
// text 图例里也没有缺数据的点
|
|
196
|
-
expect(term).not.toContain("codex-broken");
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
it("点太密时降级坐标表,不硬挤", () => {
|
|
200
|
-
const narrow = renderNodeToText(<MetricScatter data={scatterData} />, createTextContext({ width: 40 }));
|
|
201
|
-
expect(narrow).toContain("experiment");
|
|
202
|
-
expect(narrow).toContain("$5.00");
|
|
203
|
-
expect(narrow).not.toContain("└");
|
|
204
|
-
});
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
describe("MetricLine 双面", () => {
|
|
208
|
-
const html = renderToStaticMarkup(<MetricLine data={lineData} />);
|
|
209
|
-
const term = text(<MetricLine data={lineData} />);
|
|
210
|
-
|
|
211
|
-
it("text 面形态:同系列一个字母、沿 x 排布,y 轴刻度,图例 + 缺数据注脚", () => {
|
|
212
|
-
expect(term).toMatchInlineSnapshot(`
|
|
213
|
-
" pass rate ↑
|
|
214
|
-
80%│B··················
|
|
215
|
-
│ ·····································
|
|
216
|
-
│ ··················B
|
|
217
|
-
│
|
|
218
|
-
│
|
|
219
|
-
│
|
|
220
|
-
│A··················
|
|
221
|
-
│ ·····································
|
|
222
|
-
30%│ ··················A
|
|
223
|
-
└───────────────────────────────────────────────────────────────────────────→ Simulated latency
|
|
224
|
-
100ms 300ms
|
|
225
|
-
|
|
226
|
-
A 1 agents B 16 agents
|
|
227
|
-
1 point missing data"
|
|
228
|
-
`);
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
it("两面同口径:未声明 flag 的点两面都不画、注脚同数", () => {
|
|
232
|
-
expect(html).toContain("1 point missing data");
|
|
233
|
-
expect(term).toContain("1 point missing data");
|
|
234
|
-
expect(html).not.toContain('data-key="ultra/legacy"'); // 注脚 title 如实列缺数据的点,但不画
|
|
235
|
-
});
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
describe("DeltaTable 双面", () => {
|
|
239
|
-
const html = renderToStaticMarkup(<DeltaTable data={deltaData} />);
|
|
240
|
-
const term = text(<DeltaTable data={deltaData} />);
|
|
241
|
-
|
|
242
|
-
it("text 面形态:A → B + Δ,单侧缺数据是 —,Δ=0 是 ±0", () => {
|
|
243
|
-
expect(term).toMatchInlineSnapshot(`
|
|
244
|
-
"pair pass rate cost
|
|
245
|
-
bub 50% → 62% +12pp $0.20 → $0.35 +$0.15
|
|
246
|
-
codex 40% → 40% ±0 — → $0.30 —"
|
|
247
|
-
`);
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
it("两面同口径:Δ 文案一致、缺数据都不硬算", () => {
|
|
251
|
-
for (const piece of ["+12pp", "+$0.15", "±0"]) {
|
|
252
|
-
expect(html).toContain(piece);
|
|
253
|
-
expect(term).toContain(piece);
|
|
254
|
-
}
|
|
255
|
-
expect(html).toContain("nre-delta-missing");
|
|
256
|
-
expect(term).toContain("— → $0.30");
|
|
257
|
-
});
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
describe("CaseList 双面", () => {
|
|
261
|
-
const html = renderToStaticMarkup(<CaseList data={caseListData} />);
|
|
262
|
-
const term = text(<CaseList data={caseListData} />);
|
|
263
|
-
|
|
264
|
-
it("text 面形态:逐条案例 + 下钻命令 + 截断报剩余", () => {
|
|
265
|
-
expect(term).toMatchInlineSnapshot(`
|
|
266
|
-
"✗ algebra/quadratic · compare/bub · failed · 32.0s · $0.12
|
|
267
|
-
roots-correct — expected x=2, got x=3
|
|
268
|
-
judge: sign flipped when substituting into the quadratic formula
|
|
269
|
-
→ niceeval show algebra/quadratic
|
|
270
|
-
✗ geometry/angles · compare/codex · errored · 4.5s
|
|
271
|
-
TypeError: cannot read properties of undefined (reading 'foo')
|
|
272
|
-
→ niceeval show geometry/angles
|
|
273
|
-
|
|
274
|
-
(2 more not shown)"
|
|
275
|
-
`);
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
it("两面同口径:失败断言、error、truncated 数一致", () => {
|
|
279
|
-
for (const piece of ["roots-correct", "expected x=2, got x=3", "TypeError", "2 more not shown"]) {
|
|
280
|
-
expect(html).toContain(piece);
|
|
281
|
-
expect(term).toContain(piece);
|
|
282
|
-
}
|
|
283
|
-
});
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
// ───────────────────────── 排版原语 ─────────────────────────
|
|
287
|
-
|
|
288
|
-
describe("排版原语", () => {
|
|
289
|
-
it("Col 纵向堆叠;Section 标题 + 缩进;Text 折行;Style text 面为空", () => {
|
|
290
|
-
const node = (
|
|
291
|
-
<Col>
|
|
292
|
-
<Section title="考试成绩单">
|
|
293
|
-
<Text>algebra 权重 ×2,满分 100。</Text>
|
|
294
|
-
</Section>
|
|
295
|
-
<Style>{`.passbars i { background: #4a7; }`}</Style>
|
|
296
|
-
</Col>
|
|
297
|
-
);
|
|
298
|
-
expect(text(node)).toMatchInlineSnapshot(`
|
|
299
|
-
"考试成绩单
|
|
300
|
-
algebra 权重 ×2,满分 100。"
|
|
301
|
-
`);
|
|
302
|
-
const html = renderToStaticMarkup(node);
|
|
303
|
-
expect(html).toContain("<h2 class=\"nre-section-title\">考试成绩单</h2>");
|
|
304
|
-
expect(html).toContain("<style>.passbars i { background: #4a7; }</style>");
|
|
305
|
-
});
|
|
306
|
-
|
|
307
|
-
it("Row:宽度够时字符分栏,│ 分隔,各栏折到自己的宽度", () => {
|
|
308
|
-
const node = (
|
|
309
|
-
<Row>
|
|
310
|
-
<Text>left column body</Text>
|
|
311
|
-
<Text>right column body</Text>
|
|
312
|
-
</Row>
|
|
313
|
-
);
|
|
314
|
-
const wide = renderNodeToText(node, createTextContext({ width: 60 }));
|
|
315
|
-
expect(wide).toContain("│");
|
|
316
|
-
expect(wide.split("\n")[0]).toContain("left column body");
|
|
317
|
-
expect(wide.split("\n")[0]).toContain("right column body");
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
it("Row:宽度不足降级纵向,不硬挤", () => {
|
|
321
|
-
const node = (
|
|
322
|
-
<Row>
|
|
323
|
-
<Text>left column body</Text>
|
|
324
|
-
<Text>right column body</Text>
|
|
325
|
-
</Row>
|
|
326
|
-
);
|
|
327
|
-
const narrow = renderNodeToText(node, createTextContext({ width: 30 }));
|
|
328
|
-
expect(narrow).not.toContain("│");
|
|
329
|
-
expect(narrow).toContain("left column body\n\nright column body");
|
|
330
|
-
});
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
// ───────────────────────── 树校验与自定义组件 ─────────────────────────
|
|
334
|
-
|
|
335
|
-
describe("渲染前树校验", () => {
|
|
336
|
-
it("字符串 intrinsic(<div>)报错,错误指名组件路径", () => {
|
|
337
|
-
const bad = (
|
|
338
|
-
<Col>
|
|
339
|
-
<Section title="x">
|
|
340
|
-
<div>raw</div>
|
|
341
|
-
</Section>
|
|
342
|
-
</Col>
|
|
343
|
-
);
|
|
344
|
-
expect(() => validateReportTree(bad)).toThrow(
|
|
345
|
-
/Raw HTML <div> has no terminal face; use <Text>, layout primitives, or a defineComponent component\. \(in <Col> > <Section>\)/,
|
|
346
|
-
);
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
it("普通函数组件(组合页面片段)被调用展开继续校验", () => {
|
|
350
|
-
const Fragmented = () => (
|
|
351
|
-
<Col>
|
|
352
|
-
<Text>ok</Text>
|
|
353
|
-
</Col>
|
|
354
|
-
);
|
|
355
|
-
expect(() => validateReportTree(<Fragmented />)).not.toThrow();
|
|
356
|
-
const BadInside = () => <span>nope</span>;
|
|
357
|
-
expect(() => validateReportTree(<BadInside />)).toThrow(/Raw HTML <span>/);
|
|
358
|
-
});
|
|
359
|
-
});
|
|
360
|
-
|
|
361
|
-
describe("defineComponent", () => {
|
|
362
|
-
it("faces 两键必填,少一个面在运行时也拦住(编译期本就不过)", () => {
|
|
363
|
-
// @ts-expect-error 缺 text 面编译不过;运行时同样报错
|
|
364
|
-
expect(() => defineComponent({ web: () => null })).toThrow(/requires both faces/);
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
it("自定义双面组件:同一份数据两面判读一致,text 面拿到宽度", () => {
|
|
368
|
-
interface BarRow {
|
|
369
|
-
key: string;
|
|
370
|
-
ratio: number | null;
|
|
371
|
-
display: string;
|
|
372
|
-
}
|
|
373
|
-
const PassBars = defineComponent<{ rows: BarRow[] }>({
|
|
374
|
-
web({ rows }) {
|
|
375
|
-
return (
|
|
376
|
-
<ul className="passbars">
|
|
377
|
-
{rows.map((r) => (
|
|
378
|
-
<li key={r.key}>
|
|
379
|
-
<span>{r.key}</span>
|
|
380
|
-
<b>{r.ratio === null ? "—" : r.display}</b>
|
|
381
|
-
</li>
|
|
382
|
-
))}
|
|
383
|
-
</ul>
|
|
384
|
-
);
|
|
385
|
-
},
|
|
386
|
-
text({ rows }, { width }) {
|
|
387
|
-
const bar = (n: number) => "█".repeat(Math.round(n * 10)).padEnd(10, "░");
|
|
388
|
-
expect(width).toBe(80);
|
|
389
|
-
return rows
|
|
390
|
-
.map((r) => `${r.key.padEnd(8)} ${r.ratio === null ? "—".padEnd(10) : bar(r.ratio)} ${r.display}`)
|
|
391
|
-
.join("\n");
|
|
392
|
-
},
|
|
393
|
-
});
|
|
394
|
-
const rows: BarRow[] = [
|
|
395
|
-
{ key: "bub", ratio: 0.87, display: "87%" },
|
|
396
|
-
{ key: "claude", ratio: null, display: "—" },
|
|
397
|
-
];
|
|
398
|
-
const term = text(<PassBars rows={rows} />);
|
|
399
|
-
expect(term).toMatchInlineSnapshot(`
|
|
400
|
-
"bub █████████░ 87%
|
|
401
|
-
claude — —"
|
|
402
|
-
`);
|
|
403
|
-
const html = renderToStaticMarkup(<PassBars rows={rows} />);
|
|
404
|
-
expect(html).toContain("87%");
|
|
405
|
-
expect(html).toContain("—"); // 缺数据两面都是 —,不补 0
|
|
406
|
-
});
|
|
407
|
-
});
|
|
408
|
-
|
|
409
|
-
// ───────────────────────── defineReport 与两个宿主入口 ─────────────────────────
|
|
410
|
-
|
|
411
|
-
function fakeContext(): { selection: Selection; results: Results } {
|
|
412
|
-
const snapshot: Snapshot = (() => {
|
|
413
|
-
const summary = {
|
|
414
|
-
agent: "bub",
|
|
415
|
-
startedAt: "2026-07-01T10:00:00Z",
|
|
416
|
-
completedAt: "2026-07-01T10:05:00Z",
|
|
417
|
-
passed: 1,
|
|
418
|
-
failed: 1,
|
|
419
|
-
skipped: 0,
|
|
420
|
-
errored: 0,
|
|
421
|
-
durationMs: 2000,
|
|
422
|
-
results: [] as never[],
|
|
423
|
-
};
|
|
424
|
-
const runDir = { dir: "/results/run-1", summary, attempts: [] as never[] };
|
|
425
|
-
const mk = (id: string, outcome: "passed" | "failed", index: number) => ({
|
|
426
|
-
evalId: id,
|
|
427
|
-
experimentId: "compare/bub",
|
|
428
|
-
result: {
|
|
429
|
-
id,
|
|
430
|
-
agent: "bub",
|
|
431
|
-
outcome,
|
|
432
|
-
attempt: 0,
|
|
433
|
-
startedAt: `2026-07-01T10:0${index}:00Z`,
|
|
434
|
-
durationMs: 1000,
|
|
435
|
-
assertions: [],
|
|
436
|
-
},
|
|
437
|
-
ref: { run: "run-1", result: index },
|
|
438
|
-
runDir,
|
|
439
|
-
events: async () => null,
|
|
440
|
-
trace: async () => null,
|
|
441
|
-
o11y: async () => null,
|
|
442
|
-
diff: async () => null,
|
|
443
|
-
sources: async () => null,
|
|
444
|
-
});
|
|
445
|
-
const attempts = [mk("algebra/x", "passed", 0), mk("algebra/y", "failed", 1)];
|
|
446
|
-
return {
|
|
447
|
-
experimentId: "compare/bub",
|
|
448
|
-
startedAt: "2026-07-01T10:00:00Z",
|
|
449
|
-
agent: "bub",
|
|
450
|
-
schemaVersion: 1,
|
|
451
|
-
evals: attempts.map((a) => ({ id: a.evalId, attempts: [a] })),
|
|
452
|
-
attempts,
|
|
453
|
-
runDir,
|
|
454
|
-
} as unknown as Snapshot;
|
|
455
|
-
})();
|
|
456
|
-
const selection: Selection = {
|
|
457
|
-
snapshots: [snapshot],
|
|
458
|
-
warnings: [],
|
|
459
|
-
filter: () => selection,
|
|
460
|
-
};
|
|
461
|
-
const results = {
|
|
462
|
-
experiments: [{ id: "compare/bub", snapshots: [snapshot], latest: snapshot, evalIds: ["algebra/x", "algebra/y"] }],
|
|
463
|
-
skipped: [],
|
|
464
|
-
runDirs: [snapshot.runDir],
|
|
465
|
-
latest: () => selection,
|
|
466
|
-
} as Results;
|
|
467
|
-
return { selection, results };
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
describe("defineReport + 渲染入口", () => {
|
|
471
|
-
const report = defineReport(async ({ selection }) => (
|
|
472
|
-
<Col>
|
|
473
|
-
<DefaultReport />
|
|
474
|
-
<Section title="考试成绩单">
|
|
475
|
-
<Scoreboard data={await Scoreboard.data(selection, { rows: "agent", subjects: "evalGroup" })} />
|
|
476
|
-
</Section>
|
|
477
|
-
</Col>
|
|
478
|
-
));
|
|
479
|
-
|
|
480
|
-
it("isReportDefinition 识别产物;非函数报错", () => {
|
|
481
|
-
expect(isReportDefinition(report)).toBe(true);
|
|
482
|
-
expect(isReportDefinition({})).toBe(false);
|
|
483
|
-
// @ts-expect-error 非函数
|
|
484
|
-
expect(() => defineReport(42)).toThrow(/expects a build function/);
|
|
485
|
-
});
|
|
486
|
-
|
|
487
|
-
it("renderReportToText:同一棵树走 text 面,DefaultReport 渲染宿主注入的选集", async () => {
|
|
488
|
-
const out = await renderReportToText(report, fakeContext(), { width: 100 });
|
|
489
|
-
// 官方水位 = show 榜单(viewing-results.mdx):Current verdicts 头 + experiment 榜单 + 失败清单
|
|
490
|
-
expect(out).toContain("Current verdicts · 1 experiment · composed from 1 run");
|
|
491
|
-
expect(out).toContain("experiment");
|
|
492
|
-
expect(out).toContain("evals");
|
|
493
|
-
expect(out).toContain("compare/bub");
|
|
494
|
-
expect(out).toContain("1/2"); // eval 级折叠计票
|
|
495
|
-
expect(out).toContain("50%");
|
|
496
|
-
expect(out).toContain("Failing:");
|
|
497
|
-
expect(out).toContain("✗ algebra/y");
|
|
498
|
-
expect(out).toContain("→ niceeval show algebra/y"); // 失败清单每条自带下钻命令
|
|
499
|
-
// 自己的口径:成绩单
|
|
500
|
-
expect(out).toContain("考试成绩单");
|
|
501
|
-
expect(out).toContain("bub 50/100");
|
|
502
|
-
});
|
|
503
|
-
|
|
504
|
-
it("renderReportToStaticHtml:同一棵树走 web 面,官方组件自动接证据室深链", async () => {
|
|
505
|
-
const html = await renderReportToStaticHtml(report, fakeContext());
|
|
506
|
-
expect(html).toContain("nre-default-report");
|
|
507
|
-
expect(html).toContain("nre-scoreboard");
|
|
508
|
-
expect(html).toContain("考试成绩单");
|
|
509
|
-
// 宿主注入的 attemptHref:CaseList 的下钻是普通 <a>,默认 view 路由
|
|
510
|
-
expect(html).toContain('href="#/attempt/run-1/1"');
|
|
511
|
-
expect(html).not.toContain("<script");
|
|
512
|
-
});
|
|
513
|
-
|
|
514
|
-
it("树里混进 <div>:两个宿主渲染前同一遍校验拦住", async () => {
|
|
515
|
-
const bad = defineReport(() => (
|
|
516
|
-
<Col>
|
|
517
|
-
<div>nope</div>
|
|
518
|
-
</Col>
|
|
519
|
-
));
|
|
520
|
-
await expect(renderReportToText(bad, fakeContext())).rejects.toThrow(/Raw HTML <div>/);
|
|
521
|
-
await expect(renderReportToStaticHtml(bad, fakeContext())).rejects.toThrow(/Raw HTML <div>/);
|
|
522
|
-
});
|
|
523
|
-
|
|
524
|
-
it("<DefaultReport /> 在宿主之外渲染:一句直说的错误", () => {
|
|
525
|
-
expect(() => renderToStaticMarkup(<DefaultReport />)).toThrow(/renders the host-injected selection/);
|
|
526
|
-
});
|
|
527
|
-
});
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
// CaseList:失败案例清单——报告回答完「多少」,这里回答「为什么」。
|
|
2
|
-
// 逐条列出失败断言 / error 摘要 / judge 评语(evidence),每条带 attemptHref 下钻;
|
|
3
|
-
// truncated 如实报「还有 n 条没列」。长文本收进 <details>,零 JS 也能展开。
|
|
4
|
-
|
|
5
|
-
import type { ReactElement } from "react";
|
|
6
|
-
import type { AttemptRef, CaseListData } from "../types.ts";
|
|
7
|
-
import { colorClassForKey } from "./colors.ts";
|
|
8
|
-
import { cx, formatDurationMs, formatUSD } from "./format.ts";
|
|
9
|
-
|
|
10
|
-
export function CaseList({
|
|
11
|
-
data,
|
|
12
|
-
attemptHref,
|
|
13
|
-
className,
|
|
14
|
-
}: {
|
|
15
|
-
data: CaseListData;
|
|
16
|
-
attemptHref?: (ref: AttemptRef) => string;
|
|
17
|
-
className?: string;
|
|
18
|
-
}): ReactElement {
|
|
19
|
-
return (
|
|
20
|
-
<section className={cx("nre", "nre-case-list", className)}>
|
|
21
|
-
{data.rows.length === 0 && <p className="nre-case-empty">No failed or errored attempts</p>}
|
|
22
|
-
<ol className="nre-cases">
|
|
23
|
-
{data.rows.map((row) => (
|
|
24
|
-
<li key={`${row.ref.run}:${row.ref.result}`} className={cx("nre-case", `nre-case-${row.outcome}`)}>
|
|
25
|
-
<div className="nre-case-head">
|
|
26
|
-
<span className={cx("nre-case-outcome", `nre-outcome-${row.outcome}`)}>{row.outcome}</span>
|
|
27
|
-
<span className="nre-case-eval">{row.eval}</span>
|
|
28
|
-
{/* agent 键:稳定散列上色,与其它块同键同色 */}
|
|
29
|
-
<span className={cx("nre-case-agent", "nre-key", colorClassForKey(row.agent))}>{row.agent}</span>
|
|
30
|
-
<span className="nre-case-experiment">{row.experimentId}</span>
|
|
31
|
-
<span className="nre-case-duration">{formatDurationMs(row.durationMs)}</span>
|
|
32
|
-
{row.costUSD !== undefined && <span className="nre-case-cost">{formatUSD(row.costUSD)}</span>}
|
|
33
|
-
{attemptHref && (
|
|
34
|
-
<a className="nre-case-link" href={attemptHref(row.ref)}>
|
|
35
|
-
view attempt
|
|
36
|
-
</a>
|
|
37
|
-
)}
|
|
38
|
-
</div>
|
|
39
|
-
|
|
40
|
-
{/* errored 的错误摘要(计算侧已过 redact) */}
|
|
41
|
-
{row.error && <p className="nre-case-error">{row.error}</p>}
|
|
42
|
-
|
|
43
|
-
{row.failedAssertions.length > 0 && (
|
|
44
|
-
<ul className="nre-assertions">
|
|
45
|
-
{row.failedAssertions.map((assertion, j) => (
|
|
46
|
-
<li key={j} className="nre-assertion">
|
|
47
|
-
<span className="nre-assertion-name">{assertion.name}</span>
|
|
48
|
-
<span className="nre-assertion-score">score {assertion.score}</span>
|
|
49
|
-
{/* detail / evidence 可能很长:<details> 收起,不 hydrate 也能展开 */}
|
|
50
|
-
{(assertion.detail || assertion.evidence) && (
|
|
51
|
-
<details className="nre-assertion-more">
|
|
52
|
-
<summary>details</summary>
|
|
53
|
-
{assertion.detail && <p className="nre-assertion-detail">{assertion.detail}</p>}
|
|
54
|
-
{assertion.evidence && (
|
|
55
|
-
<blockquote className="nre-assertion-evidence">{assertion.evidence}</blockquote>
|
|
56
|
-
)}
|
|
57
|
-
</details>
|
|
58
|
-
)}
|
|
59
|
-
</li>
|
|
60
|
-
))}
|
|
61
|
-
</ul>
|
|
62
|
-
)}
|
|
63
|
-
</li>
|
|
64
|
-
))}
|
|
65
|
-
</ol>
|
|
66
|
-
{/* limit 之外还有几条,如实报,不静默截断 */}
|
|
67
|
-
{data.truncated > 0 && <p className="nre-truncated">and {data.truncated} more not shown</p>}
|
|
68
|
-
</section>
|
|
69
|
-
);
|
|
70
|
-
}
|