niceeval 0.5.3 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/INDEX.md +55 -0
- package/dist/agents/types.d.ts +317 -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/execution-tree.d.ts +103 -0
- package/dist/o11y/otlp/parse.d.ts +3 -0
- package/dist/o11y/otlp/receiver.d.ts +21 -0
- package/dist/o11y/otlp/select.d.ts +22 -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/cost-pass-rate-comparison.d.ts +1 -0
- package/dist/report/built-ins/cost-pass-rate-comparison.js +17 -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 +178 -0
- package/dist/report/components.js +132 -0
- package/dist/report/compute.d.ts +106 -0
- package/dist/report/compute.js +618 -0
- package/dist/report/flag.d.ts +6 -0
- package/dist/report/flag.js +8 -0
- package/dist/report/format.d.ts +32 -0
- package/dist/report/format.js +126 -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 +83 -0
- package/dist/report/locale.js +160 -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 +20 -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 +10 -0
- package/dist/report/react/ExperimentList.js +15 -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 +100 -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 +393 -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 +503 -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 +420 -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/annotated-source.d.ts +61 -0
- package/dist/results/attempt-evidence.d.ts +69 -0
- package/dist/results/attempt-source.d.ts +15 -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 +9 -4
- package/docs-site/zh/concepts/assert.mdx +8 -8
- package/docs-site/zh/concepts/evals.mdx +5 -5
- package/docs-site/zh/concepts/experiment.mdx +3 -1
- package/docs-site/zh/concepts/judge.mdx +9 -9
- package/docs-site/zh/concepts/overview.mdx +2 -2
- 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 +235 -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 -0
- package/docs-site/zh/guides/fixtures.mdx +1 -1
- 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 +35 -34
- package/docs-site/zh/guides/runner.mdx +2 -2
- package/docs-site/zh/guides/sandbox-agent.mdx +10 -5
- package/docs-site/zh/guides/sandbox-providers.mdx +102 -0
- package/docs-site/zh/guides/viewing-results.mdx +199 -79
- package/docs-site/zh/guides/write-experiment.mdx +23 -1
- package/docs-site/zh/guides/write-send.mdx +2 -2
- 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 +24 -12
- 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 +7 -1
- package/docs-site/zh/reference/expect.mdx +3 -3
- package/package.json +10 -6
- package/src/agents/bub.ts +134 -43
- 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 +11 -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 +62 -2
- package/src/cli.ts +74 -47
- package/src/context/context.test.ts +20 -2
- package/src/context/context.ts +3 -0
- package/src/context/control-flow.ts +1 -1
- package/src/context/session.test.ts +92 -1
- package/src/context/session.ts +26 -1
- package/src/context/types.ts +4 -4
- package/src/define.test.ts +82 -0
- package/src/define.ts +72 -11
- package/src/expect/index.ts +5 -5
- package/src/i18n/en.ts +36 -15
- package/src/i18n/zh-CN.ts +39 -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 +643 -0
- package/src/report/built-ins/cost-pass-rate-comparison.tsx +23 -0
- package/src/report/built-ins/index.ts +2 -0
- package/src/report/components.tsx +223 -50
- package/src/report/compute.ts +358 -53
- package/src/report/dual-render.test.tsx +1125 -0
- package/src/report/flag.ts +1 -1
- package/src/report/format.ts +50 -2
- package/src/report/index.ts +48 -14
- package/src/report/load.ts +1 -1
- package/src/report/locale.ts +206 -0
- package/src/report/metrics.ts +39 -15
- package/src/report/primitives.tsx +100 -2
- package/src/report/react/AttemptList.tsx +115 -0
- package/src/report/react/DeltaTable.tsx +9 -6
- package/src/report/react/EvalList.tsx +0 -0
- package/src/report/react/ExperimentList.tsx +108 -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 +136 -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 +129 -0
- package/src/report/react/fixtures.ts +163 -34
- package/src/report/react/format.ts +1 -1
- package/src/report/react/index.tsx +22 -6
- package/src/report/react/render.test.tsx +164 -31
- package/src/report/react/styles.css +405 -213
- package/src/report/report.test.ts +403 -94
- package/src/report/report.ts +29 -11
- package/src/report/text/faces.ts +303 -95
- 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 +196 -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 +97 -15
- 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 +69 -28
- 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 +125 -94
- package/src/show/render.ts +660 -160
- package/src/show/show.test.ts +472 -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/docs-site/zh/guides/sandbox-backends.mdx +0 -75
- 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,228 @@
|
|
|
1
|
+
// 报告的元素树与双面组件基座(docs/feature/reports/architecture.md「报告树与两个宿主」)。
|
|
2
|
+
//
|
|
3
|
+
// 报告函数返回的树不是「React 树」,只是 { type, props } 节点 —— 标准 react
|
|
4
|
+
// jsx-runtime 产的元素恰好就是这个形状。本文件是基础实现:零 react 运行时依赖
|
|
5
|
+
// (只有类型层的 `import type`,编译后擦除);text 宿主遍历渲染不需要 react-dom,
|
|
6
|
+
// web 宿主(web.ts)才真正 import react。渲染面是纯同步函数:零 IO、零 await ——
|
|
7
|
+
// 计算全部发生在报告函数体里,可达百 MB 的 artifact 永远不进渲染路径。
|
|
8
|
+
import { DEFAULT_REPORT_LOCALE } from "./locale.js";
|
|
9
|
+
// react/jsx-runtime 的 Fragment 是注册符号,跨 react 版本稳定;不 import react 也认得它
|
|
10
|
+
const REACT_FRAGMENT = Symbol.for("react.fragment");
|
|
11
|
+
function isReportElement(node) {
|
|
12
|
+
return (typeof node === "object" &&
|
|
13
|
+
node !== null &&
|
|
14
|
+
!Array.isArray(node) &&
|
|
15
|
+
"type" in node &&
|
|
16
|
+
"props" in node &&
|
|
17
|
+
typeof node.props === "object");
|
|
18
|
+
}
|
|
19
|
+
// ───────────────────────── 双面组件 ─────────────────────────
|
|
20
|
+
/** 挂 faces 的私有键:text 宿主与树校验靠它识别双面组件。 */
|
|
21
|
+
export const COMPONENT_FACES = Symbol.for("niceeval.report.faces");
|
|
22
|
+
// web 面的环境上下文:web 宿主渲染前设好;宿主之外(组件直接嵌进用户 React 应用)
|
|
23
|
+
// 用默认值 —— attemptHref 默认 view 的 attempt 路由格式(自定义组件显式调 ctx.attemptHref
|
|
24
|
+
// 时总有去处);官方组件的「宿主里自动接证据室」只在宿主上下文激活时发生,
|
|
25
|
+
// 宿主外不传 attemptHref 就是纯展示,不发明断链。
|
|
26
|
+
//
|
|
27
|
+
// URL 格式取 `#/attempt/${locator}`:AttemptLocator 本身已经是 `@` 前缀的不透明短串
|
|
28
|
+
// (如 "@1x7f3q9"),原样嵌进路径段就得到 `#/attempt/@1x7f3q9`——与 docs/feature/reports/view.md「用
|
|
29
|
+
// Reports 积木重建 view」定稿的单段路由逐字一致,不额外拆分或去掉 `@`。
|
|
30
|
+
const DEFAULT_WEB_CONTEXT = {
|
|
31
|
+
attemptHref: (locator) => `#/attempt/${locator}`,
|
|
32
|
+
locale: DEFAULT_REPORT_LOCALE,
|
|
33
|
+
};
|
|
34
|
+
let activeWebContext = null;
|
|
35
|
+
/** web 宿主用:在给定 WebContext 下同步渲染(React 静态渲染本身是同步的)。 */
|
|
36
|
+
export function runWithWebContext(ctx, fn) {
|
|
37
|
+
const prev = activeWebContext;
|
|
38
|
+
activeWebContext = ctx;
|
|
39
|
+
try {
|
|
40
|
+
return fn();
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
activeWebContext = prev;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** 官方组件的装配用:宿主上下文激活时才把 ctx.attemptHref 当默认下钻。 */
|
|
47
|
+
export function isHostWebContextActive() {
|
|
48
|
+
return activeWebContext !== null;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* 定义一个双面组件:faces 两键必填 —— 少实现一个面编译不过,配对是结构义务。
|
|
52
|
+
* 基础实现不 import react;产物以可调用组件的形状兼容 React 渲染。
|
|
53
|
+
*/
|
|
54
|
+
export function defineComponent(faces) {
|
|
55
|
+
if (typeof faces?.web !== "function" || typeof faces?.text !== "function") {
|
|
56
|
+
throw new Error("defineComponent requires both faces: { web(props, ctx), text(props, ctx) }. " +
|
|
57
|
+
"Every report component must render in both hosts (niceeval view and niceeval show).");
|
|
58
|
+
}
|
|
59
|
+
// 直接调用路径:把组件当普通 React 组件嵌进用户自己的页面时走这里,web 面只接收数据形态
|
|
60
|
+
// props(R)。带 resolve 的组件若拿 selection 形态 props 走这条裸路径,web 面会缺 data ——
|
|
61
|
+
// 这类组件只有经宿主的 resolveReportTree 解析后才安全渲染;纯数据 props 一直可以裸嵌。
|
|
62
|
+
const component = ((props) => faces.web(props, activeWebContext ?? DEFAULT_WEB_CONTEXT));
|
|
63
|
+
component[COMPONENT_FACES] = faces;
|
|
64
|
+
return component;
|
|
65
|
+
}
|
|
66
|
+
export function facesOf(type) {
|
|
67
|
+
if (typeof type !== "function")
|
|
68
|
+
return undefined;
|
|
69
|
+
return type[COMPONENT_FACES];
|
|
70
|
+
}
|
|
71
|
+
// ───────────────────────── 数据解析(渲染前唯一的 await 边界)─────────────────────────
|
|
72
|
+
/**
|
|
73
|
+
* 报告 build 之后、树校验与 text/web 渲染之前的解析遍历:把声明式数据组件(实现了
|
|
74
|
+
* `faces.resolve` 的双面组件,如 selection 形态的 MetricScatter)的 props
|
|
75
|
+
* 就地换成算好的数据形态 props(三个实体列表没有 `resolve` 面,不经这一步——它们的 `items`
|
|
76
|
+
* 由报告作者在 `build()` 里直接 `await .data(selection)` 备好)。计算发生在这里(唯一允许
|
|
77
|
+
* await 的组件级步骤,连同报告函数体自己的 `build()`),两个渲染面
|
|
78
|
+
* 之后都只看已解析的树、保持同步零 IO。遍历形状与 {@link validateReportTree} /
|
|
79
|
+
* {@link renderNodeToText} 一致:
|
|
80
|
+
*
|
|
81
|
+
* - 同层数组兄弟并行解析(`Promise.all`),保持原始顺序 / keys;
|
|
82
|
+
* - 双面组件带 resolve 的:调 `faces.resolve(props)` 换 props(有 children 再递归解析);
|
|
83
|
+
* - 双面组件无 resolve 的(Row / Col / Section / RunOverview…):只递归 children,自身 props
|
|
84
|
+
* 原样保留(title / className 等不能动);
|
|
85
|
+
* - 普通函数组件:同步调用展开,展开结果继续解析(与 validate / render 对函数组件的处理一致);
|
|
86
|
+
* - 字符串 intrinsic(<div>):原样返回,交给随后的 validateReportTree 报同一条错误。
|
|
87
|
+
*
|
|
88
|
+
* resolver 跑完后,树里已没有函数组件 / 未解析 props;validate / render 里对这两者的分支在
|
|
89
|
+
* 正常管线下是 no-op,但保留着——手搭树直接调 validate / render(不过 resolver)的低层用法仍需要。
|
|
90
|
+
*/
|
|
91
|
+
export async function resolveReportTree(node) {
|
|
92
|
+
if (node === null || node === undefined || typeof node === "boolean")
|
|
93
|
+
return node;
|
|
94
|
+
if (typeof node === "string" || typeof node === "number")
|
|
95
|
+
return node;
|
|
96
|
+
if (Array.isArray(node))
|
|
97
|
+
return Promise.all(node.map(resolveReportTree));
|
|
98
|
+
if (!isReportElement(node))
|
|
99
|
+
return node;
|
|
100
|
+
const { type, props } = node;
|
|
101
|
+
// 字符串 intrinsic:不在这里报错,让 resolve 后的 validateReportTree 抛统一的那条。
|
|
102
|
+
if (typeof type === "string")
|
|
103
|
+
return node;
|
|
104
|
+
if (type === REACT_FRAGMENT) {
|
|
105
|
+
const children = await resolveReportTree(props.children);
|
|
106
|
+
return { ...node, props: { ...props, children } };
|
|
107
|
+
}
|
|
108
|
+
const faces = facesOf(type);
|
|
109
|
+
if (faces) {
|
|
110
|
+
if (typeof faces.resolve === "function") {
|
|
111
|
+
const resolvedProps = { ...(await faces.resolve(props)) };
|
|
112
|
+
if (resolvedProps.children !== undefined) {
|
|
113
|
+
resolvedProps.children = await resolveReportTree(resolvedProps.children);
|
|
114
|
+
}
|
|
115
|
+
return { ...node, props: resolvedProps };
|
|
116
|
+
}
|
|
117
|
+
// 无 resolve 的容器 / 数据组件:只解析 children,自身 props 原样保留。
|
|
118
|
+
const children = await resolveReportTree(props.children);
|
|
119
|
+
return { ...node, props: { ...props, children } };
|
|
120
|
+
}
|
|
121
|
+
if (typeof type === "function") {
|
|
122
|
+
// 普通函数组件:调用展开,展开结果整体替换本节点后继续解析。
|
|
123
|
+
const expanded = type(props);
|
|
124
|
+
return resolveReportTree(expanded);
|
|
125
|
+
}
|
|
126
|
+
return node;
|
|
127
|
+
}
|
|
128
|
+
// ───────────────────────── 树校验 ─────────────────────────
|
|
129
|
+
function componentLabel(type) {
|
|
130
|
+
if (typeof type === "string")
|
|
131
|
+
return `<${type}>`;
|
|
132
|
+
if (typeof type === "function") {
|
|
133
|
+
const name = type.displayName || type.name;
|
|
134
|
+
return name ? `<${name}>` : "<anonymous component>";
|
|
135
|
+
}
|
|
136
|
+
if (type === REACT_FRAGMENT)
|
|
137
|
+
return "<>";
|
|
138
|
+
return `<${String(type)}>`;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* 渲染前树校验:页面树里只放双面组件、排版原语与普通组合函数,字符串 intrinsic
|
|
142
|
+
* (<div>)报错、指名组件路径。这是运行时校验而非编译期(标准 JSX 下 TS 把一切
|
|
143
|
+
* JSX 表达式统一成 JSX.Element);两个宿主渲染前跑同一遍 —— 不做单侧宽容,否则
|
|
144
|
+
* 对着 view 写的页面到 show 才炸。校验只下钻 children(children 就是报告树);
|
|
145
|
+
* 普通函数组件调用展开(渲染面纯同步,重复调用无副作用)。
|
|
146
|
+
*/
|
|
147
|
+
export function validateReportTree(node, path = []) {
|
|
148
|
+
if (node === null || node === undefined || typeof node === "boolean")
|
|
149
|
+
return;
|
|
150
|
+
if (typeof node === "string" || typeof node === "number")
|
|
151
|
+
return;
|
|
152
|
+
if (Array.isArray(node)) {
|
|
153
|
+
for (const child of node)
|
|
154
|
+
validateReportTree(child, path);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (!isReportElement(node))
|
|
158
|
+
return;
|
|
159
|
+
const { type, props } = node;
|
|
160
|
+
if (typeof type === "string") {
|
|
161
|
+
const where = path.length > 0 ? ` (in ${path.join(" > ")})` : "";
|
|
162
|
+
throw new Error(`Raw HTML <${type}> has no terminal face; use <Text>, layout primitives, or a defineComponent component.${where}`);
|
|
163
|
+
}
|
|
164
|
+
if (type === REACT_FRAGMENT) {
|
|
165
|
+
validateReportTree(props.children, path);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
const label = componentLabel(type);
|
|
169
|
+
if (facesOf(type)) {
|
|
170
|
+
// 双面组件是校验的信任边界之内的叶子,但 children 仍是报告树的一部分
|
|
171
|
+
validateReportTree(props.children, [...path, label]);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
if (typeof type === "function") {
|
|
175
|
+
// 普通函数组件 = 用户拿函数组合页面片段:调用展开继续校验
|
|
176
|
+
const expanded = type(props);
|
|
177
|
+
validateReportTree(expanded, [...path, label]);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
const where = path.length > 0 ? ` (in ${path.join(" > ")})` : "";
|
|
181
|
+
throw new Error(`Unsupported node type ${label} in report tree.${where}`);
|
|
182
|
+
}
|
|
183
|
+
export function createTextContext(options) {
|
|
184
|
+
const width = Math.max(20, options?.width ?? 80);
|
|
185
|
+
const locale = options?.locale ?? DEFAULT_REPORT_LOCALE;
|
|
186
|
+
// 默认下钻命令:AttemptLocator 是 `@` 前缀的不透明短串,`niceeval show @<locator>` 是
|
|
187
|
+
// show/index.ts 已实现的真实 CLI 语法(见该文件 `@<locator>` 位置参数解析),不需要
|
|
188
|
+
// 反查 eval id 再拼一条近似命令。
|
|
189
|
+
const attemptCommand = options?.attemptCommand ?? ((locator) => `niceeval show ${locator}`);
|
|
190
|
+
const make = (w) => ({
|
|
191
|
+
width: w,
|
|
192
|
+
locale,
|
|
193
|
+
attemptCommand,
|
|
194
|
+
render(node, childWidth) {
|
|
195
|
+
return renderNodeToText(node, childWidth === undefined ? this : make(Math.max(10, childWidth)));
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
return make(width);
|
|
199
|
+
}
|
|
200
|
+
/** text 宿主的遍历渲染:双面组件走 text 面,普通函数调用展开,块之间以换行相接。 */
|
|
201
|
+
export function renderNodeToText(node, ctx) {
|
|
202
|
+
if (node === null || node === undefined || typeof node === "boolean")
|
|
203
|
+
return "";
|
|
204
|
+
if (typeof node === "string" || typeof node === "number")
|
|
205
|
+
return String(node);
|
|
206
|
+
if (Array.isArray(node)) {
|
|
207
|
+
return node
|
|
208
|
+
.map((child) => renderNodeToText(child, ctx))
|
|
209
|
+
.filter((text) => text.length > 0)
|
|
210
|
+
.join("\n");
|
|
211
|
+
}
|
|
212
|
+
if (!isReportElement(node))
|
|
213
|
+
return "";
|
|
214
|
+
const { type, props } = node;
|
|
215
|
+
if (typeof type === "string") {
|
|
216
|
+
// 校验先行会拦住;这里兜底同一条错误,渲染路径自身也不宽容
|
|
217
|
+
throw new Error(`Raw HTML <${type}> has no terminal face; use <Text>, layout primitives, or a defineComponent component.`);
|
|
218
|
+
}
|
|
219
|
+
if (type === REACT_FRAGMENT)
|
|
220
|
+
return renderNodeToText(props.children, ctx);
|
|
221
|
+
const faces = facesOf(type);
|
|
222
|
+
if (faces)
|
|
223
|
+
return faces.text(props, ctx);
|
|
224
|
+
if (typeof type === "function") {
|
|
225
|
+
return renderNodeToText(type(props), ctx);
|
|
226
|
+
}
|
|
227
|
+
return "";
|
|
228
|
+
}
|
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
import type { AttemptHandle, SelectionWarning } from "../results/types.ts";
|
|
2
|
+
import type { AttemptLocator } from "../results/locator.ts";
|
|
3
|
+
import type { AttemptEvidenceCapabilities } from "../results/attempt-evidence.ts";
|
|
4
|
+
import type { AssertionResult, Verdict } from "../types.ts";
|
|
5
|
+
import type { LocalizedLabel, ReportLocale } from "./locale.ts";
|
|
6
|
+
export type { SelectionWarning };
|
|
7
|
+
export type { AttemptLocator, AttemptEvidenceCapabilities };
|
|
8
|
+
export type { LocalizedLabel, ReportLocale };
|
|
9
|
+
/** 两级聚合里单级的折叠方式。 */
|
|
10
|
+
export type Aggregator = "mean" | "sum" | "min" | "max" | ((values: number[]) => number);
|
|
11
|
+
/**
|
|
12
|
+
* 两级聚合:「每格 attempt 数相等」是幻觉(earlyExit 让失败的题天然比通过的题样本多),
|
|
13
|
+
* 平铺求均值会让分数和重试策略纠缠;所以先题内折叠、再跨题折叠,默认宏平均。
|
|
14
|
+
*/
|
|
15
|
+
export interface MetricAggregate {
|
|
16
|
+
/** 第一级:同一 (eval × 快照) 的多 attempt → 一个题级值;默认 "mean"。 */
|
|
17
|
+
perEval?: Aggregator;
|
|
18
|
+
/** 第二级:分组内的题级值 → 格子终值;默认 "mean"。 */
|
|
19
|
+
across?: Aggregator;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* 指标:纯函数,吃一个 AttemptHandle 吐一个值(null = 此 attempt 测不了这个指标,
|
|
23
|
+
* 不进聚合;0 = 测了结果是零,照常进),外加名字、两级聚合方式和渲染提示。
|
|
24
|
+
* 内置指标与自定义指标是同一个类型,没有特权。name 走字面量泛型:列键锚在指标
|
|
25
|
+
* 对象上(`row.cells[passRate.name]`),拼错列名编译不过。
|
|
26
|
+
*/
|
|
27
|
+
export interface Metric<Name extends string = string> {
|
|
28
|
+
/** MetricColumn.key 与列头的来源;同一次计算里重名是错误。 */
|
|
29
|
+
name: Name;
|
|
30
|
+
/**
|
|
31
|
+
* 列头;省略时用 name。可以给按 locale 的字典({ en, "zh-CN" }),
|
|
32
|
+
* 渲染面按宿主 locale 解析,缺项回退 en(display 是 format 产物,不本地化)。
|
|
33
|
+
*/
|
|
34
|
+
label?: LocalizedLabel;
|
|
35
|
+
description?: string;
|
|
36
|
+
/** 渲染提示:越高越好还是越低越好(排序方向、轴向、涨跌配色用)。 */
|
|
37
|
+
better?: "higher" | "lower";
|
|
38
|
+
/** 驱动内置格式化:"%" → 87%、"ms" → 1.2s、"$" → $0.31、其余 → 1.2k 缩写。 */
|
|
39
|
+
unit?: string;
|
|
40
|
+
/**
|
|
41
|
+
* 声明式前置:不满足 → null,语义等价于在 value 开头 return null。
|
|
42
|
+
* 单独设字段是因为这一步最容易忘(忘了它,code-golf 会奖励「写得短的坏代码」)。
|
|
43
|
+
*/
|
|
44
|
+
where?: (attempt: AttemptHandle) => boolean;
|
|
45
|
+
value(attempt: AttemptHandle): number | null | Promise<number | null>;
|
|
46
|
+
aggregate?: MetricAggregate;
|
|
47
|
+
/** 覆盖 unit 驱动的内置格式化。 */
|
|
48
|
+
display?: (value: number) => string;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* 维度:attempt 分到哪一组。内置维度就是结果已有的身份字段;自定义维度是一个函数。
|
|
52
|
+
* - "evalGroup" = eval id 的第一段:"algebra/quadratic" → "algebra"(考试里的「科目」)
|
|
53
|
+
* - "snapshot" = "<experimentId> @ <startedAt>",把两次快照并排成行,与 view 的 Compare 同口径
|
|
54
|
+
*/
|
|
55
|
+
export type Dimension = "agent" | "model" | "experiment" | "eval" | "evalGroup" | "snapshot" | {
|
|
56
|
+
name: string;
|
|
57
|
+
of: (attempt: AttemptHandle) => string;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* flag() 的产物:把 experiment 声明的 flags 当维度(series / rows / columns / points
|
|
61
|
+
* 槽,按声明值分组)或轴(MetricLine 的 x 槽,要求数值并驱动刻度)。
|
|
62
|
+
* 未声明该 flag 的 experiment 不猜:分组如实归「(unset)」,作轴不画点、注脚报数。
|
|
63
|
+
*/
|
|
64
|
+
export interface FlagRef {
|
|
65
|
+
readonly kind: "flag";
|
|
66
|
+
readonly name: string;
|
|
67
|
+
/** 组标签 / 轴标签;函数形态把声明值折成组名(如 `(v) => \`${v} agents\``)。 */
|
|
68
|
+
readonly label?: string | ((value: string | number | boolean) => string);
|
|
69
|
+
readonly unit?: string;
|
|
70
|
+
}
|
|
71
|
+
/** 维度槽的输入:内置/自定义维度,或 experiment 声明的 flag。 */
|
|
72
|
+
export type DimensionInput = Dimension | FlagRef;
|
|
73
|
+
export interface MetricColumn {
|
|
74
|
+
/** = metric.name,与 cells 的键对应。 */
|
|
75
|
+
key: string;
|
|
76
|
+
/** 数据层原样携带 metric.label(可本地化);渲染面用 resolveMetricLabel 按 locale 解析。 */
|
|
77
|
+
label: LocalizedLabel;
|
|
78
|
+
unit?: string;
|
|
79
|
+
/** 渲染提示:排序方向、轴向、涨跌配色。 */
|
|
80
|
+
better?: "higher" | "lower";
|
|
81
|
+
}
|
|
82
|
+
export interface MetricCell {
|
|
83
|
+
/** 聚合后的值;null = 该组没有任何有效样本。 */
|
|
84
|
+
value: number | null;
|
|
85
|
+
/** 已格式化("87%" / "1.2k lines" / "$0.31"),前端可直接渲染。 */
|
|
86
|
+
display: string;
|
|
87
|
+
/** 有效 attempt 数(值为 null 的不计入)。 */
|
|
88
|
+
samples: number;
|
|
89
|
+
/** 组内 attempt 总数;samples < total = 有 attempt 测不了这个指标。 */
|
|
90
|
+
total: number;
|
|
91
|
+
/**
|
|
92
|
+
* 这个格子由哪些 attempt 算出 —— 回到证据的引用。必填(可空数组):
|
|
93
|
+
* 「每个数字点进去就是证据」是页面的核心承诺,可选字段会让深链静默缺失。
|
|
94
|
+
*/
|
|
95
|
+
refs: AttemptLocator[];
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* 榜单行的元信息:rows: "experiment" 时随行(experiment 行天然有唯一的 agent/model 身份、
|
|
99
|
+
* eval 级折叠计票与「这行覆盖了多少题/多少次尝试/最近何时跑的」);其它维度不携带。
|
|
100
|
+
* web / text 面在 meta 在场时补 Model / Agent / Verdicts 列,`evals`/`attempts`/
|
|
101
|
+
* `lastRunAt` 则渲染成行键下的一行紧凑摘要——与 view 原生榜单同一份信息密度。
|
|
102
|
+
* `MetricTable` 只表达维度 × 指标,没有实体下钻——要展开到 experiment 的 Eval 或
|
|
103
|
+
* Eval 的 Attempt,用 `ExperimentList` / `EvalList`,这里不再有 `subRows`。
|
|
104
|
+
*/
|
|
105
|
+
export interface TableRowMeta<K extends string = string> {
|
|
106
|
+
agent?: string;
|
|
107
|
+
model?: string;
|
|
108
|
+
/** eval 级折叠计票(foldEvalVerdict 口径,与 view 榜单同一套):每题折成单一判定后计数。 */
|
|
109
|
+
verdicts?: {
|
|
110
|
+
passed: number;
|
|
111
|
+
failed: number;
|
|
112
|
+
errored: number;
|
|
113
|
+
skipped: number;
|
|
114
|
+
};
|
|
115
|
+
/**
|
|
116
|
+
* `rows: "experiment"` 专属:这一行覆盖的 eval 数(去重后,summarizeItems 口径,与
|
|
117
|
+
* `verdicts` 四项之和一致)。其它行维度(agent/eval/自定义…)没有「这一行是几道题」的
|
|
118
|
+
* 独立语义(题本身就是行),不携带这个字段。
|
|
119
|
+
*/
|
|
120
|
+
evals?: number;
|
|
121
|
+
/**
|
|
122
|
+
* `rows: "experiment"` 专属:这一行覆盖的 attempt 总数(原始计数,含多轮重试)。
|
|
123
|
+
* 大于 `evals` 说明存在多轮重试(early-exit 复测 / flaky 重跑);等于 `evals` 说明
|
|
124
|
+
* 每题只跑了一轮。同上,只在 `rows: "experiment"` 时语义成立。
|
|
125
|
+
*/
|
|
126
|
+
attempts?: number;
|
|
127
|
+
/**
|
|
128
|
+
* `rows: "experiment"` 专属:这一行覆盖范围内快照 `startedAt` 的最大值(最近一次运行
|
|
129
|
+
* 时间,ISO 8601,字符串比较即可比大小)。组内没有任何 item 时缺席。
|
|
130
|
+
*/
|
|
131
|
+
lastRunAt?: string;
|
|
132
|
+
}
|
|
133
|
+
/** 列键 K 来自 columns 元组的字面量 name:拼错列名编译不过,不是运行时 undefined。 */
|
|
134
|
+
export interface TableData<K extends string = string> {
|
|
135
|
+
/** 行维度名,如 "agent"。 */
|
|
136
|
+
dimension: string;
|
|
137
|
+
columns: MetricColumn[];
|
|
138
|
+
rows: {
|
|
139
|
+
key: string;
|
|
140
|
+
cells: Record<K, MetricCell>;
|
|
141
|
+
meta?: TableRowMeta<K>;
|
|
142
|
+
}[];
|
|
143
|
+
}
|
|
144
|
+
export interface MatrixData {
|
|
145
|
+
/** 行维度名,如 "eval"。 */
|
|
146
|
+
rows: string;
|
|
147
|
+
/** 列维度名,如 "agent"。 */
|
|
148
|
+
columns: string;
|
|
149
|
+
metric: MetricColumn;
|
|
150
|
+
/** 稀疏:没有样本的格子不出现。 */
|
|
151
|
+
cells: {
|
|
152
|
+
row: string;
|
|
153
|
+
column: string;
|
|
154
|
+
cell: MetricCell;
|
|
155
|
+
}[];
|
|
156
|
+
}
|
|
157
|
+
export interface ScoreboardData {
|
|
158
|
+
/**
|
|
159
|
+
* 被打分的维度名,如 "agent"。
|
|
160
|
+
* (计算函数的维度槽叫 rows,与 MetricTable.data 统一;数据形状上行数组已占用
|
|
161
|
+
* rows 一词,维度名沿用 TableData 的 dimension。)
|
|
162
|
+
*/
|
|
163
|
+
dimension: string;
|
|
164
|
+
fullMarks: number;
|
|
165
|
+
/** 实际生效的权重表(按匹配顺序:最长前缀在前)—— 成绩单可审计。 */
|
|
166
|
+
weights: {
|
|
167
|
+
prefix: string;
|
|
168
|
+
weight: number;
|
|
169
|
+
}[];
|
|
170
|
+
rows: {
|
|
171
|
+
key: string;
|
|
172
|
+
/** 已折算到 fullMarks。 */
|
|
173
|
+
total: {
|
|
174
|
+
value: number;
|
|
175
|
+
display: string;
|
|
176
|
+
};
|
|
177
|
+
subjects: {
|
|
178
|
+
/** 科目(subjects 维度的值)。 */
|
|
179
|
+
key: string;
|
|
180
|
+
/** 加权得分。 */
|
|
181
|
+
earned: number;
|
|
182
|
+
/** 科目分值合计。 */
|
|
183
|
+
possible: number;
|
|
184
|
+
/** 题数。 */
|
|
185
|
+
evals: number;
|
|
186
|
+
/** 无任何样本、按 0 计的题数 —— 固定分母的如实注脚。 */
|
|
187
|
+
missing: number;
|
|
188
|
+
}[];
|
|
189
|
+
}[];
|
|
190
|
+
}
|
|
191
|
+
export interface ScatterData {
|
|
192
|
+
/** 点维度名,如 "experiment"。 */
|
|
193
|
+
points: string;
|
|
194
|
+
/** 系列维度名,如 "agent"。 */
|
|
195
|
+
series?: string;
|
|
196
|
+
/** better: "lower" → 组件反向画轴,「好」的角落恒在右上。 */
|
|
197
|
+
x: MetricColumn;
|
|
198
|
+
y: MetricColumn;
|
|
199
|
+
rows: {
|
|
200
|
+
/** 点的键,如 "compare/bub-high"。 */
|
|
201
|
+
key: string;
|
|
202
|
+
/** 所属系列,如 "bub"。 */
|
|
203
|
+
series?: string;
|
|
204
|
+
x: MetricCell;
|
|
205
|
+
/** 任一为 null 的点组件不画,注脚如实报数(点仍留在 rows 里,可数)。 */
|
|
206
|
+
y: MetricCell;
|
|
207
|
+
}[];
|
|
208
|
+
}
|
|
209
|
+
/** MetricLine 的 x 轴:experiment 声明的 flag,数值驱动刻度。 */
|
|
210
|
+
export interface LineAxis {
|
|
211
|
+
/** flag 名。 */
|
|
212
|
+
key: string;
|
|
213
|
+
label: string;
|
|
214
|
+
unit?: string;
|
|
215
|
+
}
|
|
216
|
+
export interface LineData {
|
|
217
|
+
x: LineAxis;
|
|
218
|
+
/** 系列维度名(flag 或普通维度)。 */
|
|
219
|
+
series?: string;
|
|
220
|
+
y: MetricColumn;
|
|
221
|
+
rows: {
|
|
222
|
+
/** 点的键(experiment id):每个点 = 一个 experiment 的聚合。 */
|
|
223
|
+
key: string;
|
|
224
|
+
series?: string;
|
|
225
|
+
/** flag 声明值;未声明或非数值 → null,点不画、注脚报数。 */
|
|
226
|
+
x: number | null;
|
|
227
|
+
/** 已格式化的 x("300 ms");x 为 null 时为空串。 */
|
|
228
|
+
xDisplay: string;
|
|
229
|
+
y: MetricCell;
|
|
230
|
+
}[];
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* 一组 experiment(如自定义报告里同一 `<Section>` 内的全部 experiment)的摘要:
|
|
234
|
+
* experiment/eval/attempt 数量、eval 级折叠计票、通过率、总成本、最后运行时间——
|
|
235
|
+
* 恢复旧 `GroupSelector` 卡片曾展示的信息密度,但通过率是官方 `MetricCell` 形态,
|
|
236
|
+
* 不是裸数字,渲染面不用另外拼格式。
|
|
237
|
+
*/
|
|
238
|
+
export interface GroupSummaryData {
|
|
239
|
+
/** 组内 experiment 数(去重后的 experimentId 个数)。 */
|
|
240
|
+
experiments: number;
|
|
241
|
+
/**
|
|
242
|
+
* 组内 eval 数,按完整身份键(experimentId + eval id)去重——多 experiment 的组里
|
|
243
|
+
* 两个 experiment 各自的同名 eval(如都叫 "algebra/a")算两道题,不会被误合并成一道。
|
|
244
|
+
*/
|
|
245
|
+
evals: number;
|
|
246
|
+
/** 组内 attempt 总数(原始计数,一轮 attempt 一票,不折叠)。 */
|
|
247
|
+
attempts: number;
|
|
248
|
+
/**
|
|
249
|
+
* eval 级折叠计票:同一 eval 的多轮 attempt 先折成一个判定(`foldEvalVerdict`,任一轮
|
|
250
|
+
* 通过则通过,否则取最严重的),再计数——与 `TableRowMeta.verdicts`、view 榜单同一口径,
|
|
251
|
+
* 不是 attempt 原始票数的直接计票。
|
|
252
|
+
*/
|
|
253
|
+
verdicts: {
|
|
254
|
+
passed: number;
|
|
255
|
+
failed: number;
|
|
256
|
+
errored: number;
|
|
257
|
+
skipped: number;
|
|
258
|
+
};
|
|
259
|
+
/**
|
|
260
|
+
* 组的通过率:eval 级折叠计票的 `passed / (passed + failed + errored)`(`skipped` 不进
|
|
261
|
+
* 分母)——这是旧 `GroupSelector` 卡片的口径,不是 `OverviewData.totals.passRate` 那种
|
|
262
|
+
* `computeCell` 两级聚合(两者服务不同问题:「这组题多少算过」vs「整体质量几分」)。
|
|
263
|
+
* 分母为 0(组内没有任何已跑的 eval)时 `value` 为 `null`,不编 0%。
|
|
264
|
+
*/
|
|
265
|
+
passRate: MetricCell;
|
|
266
|
+
/** 组内可测成本(`attemptCostUSD`)求和;一次 attempt 都没报成本 = `null`,不编 `0`。 */
|
|
267
|
+
totalCostUSD: number | null;
|
|
268
|
+
/** 组内快照 `startedAt` 的最大值(字符串比较,ISO 8601 天然可比);组内没有任何 item 时缺席。 */
|
|
269
|
+
lastRunAt?: string;
|
|
270
|
+
}
|
|
271
|
+
export interface OverviewData {
|
|
272
|
+
snapshots: {
|
|
273
|
+
experimentId: string;
|
|
274
|
+
agent: string;
|
|
275
|
+
model?: string;
|
|
276
|
+
startedAt: string;
|
|
277
|
+
}[];
|
|
278
|
+
totals: {
|
|
279
|
+
evals: number;
|
|
280
|
+
attempts: number;
|
|
281
|
+
/**
|
|
282
|
+
* 四个 attempt 原始判定计票(一个 attempt 一票),独立于 `passRate`:驱动页头的
|
|
283
|
+
* 判定计数展示,不是通过率公式的输入——不要从这四个数现场重算百分比。
|
|
284
|
+
*/
|
|
285
|
+
passed: number;
|
|
286
|
+
failed: number;
|
|
287
|
+
errored: number;
|
|
288
|
+
skipped: number;
|
|
289
|
+
/**
|
|
290
|
+
* 通过率的唯一官方口径:`computeCell(passRate, items)`,与 `MetricTable.data(...,
|
|
291
|
+
* columns: [passRate])` 同一台两级聚合引擎(题内折叠 perEval、跨题折叠 across,默认都是
|
|
292
|
+
* mean)——一道题内多个 attempt 部分通过,贡献的是小数份额而不是二元票。`samples`/`total`
|
|
293
|
+
* 是两级聚合口径下的 attempt 计数(`total` 含 skipped,`samples` 不含),不等于上面四个
|
|
294
|
+
* verdict 计票的任何一个之和。
|
|
295
|
+
*/
|
|
296
|
+
passRate: MetricCell;
|
|
297
|
+
/** 任一 attempt 报了成本才有;全缺 = null,不编 0。 */
|
|
298
|
+
costUSD: number | null;
|
|
299
|
+
durationMs: number;
|
|
300
|
+
};
|
|
301
|
+
/** Selection 的警告随行(结构化,含渲染好的 message),RunOverview 直接渲染。 */
|
|
302
|
+
warnings: SelectionWarning[];
|
|
303
|
+
}
|
|
304
|
+
export interface DeltaData<K extends string = string> {
|
|
305
|
+
columns: MetricColumn[];
|
|
306
|
+
rows: {
|
|
307
|
+
/** pair 的 label,如 "bub"。 */
|
|
308
|
+
key: string;
|
|
309
|
+
/** 基线侧:experiment id 或快照键 "<experimentId> @ <startedAt>"。 */
|
|
310
|
+
a: {
|
|
311
|
+
experimentId: string;
|
|
312
|
+
};
|
|
313
|
+
/** 对比侧。 */
|
|
314
|
+
b: {
|
|
315
|
+
experimentId: string;
|
|
316
|
+
};
|
|
317
|
+
cells: Record<K, {
|
|
318
|
+
a: MetricCell;
|
|
319
|
+
b: MetricCell;
|
|
320
|
+
/** b.value - a.value;任一侧 null → null,不硬算。 */
|
|
321
|
+
delta: number | null;
|
|
322
|
+
/** 已带符号("+12%" / "-$0.80" / "±0"),涨跌好坏由 better 判定。 */
|
|
323
|
+
display: string;
|
|
324
|
+
}>;
|
|
325
|
+
}[];
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* `AttemptList` 一项 = 一个 Attempt:身份、判定、断言、error、耗时、成本、locator,外加
|
|
329
|
+
* 证据能力标记(有没有保存的 Eval 源码 / 执行事件 / OTel 计时 / diff——与 `AttemptEvidence.capabilities`
|
|
330
|
+
* 同一个类型,不重新发明四个布尔位的含义)。`ExperimentList` / `EvalList` 的下钻数组复用同一个
|
|
331
|
+
* 类型,不是各自的精简版。
|
|
332
|
+
*/
|
|
333
|
+
export interface AttemptListItem {
|
|
334
|
+
evalId: string;
|
|
335
|
+
experimentId: string;
|
|
336
|
+
attempt: number;
|
|
337
|
+
agent: string;
|
|
338
|
+
verdict: Verdict;
|
|
339
|
+
error?: string;
|
|
340
|
+
assertions: AssertionResult[];
|
|
341
|
+
durationMs: number;
|
|
342
|
+
costUSD?: number;
|
|
343
|
+
locator: AttemptLocator;
|
|
344
|
+
/**
|
|
345
|
+
* 证据能力标记:`eval`(运行时 Eval 源码已保存)、`execution`(执行事件非空)、
|
|
346
|
+
* `timing`(执行事件 + 这次运行接入过 OTel)、`diff`(工作区有文件改动)。逐位定义与
|
|
347
|
+
* `AttemptEvidence.capabilities` 完全一致——这里复用同一个类型,不重复声明四个布尔位的门槛。
|
|
348
|
+
* 计算函数从 `EvalResult` 上已有的 `hasEvents` / `hasSources` / `hasTrace` 摘要位与一次
|
|
349
|
+
* `attempt.diff()` 懒加载算出,不对每个 item 调用完整的 `loadAttemptEvidence`
|
|
350
|
+
* (那还会额外装配 Eval 源码标注与 ExecutionTree,这里只要四个布尔位)。
|
|
351
|
+
*/
|
|
352
|
+
capabilities: AttemptEvidenceCapabilities;
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* `ExperimentList` 一项里,一个 Eval 的展开行:折叠判定(`foldEvalVerdict`)、失败原因摘要
|
|
356
|
+
* (`error` → `skipReason` → 未通过的 gate 断言,`reasonFor` 的口径,soft 断言永不进入)、
|
|
357
|
+
* 该 Eval 内 attempt 的平均耗时/成本(两级聚合引擎在单一 eval 上退化成组内均值),以及这道题
|
|
358
|
+
* 的全部 Attempt(升序,供进一步展开到 `AttemptList`)。
|
|
359
|
+
*/
|
|
360
|
+
export interface ExperimentListEvalRow {
|
|
361
|
+
evalId: string;
|
|
362
|
+
/** 折叠判定(任一 attempt 通过则通过,否则取最严重的)。 */
|
|
363
|
+
verdict: Verdict;
|
|
364
|
+
reason?: string;
|
|
365
|
+
/** 这道题内 attempt 的平均耗时(`computeCell(durationMs, …)`,单一 eval 分组下即均值)。 */
|
|
366
|
+
duration: MetricCell;
|
|
367
|
+
/** 这道题内 attempt 的平均成本。 */
|
|
368
|
+
cost: MetricCell;
|
|
369
|
+
/** 这道题的全部 Attempt,按 attempt 序号升序。 */
|
|
370
|
+
attempts: AttemptListItem[];
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* `ExperimentList.data(selection)` 的一项 = 一个 experiment:身份(experimentId/agent/model)、
|
|
374
|
+
* 声明的 flags、Eval 判定构成(`foldEvalVerdict` 计票,与 view 榜单同一口径)、官方两级聚合
|
|
375
|
+
* 汇总指标(passRate/cost/duration/tokens,直接来自 `computeCell`,不现场重算),以及展开到
|
|
376
|
+
* 这个 experiment 每道 Eval 的 `evalRows`(按 eval id 升序)。
|
|
377
|
+
*/
|
|
378
|
+
export interface ExperimentListItem {
|
|
379
|
+
experimentId: string;
|
|
380
|
+
agent: string;
|
|
381
|
+
model?: string;
|
|
382
|
+
flags?: Record<string, unknown>;
|
|
383
|
+
/** eval 级折叠计票(foldEvalVerdict 口径,与 `TableRowMeta.verdicts`、view 榜单同一套)。 */
|
|
384
|
+
verdicts: {
|
|
385
|
+
passed: number;
|
|
386
|
+
failed: number;
|
|
387
|
+
errored: number;
|
|
388
|
+
skipped: number;
|
|
389
|
+
};
|
|
390
|
+
/** 官方两级聚合口径,与 `MetricTable.data(..., columns: [passRate])` 同一台引擎。 */
|
|
391
|
+
passRate: MetricCell;
|
|
392
|
+
cost: MetricCell;
|
|
393
|
+
duration: MetricCell;
|
|
394
|
+
tokens: MetricCell;
|
|
395
|
+
/** 这个 experiment 覆盖的 eval 数(去重后,与 `verdicts` 四项之和一致)。 */
|
|
396
|
+
evals: number;
|
|
397
|
+
/** 这个 experiment 覆盖的 attempt 总数(原始计数,含多轮重试)。 */
|
|
398
|
+
attempts: number;
|
|
399
|
+
/** 所含快照中最近的 startedAt。 */
|
|
400
|
+
lastRunAt: string;
|
|
401
|
+
/** 展开到这个 experiment 的 Eval,按 eval id 升序。 */
|
|
402
|
+
evalRows: ExperimentListEvalRow[];
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* `EvalList.data(selection)` 的一项 = 一个 `experimentId + evalId`(同一个 Eval 跑在两个
|
|
406
|
+
* experiment 上是两条不同结果,不合并)。判定、分数(examScore 的两级聚合)、这道题内 attempt
|
|
407
|
+
* 的平均耗时/成本,失败原因摘要(与 `ExperimentListEvalRow.reason` 同一口径),外加展开到这道题
|
|
408
|
+
* 全部 Attempt 的 `attempts`(按 attempt 序号升序)。
|
|
409
|
+
*/
|
|
410
|
+
export interface EvalListItem {
|
|
411
|
+
evalId: string;
|
|
412
|
+
experimentId: string;
|
|
413
|
+
verdict: Verdict;
|
|
414
|
+
reason?: string;
|
|
415
|
+
/** examScore 的两级聚合;单一 eval 分组下即这道题的题级分数。 */
|
|
416
|
+
score: MetricCell;
|
|
417
|
+
duration: MetricCell;
|
|
418
|
+
cost: MetricCell;
|
|
419
|
+
attempts: AttemptListItem[];
|
|
420
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { AttemptLocator } from "../results/locator.ts";
|
|
2
|
+
import { type ReportLocale } from "./locale.ts";
|
|
3
|
+
import type { ReportContext, ReportDefinition } from "./report.ts";
|
|
4
|
+
export interface StaticHtmlOptions {
|
|
5
|
+
/** 证据室深链;缺省用 view 的 attempt 路由 `#/attempt/@<locator>`(单段、不透明)。 */
|
|
6
|
+
attemptHref?: (locator: AttemptLocator) => string;
|
|
7
|
+
/** 官方组件 chrome 文案的 locale;默认 "en"。 */
|
|
8
|
+
locale?: ReportLocale;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* build → 渲染前解析数据组件(唯一的 await 边界)→ 树校验(与 text 宿主同一遍)→ 静态渲染
|
|
12
|
+
* web 面;Selection 有挑选警告时在报告顶部前置一块警告 HTML;报告树里的 RunOverview
|
|
13
|
+
* 已经渲染同一条时不重复。
|
|
14
|
+
*/
|
|
15
|
+
export declare function renderReportToStaticHtml(definition: ReportDefinition, ctx: ReportContext, options?: StaticHtmlOptions): Promise<string>;
|