niceeval 0.6.1 → 0.7.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 +23 -23
- package/README.zh.md +6 -6
- package/dist/agents/types.d.ts +69 -7
- package/dist/context/types.d.ts +32 -12
- package/dist/i18n/en.d.ts +54 -0
- package/dist/i18n/zh-CN.d.ts +57 -3
- package/dist/o11y/types.d.ts +16 -2
- package/dist/report/aggregate.d.ts +32 -24
- package/dist/report/aggregate.js +158 -50
- package/dist/report/built-in/index.d.ts +2 -0
- package/dist/report/built-in/index.js +8 -0
- package/dist/report/components.d.ts +93 -160
- package/dist/report/components.js +377 -114
- package/dist/report/compute.d.ts +87 -81
- package/dist/report/compute.js +597 -417
- package/dist/report/flag.d.ts +32 -6
- package/dist/report/flag.js +92 -4
- package/dist/report/format.d.ts +19 -11
- package/dist/report/format.js +30 -13
- package/dist/report/index.d.ts +16 -16
- package/dist/report/index.js +20 -21
- package/dist/report/load.js +3 -2
- package/dist/report/locale.d.ts +57 -33
- package/dist/report/locale.js +122 -56
- package/dist/report/metrics.d.ts +23 -4
- package/dist/report/metrics.js +110 -25
- package/dist/report/primitives.d.ts +48 -15
- package/dist/report/primitives.js +135 -26
- package/dist/report/react/AttemptList.d.ts +9 -7
- package/dist/report/react/AttemptList.js +17 -10
- package/dist/report/react/DeltaTable.js +19 -18
- package/dist/report/react/EvalList.d.ts +4 -4
- package/dist/report/react/EvalList.js +0 -0
- package/dist/report/react/ExperimentComparison.d.ts +10 -0
- package/dist/report/react/ExperimentComparison.js +12 -0
- package/dist/report/react/ExperimentList.d.ts +4 -3
- package/dist/report/react/ExperimentList.js +17 -18
- package/dist/report/react/MetricBars.js +5 -4
- package/dist/report/react/MetricLine.js +12 -5
- package/dist/report/react/MetricMatrix.js +1 -1
- package/dist/report/react/MetricScatter.js +59 -28
- package/dist/report/react/MetricTable.js +2 -12
- package/dist/report/react/ScopeSummary.d.ts +10 -0
- package/dist/report/react/ScopeSummary.js +17 -0
- package/dist/report/react/Scoreboard.js +6 -6
- package/dist/report/react/cell.js +2 -2
- 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 +5 -9
- package/dist/report/react/fixtures.js +110 -147
- package/dist/report/react/index.d.ts +15 -5
- package/dist/report/react/index.js +18 -7
- package/dist/report/report.d.ts +137 -16
- package/dist/report/report.js +259 -28
- package/dist/report/text/faces.d.ts +17 -19
- package/dist/report/text/faces.js +253 -184
- package/dist/report/text/plot.js +1 -1
- package/dist/report/text/table.js +38 -7
- package/dist/report/tree.d.ts +90 -40
- package/dist/report/tree.js +252 -94
- package/dist/report/types.d.ts +247 -284
- package/dist/report/types.js +4 -3
- package/dist/report/web.d.ts +21 -5
- package/dist/report/web.js +42 -16
- package/dist/results/select.d.ts +38 -16
- package/dist/results/select.js +73 -25
- package/dist/results/types.d.ts +49 -14
- 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 +4 -2
- package/dist/shared/aggregate.js +8 -7
- 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/README.md +44 -0
- package/docs-site/zh/examples/ai-agent-application.mdx +63 -0
- package/docs-site/zh/examples/coding-agent-extensions.mdx +57 -0
- package/docs-site/zh/examples/index.mdx +50 -0
- package/docs-site/zh/{concepts → explanation}/adapter.mdx +31 -13
- package/docs-site/zh/{concepts → explanation}/assert.mdx +7 -7
- package/docs-site/zh/{concepts → explanation}/drive.mdx +8 -8
- package/docs-site/zh/{concepts → explanation}/evals.mdx +4 -4
- package/docs-site/zh/{concepts → explanation}/experiment.mdx +8 -8
- package/docs-site/zh/{concepts → explanation}/hitl.mdx +8 -8
- package/docs-site/zh/{concepts → explanation}/judge.mdx +5 -5
- package/docs-site/zh/{concepts → explanation}/overview.mdx +11 -11
- package/docs-site/zh/{guides → explanation}/runner.mdx +18 -8
- package/docs-site/zh/{concepts → explanation}/tier.mdx +6 -6
- package/docs-site/zh/{guides → how-to}/agent-feedback-loop.mdx +35 -33
- package/docs-site/zh/{guides → how-to}/authoring.mdx +35 -2
- package/docs-site/zh/{guides → how-to}/ci-integration.mdx +23 -12
- package/docs-site/zh/{guides → how-to}/connect-otel.mdx +6 -6
- package/docs-site/zh/{guides → how-to}/connect-your-agent.mdx +47 -21
- package/docs-site/zh/{guides → how-to}/custom-reports.mdx +34 -39
- package/docs-site/zh/{guides → how-to}/dataset-fanout.mdx +25 -3
- package/docs-site/zh/{guides → how-to}/experiments.mdx +12 -5
- package/docs-site/zh/how-to/publish-report.mdx +105 -0
- package/docs-site/zh/{guides → how-to}/reporters.mdx +2 -2
- package/docs-site/zh/{guides → how-to}/sandbox-agent.mdx +56 -7
- package/docs-site/zh/how-to/sandbox-providers.mdx +350 -0
- package/docs-site/zh/{guides → how-to}/scoring-guide.mdx +4 -4
- package/docs-site/zh/{guides → how-to}/viewing-results.mdx +82 -39
- package/docs-site/zh/{guides → how-to}/write-experiment.mdx +6 -4
- package/docs-site/zh/{guides → how-to}/write-send.mdx +30 -14
- package/docs-site/zh/index.mdx +24 -26
- package/docs-site/zh/introduction.mdx +8 -8
- package/docs-site/zh/reference/builtin-agents.mdx +32 -5
- package/docs-site/zh/reference/capabilities.mdx +8 -8
- package/docs-site/zh/reference/cli.mdx +40 -12
- package/docs-site/zh/reference/define-agent.mdx +58 -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 +3 -3
- package/docs-site/zh/reference/expect.mdx +26 -1
- package/docs-site/zh/{guides → reference}/official-adapters.mdx +32 -8
- package/docs-site/zh/{guides → reference}/report-components.mdx +45 -33
- package/docs-site/zh/{guides → reference}/results-data.mdx +21 -13
- package/docs-site/zh/troubleshooting/debug-sandbox.mdx +57 -0
- package/docs-site/zh/troubleshooting/debugging.mdx +212 -0
- package/docs-site/zh/{quickstart.mdx → tutorials/quickstart.mdx} +5 -17
- package/package.json +10 -2
- 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 +13 -2
- 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/openai-compat.ts +1 -1
- 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/streaming.ts +2 -2
- package/src/agents/types.ts +71 -8
- package/src/agents/ui-message-stream.test.ts +3 -0
- package/src/cli.ts +446 -124
- package/src/context/context.test.ts +51 -12
- package/src/context/context.ts +162 -30
- package/src/context/session.test.ts +2 -1
- package/src/context/session.ts +115 -7
- 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 +81 -17
- package/src/i18n/zh-CN.ts +80 -17
- 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 +178 -61
- package/src/report/built-in/index.tsx +9 -0
- package/src/report/components.tsx +625 -279
- package/src/report/compute.ts +723 -491
- package/src/report/dual-render.test.tsx +741 -1024
- package/src/report/flag.ts +104 -12
- package/src/report/format.ts +32 -12
- package/src/report/index.ts +119 -46
- package/src/report/load.ts +3 -2
- package/src/report/locale.ts +136 -65
- package/src/report/metrics.ts +108 -25
- package/src/report/primitives.tsx +196 -45
- package/src/report/react/AttemptList.tsx +30 -43
- package/src/report/react/DeltaTable.tsx +63 -45
- package/src/report/react/EvalList.tsx +0 -0
- package/src/report/react/ExperimentComparison.tsx +73 -0
- package/src/report/react/ExperimentList.tsx +50 -32
- package/src/report/react/MetricBars.tsx +5 -4
- package/src/report/react/MetricLine.tsx +13 -8
- package/src/report/react/MetricMatrix.tsx +2 -2
- package/src/report/react/MetricScatter.tsx +86 -34
- package/src/report/react/MetricTable.tsx +4 -76
- package/src/report/react/ScopeSummary.tsx +86 -0
- package/src/report/react/Scoreboard.tsx +28 -10
- package/src/report/react/cell.tsx +2 -2
- 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 +89 -5
- package/src/report/react/fixtures.ts +114 -154
- package/src/report/react/index.tsx +24 -39
- package/src/report/react/render.test.tsx +138 -158
- package/src/report/react/styles.css +243 -82
- package/src/report/report.test.ts +779 -841
- package/src/report/report.ts +423 -41
- package/src/report/text/faces.ts +290 -193
- package/src/report/text/plot.ts +1 -1
- package/src/report/text/table.ts +44 -7
- package/src/report/tree.ts +362 -104
- package/src/report/types.ts +261 -271
- package/src/report/web.ts +63 -20
- package/src/results/annotated-source.test.ts +62 -9
- package/src/results/annotated-source.ts +64 -6
- package/src/results/attempt-evidence.test.ts +13 -11
- package/src/results/attempt-evidence.ts +20 -13
- package/src/results/attempt-source.ts +6 -3
- package/src/results/copy.ts +150 -60
- package/src/results/host-equivalence.test.ts +34 -20
- package/src/results/index.ts +12 -4
- package/src/results/locator.test.ts +1 -22
- package/src/results/open.ts +15 -5
- package/src/results/publish.ts +149 -0
- package/src/results/results.test.ts +89 -54
- package/src/results/select.ts +104 -34
- package/src/results/truncate.ts +90 -0
- package/src/results/types.ts +43 -14
- 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 +243 -37
- 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 +8 -6
- package/src/shared/types.ts +31 -0
- package/src/show/compose.ts +50 -67
- package/src/show/index.ts +127 -56
- package/src/show/render.ts +662 -131
- package/src/show/report-host.test.ts +188 -0
- package/src/show/report-host.ts +375 -0
- package/src/show/show.test.ts +320 -54
- package/src/tty-line.ts +8 -26
- package/src/util.test.ts +1 -0
- package/src/util.ts +41 -0
- package/src/view/app/App.test.tsx +69 -0
- package/src/view/app/App.tsx +144 -48
- package/src/view/app/components/AttemptModal.tsx +423 -11
- package/src/view/app/components/CodeView.tsx +41 -14
- package/src/view/app/components/CopyControls.tsx +2 -2
- package/src/view/app/i18n.ts +37 -17
- package/src/view/app/lib/attempt-route.test.ts +1 -0
- package/src/view/app/lib/verdict.ts +7 -9
- package/src/view/app/main.tsx +13 -8
- package/src/view/app/pages/{RunsPage.tsx → AttemptsPage.tsx} +6 -6
- package/src/view/app/types.ts +4 -1
- 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 +10 -3
- package/src/view/data.ts +155 -49
- package/src/view/index.ts +56 -41
- package/src/view/server.ts +37 -15
- package/src/view/shared/types.ts +34 -5
- package/src/view/styles.css +227 -0
- package/src/view/view-report.test.ts +167 -62
- package/dist/report/built-ins/experiment-comparison.d.ts +0 -1
- package/dist/report/built-ins/experiment-comparison.js +0 -13
- package/dist/report/built-ins/index.d.ts +0 -1
- package/dist/report/built-ins/index.js +0 -2
- package/dist/report/react/GroupSummary.d.ts +0 -8
- package/dist/report/react/GroupSummary.js +0 -8
- package/dist/report/react/RunOverview.d.ts +0 -8
- package/dist/report/react/RunOverview.js +0 -12
- package/docs-site/zh/example/ai-agent-application.mdx +0 -152
- package/docs-site/zh/example/claude-code-codex-plugin.mdx +0 -167
- package/docs-site/zh/example/claude-code-codex-skill.mdx +0 -152
- package/docs-site/zh/example/showcase.mdx +0 -39
- package/docs-site/zh/guides/publish-report.mdx +0 -91
- package/docs-site/zh/guides/sandbox-providers.mdx +0 -102
- package/src/report/built-in-user-parity.test.tsx +0 -640
- package/src/report/built-ins/experiment-comparison.tsx +0 -19
- package/src/report/built-ins/index.ts +0 -2
- package/src/report/react/GroupSummary.tsx +0 -66
- package/src/report/react/RunOverview.tsx +0 -109
- 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/docs-site/zh/{example/tier1-ai-sdk-v7.mdx → examples/integrations/ai-sdk-v7.mdx} +0 -0
- /package/docs-site/zh/{example/tier1-claude-sdk.mdx → examples/integrations/claude-sdk.mdx} +0 -0
- /package/docs-site/zh/{example/tier1-codex-sdk.mdx → examples/integrations/codex-sdk.mdx} +0 -0
- /package/docs-site/zh/{example/tier1-langgraph.mdx → examples/integrations/langgraph.mdx} +0 -0
- /package/docs-site/zh/{example/tier1-pi-sdk.mdx → examples/integrations/pi-sdk.mdx} +0 -0
- /package/docs-site/zh/{guides → how-to}/fixtures.mdx +0 -0
package/src/o11y/types.ts
CHANGED
|
@@ -53,11 +53,22 @@ export interface InputRequest {
|
|
|
53
53
|
readonly options?: readonly { id: string; label?: string }[];
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
/**
|
|
57
|
+
* 落盘截断的结构化标记(见 docs/feature/results/architecture.md「大值截断」):
|
|
58
|
+
* `path` 是被截断的位置(事件里是字段名,如 "output";span 里是 attribute key,如 "output.value"),
|
|
59
|
+
* `originalBytes` 是截断前的 UTF-8 字节数。view 显示「输出过大,已截断」靠它,不正则匹配 marker。
|
|
60
|
+
*/
|
|
61
|
+
export interface Truncation {
|
|
62
|
+
path: string;
|
|
63
|
+
originalBytes: number;
|
|
64
|
+
}
|
|
65
|
+
|
|
56
66
|
/**
|
|
57
67
|
* 标准事件流的词汇(对标 docs/feature/adapters/architecture/events.md)。adapter 唯一的硬活就是把
|
|
58
68
|
* 各 agent 五花八门的原始 transcript 映射成 StreamEvent[];映射完,整套断言免费。
|
|
69
|
+
* `truncated` 只由 results writer 在落盘时刻写入(运行时全量,落盘截断);adapter 不产出它。
|
|
59
70
|
*/
|
|
60
|
-
export type StreamEvent =
|
|
71
|
+
export type StreamEvent = { truncated?: Truncation[] } & (
|
|
61
72
|
/** 一条文本消息(assistant 回复或 user 输入);`loc` 是可选的源码位置,用于把消息叠回 eval 源码。 */
|
|
62
73
|
| { type: "message"; role: "assistant" | "user"; text: string; loc?: SourceLoc }
|
|
63
74
|
/** 发起一次工具/动作调用;`tool` 是归一化后的规范工具名,原始名保留在 DerivedFacts.ToolCall.originalName。 */
|
|
@@ -85,7 +96,8 @@ export type StreamEvent =
|
|
|
85
96
|
/** 上下文被压缩/摘要(如超长会话截断历史);`reason` 是可选的压缩原因说明。 */
|
|
86
97
|
| { type: "compaction"; reason?: string }
|
|
87
98
|
/** 运行中出现的错误。 */
|
|
88
|
-
| { type: "error"; message: string }
|
|
99
|
+
| { type: "error"; message: string }
|
|
100
|
+
);
|
|
89
101
|
|
|
90
102
|
/** core 从事件流折叠出的结构化事实(deriveRunFacts)。 */
|
|
91
103
|
export interface ToolCall {
|
|
@@ -145,6 +157,8 @@ export interface TraceSpan {
|
|
|
145
157
|
kind?: SpanKind;
|
|
146
158
|
/** OTLP span 属性(gen_ai.* / tool 名 / token 等),按 key 摊平。raw 属性始终保留供下钻。 */
|
|
147
159
|
attributes?: Record<string, JsonValue>;
|
|
160
|
+
/** 落盘截断标记(只由 results writer 在序列化时写入,运行时全量;见 Truncation)。 */
|
|
161
|
+
truncated?: Truncation[];
|
|
148
162
|
}
|
|
149
163
|
|
|
150
164
|
/** 给人 / 给 EVAL.ts 看的 o11y 摘要(注入沙箱 __niceeval__/results.json)。 */
|
package/src/report/aggregate.ts
CHANGED
|
@@ -1,38 +1,39 @@
|
|
|
1
|
-
// 两级聚合引擎:去重 → 按维度分组 → 组内按 (
|
|
1
|
+
// 两级聚合引擎:去重 → 按维度分组 → 组内按 (experiment × eval) 折叠(perEval)→ 跨题折叠
|
|
2
|
+
// (acrossEvals)→ MetricCell(docs/feature/reports/architecture.md「指标聚合不变量」、
|
|
3
|
+
// docs/feature/reports/library/metrics.md「公开计算模型」)。
|
|
2
4
|
//
|
|
3
5
|
// 为什么是两级:earlyExit 默认开,失败的题天然比通过的题样本多;平铺求均值会把分数
|
|
4
6
|
// 和重试策略纠缠在一起(eval A=[1]、eval B=[0,0,0] 平铺 = 0.25,两级宏平均 = 0.5)。
|
|
5
7
|
// 自定义维度把同一道题的 attempt 分进不同组时,第一级折叠发生在各组内部。
|
|
6
8
|
|
|
7
9
|
import { dedupeAttempts } from "../results/select.ts";
|
|
8
|
-
import type { AttemptHandle,
|
|
10
|
+
import type { AttemptHandle, Scope, ScopeWarning, Snapshot } from "../results/types.ts";
|
|
9
11
|
import { encodeAttemptLocator, type AttemptLocator } from "../results/locator.ts";
|
|
10
12
|
import type {
|
|
11
13
|
Aggregator,
|
|
12
14
|
DimensionInput,
|
|
15
|
+
DimensionRef,
|
|
13
16
|
Metric,
|
|
14
17
|
MetricCell,
|
|
15
18
|
MetricColumn,
|
|
16
|
-
|
|
19
|
+
NumericAxis,
|
|
20
|
+
ReportInput,
|
|
17
21
|
} from "./types.ts";
|
|
18
|
-
import {
|
|
22
|
+
import { flagValueOf, runConfigValueOf } from "./flag.ts";
|
|
23
|
+
import { formatMetricValue, localizedDisplay } from "./format.ts";
|
|
24
|
+
import { localeText, type LocalizedText } from "./locale.ts";
|
|
19
25
|
import { evalPrefixPredicate } from "../shared/aggregate.ts";
|
|
20
26
|
|
|
21
27
|
// 复合键分隔符:NUL 不会出现在 eval id / experimentId / ISO 时间里,拼接键不会串味
|
|
22
28
|
const KEY_SEP = "\u0000";
|
|
23
29
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
/** 计算函数的第一参:Selection(warnings 随行)或手工挑的快照数组(没有挑选过程,自然无警告)。 */
|
|
28
|
-
export type SnapshotsInput = Selection | Snapshot[];
|
|
29
|
-
|
|
30
|
-
export function resolveInput(input: SnapshotsInput): {
|
|
31
|
-
snapshots: Snapshot[];
|
|
32
|
-
warnings: SelectionWarning[];
|
|
30
|
+
export function resolveInput(input: ReportInput): {
|
|
31
|
+
snapshots: readonly Snapshot[];
|
|
32
|
+
warnings: readonly ScopeWarning[];
|
|
33
33
|
} {
|
|
34
|
-
if (Array.isArray(input)) return { snapshots: input, warnings: [] };
|
|
35
|
-
|
|
34
|
+
if (Array.isArray(input)) return { snapshots: input as readonly Snapshot[], warnings: [] };
|
|
35
|
+
const scope = input as Scope;
|
|
36
|
+
return { snapshots: scope.snapshots, warnings: scope.warnings };
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
/** 展平后的一条样本:attempt + 它所属的快照(维度解析与题级折叠都需要快照身份)。 */
|
|
@@ -49,16 +50,19 @@ export function evalIdOf(item: Item): string {
|
|
|
49
50
|
return item.attempt.evalId || item.attempt.result.id;
|
|
50
51
|
}
|
|
51
52
|
|
|
52
|
-
/**
|
|
53
|
+
/** 快照键:"<experimentId> @ <startedAt>"("snapshot" 维度与手挑快照数组的对比用)。 */
|
|
53
54
|
export function snapshotKeyOf(snapshot: Snapshot): string {
|
|
54
55
|
return `${snapshot.experimentId} @ ${snapshot.startedAt}`;
|
|
55
56
|
}
|
|
56
57
|
|
|
58
|
+
/** 一组 Item 的 eval 全身份键:experimentId + eval id(聚合中的题级身份始终是这一对)。 */
|
|
59
|
+
export function fullEvalKey(item: Item): string {
|
|
60
|
+
return `${experimentIdOf(item)}${KEY_SEP}${evalIdOf(item)}`;
|
|
61
|
+
}
|
|
62
|
+
|
|
57
63
|
/**
|
|
58
64
|
* 一条 Item 的 AttemptLocator:真实读取路径(openResults() 产出的 handle)恒有
|
|
59
|
-
* `attempt.locator`;手工构造的测试 fixture
|
|
60
|
-
* (与 `loadAttemptEvidence`、`open.ts` 的回填同一口径)——只服务这类场景,不改变真实
|
|
61
|
-
* 读取路径的行为。
|
|
65
|
+
* `attempt.locator`;手工构造的测试 fixture 若省略它,按当前身份元组兜底算一份。
|
|
62
66
|
*/
|
|
63
67
|
export function locatorOf(item: Item): AttemptLocator {
|
|
64
68
|
return (
|
|
@@ -77,7 +81,7 @@ export function locatorOf(item: Item): AttemptLocator {
|
|
|
77
81
|
* (experimentId, evalId, attempt, startedAt))。missing-startedAt 的警告不透出:
|
|
78
82
|
* 官方产出永不缺 startedAt,缺失只可能来自 legacy 落盘,「不去重、如实保留重复」即终稿。
|
|
79
83
|
*/
|
|
80
|
-
export function collectItems(snapshots: Snapshot[]): Item[] {
|
|
84
|
+
export function collectItems(snapshots: readonly Snapshot[]): Item[] {
|
|
81
85
|
const snapshotByAttempt = new Map<AttemptHandle, Snapshot>();
|
|
82
86
|
const flattened: AttemptHandle[] = [];
|
|
83
87
|
for (const snapshot of snapshots) {
|
|
@@ -93,9 +97,9 @@ export function collectItems(snapshots: Snapshot[]): Item[] {
|
|
|
93
97
|
|
|
94
98
|
export { evalPrefixPredicate };
|
|
95
99
|
|
|
96
|
-
export function filterItems(items: Item[], evals?: string | string[]): Item[] {
|
|
100
|
+
export function filterItems(items: Item[], evals?: string | readonly string[]): Item[] {
|
|
97
101
|
if (evals === undefined) return items;
|
|
98
|
-
const match = evalPrefixPredicate(evals);
|
|
102
|
+
const match = evalPrefixPredicate(Array.isArray(evals) ? [...evals] : (evals as string));
|
|
99
103
|
return items.filter((item) => match(evalIdOf(item)));
|
|
100
104
|
}
|
|
101
105
|
|
|
@@ -105,39 +109,52 @@ export function dimensionName(dimension: DimensionInput): string {
|
|
|
105
109
|
return typeof dimension === "string" ? dimension : dimension.name;
|
|
106
110
|
}
|
|
107
111
|
|
|
108
|
-
/** eval id
|
|
112
|
+
/** eval id 的完整父路径:"a/b/c" → "a/b";没有 "/" 时取完整 id(与可比组同一条派生规则)。 */
|
|
109
113
|
export function evalGroupOf(id: string): string {
|
|
110
|
-
const slash = id.
|
|
114
|
+
const slash = id.lastIndexOf("/");
|
|
111
115
|
return slash === -1 ? id : id.slice(0, slash);
|
|
112
116
|
}
|
|
113
117
|
|
|
114
|
-
function
|
|
115
|
-
return
|
|
118
|
+
function isDimensionRef(dimension: DimensionInput): dimension is DimensionRef {
|
|
119
|
+
return (
|
|
120
|
+
typeof dimension === "object" &&
|
|
121
|
+
"kind" in dimension &&
|
|
122
|
+
((dimension as DimensionRef).kind === "flag" || (dimension as DimensionRef).kind === "runConfig")
|
|
123
|
+
);
|
|
116
124
|
}
|
|
117
125
|
|
|
118
|
-
/**
|
|
119
|
-
|
|
120
|
-
|
|
126
|
+
/** flag / runConfig 未声明时的显示键:内置文案,en / zh-CN 同形。 */
|
|
127
|
+
export const MISSING_GROUP_KEY = localeText("en", "cell.missingValue");
|
|
128
|
+
|
|
129
|
+
/** 对象键递归排序后的稳定 JSON(分组显示键与冲突检测共用)。 */
|
|
130
|
+
function canonicalJson(value: unknown): string {
|
|
131
|
+
if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
|
|
132
|
+
if (typeof value === "object" && value !== null) {
|
|
133
|
+
const keys = Object.keys(value as Record<string, unknown>).sort();
|
|
134
|
+
return `{${keys.map((k) => `${JSON.stringify(k)}:${canonicalJson((value as Record<string, unknown>)[k])}`).join(",")}}`;
|
|
135
|
+
}
|
|
136
|
+
return JSON.stringify(value);
|
|
121
137
|
}
|
|
122
138
|
|
|
123
|
-
/**
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
if (
|
|
129
|
-
|
|
139
|
+
/**
|
|
140
|
+
* flag / runConfig 声明值 → 分组显示键(稳定 JSON 规则):字符串直接显示,其它值用对象键
|
|
141
|
+
* 递归排序后的 JSON,缺失值显示内置文案 `(missing)`。返回 [显示键, 冲突检测用的规范形]。
|
|
142
|
+
*/
|
|
143
|
+
export function refDisplayKey(value: unknown): [display: string, canonical: string] {
|
|
144
|
+
if (value === undefined) return [MISSING_GROUP_KEY, "undefined"];
|
|
145
|
+
if (typeof value === "string") return [value, `string:${value}`];
|
|
146
|
+
return [canonicalJson(value), `json:${canonicalJson(value)}`];
|
|
130
147
|
}
|
|
131
148
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
149
|
+
function refValueOf(ref: DimensionRef, item: Item): unknown {
|
|
150
|
+
return ref.kind === "flag"
|
|
151
|
+
? flagValueOf(item.attempt, ref.name)
|
|
152
|
+
: runConfigValueOf(item.attempt, ref.name as Parameters<typeof runConfigValueOf>[1]);
|
|
136
153
|
}
|
|
137
154
|
|
|
138
155
|
export function dimensionKey(dimension: DimensionInput, item: Item): string {
|
|
139
156
|
if (typeof dimension !== "string") {
|
|
140
|
-
if (
|
|
157
|
+
if (isDimensionRef(dimension)) return refDisplayKey(refValueOf(dimension, item))[0];
|
|
141
158
|
return dimension.of(item.attempt);
|
|
142
159
|
}
|
|
143
160
|
const result = item.attempt.result;
|
|
@@ -162,21 +179,42 @@ export function dimensionKey(dimension: DimensionInput, item: Item): string {
|
|
|
162
179
|
}
|
|
163
180
|
}
|
|
164
181
|
|
|
165
|
-
/**
|
|
182
|
+
/**
|
|
183
|
+
* 按维度分组;维度 domain 按稳定 key 字典序(Unicode)排列,不让文件扫描顺序渗进报告。
|
|
184
|
+
* flag / runConfig 维度做显示键冲突检测:不同原始值生成同一显示键时报错并要求改用
|
|
185
|
+
* CustomDimension,绝不静默合组。
|
|
186
|
+
*/
|
|
166
187
|
export function groupItems(items: Item[], dimension: DimensionInput): Map<string, Item[]> {
|
|
167
188
|
const groups = new Map<string, Item[]>();
|
|
189
|
+
const canonicalByDisplay = new Map<string, string>();
|
|
190
|
+
const checkConflicts = typeof dimension !== "string" && isDimensionRef(dimension);
|
|
168
191
|
for (const item of items) {
|
|
169
|
-
|
|
192
|
+
let key: string;
|
|
193
|
+
if (checkConflicts) {
|
|
194
|
+
const [display, canonical] = refDisplayKey(refValueOf(dimension as DimensionRef, item));
|
|
195
|
+
const existing = canonicalByDisplay.get(display);
|
|
196
|
+
if (existing === undefined) canonicalByDisplay.set(display, canonical);
|
|
197
|
+
else if (existing !== canonical) {
|
|
198
|
+
throw new Error(
|
|
199
|
+
`Dimension "${dimensionName(dimension)}" maps two different raw values to the same display key "${display}" ` +
|
|
200
|
+
`(e.g. the string "5" and the number 5). Grouping them silently would merge distinct configurations; ` +
|
|
201
|
+
`use a CustomDimension ({ name, of(attempt) }) to define an unambiguous key.`,
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
key = display;
|
|
205
|
+
} else {
|
|
206
|
+
key = dimensionKey(dimension, item);
|
|
207
|
+
}
|
|
170
208
|
const list = groups.get(key);
|
|
171
209
|
if (list) list.push(item);
|
|
172
210
|
else groups.set(key, [item]);
|
|
173
211
|
}
|
|
174
|
-
return groups;
|
|
212
|
+
return new Map([...groups.entries()].sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)));
|
|
175
213
|
}
|
|
176
214
|
|
|
177
215
|
// ───────────────────────── 聚合 ─────────────────────────
|
|
178
216
|
|
|
179
|
-
export function applyAggregator(aggregator: Aggregator, values: number[]): number {
|
|
217
|
+
export function applyAggregator(aggregator: Aggregator, values: readonly number[]): number {
|
|
180
218
|
if (typeof aggregator === "function") return aggregator(values);
|
|
181
219
|
switch (aggregator) {
|
|
182
220
|
case "mean":
|
|
@@ -190,50 +228,108 @@ export function applyAggregator(aggregator: Aggregator, values: number[]): numbe
|
|
|
190
228
|
}
|
|
191
229
|
}
|
|
192
230
|
|
|
193
|
-
|
|
231
|
+
function metricError(metric: Metric, step: string, cause: unknown, locator?: AttemptLocator): Error {
|
|
232
|
+
const at = locator === undefined ? "" : ` at attempt ${locator}`;
|
|
233
|
+
return new Error(
|
|
234
|
+
`Metric "${metric.name}" ${step} failed${at}: ${cause instanceof Error ? cause.message : String(cause)}. ` +
|
|
235
|
+
"Computation errors are not disguised as missing data — fix the metric (return null for expected gaps), then re-run.",
|
|
236
|
+
{ cause },
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/** where 不满足 → null,语义等价于 value 开头 return null;抛错与非有限数按完整用户反馈失败。 */
|
|
194
241
|
export async function evaluateMetric(metric: Metric, attempt: AttemptHandle): Promise<number | null> {
|
|
195
|
-
|
|
196
|
-
|
|
242
|
+
const locator = attempt.locator;
|
|
243
|
+
if (metric.where) {
|
|
244
|
+
let pass: boolean;
|
|
245
|
+
try {
|
|
246
|
+
pass = metric.where(attempt);
|
|
247
|
+
} catch (e) {
|
|
248
|
+
throw metricError(metric, "where()", e, locator);
|
|
249
|
+
}
|
|
250
|
+
if (!pass) return null;
|
|
251
|
+
}
|
|
252
|
+
let value: number | null;
|
|
253
|
+
try {
|
|
254
|
+
value = await metric.value(attempt);
|
|
255
|
+
} catch (e) {
|
|
256
|
+
throw metricError(metric, "value()", e, locator);
|
|
257
|
+
}
|
|
258
|
+
if (value !== null && !Number.isFinite(value)) {
|
|
259
|
+
throw metricError(metric, "value()", `returned a non-finite number (${String(value)}); return null for "not measurable"`, locator);
|
|
260
|
+
}
|
|
261
|
+
return value;
|
|
197
262
|
}
|
|
198
263
|
|
|
199
|
-
|
|
264
|
+
/** 单值 → LocalizedText display:metric.display 覆盖内置 unit 格式化;null 的兜底归渲染面。 */
|
|
265
|
+
export function displayValue(metric: Metric, value: number | null): LocalizedText {
|
|
200
266
|
// null 的纯文本兜底;组件把 null 渲染成「缺数据」,绝不画 0
|
|
201
267
|
if (value === null) return "—";
|
|
202
|
-
|
|
268
|
+
if (metric.display) {
|
|
269
|
+
const display = metric.display;
|
|
270
|
+
return localizedDisplay((locale) => {
|
|
271
|
+
try {
|
|
272
|
+
return display(value, locale);
|
|
273
|
+
} catch (e) {
|
|
274
|
+
throw metricError(metric, "display()", e);
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
return formatMetricValue(value, metric.unit);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function foldAggregator(metric: Metric, step: "perEval" | "acrossEvals", values: readonly number[]): number {
|
|
282
|
+
const aggregator = metric.aggregate?.[step] ?? "mean";
|
|
283
|
+
let folded: number;
|
|
284
|
+
try {
|
|
285
|
+
folded = applyAggregator(aggregator, values);
|
|
286
|
+
} catch (e) {
|
|
287
|
+
throw metricError(metric, `${step} aggregator`, e);
|
|
288
|
+
}
|
|
289
|
+
if (!Number.isFinite(folded)) {
|
|
290
|
+
throw metricError(metric, `${step} aggregator`, `returned a non-finite number (${String(folded)})`);
|
|
291
|
+
}
|
|
292
|
+
return folded;
|
|
203
293
|
}
|
|
204
294
|
|
|
205
295
|
/**
|
|
206
296
|
* 一个格子:组内 attempt → 两级聚合 → 终值。
|
|
207
297
|
* null 值不进聚合但计入 total(覆盖率经 samples/total 如实暴露);全 null → value null。
|
|
298
|
+
* refs 跟随覆盖范围(含值为 null 的 attempt),去重后按 locator 字典序。
|
|
208
299
|
*/
|
|
209
300
|
export async function computeCell(metric: Metric, items: Item[]): Promise<MetricCell> {
|
|
210
|
-
// 第一级桶:同一 (eval × 快照) 的 attempt 折成一个题级值
|
|
301
|
+
// 第一级桶:同一 (experiment × eval × 快照) 的 attempt 折成一个题级值
|
|
211
302
|
const buckets = new Map<string, number[]>();
|
|
212
|
-
const refs
|
|
303
|
+
const refs = new Set<AttemptLocator>();
|
|
213
304
|
let samples = 0;
|
|
214
305
|
for (const item of items) {
|
|
306
|
+
refs.add(locatorOf(item));
|
|
215
307
|
const value = await evaluateMetric(metric, item.attempt);
|
|
216
308
|
if (value === null) continue;
|
|
217
309
|
samples += 1;
|
|
218
|
-
|
|
219
|
-
const bucketKey = `${evalIdOf(item)}${KEY_SEP}${snapshotKeyOf(item.snapshot)}`;
|
|
310
|
+
const bucketKey = `${fullEvalKey(item)}${KEY_SEP}${snapshotKeyOf(item.snapshot)}`;
|
|
220
311
|
const bucket = buckets.get(bucketKey);
|
|
221
312
|
if (bucket) bucket.push(value);
|
|
222
313
|
else buckets.set(bucketKey, [value]);
|
|
223
314
|
}
|
|
224
|
-
const
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
315
|
+
const evalValues = [...buckets.values()].map((values) => foldAggregator(metric, "perEval", values));
|
|
316
|
+
const value = evalValues.length === 0 ? null : foldAggregator(metric, "acrossEvals", evalValues);
|
|
317
|
+
return {
|
|
318
|
+
value,
|
|
319
|
+
display: displayValue(metric, value),
|
|
320
|
+
samples,
|
|
321
|
+
total: items.length,
|
|
322
|
+
refs: [...refs].sort(),
|
|
323
|
+
};
|
|
229
324
|
}
|
|
230
325
|
|
|
231
326
|
export function toColumn(metric: Metric): MetricColumn {
|
|
232
327
|
return {
|
|
233
328
|
key: metric.name,
|
|
234
329
|
label: metric.label ?? metric.name,
|
|
235
|
-
|
|
236
|
-
|
|
330
|
+
...(metric.description !== undefined ? { description: metric.description } : {}),
|
|
331
|
+
...(metric.unit !== undefined ? { unit: metric.unit } : {}),
|
|
332
|
+
...(metric.better !== undefined ? { better: metric.better } : {}),
|
|
237
333
|
};
|
|
238
334
|
}
|
|
239
335
|
|
|
@@ -248,3 +344,24 @@ export function assertUniqueMetricNames(metrics: readonly Metric[], where: strin
|
|
|
248
344
|
seen.add(metric.name);
|
|
249
345
|
}
|
|
250
346
|
}
|
|
347
|
+
|
|
348
|
+
/** NumericAxis 的取值,带完整反馈的非法值检查(NaN / Infinity 一律报错,不静默当缺失)。 */
|
|
349
|
+
export function axisValueOf(axis: NumericAxis, attempt: AttemptHandle): number | null {
|
|
350
|
+
let value: number | null;
|
|
351
|
+
try {
|
|
352
|
+
value = axis.of(attempt);
|
|
353
|
+
} catch (e) {
|
|
354
|
+
throw new Error(
|
|
355
|
+
`Numeric axis "${axis.name}" of() failed${attempt.locator ? ` at attempt ${attempt.locator}` : ""}: ${
|
|
356
|
+
e instanceof Error ? e.message : String(e)
|
|
357
|
+
}`,
|
|
358
|
+
{ cause: e },
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
if (value !== null && !Number.isFinite(value)) {
|
|
362
|
+
throw new Error(
|
|
363
|
+
`Numeric axis "${axis.name}" of() returned a non-finite number (${String(value)}); return null for attempts without a plottable x value.`,
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
return value;
|
|
367
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// niceeval/report/built-in —— 包里自带的一个报告文件,没有任何私有钩子
|
|
2
|
+
// (docs/feature/reports/library/built-in.md)。裸 `niceeval show` 与 `niceeval view`
|
|
3
|
+
// 装载的就是这份默认导出:一份普通 defineReport,与用户的 --report 文件同层、
|
|
4
|
+
// 走同一条 装载 → resolve → validate → render 管线。「builtin」不是装载逻辑里的类别,
|
|
5
|
+
// 只是宿主默认拿哪个值的事实。
|
|
6
|
+
|
|
7
|
+
import { ExperimentComparison, defineReport } from "../index.ts";
|
|
8
|
+
|
|
9
|
+
export default defineReport(<ExperimentComparison />);
|