niceeval 0.6.0 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/types.d.ts +72 -6
- package/dist/context/types.d.ts +32 -12
- package/dist/i18n/en.d.ts +54 -0
- package/dist/i18n/zh-CN.d.ts +55 -1
- package/dist/o11y/types.d.ts +16 -2
- package/dist/report/aggregate.d.ts +5 -3
- package/dist/report/aggregate.js +32 -5
- package/dist/report/built-ins/experiment-comparison.d.ts +39 -0
- package/dist/report/built-ins/experiment-comparison.js +119 -0
- package/dist/report/built-ins/index.d.ts +2 -1
- package/dist/report/built-ins/index.js +2 -2
- package/dist/report/components.d.ts +10 -2
- package/dist/report/components.js +3 -3
- package/dist/report/compute.d.ts +11 -18
- package/dist/report/compute.js +68 -66
- package/dist/report/flag.d.ts +16 -1
- package/dist/report/flag.js +19 -1
- package/dist/report/format.d.ts +16 -14
- package/dist/report/format.js +28 -30
- package/dist/report/index.d.ts +5 -4
- package/dist/report/index.js +6 -5
- package/dist/report/locale.d.ts +23 -3
- package/dist/report/locale.js +47 -6
- package/dist/report/metrics.d.ts +13 -1
- package/dist/report/metrics.js +66 -15
- package/dist/report/primitives.d.ts +6 -0
- package/dist/report/react/AttemptList.d.ts +4 -4
- package/dist/report/react/AttemptList.js +8 -10
- package/dist/report/react/EvalList.d.ts +1 -1
- package/dist/report/react/EvalList.js +0 -0
- package/dist/report/react/ExperimentComparison.d.ts +8 -0
- package/dist/report/react/ExperimentComparison.js +11 -0
- package/dist/report/react/ExperimentList.d.ts +4 -2
- package/dist/report/react/ExperimentList.js +57 -7
- package/dist/report/react/MetricScatter.js +6 -16
- package/dist/report/react/chart-math.d.ts +23 -6
- package/dist/report/react/chart-math.js +71 -19
- package/dist/report/react/fixtures.d.ts +3 -3
- package/dist/report/react/fixtures.js +30 -18
- package/dist/report/react/format.d.ts +1 -1
- package/dist/report/react/format.js +1 -1
- package/dist/report/react/index.d.ts +1 -1
- package/dist/report/report.d.ts +5 -1
- package/dist/report/report.js +6 -2
- package/dist/report/text/faces.d.ts +1 -1
- package/dist/report/text/faces.js +100 -61
- package/dist/report/text/table.js +36 -5
- package/dist/report/types.d.ts +40 -34
- package/dist/results/types.d.ts +11 -0
- package/dist/runner/feedback/sink.d.ts +110 -0
- package/dist/runner/types.d.ts +513 -22
- package/dist/sandbox/docker.d.ts +23 -2
- package/dist/sandbox/e2b.d.ts +15 -1
- package/dist/sandbox/errors.d.ts +30 -3
- package/dist/sandbox/io-retry.d.ts +17 -0
- package/dist/sandbox/registry.d.ts +2 -0
- package/dist/sandbox/resolve.d.ts +18 -5
- package/dist/sandbox/retry.d.ts +11 -1
- package/dist/sandbox/types.d.ts +39 -5
- package/dist/sandbox/vercel.d.ts +7 -1
- package/dist/scoring/coverage.d.ts +30 -0
- package/dist/scoring/display.d.ts +21 -0
- package/dist/scoring/display.js +120 -0
- package/dist/scoring/types.d.ts +103 -20
- package/dist/shared/aggregate.d.ts +1 -0
- package/dist/shared/aggregate.js +3 -3
- package/dist/shared/types.d.ts +28 -0
- package/dist/tty-line.d.ts +0 -4
- package/dist/util.d.ts +23 -0
- package/docs-site/zh/concepts/adapter.mdx +24 -6
- package/docs-site/zh/concepts/assert.mdx +11 -10
- package/docs-site/zh/concepts/evals.mdx +7 -6
- package/docs-site/zh/concepts/experiment.mdx +1 -1
- package/docs-site/zh/concepts/overview.mdx +7 -7
- package/docs-site/zh/guides/agent-feedback-loop.mdx +35 -31
- package/docs-site/zh/guides/authoring.mdx +33 -0
- package/docs-site/zh/guides/ci-integration.mdx +23 -12
- package/docs-site/zh/guides/connect-your-agent.mdx +29 -3
- package/docs-site/zh/guides/custom-reports.mdx +29 -34
- package/docs-site/zh/guides/dataset-fanout.mdx +25 -3
- package/docs-site/zh/guides/debug-sandbox.mdx +57 -0
- package/docs-site/zh/guides/debugging.mdx +210 -0
- package/docs-site/zh/guides/experiments.mdx +10 -3
- package/docs-site/zh/guides/fixtures.mdx +3 -1
- package/docs-site/zh/guides/official-adapters.mdx +27 -3
- package/docs-site/zh/guides/publish-report.mdx +30 -16
- package/docs-site/zh/guides/report-components.mdx +49 -37
- package/docs-site/zh/guides/reporters.mdx +2 -2
- package/docs-site/zh/guides/results-data.mdx +42 -8
- package/docs-site/zh/guides/runner.mdx +17 -7
- package/docs-site/zh/guides/sandbox-agent.mdx +57 -7
- package/docs-site/zh/guides/sandbox-providers.mdx +258 -10
- package/docs-site/zh/guides/scoring-guide.mdx +4 -4
- package/docs-site/zh/guides/viewing-results.mdx +85 -41
- package/docs-site/zh/guides/write-experiment.mdx +5 -3
- package/docs-site/zh/guides/write-send.mdx +19 -2
- package/docs-site/zh/index.mdx +1 -1
- package/docs-site/zh/reference/builtin-agents.mdx +27 -0
- package/docs-site/zh/reference/capabilities.mdx +2 -2
- package/docs-site/zh/reference/cli.mdx +35 -9
- package/docs-site/zh/reference/define-agent.mdx +60 -5
- package/docs-site/zh/reference/define-config.mdx +1 -1
- package/docs-site/zh/reference/define-eval.mdx +42 -9
- package/docs-site/zh/reference/events.mdx +2 -2
- package/docs-site/zh/reference/expect.mdx +36 -6
- package/package.json +5 -1
- package/src/agents/ai-sdk-otel.test.ts +1 -0
- package/src/agents/ai-sdk.test.ts +3 -0
- package/src/agents/ai-sdk.ts +3 -0
- package/src/agents/bub-install-spec.test.ts +34 -0
- package/src/agents/bub-install-spec.ts +32 -0
- package/src/agents/bub.ts +31 -32
- package/src/agents/claude-code.test.ts +130 -9
- package/src/agents/claude-code.ts +76 -4
- package/src/agents/codex.test.ts +189 -40
- package/src/agents/codex.ts +155 -14
- package/src/agents/coding-cli-versions.test.ts +15 -0
- package/src/agents/coding-cli-versions.ts +3 -0
- package/src/agents/index.ts +11 -0
- package/src/agents/langgraph.test.ts +204 -0
- package/src/agents/langgraph.ts +495 -0
- package/src/agents/marketplace.ts +85 -0
- package/src/agents/native-config.test.ts +179 -0
- package/src/agents/native-config.ts +267 -0
- package/src/agents/openai-compat.test.ts +1 -0
- package/src/agents/openclaw.test.ts +31 -0
- package/src/agents/openclaw.ts +171 -0
- package/src/agents/plugin-config.test.ts +1 -0
- package/src/agents/sdk-streams.test.ts +79 -0
- package/src/agents/sdk-streams.ts +55 -10
- package/src/agents/skills.test.ts +1 -0
- package/src/agents/streaming.test.ts +3 -9
- package/src/agents/types.ts +73 -6
- package/src/agents/ui-message-stream.test.ts +3 -0
- package/src/cli.ts +411 -108
- package/src/context/context.test.ts +51 -12
- package/src/context/context.ts +161 -29
- package/src/context/session.test.ts +1 -0
- package/src/context/session.ts +114 -6
- package/src/context/types.ts +30 -12
- package/src/define.test.ts +13 -8
- package/src/define.ts +25 -4
- package/src/expect/index.ts +53 -23
- package/src/i18n/en.ts +65 -4
- package/src/i18n/zh-CN.ts +66 -4
- package/src/o11y/cost.test.ts +1 -0
- package/src/o11y/execution-tree.test.ts +1 -20
- package/src/o11y/otlp/mappers/claude-code.test.ts +1 -0
- package/src/o11y/otlp/parse.test.ts +1 -0
- package/src/o11y/otlp/turn-otel.test.ts +1 -0
- package/src/o11y/parsers/bub.test.ts +1 -0
- package/src/o11y/parsers/claude-code.test.ts +1 -34
- package/src/o11y/parsers/openclaw.test.ts +154 -0
- package/src/o11y/parsers/openclaw.ts +310 -0
- package/src/o11y/prices.json +746 -311
- package/src/o11y/tool-names.test.ts +1 -0
- package/src/o11y/types.ts +16 -2
- package/src/report/aggregate.ts +34 -5
- package/src/report/built-in-user-parity.test.tsx +127 -173
- package/src/report/built-ins/experiment-comparison.tsx +179 -0
- package/src/report/built-ins/index.ts +7 -2
- package/src/report/components.tsx +11 -3
- package/src/report/compute.ts +80 -74
- package/src/report/dual-render.test.tsx +222 -91
- package/src/report/flag.ts +30 -2
- package/src/report/format.ts +36 -27
- package/src/report/index.ts +23 -6
- package/src/report/locale.ts +49 -6
- package/src/report/metrics.ts +68 -15
- package/src/report/primitives.tsx +6 -0
- package/src/report/react/AttemptList.tsx +9 -36
- package/src/report/react/EvalList.tsx +0 -0
- package/src/report/react/ExperimentComparison.tsx +68 -0
- package/src/report/react/ExperimentList.tsx +173 -55
- package/src/report/react/MetricScatter.tsx +13 -25
- package/src/report/react/chart-math.test.ts +85 -0
- package/src/report/react/chart-math.ts +101 -22
- package/src/report/react/enhance.js +72 -1
- package/src/report/react/fixtures.ts +34 -21
- package/src/report/react/format.ts +1 -1
- package/src/report/react/index.tsx +0 -1
- package/src/report/react/render.test.tsx +30 -69
- package/src/report/react/styles.css +112 -14
- package/src/report/report.test.ts +308 -105
- package/src/report/report.ts +6 -2
- package/src/report/text/faces.ts +111 -67
- package/src/report/text/table.ts +42 -5
- package/src/report/types.ts +42 -34
- package/src/results/annotated-source.test.ts +62 -9
- package/src/results/annotated-source.ts +64 -6
- package/src/results/attempt-evidence.test.ts +9 -7
- package/src/results/attempt-evidence.ts +15 -8
- package/src/results/attempt-source.ts +6 -3
- package/src/results/copy.ts +145 -55
- package/src/results/host-equivalence.test.ts +11 -9
- package/src/results/index.ts +2 -0
- package/src/results/locator.test.ts +1 -22
- package/src/results/open.ts +7 -1
- package/src/results/publish.ts +149 -0
- package/src/results/results.test.ts +85 -51
- package/src/results/truncate.ts +90 -0
- package/src/results/types.ts +7 -0
- package/src/results/writer.ts +31 -13
- package/src/runner/attempt.test.ts +138 -7
- package/src/runner/attempt.ts +603 -104
- package/src/runner/discover.test.ts +47 -0
- package/src/runner/discover.ts +36 -2
- package/src/runner/eval-source.test.ts +1 -27
- package/src/runner/feedback/agent.test.ts +504 -0
- package/src/runner/feedback/agent.ts +409 -0
- package/src/runner/feedback/ci.test.ts +562 -0
- package/src/runner/feedback/ci.ts +401 -0
- package/src/runner/feedback/coordinator.test.ts +317 -0
- package/src/runner/feedback/coordinator.ts +397 -0
- package/src/runner/feedback/failure.ts +40 -0
- package/src/runner/feedback/human.test.ts +616 -0
- package/src/runner/feedback/human.ts +535 -0
- package/src/runner/feedback/index.ts +66 -0
- package/src/runner/feedback/io.ts +78 -0
- package/src/runner/feedback/profile.test.ts +50 -0
- package/src/runner/feedback/profile.ts +58 -0
- package/src/runner/feedback/reducer.test.ts +395 -0
- package/src/runner/feedback/reducer.ts +260 -0
- package/src/runner/feedback/renderer.ts +82 -0
- package/src/runner/feedback/sink.ts +203 -0
- package/src/runner/feedback/testing.ts +106 -0
- package/src/runner/ledger.test.ts +230 -0
- package/src/runner/ledger.ts +329 -0
- package/src/runner/report.test.ts +128 -3
- package/src/runner/report.ts +33 -9
- package/src/runner/reporters/artifacts.ts +8 -2
- package/src/runner/reporters/braintrust.test.ts +8 -7
- package/src/runner/reporters/braintrust.ts +9 -2
- package/src/runner/reporters/index.ts +2 -2
- package/src/runner/reporters/json.test.ts +162 -0
- package/src/runner/reporters/json.ts +35 -8
- package/src/runner/reporters/shared.ts +1 -5
- package/src/runner/run.test.ts +760 -3
- package/src/runner/run.ts +242 -36
- package/src/runner/sandbox-prep.ts +3 -42
- package/src/runner/timing.ts +158 -0
- package/src/runner/types.ts +518 -22
- package/src/sandbox/checkpoint.test.ts +55 -0
- package/src/sandbox/checkpoint.ts +29 -8
- package/src/sandbox/cli-commands.ts +407 -0
- package/src/sandbox/docker.ts +115 -16
- package/src/sandbox/e2b-agent-template.test.ts +56 -0
- package/src/sandbox/e2b-agent-template.ts +94 -0
- package/src/sandbox/e2b.ts +74 -9
- package/src/sandbox/errors.ts +111 -4
- package/src/sandbox/index.ts +2 -0
- package/src/sandbox/io-retry.test.ts +58 -0
- package/src/sandbox/io-retry.ts +45 -0
- package/src/sandbox/keep-registry.test.ts +86 -0
- package/src/sandbox/keep-registry.ts +142 -0
- package/src/sandbox/keep.ts +178 -0
- package/src/sandbox/paths.test.ts +1 -0
- package/src/sandbox/paths.ts +19 -8
- package/src/sandbox/registry.ts +20 -3
- package/src/sandbox/resolve.ts +76 -11
- package/src/sandbox/retry.test.ts +70 -0
- package/src/sandbox/retry.ts +46 -4
- package/src/sandbox/types.ts +44 -6
- package/src/sandbox/vercel.ts +43 -20
- package/src/scoring/collector.ts +60 -17
- package/src/scoring/coverage.ts +95 -0
- package/src/scoring/diff.ts +81 -0
- package/src/scoring/display.test.ts +121 -0
- package/src/scoring/display.ts +133 -0
- package/src/scoring/evidence.test.ts +189 -0
- package/src/scoring/judge.test.ts +142 -0
- package/src/scoring/judge.ts +15 -18
- package/src/scoring/scoped.ts +217 -50
- package/src/scoring/types.ts +117 -20
- package/src/scoring/verdict.ts +16 -4
- package/src/shared/aggregate.ts +3 -2
- package/src/shared/types.ts +31 -0
- package/src/show/compose.ts +2 -2
- package/src/show/index.ts +29 -16
- package/src/show/render.ts +626 -308
- package/src/show/show.test.ts +251 -36
- package/src/tty-line.ts +8 -26
- package/src/util.test.ts +1 -0
- package/src/util.ts +41 -0
- package/src/view/app/components/AttemptModal.tsx +153 -2
- package/src/view/app/components/CodeView.tsx +32 -11
- package/src/view/app/components/CopyControls.tsx +2 -2
- package/src/view/app/i18n.ts +6 -0
- package/src/view/app/lib/attempt-route.test.ts +1 -0
- package/src/view/app/lib/verdict.ts +7 -9
- package/src/view/artifact-serving.test.ts +2 -1
- package/src/view/client-dist/app.css +1 -1
- package/src/view/client-dist/app.js +17 -17
- package/src/view/data.test.ts +2 -1
- package/src/view/data.ts +17 -7
- package/src/view/index.ts +12 -1
- package/src/view/server.ts +2 -0
- package/src/view/shared/types.ts +1 -1
- package/src/view/styles.css +3 -0
- package/src/view/view-report.test.ts +11 -10
- package/dist/o11y/execution-tree.d.ts +0 -103
- package/dist/o11y/otlp/select.d.ts +0 -22
- package/dist/report/built-ins/cost-pass-rate-comparison.d.ts +0 -1
- package/dist/report/built-ins/cost-pass-rate-comparison.js +0 -17
- package/dist/results/annotated-source.d.ts +0 -61
- package/dist/results/attempt-evidence.d.ts +0 -69
- package/dist/results/attempt-source.d.ts +0 -15
- package/src/report/built-ins/cost-pass-rate-comparison.tsx +0 -23
- package/src/runner/reporters/console.ts +0 -70
- package/src/runner/reporters/live.test.ts +0 -56
- package/src/runner/reporters/live.ts +0 -247
- package/src/runner/reporters/quiet.test.ts +0 -66
- package/src/runner/reporters/quiet.ts +0 -49
- package/src/runner/reporters/table.ts +0 -277
package/src/view/data.test.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// cases: docs/engineering/unit-tests/reports/cases.md
|
|
1
2
|
// view 数据层(data.ts)的单测:loader 收编到 openResults、统计整体住进报告槽之后,
|
|
2
3
|
// 守护三件事——skipped 三种原因如实进 viewData(producer 感知的 npx 提示)、报告槽是
|
|
3
4
|
// 现刻水位口径(裸跑经 selectCurrentResults 跨快照合成每 experiment × eval 的最新判定,
|
|
@@ -251,7 +252,7 @@ describe("loadViewScan · 新布局落盘直接可读(写入面 / 读取面同
|
|
|
251
252
|
);
|
|
252
253
|
|
|
253
254
|
const { viewData, reportHtml } = await loadViewScan(root);
|
|
254
|
-
// 报告槽(
|
|
255
|
+
// 报告槽(ExperimentComparison 的 web 面):Experiment 工作台行 + 官方通过率格子(1 过 1 败 = 50%)。
|
|
255
256
|
expect(reportHtml.en).toContain("compare/bub");
|
|
256
257
|
expect(reportHtml.en).toContain("50%");
|
|
257
258
|
const snapshot = viewData.snapshots[0]!;
|
package/src/view/data.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// view 的数据层:读取经 niceeval/results 的 openResults(布局/版本知识只住在那)。
|
|
2
2
|
// 这里只做编排:报告槽 Selection 恒经 selectCurrentResults(现刻水位;与 show 调同一个函数,
|
|
3
3
|
// 两扇门判定不分叉)、快照明细注入(locator / artifactBase)、skipped 透传、报告槽渲染
|
|
4
|
-
// (renderReportSlot:裸跑填充
|
|
4
|
+
// (renderReportSlot:裸跑填充 ExperimentComparison,--report 整槽替换,en / zh-CN 双语各渲染一遍)。
|
|
5
5
|
// --report 只换报告定义,注入的 Selection 与裸跑同一份。统计口径整体住在报告槽里
|
|
6
6
|
// (报告组件的官方计算函数),viewData 不再携带 overview / 榜单这类统计产物,
|
|
7
7
|
// 见 docs/feature/reports/view.md「打开与收窄」。
|
|
@@ -37,11 +37,17 @@ export interface ViewScan {
|
|
|
37
37
|
attemptsByBase: Map<string, AttemptHandle>;
|
|
38
38
|
/**
|
|
39
39
|
* 报告槽:报告树经 renderReportToStaticHtml 渲染出的静态 HTML(en / zh-CN 各一份),
|
|
40
|
-
* 裸跑填充内置默认报告
|
|
40
|
+
* 裸跑填充内置默认报告 ExperimentComparison,--report 整槽替换。作为两个 <template> 静态块
|
|
41
41
|
* 烘进页面(与 __NICEEVAL_VIEW_DATA__ 相邻),不进 viewData —— 前端只负责把当前
|
|
42
42
|
* 界面语言对应的那块 HTML 摆进报告槽位置,不解析。
|
|
43
43
|
*/
|
|
44
44
|
reportHtml: ReportSlotHtml;
|
|
45
|
+
/**
|
|
46
|
+
* --out 的数据等级(见 docs/feature/reports/view.md「静态导出」):全部选中快照带
|
|
47
|
+
* publish:{redaction:"applied"} 才是 "applied",否则 "sensitive"(含本地事实根与
|
|
48
|
+
* redaction:"none"——上游声明过原文发布也不豁免导出时的确认)。
|
|
49
|
+
*/
|
|
50
|
+
publishState: "applied" | "sensitive";
|
|
45
51
|
}
|
|
46
52
|
|
|
47
53
|
/** view 宿主输入的组合语义(与 show 对齐,docs/feature/reports/architecture.md「Selection 是计算入口」)。 */
|
|
@@ -154,7 +160,7 @@ export async function loadLatestResultsPerEval(root = ".niceeval"): Promise<Eval
|
|
|
154
160
|
* 报告槽 Selection 恒经 selectCurrentResults 合成(现刻水位;与 `niceeval show` 调同一个
|
|
155
161
|
* 函数,裸跑与局部收窄不分叉),位置前缀 / --experiment 只作为 scope 传入,不切换选择口径。
|
|
156
162
|
* --report 本身不改挑选——它只换报告槽的填充,注入的 Selection 与裸跑同一份,
|
|
157
|
-
* 「裸跑 ≡ --report <
|
|
163
|
+
* 「裸跑 ≡ --report <ExperimentComparison>」靠这条成立(docs/feature/reports/architecture.md「Selection 是计算入口」)。
|
|
158
164
|
* 证据室数据(快照明细 / skipped)恒为全量,深链在任何收窄下都可达。
|
|
159
165
|
* 零可读结果一律抛 ViewInputError,不渲染/导出空页面(server 起不来,--out 非零退出)。
|
|
160
166
|
*/
|
|
@@ -200,7 +206,7 @@ export async function loadViewScan(input?: string, opts: ViewScanOptions = {}):
|
|
|
200
206
|
);
|
|
201
207
|
}
|
|
202
208
|
|
|
203
|
-
// 报告槽:裸跑填充
|
|
209
|
+
// 报告槽:裸跑填充 ExperimentComparison,--report 整槽替换。报告吃同一份注入 Selection,
|
|
204
210
|
// web 面在计算侧静态渲染成 HTML(en / zh-CN 各一遍,切界面语言不重算数据)。
|
|
205
211
|
const reportHtml = await renderReportSlot(opts.report, results, selection);
|
|
206
212
|
|
|
@@ -261,7 +267,11 @@ export async function loadViewScan(input?: string, opts: ViewScanOptions = {}):
|
|
|
261
267
|
snapshots,
|
|
262
268
|
skippedRuns: results.skipped.map(toSkippedNotice),
|
|
263
269
|
};
|
|
264
|
-
|
|
270
|
+
const publishState =
|
|
271
|
+
selection.snapshots.length > 0 && selection.snapshots.every((snap) => snap.publish?.redaction === "applied")
|
|
272
|
+
? ("applied" as const)
|
|
273
|
+
: ("sensitive" as const);
|
|
274
|
+
return { viewData, artifactDirs, attemptsByBase, reportHtml, publishState };
|
|
265
275
|
}
|
|
266
276
|
|
|
267
277
|
/**
|
|
@@ -288,7 +298,7 @@ export async function loadAttemptIndex(input?: string): Promise<Map<string, Atte
|
|
|
288
298
|
|
|
289
299
|
/**
|
|
290
300
|
* 报告槽渲染:装载报告文件(--report;dev server 语义 —— 文件变更下次请求整页重算,
|
|
291
|
-
* 经 mtime cache-busting),缺省用内置默认报告
|
|
301
|
+
* 经 mtime cache-busting),缺省用内置默认报告 ExperimentComparison → 注入 Selection →
|
|
292
302
|
* web 面 renderToStaticMarkup 成静态 HTML,en / zh-CN 各渲染一遍(chrome 文案按 locale)。
|
|
293
303
|
* react / react-dom 动态加载:data.ts 还被 runner 的续跑携带(loadLatestResultsPerEval)
|
|
294
304
|
* 消费,渲染依赖不进那条路径。attemptHref 缺省即 `#/attempt/@<locator>` 深链路由(web.ts 的默认值),
|
|
@@ -307,7 +317,7 @@ async function renderReportSlot(
|
|
|
307
317
|
// 一个走 src。
|
|
308
318
|
const definition = report
|
|
309
319
|
? await loadReportFile(report.cwd, report.path, { freshImport: true })
|
|
310
|
-
: (await import("../../dist/report/built-ins/index.js")).
|
|
320
|
+
: (await import("../../dist/report/built-ins/index.js")).ExperimentComparison;
|
|
311
321
|
const { renderReportToStaticHtml } = await import("../../dist/report/web.js");
|
|
312
322
|
const ctx = { selection, results };
|
|
313
323
|
return {
|
package/src/view/index.ts
CHANGED
|
@@ -77,7 +77,7 @@ export function resolveViewInput(
|
|
|
77
77
|
* 导出静态报告(--out):只有目录式一种形态。写 <dir>/index.html,并把前端会 fetch 的 artifact
|
|
78
78
|
* (sources.json / events.json / trace.json)复制到 <dir>/artifact/<base>/——与本地
|
|
79
79
|
* server 的 /artifact/<rel> 路由同一布局,整个目录扔给任何静态托管即是完整体验。
|
|
80
|
-
* 首页即报告槽(裸跑填充
|
|
80
|
+
* 首页即报告槽(裸跑填充 ExperimentComparison,--report 整槽替换),证据室同站。
|
|
81
81
|
* 单文件(*.html)导出已移除:代码/transcript/trace 视图依赖 artifact 文件,单文件注定残缺,
|
|
82
82
|
* 存在本身就在诱导用户导出一份看不了证据的报告(docs/feature/reports/view.md「静态导出」)。
|
|
83
83
|
*/
|
|
@@ -89,6 +89,17 @@ export async function buildView(opts: ViewOptions = {}): Promise<string> {
|
|
|
89
89
|
);
|
|
90
90
|
}
|
|
91
91
|
const scan = await loadViewScan(opts.input, opts.scan);
|
|
92
|
+
// --out 按数据等级防呆(见 docs/feature/reports/view.md「静态导出」):目标结果根的全部快照
|
|
93
|
+
// 带 publish:{redaction:"applied"}(copySnapshots 补记)时直接导出;redaction:"none"、
|
|
94
|
+
// 无标记结果或本地事实根,都必须显式传 --allow-sensitive-artifacts——静态站原样携带证据文件,
|
|
95
|
+
// 上游声明过原文发布也不豁免这里的确认。
|
|
96
|
+
if (!opts.allowSensitiveArtifacts && scan.publishState !== "applied") {
|
|
97
|
+
throw new ViewInputError(
|
|
98
|
+
`--out refuses to export unsanitized results: not every selected snapshot carries publish: { redaction: "applied" }. ` +
|
|
99
|
+
`Produce a publish root first with copySnapshots({ redact }) and export that (niceeval view --run <publish-root> --out <site>), ` +
|
|
100
|
+
`or pass --allow-sensitive-artifacts to explicitly export raw evidence (prompts, tool args, full outputs, sources).`,
|
|
101
|
+
);
|
|
102
|
+
}
|
|
92
103
|
await mkdir(out, { recursive: true });
|
|
93
104
|
await writeFile(join(out, "index.html"), await renderHtml(scan), "utf-8");
|
|
94
105
|
await copyFetchedArtifacts(scan, join(out, "artifact"));
|
package/src/view/server.ts
CHANGED
|
@@ -12,6 +12,8 @@ export interface ViewOptions {
|
|
|
12
12
|
input?: string;
|
|
13
13
|
out?: string;
|
|
14
14
|
port?: number;
|
|
15
|
+
/** `--out` 对非发布根(无 publish:applied 标记)导出时的显式确认;静态站原样携带证据文件。 */
|
|
16
|
+
allowSensitiveArtifacts?: boolean;
|
|
15
17
|
/** 报告槽的组合语义(位置前缀 / --experiment / --report),透传给 loadViewScan。 */
|
|
16
18
|
scan?: ViewScanOptions;
|
|
17
19
|
}
|
package/src/view/shared/types.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
//
|
|
4
4
|
// viewData 只携带证据室与壳需要的东西:快照明细(locator / artifactBase 已注入)、
|
|
5
5
|
// skipped、项目名与 run 元信息。统计口径(KPI / 榜单 / 挑选警告)整体住在报告槽的
|
|
6
|
-
// 静态 HTML 里(
|
|
6
|
+
// 静态 HTML 里(ExperimentComparison 或 --report 的报告自己算),壳与报告之间没有第二条数据通道。
|
|
7
7
|
|
|
8
8
|
import type { EvalResult, LocalizedText } from "../../types.ts";
|
|
9
9
|
import type { ReportLocale } from "../../report/locale.ts";
|
package/src/view/styles.css
CHANGED
|
@@ -849,6 +849,7 @@ a.brand:hover { opacity: 0.75; }
|
|
|
849
849
|
.gstat.good { color: var(--good); }
|
|
850
850
|
.gstat.bad { color: var(--bad); }
|
|
851
851
|
.gstat.warn { color: var(--warn); }
|
|
852
|
+
.gstat.na { color: var(--muted, #8a8f98); }
|
|
852
853
|
.gsend { width: 13px; height: 13px; flex-shrink: 0; color: var(--blue); }
|
|
853
854
|
|
|
854
855
|
.ctext { white-space: pre; color: var(--text); padding-left: 8px; }
|
|
@@ -866,6 +867,8 @@ a.brand:hover { opacity: 0.75; }
|
|
|
866
867
|
.abadge.good { color: var(--good); border-color: color-mix(in oklch, var(--good), var(--line) 55%); background: color-mix(in oklch, var(--good), transparent 90%); }
|
|
867
868
|
.abadge.bad { color: var(--bad); border-color: color-mix(in oklch, var(--bad), var(--line) 55%); background: color-mix(in oklch, var(--bad), transparent 90%); }
|
|
868
869
|
.abadge.warn { color: var(--warn); border-color: color-mix(in oklch, var(--warn), var(--line) 55%); background: color-mix(in oklch, var(--warn), transparent 90%); }
|
|
870
|
+
/* unavailable:独立第三态(非红非绿),证据评不了 ≠ 失败 */
|
|
871
|
+
.abadge.na { color: var(--muted, #8a8f98); border-color: var(--line); background: color-mix(in oklch, var(--muted, #8a8f98), transparent 92%); }
|
|
869
872
|
.abadge-th { opacity: 0.55; font-weight: 400; }
|
|
870
873
|
|
|
871
874
|
/* 可点击行的显式提示:send 行一个 "reply" 药丸,断言行一个右侧展开箭头;hover 时变亮。 */
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
// cases: docs/engineering/unit-tests/reports/cases.md
|
|
1
2
|
// niceeval view 的报告槽与宿主组合语义(docs/feature/reports/architecture.md「Selection 是计算入口」
|
|
2
3
|
// 与裁决记录 6;公开行为准绳 docs-site/zh/guides/viewing-results.mdx / custom-reports.mdx)。
|
|
3
4
|
// 覆盖:
|
|
4
5
|
// - 组合语义与 show 对齐:位置前缀收窄报告槽 Selection、--experiment 过滤、匹配不到直说;
|
|
5
6
|
// - 单文件模式共存:存在的文件路径 → 单文件模式,目录报错直说走 --run,其余按 eval 前缀;
|
|
6
|
-
// - 报告槽恒在:裸跑填充
|
|
7
|
-
// 裸跑 ≡ --report <re-export
|
|
7
|
+
// - 报告槽恒在:裸跑填充 ExperimentComparison,--report 整槽替换;en / zh-CN 双语各渲染一遍;
|
|
8
|
+
// 裸跑 ≡ --report <re-export ExperimentComparison 的文件>(等价性);
|
|
8
9
|
// - --out 静态导出:index.html 含两个语言的报告块、官方样式与增强 runtime,报告块零 <script>;
|
|
9
10
|
// - dev server 装载语义:报告文件变更 → 下次装载整页重算(mtime cache-busting)。
|
|
10
11
|
//
|
|
@@ -84,7 +85,7 @@ async function seedRoot(): Promise<string> {
|
|
|
84
85
|
await writeSnapshot(root, "compare_bub", "2026-07-08T10-00-00-000Z", { experimentId: "compare/bub", agent: "bub", startedAt: "2026-07-08T10:00:00.000Z" }, [
|
|
85
86
|
res("weather/brooklyn", "passed"),
|
|
86
87
|
res("fixtures/button", "failed", {
|
|
87
|
-
assertions: [{ name: 'fileChanged("Button.tsx")', severity: "gate", score: 0,
|
|
88
|
+
assertions: [{ name: 'fileChanged("Button.tsx")', severity: "gate", score: 0, outcome: "failed" as const }],
|
|
88
89
|
}),
|
|
89
90
|
]);
|
|
90
91
|
await writeSnapshot(root, "compare_codex", "2026-07-09T10-00-00-000Z", { experimentId: "compare/codex", agent: "codex", startedAt: "2026-07-09T10:00:00.000Z" }, [
|
|
@@ -169,10 +170,10 @@ describe("loadViewScan · 组合语义", () => {
|
|
|
169
170
|
});
|
|
170
171
|
});
|
|
171
172
|
|
|
172
|
-
// ─────────────────── 报告槽恒在:裸跑 ≡ --report <
|
|
173
|
+
// ─────────────────── 报告槽恒在:裸跑 ≡ --report <ExperimentComparison> ───────────────────
|
|
173
174
|
|
|
174
175
|
describe("loadViewScan · 默认报告槽(裸跑)", () => {
|
|
175
|
-
it("裸跑产出的报告槽 HTML 与 --report <re-export
|
|
176
|
+
it("裸跑产出的报告槽 HTML 与 --report <re-export ExperimentComparison 的文件> 完全一致(双语)", async () => {
|
|
176
177
|
const root = await seedRoot();
|
|
177
178
|
const bare = await loadViewScan(root);
|
|
178
179
|
const viaReport = await loadViewScan(root, {
|
|
@@ -185,8 +186,8 @@ describe("loadViewScan · 默认报告槽(裸跑)", () => {
|
|
|
185
186
|
it("报告槽双语渲染:同一棵树按 locale 渲染两遍,chrome 文案分语言、数据不分语言", async () => {
|
|
186
187
|
const root = await seedRoot();
|
|
187
188
|
const { reportHtml } = await loadViewScan(root);
|
|
188
|
-
expect(reportHtml.en).toContain("
|
|
189
|
-
expect(reportHtml["zh-CN"]).toContain("
|
|
189
|
+
expect(reportHtml.en).toContain("End-to-end pass rate"); // ExperimentList 主行(en)
|
|
190
|
+
expect(reportHtml["zh-CN"]).toContain("端到端成功率"); // ExperimentList 主行(zh-CN)
|
|
190
191
|
for (const html of [reportHtml.en, reportHtml["zh-CN"]]) {
|
|
191
192
|
expect(html).toContain("compare/bub");
|
|
192
193
|
// 失败案例深链进证据室:不透明 AttemptLocator 单段路由 `#/attempt/@<locator>`,
|
|
@@ -323,7 +324,7 @@ describe("buildView · --out 与 --report", () => {
|
|
|
323
324
|
await writeFile(join(artifactDir, "events.json"), "[]", "utf-8");
|
|
324
325
|
|
|
325
326
|
const out = join(root, "site");
|
|
326
|
-
await buildView({ input: root, out, scan: { report: { path: EXAM_REPORT, cwd: root } } });
|
|
327
|
+
await buildView({ input: root, out, allowSensitiveArtifacts: true, scan: { report: { path: EXAM_REPORT, cwd: root } } });
|
|
327
328
|
|
|
328
329
|
const html = await readFile(join(out, "index.html"), "utf-8");
|
|
329
330
|
// 双语两个 <template> 静态块都在,壳按界面语言摆放。
|
|
@@ -340,10 +341,10 @@ describe("buildView · --out 与 --report", () => {
|
|
|
340
341
|
).toBe(true);
|
|
341
342
|
});
|
|
342
343
|
|
|
343
|
-
it("默认导出(无 --report):报告槽填充
|
|
344
|
+
it("默认导出(无 --report):报告槽填充 ExperimentComparison,双语块与增强 runtime 恒内联", async () => {
|
|
344
345
|
const root = await seedRoot();
|
|
345
346
|
const out = join(root, "site");
|
|
346
|
-
await buildView({ input: root, out });
|
|
347
|
+
await buildView({ input: root, out, allowSensitiveArtifacts: true });
|
|
347
348
|
const html = await readFile(join(out, "index.html"), "utf-8");
|
|
348
349
|
expect(html).toContain('<template id="niceeval-report-en">');
|
|
349
350
|
expect(html).toContain('<template id="niceeval-report-zh-CN">');
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import type { InputRequest, JsonValue, StreamEvent, ToolName, TraceSpan } from "../types.ts";
|
|
2
|
-
interface ExecutionNodeBase {
|
|
3
|
-
/** 本函数内确定性生成,同一份 (events, spans) 输入永远产出同一批 id(不是全局稳定 id,
|
|
4
|
-
* 不跨调用持久化——目前没有下游需要跨次运行比对同一个节点)。 */
|
|
5
|
-
id: string;
|
|
6
|
-
/** 唯一关联上的 OTel span(供下钻;渲染层用 endMs - startMs 算耗时)。action 节点的
|
|
7
|
-
* span.attributes 额外补了 io.tool/io.input/io.output/io.status(见 withIoAttributes);
|
|
8
|
-
* 其余节点原样保留。缺失 = timing unavailable——要么这次运行没有 OTel 接入,要么有 span
|
|
9
|
-
* 但没能唯一关联到这个节点上;两种情况都不是「假装有耗时」,统一表现为字段不存在。 */
|
|
10
|
-
span?: TraceSpan;
|
|
11
|
-
}
|
|
12
|
-
export interface ExecutionMessageNode extends ExecutionNodeBase {
|
|
13
|
-
kind: "message";
|
|
14
|
-
role: "assistant" | "user";
|
|
15
|
-
text: string;
|
|
16
|
-
}
|
|
17
|
-
export interface ExecutionThinkingNode extends ExecutionNodeBase {
|
|
18
|
-
kind: "thinking";
|
|
19
|
-
text: string;
|
|
20
|
-
}
|
|
21
|
-
/** Skill 加载节点——一等,直接来自 StreamEvent 的 "skill.loaded",不靠工具名/文本猜。 */
|
|
22
|
-
export interface ExecutionSkillNode extends ExecutionNodeBase {
|
|
23
|
-
kind: "skill.loaded";
|
|
24
|
-
skill: string;
|
|
25
|
-
/**
|
|
26
|
-
* 仅当原生协议把 Skill 加载表达成可关联的工具调用时才有,和 StreamEvent 同名字段同一含义。
|
|
27
|
-
* 存在时参与和 action/subagent 节点同一套 callId 关联(见主函数 nodeByCallId):Claude Code
|
|
28
|
-
* 的 Skill 调用本身就是一次 tool_use,OTel mapper 同样会把它的 tool_use_id 复制成
|
|
29
|
-
* span.attributes.call_id,唯一命中时这个节点也应该拿到 timing,不因为节点 kind 是
|
|
30
|
-
* skill.loaded 就被排除在 enrichment 之外。
|
|
31
|
-
*/
|
|
32
|
-
callId?: string;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* action.called + action.result 按 callId 合并成一个节点。`status` 多了 "pending"
|
|
36
|
-
* (StreamEvent 的 action.result.status 没有这一档)——这是 ExecutionTree 独有的中间态,
|
|
37
|
-
* 表示这次运行结束时结果始终没有回来(如超时截断的 transcript),诚实地区分「还没完成」
|
|
38
|
-
* 和「跑完但失败/被拒绝」。
|
|
39
|
-
*/
|
|
40
|
-
export interface ExecutionActionNode extends ExecutionNodeBase {
|
|
41
|
-
kind: "action";
|
|
42
|
-
callId: string;
|
|
43
|
-
/** 原始工具名(未归一化),对齐 StreamEvent.action.called.name。 */
|
|
44
|
-
name: string;
|
|
45
|
-
/** 归一化后的规范工具名,省略表示 adapter 没能归一(等同 ToolCall.name 缺省为 "unknown")。 */
|
|
46
|
-
tool?: ToolName;
|
|
47
|
-
input: JsonValue;
|
|
48
|
-
output?: JsonValue;
|
|
49
|
-
status: "pending" | "completed" | "failed" | "rejected";
|
|
50
|
-
}
|
|
51
|
-
/** subagent.called + subagent.completed 按 callId 合并;pending 语义同 ExecutionActionNode。 */
|
|
52
|
-
export interface ExecutionSubagentNode extends ExecutionNodeBase {
|
|
53
|
-
kind: "subagent";
|
|
54
|
-
callId: string;
|
|
55
|
-
name: string;
|
|
56
|
-
remoteUrl?: string;
|
|
57
|
-
output?: JsonValue;
|
|
58
|
-
status: "pending" | "completed" | "failed";
|
|
59
|
-
}
|
|
60
|
-
export interface ExecutionInputRequestedNode extends ExecutionNodeBase {
|
|
61
|
-
kind: "input.requested";
|
|
62
|
-
request: InputRequest;
|
|
63
|
-
}
|
|
64
|
-
export interface ExecutionCompactionNode extends ExecutionNodeBase {
|
|
65
|
-
kind: "compaction";
|
|
66
|
-
reason?: string;
|
|
67
|
-
}
|
|
68
|
-
export interface ExecutionErrorNode extends ExecutionNodeBase {
|
|
69
|
-
kind: "error";
|
|
70
|
-
message: string;
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* span 存在、有意义,但唯一关联不到任何骨架节点——原样保留成独立节点,清楚标注「只有遥测、
|
|
74
|
-
* 没有对应事件」,不悄悄猜着并进某个骨架节点。`span` 是必填字段:这类节点的全部内容就是
|
|
75
|
-
* 这一条 span 本身,没有骨架事件可以叠加。
|
|
76
|
-
*/
|
|
77
|
-
export interface ExecutionTelemetryNode {
|
|
78
|
-
kind: "telemetry";
|
|
79
|
-
id: string;
|
|
80
|
-
span: TraceSpan;
|
|
81
|
-
}
|
|
82
|
-
export type ExecutionNode = ExecutionMessageNode | ExecutionThinkingNode | ExecutionSkillNode | ExecutionActionNode | ExecutionSubagentNode | ExecutionInputRequestedNode | ExecutionCompactionNode | ExecutionErrorNode | ExecutionTelemetryNode;
|
|
83
|
-
export interface ExecutionTree {
|
|
84
|
-
/**
|
|
85
|
-
* 骨架节点在前,顺序 = 事件出现顺序;telemetry-only 节点(未能唯一关联的 span)
|
|
86
|
-
* 按 span.startMs 追加在骨架之后——它们不属于骨架,不改变骨架的节点/顺序/内容,
|
|
87
|
-
* 只是叠加在末尾的额外证据。
|
|
88
|
-
*/
|
|
89
|
-
nodes: ExecutionNode[];
|
|
90
|
-
/**
|
|
91
|
-
* 这次运行是否提供过任何 span——不代表关联成功,只代表「OTel 接入过」。供渲染层区分
|
|
92
|
-
* 两种不同的诚实提示:整体没有 OTel 接入(该字段为 false,所有节点自然都没有 span),
|
|
93
|
-
* 和 OTel 接入了但这一个节点恰好关联不上(该字段为 true,该节点的 span 仍缺失)。
|
|
94
|
-
*/
|
|
95
|
-
timingAvailable: boolean;
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* 纯函数:把标准事件流(骨架)与一批已挑选好的 OTel span(可选 enrichment)合成一棵
|
|
99
|
-
* ExecutionTree。events 决定节点的存在、顺序与内容;spans 只能给已存在的节点补时间,
|
|
100
|
-
* 从不新增、删除或重排骨架节点。
|
|
101
|
-
*/
|
|
102
|
-
export declare function buildExecutionTree(events: readonly StreamEvent[], spans: readonly TraceSpan[]): ExecutionTree;
|
|
103
|
-
export {};
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import type { ToolCall, TraceSpan } from "../../types.ts";
|
|
2
|
-
/**
|
|
3
|
-
* 选出要保留并落盘的 span。
|
|
4
|
-
* 小 trace 原样返回;大 trace 按 canonical kind 过滤(kind ≠ "other" 留);
|
|
5
|
-
* 没 mapper(kind 全 "other")则回落到 firehose 频率过滤;仍过多再按耗时硬截断。
|
|
6
|
-
* 最后一律按起点排序,view 直接当瀑布图渲染。
|
|
7
|
-
*/
|
|
8
|
-
export declare function selectTraceSpans(spans: TraceSpan[]): TraceSpan[];
|
|
9
|
-
/**
|
|
10
|
-
* I/O 文本上限:文件内容/命令输出可能很大,截一下别把 trace 撑爆。导出给
|
|
11
|
-
* `execution-tree.ts` 复用——ExecutionTree 的 action 节点给合并上去的 span 补同一份
|
|
12
|
-
* io.* attributes,必须和这里同一个截断预算,不能另立一个数字。
|
|
13
|
-
*/
|
|
14
|
-
export declare const IO_MAX = 4000;
|
|
15
|
-
export declare function ioText(v: unknown): string;
|
|
16
|
-
/**
|
|
17
|
-
* 给工具执行 span 补上真实「入参/出参」:codex 等的 OTLP span 只带 tool_name/call_id,
|
|
18
|
-
* 命令文本与输出在 stdout transcript(events)里 —— 按 call_id 把 deriveRunFacts 的
|
|
19
|
-
* ToolCall.input/output join 到对应 span 的 attributes(io.input / io.output / io.tool)。
|
|
20
|
-
* 没匹配上的 span 原样返回。
|
|
21
|
-
*/
|
|
22
|
-
export declare function enrichTraceWithIO(spans: TraceSpan[], toolCalls: readonly ToolCall[]): TraceSpan[];
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const CostPassRateComparison: import("../report.ts").ReportDefinition;
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
// CostPassRateComparison:show / view 裸跑时的出厂报告槽。
|
|
3
|
-
//
|
|
4
|
-
// 它是一份普通 ReportDefinition,零特权:只声明「摆什么」——成本 × 通过率的散点(比较
|
|
5
|
-
// experiments)和实验列表。散点走 selection-form(`MetricScatter` 声明了 `resolve`,宿主
|
|
6
|
-
// 渲染前的 resolveReportTree 会替它调 `.data`);`ExperimentList` 没有 selection-form,
|
|
7
|
-
// 报告函数体里直接 `await ExperimentList.data(selection)` 拿到普通数组再传 `items`——
|
|
8
|
-
// 这正是三个实体列表统一的用法,内置默认报告不搞一套特权写法。包外用户复制这段 TSX
|
|
9
|
-
// (只改 import 路径与 export 形式)会走完全相同的解析与渲染管线。
|
|
10
|
-
import { Col } from "../primitives.js";
|
|
11
|
-
import { ExperimentList, MetricScatter } from "../components.js";
|
|
12
|
-
import { costUSD, passRate } from "../metrics.js";
|
|
13
|
-
import { defineReport } from "../report.js";
|
|
14
|
-
export const CostPassRateComparison = defineReport(async ({ selection }) => {
|
|
15
|
-
const experiments = await ExperimentList.data(selection);
|
|
16
|
-
return (_jsxs(Col, { children: [_jsx(MetricScatter, { selection: selection, points: "experiment", series: "agent", x: costUSD, y: passRate }), _jsx(ExperimentList, { items: experiments })] }));
|
|
17
|
-
});
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import type { AssertionResult } from "../types.ts";
|
|
2
|
-
/** 一行源码 + 映射到这一行的全部断言(保持原始顺序;可以是空数组)。 */
|
|
3
|
-
export interface AnnotatedSourceLine {
|
|
4
|
-
/** 1-indexed 行号,与 SourceLoc.line 同一坐标系。 */
|
|
5
|
-
line: number;
|
|
6
|
-
/** 这一行的原始文本(已按 normalizeEvalSource 归一化,不含行尾换行符)。 */
|
|
7
|
-
text: string;
|
|
8
|
-
/**
|
|
9
|
-
* 映射到这一行的断言,按输入顺序(通常即执行顺序)排列。每条断言自带 severity / score /
|
|
10
|
-
* passed / detail / evidence —— 这就是"状态 / 严重度 / 分数 / detail / evidence"标回源码行
|
|
11
|
-
* 的完整信息,不需要额外的行级折叠字段:折叠成单一显示态是 renderer 的展示决定,不是这份
|
|
12
|
-
* 领域 model 的职责(与 indexAsserts() 的先例一致,它同样只分桶、不折叠)。
|
|
13
|
-
*/
|
|
14
|
-
assertions: AssertionResult[];
|
|
15
|
-
}
|
|
16
|
-
export interface AnnotatedEvalSourceSummary {
|
|
17
|
-
/** 参与统计的断言总数 = mappedAssertions + unmappedAssertions。 */
|
|
18
|
-
totalAssertions: number;
|
|
19
|
-
/** 成功映射到某一行源码的断言数。 */
|
|
20
|
-
mappedAssertions: number;
|
|
21
|
-
/** 落进 unmapped 桶的断言数。 */
|
|
22
|
-
unmappedAssertions: number;
|
|
23
|
-
/** 按 passed 计票(映射与未映射的断言都计入)。 */
|
|
24
|
-
passed: number;
|
|
25
|
-
failed: number;
|
|
26
|
-
/** 按 severity 计票(映射与未映射的断言都计入)。 */
|
|
27
|
-
gate: number;
|
|
28
|
-
soft: number;
|
|
29
|
-
/** 源码总行数,等于 lines.length。 */
|
|
30
|
-
totalLines: number;
|
|
31
|
-
/** 至少挂了一条断言的行数。 */
|
|
32
|
-
annotatedLines: number;
|
|
33
|
-
}
|
|
34
|
-
export interface AnnotatedEvalSource {
|
|
35
|
-
/** 项目相对路径,与 SourceArtifact.path / SourceLoc.file 同一约定。 */
|
|
36
|
-
sourcePath: string;
|
|
37
|
-
/** 归一化后源码文本的 SHA-256(与 captureEvalSource() 的算法一致,见 source-hash.ts)。 */
|
|
38
|
-
sourceSha256: string;
|
|
39
|
-
lines: AnnotatedSourceLine[];
|
|
40
|
-
/**
|
|
41
|
-
* 没有落到 lines 里任何一行的断言,原始顺序保留,永不丢弃。三种情况都进这里:
|
|
42
|
-
* 没有 SourceLoc(如 skip 前就出错的 eval、或调用方尚未收集到位置)、SourceLoc 指向
|
|
43
|
-
* 另一个文件(loc.file !== sourcePath —— 罕见,如断言写在共享 helper 里)、
|
|
44
|
-
* SourceLoc 的行号落在这份源码的行范围之外(源码与断言记录不同步的边界情况)。
|
|
45
|
-
* 后两种不是文档明确要求的场景,但"never silently dropped"的契约对它们同样成立 ——
|
|
46
|
-
* 这份 model 是分桶断言的唯一出口,不属于任何一行就必须出现在这里。
|
|
47
|
-
*/
|
|
48
|
-
unmapped: AssertionResult[];
|
|
49
|
-
summary: AnnotatedEvalSourceSummary;
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* 纯函数:给定一份源码文本(未归一化也可以,内部会 normalizeEvalSource)与一批断言,
|
|
53
|
-
* 产出标注好的 AnnotatedEvalSource。幂等 / 无 IO —— 可以在渲染路径上安全调用。
|
|
54
|
-
*
|
|
55
|
-
* 断言 → 源码行的映射规则:`a.loc` 存在、`a.loc.file === source.path`、且
|
|
56
|
-
* `1 <= a.loc.line <= lines.length` 三者同时成立才落到对应行;否则进 `unmapped`。
|
|
57
|
-
*/
|
|
58
|
-
export declare function buildAnnotatedEvalSource(source: {
|
|
59
|
-
path: string;
|
|
60
|
-
content: string;
|
|
61
|
-
}, assertions: readonly AssertionResult[]): AnnotatedEvalSource;
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import type { AttemptHandle } from "./types.ts";
|
|
2
|
-
import { type AttemptIdentity, type AttemptLocator } from "./locator.ts";
|
|
3
|
-
import type { AnnotatedEvalSource } from "./annotated-source.ts";
|
|
4
|
-
import { type ExecutionTree } from "../o11y/execution-tree.ts";
|
|
5
|
-
import type { DiffData, EvalResult } from "../types.ts";
|
|
6
|
-
/**
|
|
7
|
-
* 一个 Attempt 的 artifact 落盘位置。单一目录足够:一个 attempt 的全部 artifact
|
|
8
|
-
* (events.json / trace.json / o11y.json / diff.json / sources.json,见 ARTIFACT_KINDS)
|
|
9
|
-
* 要么全在它自己的 attempt 目录,要么(--resume 携带条目——本轮没写任何新数据,
|
|
10
|
-
* 见 writer.ts 对携带分支的说明)全在 artifactBase 回退到的原快照 attempt 目录,
|
|
11
|
-
* 从不出现"这个 kind 在自己目录、那个 kind 在 artifactBase"的拆分场景,不需要
|
|
12
|
-
* 按 kind 逐个给路径。想要某一种 artifact 的具体文件,自己拼
|
|
13
|
-
* `join(artifactPaths.dir, "<kind>.json")`(kind 见 `ArtifactKind`)。
|
|
14
|
-
*
|
|
15
|
-
* 与 show/render.ts 的 `attemptArtifactsPath(attempt, cwd)` 是同一份"这个 attempt 的证据在哪"
|
|
16
|
-
* 知识的两种呈现:那边为 CLI 文本展示把它转成 cwd 相对的短路径,这里给 evidence 层的调用方
|
|
17
|
-
* (view / 静态导出 / 报告列表,它们没有统一的"cwd"概念)一份绝对路径,不做相对化。
|
|
18
|
-
*/
|
|
19
|
-
export interface EvidencePaths {
|
|
20
|
-
/** 绝对路径。 */
|
|
21
|
-
dir: string;
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* 每一位只在数据真的存在且非空时为 true——从不因为对应的 artifact 文件"存在"就点亮,
|
|
25
|
-
* 见本文件头注逐位的门槛说明。show/view/静态导出/报告列表读这四位来决定要不要显示
|
|
26
|
-
* 对应的证据切面,不自己重新判断"这个 attempt 有没有 X"。
|
|
27
|
-
*/
|
|
28
|
-
export interface AttemptEvidenceCapabilities {
|
|
29
|
-
/** evalSource 非空且有内容(源码行或断言二选一非空)。 */
|
|
30
|
-
eval: boolean;
|
|
31
|
-
/** events 非空数组——有骨架可看。 */
|
|
32
|
-
execution: boolean;
|
|
33
|
-
/** execution 成立且这次运行接入过 OTel(ExecutionTree.timingAvailable)。 */
|
|
34
|
-
timing: boolean;
|
|
35
|
-
/** diff 非空且至少改动/删除了一个文件。 */
|
|
36
|
-
diff: boolean;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* 一个 Attempt 的完整证据聚合。除 `locator` 外每个字段要么是别的模块已经产出的纯数据类型,
|
|
40
|
-
* 要么是这四样拼出来的判定——本类型自己不发明新的证据形状。
|
|
41
|
-
*/
|
|
42
|
-
export interface AttemptEvidence {
|
|
43
|
-
locator: AttemptLocator;
|
|
44
|
-
/** locator 派生自的不可变身份元组,与 `locator` 编码的是同一份数据,供不想解码 locator
|
|
45
|
-
* 的调用方直接读结构化字段(如按 experimentId 分组、按时间排序)。 */
|
|
46
|
-
identity: AttemptIdentity;
|
|
47
|
-
result: EvalResult;
|
|
48
|
-
/** 运行时保存的 Eval 源码标注(`--eval` 切面);没有 sources() 就是 null,不伪造空文档。 */
|
|
49
|
-
evalSource: AnnotatedEvalSource | null;
|
|
50
|
-
/** 标准事件流 + OTel enrichment(`--execution` 切面);没有非空 events 就是 null,
|
|
51
|
-
* 不产出一棵只剩 telemetry-only 节点、没有真实骨架的误导性树(见头注 execution 门槛)。 */
|
|
52
|
-
execution: ExecutionTree | null;
|
|
53
|
-
/** 被测 Agent 对 Sandbox 工作区的文件变化(`--diff` 切面);原样透传 attempt.diff()。 */
|
|
54
|
-
diff: DiffData | null;
|
|
55
|
-
artifactPaths: EvidencePaths;
|
|
56
|
-
capabilities: AttemptEvidenceCapabilities;
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* 纯组合:一次性 await 好 attempt 的四类懒加载证据(events / trace / diff / 经
|
|
60
|
-
* loadAnnotatedEvalSource 解引用的 eval 源码),拼成一份 AttemptEvidence。不重新实现
|
|
61
|
-
* Eval 源码标注、ExecutionTree 合并或 diff 语义的任何一条规则——那些规则的家分别在
|
|
62
|
-
* annotated-source.ts / execution-tree.ts / attempt.diff() 自身,这里只调用与判定"够不够
|
|
63
|
-
* 亮起对应的证据切面"。
|
|
64
|
-
*
|
|
65
|
-
* `attempt.locator` 在真实读取路径(openResults() 产出的 handle)恒有值;缺失时按当前身份
|
|
66
|
-
* 元组按 encodeAttemptLocator 兜底算一份,同 open.ts 给 record.locator 做的兜底同一口径——
|
|
67
|
-
* 只服务手工构造 AttemptHandle 的测试场景,不改变真实读取路径的行为。
|
|
68
|
-
*/
|
|
69
|
-
export declare function loadAttemptEvidence(attempt: AttemptHandle): Promise<AttemptEvidence>;
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { AttemptHandle } from "./types.ts";
|
|
2
|
-
import { type AnnotatedEvalSource } from "./annotated-source.ts";
|
|
3
|
-
/**
|
|
4
|
-
* 给定一个 attempt,取回它的 eval 源码(经 sources() 解引用,可能来自本快照或
|
|
5
|
-
* artifactBase 回退到的原快照)与它的断言,产出标注好的 AnnotatedEvalSource。
|
|
6
|
-
*
|
|
7
|
-
* 没有 sources()(eval 从没被 `loc` 引用到、或落盘里压根没有 sources.json/artifactBase
|
|
8
|
-
* 已失效)时返回 null——没有源码就没有「标注源码」这回事,不伪造一份空文档。
|
|
9
|
-
*
|
|
10
|
-
* 一个 attempt 的 sources() 理论上可以有多份文件(主 eval 文件 + 罕见的跨文件 loc);
|
|
11
|
-
* 这里按「哪份文件被最多断言的 loc 命中」选一份作为主文件喂给 buildAnnotatedEvalSource——
|
|
12
|
-
* 只服务这条打通链路的验证目的,不是最终选择策略(后续阶段如需要为每份引用到的文件
|
|
13
|
-
* 都出一份 AnnotatedEvalSource,在这基础上很容易扩成返回数组)。
|
|
14
|
-
*/
|
|
15
|
-
export declare function loadAnnotatedEvalSource(attempt: AttemptHandle): Promise<AnnotatedEvalSource | null>;
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
// CostPassRateComparison:show / view 裸跑时的出厂报告槽。
|
|
2
|
-
//
|
|
3
|
-
// 它是一份普通 ReportDefinition,零特权:只声明「摆什么」——成本 × 通过率的散点(比较
|
|
4
|
-
// experiments)和实验列表。散点走 selection-form(`MetricScatter` 声明了 `resolve`,宿主
|
|
5
|
-
// 渲染前的 resolveReportTree 会替它调 `.data`);`ExperimentList` 没有 selection-form,
|
|
6
|
-
// 报告函数体里直接 `await ExperimentList.data(selection)` 拿到普通数组再传 `items`——
|
|
7
|
-
// 这正是三个实体列表统一的用法,内置默认报告不搞一套特权写法。包外用户复制这段 TSX
|
|
8
|
-
// (只改 import 路径与 export 形式)会走完全相同的解析与渲染管线。
|
|
9
|
-
|
|
10
|
-
import { Col } from "../primitives.tsx";
|
|
11
|
-
import { ExperimentList, MetricScatter } from "../components.tsx";
|
|
12
|
-
import { costUSD, passRate } from "../metrics.ts";
|
|
13
|
-
import { defineReport } from "../report.ts";
|
|
14
|
-
|
|
15
|
-
export const CostPassRateComparison = defineReport(async ({ selection }) => {
|
|
16
|
-
const experiments = await ExperimentList.data(selection);
|
|
17
|
-
return (
|
|
18
|
-
<Col>
|
|
19
|
-
<MetricScatter selection={selection} points="experiment" series="agent" x={costUSD} y={passRate} />
|
|
20
|
-
<ExperimentList items={experiments} />
|
|
21
|
-
</Col>
|
|
22
|
-
);
|
|
23
|
-
});
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
// 控制台报告器:流式逐行输出,失败断言内联展开,末尾出效率三件套(时间 / token / $)。
|
|
2
|
-
|
|
3
|
-
import type { EvalResult, Reporter, RunSummary } from "../../types.ts";
|
|
4
|
-
import { t } from "../../i18n/index.ts";
|
|
5
|
-
import { formatCost, formatDuration, formatTokens, renderRunReport } from "./table.ts";
|
|
6
|
-
import { verdictSymbol } from "./shared.ts";
|
|
7
|
-
|
|
8
|
-
export function Console(): Reporter {
|
|
9
|
-
return {
|
|
10
|
-
onRunStart(evals, _agent, shape) {
|
|
11
|
-
// compare(多 agent / 多 model)或 runs>1 时,实际 attempt 数 > eval 数。头部如实报清,
|
|
12
|
-
// 否则「本次运行 5 个 eval」会和末尾「5 passed, 5 failed」(按 attempt 计 10)对不上。
|
|
13
|
-
const n = shape?.evals ?? evals.length;
|
|
14
|
-
const extra =
|
|
15
|
-
shape && shape.totalRuns > n
|
|
16
|
-
? t("report.runStartExtra", { configs: shape.configs, totalRuns: shape.totalRuns })
|
|
17
|
-
: "";
|
|
18
|
-
process.stdout.write(t("report.runStart", { count: n, extra, concurrency: shape?.maxConcurrency ?? "?" }));
|
|
19
|
-
},
|
|
20
|
-
onEvalComplete(result: EvalResult) {
|
|
21
|
-
const sym = verdictSymbol(result.verdict);
|
|
22
|
-
const tok = (result.usage?.inputTokens ?? 0) + (result.usage?.outputTokens ?? 0);
|
|
23
|
-
// requests > 0 但 tokens = 0 → agent 跑了但不上报用量(如 bub);显示 — 而非误导性的 0
|
|
24
|
-
const tokStr = tok > 0 ? `${formatTokens(tok)} tok` : (result.usage?.requests ?? 0) > 0 ? `— tok` : `0 tok`;
|
|
25
|
-
const cost = result.estimatedCostUSD !== undefined ? ` ${formatCost(result.estimatedCostUSD)}` : "";
|
|
26
|
-
const who = result.model ? `${result.agent}/${result.model}` : result.agent;
|
|
27
|
-
const meta = `(${formatDuration(result.durationMs)} ${tokStr}${cost})`;
|
|
28
|
-
const label = result.verdict === "passed" ? "" : ` ${formatVerdict(result.verdict)}`;
|
|
29
|
-
process.stdout.write(` ${sym} ${result.id}${label} [${who}] ${meta}\n`);
|
|
30
|
-
|
|
31
|
-
if (result.skipReason) {
|
|
32
|
-
process.stdout.write(` ○ ${t("report.skipped")}: ${result.skipReason}\n`);
|
|
33
|
-
}
|
|
34
|
-
if (result.error) {
|
|
35
|
-
process.stdout.write(` ! ${t("report.error")}: ${truncate(result.error, 400)}\n`);
|
|
36
|
-
}
|
|
37
|
-
let lastGroup: string | undefined;
|
|
38
|
-
for (const a of result.assertions) {
|
|
39
|
-
if (a.passed) continue;
|
|
40
|
-
if (a.group !== undefined && a.group !== lastGroup) {
|
|
41
|
-
process.stdout.write(` ▸ ${a.group}\n`);
|
|
42
|
-
}
|
|
43
|
-
lastGroup = a.group;
|
|
44
|
-
const sev = a.severity === "gate" ? t("report.gate") : t("report.soft");
|
|
45
|
-
const thr = a.threshold !== undefined
|
|
46
|
-
? t("report.assertionThreshold", { score: a.score.toFixed(2), threshold: a.threshold })
|
|
47
|
-
: "";
|
|
48
|
-
const indent = a.group !== undefined ? " " : " ";
|
|
49
|
-
process.stdout.write(`${indent}- ${sev}: ${a.name}${thr}${a.detail ? ` — ${truncate(a.detail, 300)}` : ""}\n`);
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
|
-
onRunComplete(summary: RunSummary) {
|
|
53
|
-
process.stdout.write(renderRunReport(summary));
|
|
54
|
-
},
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function formatVerdict(verdict: string): string {
|
|
59
|
-
switch (verdict) {
|
|
60
|
-
case "passed": return t("report.passed");
|
|
61
|
-
case "failed": return t("report.failed");
|
|
62
|
-
case "errored": return t("report.errored");
|
|
63
|
-
case "skipped": return t("report.skipped");
|
|
64
|
-
default: return verdict;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function truncate(s: string, n: number): string {
|
|
69
|
-
return s.length > n ? s.slice(0, n) + "…" : s;
|
|
70
|
-
}
|