niceeval 0.9.1 → 0.10.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 +31 -33
- package/dist/agents/types.d.ts +20 -17
- package/dist/context/types.d.ts +21 -21
- package/dist/i18n/en.d.ts +10 -2
- package/dist/i18n/zh-CN.d.ts +14 -6
- package/dist/o11y/execution-tree.d.ts +103 -0
- package/dist/o11y/otlp/select.d.ts +22 -0
- package/dist/report/{react → assets}/colors.d.ts +8 -0
- package/dist/report/{react → assets}/colors.js +23 -0
- package/dist/report/built-in/index.d.ts +2 -2
- package/dist/report/built-in/index.js +2 -2
- package/dist/report/built-in/standard.d.ts +8 -1
- package/dist/report/built-in/standard.js +17 -7
- package/dist/report/components/attempt-detail/AttemptAssertions.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptAssertions.js +17 -0
- package/dist/report/components/attempt-detail/AttemptConversation.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptConversation.js +34 -0
- package/dist/report/components/attempt-detail/AttemptDiagnostics.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptDiagnostics.js +7 -0
- package/dist/report/components/attempt-detail/AttemptDiff.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptDiff.js +11 -0
- package/dist/report/components/attempt-detail/AttemptError.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptError.js +16 -0
- package/dist/report/components/attempt-detail/AttemptFixPrompt.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptFixPrompt.js +7 -0
- package/dist/report/components/attempt-detail/AttemptSource.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptSource.js +27 -0
- package/dist/report/components/attempt-detail/AttemptSummary.d.ts +8 -0
- package/dist/report/components/attempt-detail/AttemptSummary.js +20 -0
- package/dist/report/components/attempt-detail/AttemptTimeline.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptTimeline.js +28 -0
- package/dist/report/components/attempt-detail/AttemptTrace.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptTrace.js +29 -0
- package/dist/report/components/attempt-detail/AttemptUsage.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptUsage.js +20 -0
- package/dist/report/components/attempt-detail/compute.d.ts +24 -0
- package/dist/report/components/attempt-detail/compute.js +254 -0
- package/dist/report/components/attempt-detail/faces.d.ts +14 -0
- package/dist/report/components/attempt-detail/faces.js +235 -0
- package/dist/report/components/attempt-detail/index.d.ts +38 -0
- package/dist/report/components/attempt-detail/index.js +527 -0
- package/dist/report/{react → components}/cell.d.ts +5 -3
- package/dist/report/{react → components}/cell.js +3 -3
- package/dist/report/{react → components/entity-lists}/AttemptList.d.ts +8 -4
- package/dist/report/{react → components/entity-lists}/AttemptList.js +16 -10
- package/dist/report/{react → components/entity-lists}/EvalList.d.ts +3 -3
- package/dist/report/{react → components/entity-lists}/EvalList.js +0 -0
- package/dist/report/{react → components/entity-lists}/ExperimentList.d.ts +3 -3
- package/dist/report/{react → components/entity-lists}/ExperimentList.js +10 -10
- package/dist/report/components/entity-lists/compute.d.ts +23 -0
- package/dist/report/components/entity-lists/compute.js +171 -0
- package/dist/report/components/entity-lists/faces.d.ts +5 -0
- package/dist/report/components/entity-lists/faces.js +174 -0
- package/dist/report/components/entity-lists/index.d.ts +51 -0
- package/dist/report/components/entity-lists/index.js +165 -0
- package/dist/report/{react → components}/fixtures.d.ts +2 -2
- package/dist/report/{react → components}/fixtures.js +1 -1
- package/dist/report/{react → components/metric-views}/DeltaTable.d.ts +2 -2
- package/dist/report/{react → components/metric-views}/DeltaTable.js +4 -4
- package/dist/report/{react → components/metric-views}/MetricBars.d.ts +3 -3
- package/dist/report/{react → components/metric-views}/MetricBars.js +3 -3
- package/dist/report/{react → components/metric-views}/MetricLine.d.ts +2 -2
- package/dist/report/{react → components/metric-views}/MetricLine.js +8 -5
- package/dist/report/{react → components/metric-views}/MetricMatrix.d.ts +3 -3
- package/dist/report/{react → components/metric-views}/MetricMatrix.js +4 -4
- package/dist/report/components/metric-views/MetricScatter.d.ts +11 -0
- package/dist/report/{react → components/metric-views}/MetricScatter.js +26 -23
- package/dist/report/{react → components/metric-views}/MetricTable.d.ts +3 -3
- package/dist/report/{react → components/metric-views}/MetricTable.js +4 -4
- package/dist/report/{react → components/metric-views}/Scoreboard.d.ts +2 -2
- package/dist/report/{react → components/metric-views}/Scoreboard.js +3 -3
- package/dist/report/components/metric-views/compute.d.ts +89 -0
- package/dist/report/{compute.js → components/metric-views/compute.js} +32 -415
- package/dist/report/components/metric-views/faces.d.ts +13 -0
- package/dist/report/components/metric-views/faces.js +381 -0
- package/dist/report/components/metric-views/index.d.ts +50 -0
- package/dist/report/components/metric-views/index.js +273 -0
- package/dist/report/{text → components/metric-views}/plot.js +1 -1
- package/dist/report/components/shared-compute.d.ts +22 -0
- package/dist/report/components/shared-compute.js +47 -0
- package/dist/report/components/shared-faces.d.ts +9 -0
- package/dist/report/components/shared-faces.js +26 -0
- package/dist/report/components/shared.d.ts +68 -0
- package/dist/report/components/shared.js +125 -0
- package/dist/report/{react → components/site-components}/CopyFixPrompt.d.ts +2 -2
- package/dist/report/{react → components/site-components}/CopyFixPrompt.js +2 -2
- package/dist/report/{react → components/site-components}/HeroCard.d.ts +2 -2
- package/dist/report/{react → components/site-components}/HeroCard.js +2 -2
- package/dist/report/{react → components/site-components}/ScopeWarnings.d.ts +2 -2
- package/dist/report/{react → components/site-components}/ScopeWarnings.js +3 -3
- package/dist/report/{react → components/site-components}/TraceWaterfall.d.ts +3 -3
- package/dist/report/{react → components/site-components}/TraceWaterfall.js +5 -5
- package/dist/report/components/site-components/compute.d.ts +28 -0
- package/dist/report/components/site-components/compute.js +132 -0
- package/dist/report/components/site-components/faces.d.ts +21 -0
- package/dist/report/components/site-components/faces.js +75 -0
- package/dist/report/components/site-components/index.d.ts +74 -0
- package/dist/report/components/site-components/index.js +220 -0
- package/dist/report/{scope-warnings.d.ts → components/site-components/scope-warnings.d.ts} +2 -2
- package/dist/report/{scope-warnings.js → components/site-components/scope-warnings.js} +2 -2
- package/dist/report/{react → components/summaries}/ScopeSummary.d.ts +2 -2
- package/dist/report/{react → components/summaries}/ScopeSummary.js +17 -8
- package/dist/report/components/summaries/compute.d.ts +7 -0
- package/dist/report/components/summaries/compute.js +51 -0
- package/dist/report/components/summaries/faces.d.ts +7 -0
- package/dist/report/components/summaries/faces.js +38 -0
- package/dist/report/components/summaries/index.d.ts +27 -0
- package/dist/report/components/summaries/index.js +78 -0
- package/dist/report/definition/grid-layout.d.ts +50 -0
- package/dist/report/definition/grid-layout.js +89 -0
- package/dist/report/{primitives.d.ts → definition/primitives.d.ts} +34 -4
- package/dist/report/{primitives.js → definition/primitives.js} +101 -10
- package/dist/report/{report.d.ts → definition/report.d.ts} +36 -65
- package/dist/report/{report.js → definition/report.js} +58 -90
- package/dist/report/{text/table.d.ts → definition/table-text.d.ts} +2 -2
- package/dist/report/{text/table.js → definition/table-text.js} +2 -2
- package/dist/report/{tree.d.ts → definition/tree.d.ts} +43 -11
- package/dist/report/{tree.js → definition/tree.js} +9 -11
- package/dist/report/index.d.ts +33 -18
- package/dist/report/index.js +23 -14
- package/dist/report/{aggregate.d.ts → model/aggregate.d.ts} +12 -4
- package/dist/report/{aggregate.js → model/aggregate.js} +28 -8
- package/dist/report/{flag.d.ts → model/flag.d.ts} +14 -1
- package/dist/report/{flag.js → model/flag.js} +35 -0
- package/dist/report/{format.d.ts → model/format.d.ts} +11 -5
- package/dist/report/{format.js → model/format.js} +48 -4
- package/dist/report/{locale.d.ts → model/locale.d.ts} +17 -17
- package/dist/report/{locale.js → model/locale.js} +31 -33
- package/dist/report/{metrics.d.ts → model/metrics.d.ts} +2 -2
- package/dist/report/{metrics.js → model/metrics.js} +2 -2
- package/dist/report/{types.d.ts → model/types.d.ts} +140 -20
- package/dist/report/react/index.d.ts +32 -21
- package/dist/report/react/index.js +33 -21
- package/dist/report/{load.d.ts → runtime/load.d.ts} +1 -1
- package/dist/report/{load.js → runtime/load.js} +1 -1
- package/dist/report/runtime/text.d.ts +67 -0
- package/dist/report/runtime/text.js +114 -0
- package/dist/report/runtime/web.d.ts +35 -0
- package/dist/report/runtime/web.js +70 -0
- package/dist/results/annotated-source.d.ts +87 -0
- package/dist/results/attempt-evidence.d.ts +74 -0
- package/dist/results/attempt-source.d.ts +15 -0
- package/dist/results/select.d.ts +11 -1
- package/dist/results/select.js +27 -5
- package/dist/runner/eval-selection.d.ts +26 -0
- package/dist/runner/feedback/sink.d.ts +26 -1
- package/dist/runner/types.d.ts +149 -23
- package/dist/sandbox/docker.d.ts +6 -0
- package/dist/sandbox/types.d.ts +23 -21
- package/dist/scoring/display.d.ts +7 -2
- package/dist/scoring/display.js +42 -4
- package/dist/scoring/types.d.ts +3 -3
- package/dist/shared/aggregate.d.ts +14 -0
- package/dist/shared/aggregate.js +31 -0
- package/dist/shared/types.d.ts +6 -1
- package/docs-site/images/logo-dark.svg +7 -0
- package/docs-site/images/logo.svg +6 -5
- package/docs-site/zh/README.md +8 -7
- package/docs-site/zh/examples/ai-agent-application.mdx +5 -5
- package/docs-site/zh/examples/coding-agent-extensions.mdx +4 -4
- package/docs-site/zh/examples/index.mdx +3 -3
- package/docs-site/zh/examples/integrations/ai-sdk-v7.mdx +1 -1
- package/docs-site/zh/examples/integrations/claude-sdk.mdx +3 -3
- package/docs-site/zh/examples/integrations/codex-sdk.mdx +3 -3
- package/docs-site/zh/examples/integrations/langgraph.mdx +3 -3
- package/docs-site/zh/examples/integrations/pi-sdk.mdx +2 -2
- package/docs-site/zh/explanation/adapter.mdx +19 -19
- package/docs-site/zh/explanation/assert.mdx +9 -9
- package/docs-site/zh/explanation/drive.mdx +4 -4
- package/docs-site/zh/explanation/evals.mdx +8 -8
- package/docs-site/zh/explanation/experiment.mdx +8 -8
- package/docs-site/zh/explanation/hitl.mdx +13 -13
- package/docs-site/zh/explanation/judge.mdx +3 -3
- package/docs-site/zh/explanation/overview.mdx +7 -7
- package/docs-site/zh/explanation/runner.mdx +11 -11
- package/docs-site/zh/explanation/tier.mdx +6 -6
- package/docs-site/zh/index.mdx +14 -14
- package/docs-site/zh/introduction.mdx +13 -16
- package/docs-site/zh/reference/builtin-agents.mdx +61 -28
- package/docs-site/zh/reference/capabilities.mdx +7 -27
- package/docs-site/zh/reference/cli.mdx +35 -32
- package/docs-site/zh/reference/define-agent.mdx +32 -31
- package/docs-site/zh/reference/define-config.mdx +6 -6
- package/docs-site/zh/reference/define-eval.mdx +33 -20
- package/docs-site/zh/reference/events.mdx +4 -4
- package/docs-site/zh/reference/expect.mdx +3 -3
- package/docs-site/zh/reference/official-adapters.mdx +17 -17
- package/docs-site/zh/reference/report-components.mdx +193 -121
- package/docs-site/zh/reference/results-data.mdx +13 -13
- package/docs-site/zh/troubleshooting/debug-sandbox.mdx +11 -11
- package/docs-site/zh/troubleshooting/debugging.mdx +40 -16
- package/docs-site/zh/{how-to → tutorials}/agent-feedback-loop.mdx +38 -38
- package/docs-site/zh/tutorials/agent-onboarding.mdx +100 -0
- package/docs-site/zh/tutorials/authoring.mdx +211 -0
- package/docs-site/zh/{how-to → tutorials}/ci-integration.mdx +8 -8
- package/docs-site/zh/{how-to → tutorials}/connect-otel.mdx +22 -22
- package/docs-site/zh/{how-to → tutorials}/connect-your-agent.mdx +58 -63
- package/docs-site/zh/tutorials/custom-reports.mdx +453 -0
- package/docs-site/zh/{how-to → tutorials}/dataset-fanout.mdx +4 -4
- package/docs-site/zh/tutorials/experiments.mdx +103 -0
- package/docs-site/zh/{how-to → tutorials}/fixtures.mdx +9 -9
- package/docs-site/zh/{how-to → tutorials}/publish-report.mdx +6 -6
- package/docs-site/zh/tutorials/quickstart.mdx +18 -18
- package/docs-site/zh/{how-to → tutorials}/reporters.mdx +6 -6
- package/docs-site/zh/{how-to → tutorials}/sandbox-agent.mdx +6 -6
- package/docs-site/zh/{how-to → tutorials}/sandbox-providers.mdx +41 -19
- package/docs-site/zh/{how-to → tutorials}/scoring-guide.mdx +4 -4
- package/docs-site/zh/{how-to → tutorials}/viewing-results.mdx +49 -49
- package/docs-site/zh/tutorials/write-experiment.mdx +355 -0
- package/docs-site/zh/{how-to → tutorials}/write-send.mdx +40 -40
- package/package.json +8 -4
- package/src/agents/bub.ts +21 -8
- package/src/agents/claude-code.ts +23 -10
- package/src/agents/codex.test.ts +22 -7
- package/src/agents/codex.ts +22 -9
- package/src/agents/index.ts +2 -2
- package/src/agents/openai-compat.ts +1 -1
- package/src/agents/post-setup.ts +45 -22
- package/src/agents/streaming.ts +1 -1
- package/src/agents/types.ts +20 -17
- package/src/agents/ui-message-stream.test.ts +10 -0
- package/src/agents/ui-message-stream.ts +12 -1
- package/src/cli.ts +71 -57
- package/src/context/types.ts +21 -21
- package/src/define.ts +13 -0
- package/src/i18n/en.ts +27 -12
- package/src/i18n/zh-CN.ts +27 -12
- package/src/index.ts +2 -0
- package/src/report/{react → assets}/colors.ts +22 -0
- package/src/report/{react → assets}/enhance.js +1 -33
- package/src/report/{react → assets}/styles.css +124 -106
- package/src/report/built-in/index.tsx +2 -2
- package/src/report/built-in/standard.tsx +18 -6
- package/src/report/components/attempt-detail/AttemptAssertions.tsx +67 -0
- package/src/report/components/attempt-detail/AttemptConversation.tsx +110 -0
- package/src/report/components/attempt-detail/AttemptDiagnostics.tsx +38 -0
- package/src/report/components/attempt-detail/AttemptDiff.tsx +35 -0
- package/src/report/components/attempt-detail/AttemptError.tsx +31 -0
- package/src/report/components/attempt-detail/AttemptFixPrompt.tsx +26 -0
- package/src/report/components/attempt-detail/AttemptSource.tsx +80 -0
- package/src/report/components/attempt-detail/AttemptSummary.tsx +74 -0
- package/src/report/components/attempt-detail/AttemptTimeline.tsx +108 -0
- package/src/report/components/attempt-detail/AttemptTrace.tsx +63 -0
- package/src/report/components/attempt-detail/AttemptUsage.tsx +29 -0
- package/src/report/components/attempt-detail/attempt-components.test.tsx +680 -0
- package/src/report/components/attempt-detail/compute.ts +287 -0
- package/src/report/components/attempt-detail/faces.ts +257 -0
- package/src/report/components/attempt-detail/index.tsx +583 -0
- package/src/report/components/attempt-detail/validate.test.ts +235 -0
- package/src/report/{react → components}/cell.tsx +6 -3
- package/src/report/{report.test.ts → components/compute.test.ts} +316 -46
- package/src/report/{react → components/entity-lists}/AttemptList.tsx +24 -16
- package/src/report/{react → components/entity-lists}/EvalList.tsx +0 -0
- package/src/report/{react → components/entity-lists}/ExperimentList.tsx +23 -20
- package/src/report/components/entity-lists/compute.ts +196 -0
- package/src/report/components/entity-lists/faces.ts +207 -0
- package/src/report/components/entity-lists/index.tsx +241 -0
- package/src/report/components/entity-lists/validate.test.ts +114 -0
- package/src/report/{react → components}/fixtures.ts +2 -2
- package/src/report/{react → components/metric-views}/DeltaTable.tsx +5 -5
- package/src/report/{react → components/metric-views}/MetricBars.tsx +5 -5
- package/src/report/{react → components/metric-views}/MetricLine.tsx +9 -6
- package/src/report/{react → components/metric-views}/MetricMatrix.tsx +6 -6
- package/src/report/{react → components/metric-views}/MetricScatter.tsx +41 -33
- package/src/report/{react → components/metric-views}/MetricTable.tsx +6 -6
- package/src/report/{react → components/metric-views}/Scoreboard.tsx +4 -4
- package/src/report/{compute.ts → components/metric-views/compute.ts} +51 -477
- package/src/report/components/metric-views/faces.ts +423 -0
- package/src/report/components/metric-views/index.tsx +352 -0
- package/src/report/{text → components/metric-views}/plot.ts +1 -1
- package/src/report/components/metric-views/validate.test.ts +211 -0
- package/src/report/{react → components}/render.test.tsx +47 -17
- package/src/report/components/shared-compute.ts +59 -0
- package/src/report/components/shared-faces.ts +30 -0
- package/src/report/components/shared.ts +192 -0
- package/src/report/{react → components/site-components}/CopyFixPrompt.tsx +3 -3
- package/src/report/{react → components/site-components}/HeroCard.tsx +3 -3
- package/src/report/{react → components/site-components}/ScopeWarnings.tsx +4 -4
- package/src/report/{react → components/site-components}/TraceWaterfall.tsx +13 -10
- package/src/report/components/site-components/compute.ts +144 -0
- package/src/report/components/site-components/faces.ts +80 -0
- package/src/report/components/site-components/index.tsx +265 -0
- package/src/report/{scope-warnings.ts → components/site-components/scope-warnings.ts} +3 -3
- package/src/report/{site-components.test.tsx → components/site-components/site-components.test.tsx} +23 -21
- package/src/report/components/site-components/validate.test.ts +89 -0
- package/src/report/{react → components/summaries}/ScopeSummary.tsx +26 -11
- package/src/report/components/summaries/compute.ts +56 -0
- package/src/report/components/summaries/faces.ts +48 -0
- package/src/report/components/summaries/index.tsx +127 -0
- package/src/report/components/summaries/validate.test.ts +49 -0
- package/src/report/definition/grid-layout.test.ts +124 -0
- package/src/report/definition/grid-layout.ts +146 -0
- package/src/report/{primitives.tsx → definition/primitives.tsx} +168 -18
- package/src/report/{report.ts → definition/report.ts} +98 -165
- package/src/report/{shell-head.test.ts → definition/shell-head.test.ts} +2 -2
- package/src/report/{text/table.ts → definition/table-text.ts} +4 -4
- package/src/report/{tree.ts → definition/tree.ts} +56 -20
- package/src/report/index.ts +126 -66
- package/src/report/{aggregate.ts → model/aggregate.ts} +31 -9
- package/src/report/{flag.ts → model/flag.ts} +39 -1
- package/src/report/{format.ts → model/format.ts} +59 -5
- package/src/report/{locale.ts → model/locale.ts} +34 -36
- package/src/report/{metrics.ts → model/metrics.ts} +3 -3
- package/src/report/{types.ts → model/types.ts} +143 -22
- package/src/report/react/index.tsx +52 -26
- package/src/report/{dual-render.test.tsx → runtime/dual-render.test.tsx} +564 -49
- package/src/report/runtime/host.test.ts +44 -0
- package/src/report/runtime/host.ts +138 -0
- package/src/report/{load.ts → runtime/load.ts} +1 -1
- package/src/report/runtime/text.ts +192 -0
- package/src/report/runtime/web.ts +106 -0
- package/src/results/attempt-evidence.ts +5 -1
- package/src/results/host-equivalence.test.ts +53 -2
- package/src/results/select.ts +29 -5
- package/src/runner/attempt.test.ts +48 -1
- package/src/runner/attempt.ts +62 -42
- package/src/runner/cleanup-timeout.test.ts +16 -0
- package/src/runner/cleanup-timeout.ts +23 -0
- package/src/runner/discover.ts +1 -2
- package/src/runner/eval-selection.test.ts +187 -0
- package/src/runner/eval-selection.ts +109 -0
- package/src/runner/experiment-cleanup-registry.test.ts +89 -0
- package/src/runner/experiment-cleanup-registry.ts +39 -0
- package/src/runner/experiment-labels.test.ts +71 -0
- package/src/runner/feedback/agent.test.ts +33 -1
- package/src/runner/feedback/agent.ts +12 -16
- package/src/runner/feedback/ci.test.ts +35 -1
- package/src/runner/feedback/ci.ts +16 -18
- package/src/runner/feedback/coordinator.ts +28 -0
- package/src/runner/feedback/human.test.ts +108 -1
- package/src/runner/feedback/human.ts +71 -21
- package/src/runner/feedback/reducer.test.ts +64 -0
- package/src/runner/feedback/reducer.ts +26 -0
- package/src/runner/feedback/sink.ts +40 -1
- package/src/runner/fingerprint.ts +2 -1
- package/src/runner/report.test.ts +14 -0
- package/src/runner/report.ts +3 -1
- package/src/runner/run.test.ts +390 -12
- package/src/runner/run.ts +259 -26
- package/src/runner/sandbox-selection.test.ts +13 -3
- package/src/runner/sandbox-selection.ts +4 -2
- package/src/runner/types.ts +158 -23
- package/src/sandbox/docker.ts +7 -0
- package/src/sandbox/e2b.ts +12 -13
- package/src/sandbox/types.ts +23 -21
- package/src/scoring/display.test.ts +28 -2
- package/src/scoring/display.ts +38 -5
- package/src/scoring/types.ts +3 -3
- package/src/shared/aggregate.test.ts +52 -0
- package/src/shared/aggregate.ts +29 -0
- package/src/shared/types.ts +6 -1
- package/src/show/command.test.ts +34 -0
- package/src/show/command.ts +16 -0
- package/src/show/compose.ts +1 -1
- package/src/show/index.ts +56 -20
- package/src/show/render.ts +15 -239
- package/src/show/show.test.ts +127 -34
- package/src/view/app/App.test.tsx +1 -1
- package/src/view/app/App.tsx +119 -47
- package/src/view/app/i18n.ts +7 -147
- package/src/view/app/lib/attempt-dialog.test.ts +65 -0
- package/src/view/app/lib/attempt-dialog.ts +69 -0
- package/src/view/app/main.tsx +0 -1
- package/src/view/app/types.ts +3 -86
- package/src/view/artifact-serving.test.ts +22 -38
- package/src/view/client-dist/app.css +1 -1
- package/src/view/client-dist/app.js +15 -22
- package/src/view/data.test.ts +48 -36
- package/src/view/data.ts +105 -83
- package/src/view/index.ts +1 -1
- package/src/view/server.ts +32 -2
- package/src/view/shared/types.ts +6 -31
- package/src/view/site-parity.test.ts +24 -1
- package/src/view/site.ts +144 -17
- package/src/view/styles.css +0 -805
- package/src/view/view-report.test.ts +141 -22
- package/dist/report/components.d.ts +0 -179
- package/dist/report/components.js +0 -544
- package/dist/report/compute.d.ts +0 -139
- package/dist/report/react/ExperimentComparison.d.ts +0 -10
- package/dist/report/react/ExperimentComparison.js +0 -12
- package/dist/report/react/MetricScatter.d.ts +0 -9
- package/dist/report/react/format.d.ts +0 -3
- package/dist/report/react/format.js +0 -7
- package/dist/report/text/faces.d.ts +0 -45
- package/dist/report/text/faces.js +0 -671
- package/dist/report/web.d.ts +0 -32
- package/dist/report/web.js +0 -48
- package/docs-site/zh/how-to/authoring.mdx +0 -162
- package/docs-site/zh/how-to/custom-reports.mdx +0 -414
- package/docs-site/zh/how-to/experiments.mdx +0 -86
- package/docs-site/zh/how-to/write-experiment.mdx +0 -164
- package/src/report/components.tsx +0 -925
- package/src/report/react/ExperimentComparison.tsx +0 -73
- package/src/report/react/format.ts +0 -9
- package/src/report/text/faces.ts +0 -767
- package/src/report/web.ts +0 -77
- package/src/show/report-host.test.ts +0 -205
- package/src/show/report-host.ts +0 -389
- package/src/view/app/components/AttemptModal.tsx +0 -496
- package/src/view/app/components/CodeView.test.tsx +0 -142
- package/src/view/app/components/CodeView.tsx +0 -310
- package/src/view/app/components/CopyControls.tsx +0 -106
- package/src/view/app/components/Trace.tsx +0 -100
- package/src/view/app/components/Transcript.tsx +0 -157
- package/src/view/app/components/ui/badge.tsx +0 -21
- package/src/view/app/lib/artifact-url.ts +0 -17
- package/src/view/app/lib/attempt-route.test.ts +0 -80
- package/src/view/app/lib/attempt-route.ts +0 -52
- package/src/view/app/lib/format.ts +0 -70
- package/src/view/app/lib/guards.test.ts +0 -108
- package/src/view/app/lib/guards.ts +0 -71
- package/src/view/app/lib/rows.ts +0 -22
- package/src/view/app/lib/transcript-data.tsx +0 -151
- package/src/view/app/lib/verdict.ts +0 -23
- package/src/view/app/shared.ts +0 -8
- /package/dist/report/{react → components/metric-views}/chart-math.d.ts +0 -0
- /package/dist/report/{react → components/metric-views}/chart-math.js +0 -0
- /package/dist/report/{text → components/metric-views}/plot.d.ts +0 -0
- /package/dist/report/{react → components/site-components}/PoweredBy.d.ts +0 -0
- /package/dist/report/{react → components/site-components}/PoweredBy.js +0 -0
- /package/dist/report/{text/layout.d.ts → model/text-layout.d.ts} +0 -0
- /package/dist/report/{text/layout.js → model/text-layout.js} +0 -0
- /package/dist/report/{types.js → model/types.js} +0 -0
- /package/src/report/{react → components/metric-views}/chart-math.test.ts +0 -0
- /package/src/report/{react → components/metric-views}/chart-math.ts +0 -0
- /package/src/report/{react → components/site-components}/PoweredBy.tsx +0 -0
- /package/src/report/{text/layout.ts → model/text-layout.ts} +0 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// cases: docs/engineering/unit-tests/experiments-runner/cases.md
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { matchExperimentSelector } from "./aggregate.ts";
|
|
4
|
+
|
|
5
|
+
describe("matchExperimentSelector", () => {
|
|
6
|
+
const ids = [
|
|
7
|
+
"compare/bub-gpt-5.6-luna",
|
|
8
|
+
"compare/codex-gpt-5.6-luna",
|
|
9
|
+
"compare/codex-gpt-5.6-luna--agents-md",
|
|
10
|
+
"compare/codex-gpt-5.6-luna--mempal",
|
|
11
|
+
"dev-e2b/bub-e2b",
|
|
12
|
+
"dev/bub-gpt-5.4-mini",
|
|
13
|
+
"top-level",
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
it("selects an entire group by exact directory match", () => {
|
|
17
|
+
expect(matchExperimentSelector(ids, "compare")).toEqual([
|
|
18
|
+
"compare/bub-gpt-5.6-luna",
|
|
19
|
+
"compare/codex-gpt-5.6-luna",
|
|
20
|
+
"compare/codex-gpt-5.6-luna--agents-md",
|
|
21
|
+
"compare/codex-gpt-5.6-luna--mempal",
|
|
22
|
+
]);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("an exact id match wins even when it is a prefix of sibling variants", () => {
|
|
26
|
+
expect(matchExperimentSelector(ids, "compare/codex-gpt-5.6-luna")).toEqual([
|
|
27
|
+
"compare/codex-gpt-5.6-luna",
|
|
28
|
+
]);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("falls back to a filename prefix within an exact-matched directory to select a family", () => {
|
|
32
|
+
expect(matchExperimentSelector(ids, "compare/codex")).toEqual([
|
|
33
|
+
"compare/codex-gpt-5.6-luna",
|
|
34
|
+
"compare/codex-gpt-5.6-luna--agents-md",
|
|
35
|
+
"compare/codex-gpt-5.6-luna--mempal",
|
|
36
|
+
]);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("requires an exact directory segment; a group-like prefix does not leak across groups", () => {
|
|
40
|
+
expect(matchExperimentSelector(ids, "dev")).toEqual(["dev/bub-gpt-5.4-mini"]);
|
|
41
|
+
expect(matchExperimentSelector(ids, "dev")).not.toContain("dev-e2b/bub-e2b");
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("matches a top-level (groupless) experiment by exact id", () => {
|
|
45
|
+
expect(matchExperimentSelector(ids, "top-level")).toEqual(["top-level"]);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("returns empty when nothing matches", () => {
|
|
49
|
+
expect(matchExperimentSelector(ids, "dev-e3b")).toEqual([]);
|
|
50
|
+
expect(matchExperimentSelector(ids, "compare/nope")).toEqual([]);
|
|
51
|
+
});
|
|
52
|
+
});
|
package/src/shared/aggregate.ts
CHANGED
|
@@ -55,6 +55,35 @@ export function evalPrefixPredicate(evals?: string | string[]): (id: string) =>
|
|
|
55
55
|
return (id) => prefixes.some((prefix) => id.startsWith(prefix));
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
/**
|
|
59
|
+
* 实验选择器解析:`niceeval exp` 位置参数与 `--exp` 共用同一条规则(docs/feature/experiments/cli.md
|
|
60
|
+
* 「实验选择器怎样解析」)。按序:①精确 id 优先,即使它同时是同目录内其它文件名的前缀;
|
|
61
|
+
* ②目录段精确前缀(含更深层目录),选中该目录下全部实验;③以上都不命中且选择器形如
|
|
62
|
+
* `目录/文件名前缀` 时,目录段精确匹配后按文件名段前缀选中一族配置——目录段永远精确,不跨目录
|
|
63
|
+
* 误配,文件名段裸前缀,与 `evalPrefixPredicate` 同一条"共享前缀即家族"的逻辑。
|
|
64
|
+
*/
|
|
65
|
+
export function matchExperimentSelector(ids: readonly string[], selector: string): string[] {
|
|
66
|
+
const exact = ids.find((id) => id === selector);
|
|
67
|
+
if (exact !== undefined) return [exact];
|
|
68
|
+
const dirMatches = ids.filter((id) => id.startsWith(selector + "/"));
|
|
69
|
+
if (dirMatches.length > 0) return dirMatches;
|
|
70
|
+
const lastSlash = selector.lastIndexOf("/");
|
|
71
|
+
if (lastSlash === -1) return [];
|
|
72
|
+
const dir = selector.slice(0, lastSlash);
|
|
73
|
+
const namePrefix = selector.slice(lastSlash + 1);
|
|
74
|
+
if (namePrefix === "") return [];
|
|
75
|
+
return ids.filter((id) => id.startsWith(dir + "/") && id.slice(dir.length + 1).startsWith(namePrefix));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* 零命中时展示的可浏览路径清单(docs/feature/experiments/cli.md「零命中」):按 id 的顶层目录段
|
|
80
|
+
* 去重、排序,带尾斜杠;没有目录段的顶层 experiment id 原样列出(它本身就是一个可选择的目标,
|
|
81
|
+
* 不是一段路径)。这是运行选择的展示投影,不是报告分组。
|
|
82
|
+
*/
|
|
83
|
+
export function browsableExperimentPaths(ids: readonly string[]): string[] {
|
|
84
|
+
return [...new Set(ids.map((id) => (id.includes("/") ? `${id.split("/")[0]}/` : id)))].sort();
|
|
85
|
+
}
|
|
86
|
+
|
|
58
87
|
/** 无 experimentId 时的兜底标签。 */
|
|
59
88
|
export function fallbackExperimentLabel(result: {
|
|
60
89
|
experimentId?: string;
|
package/src/shared/types.ts
CHANGED
|
@@ -30,7 +30,12 @@ export interface SourceArtifact {
|
|
|
30
30
|
content: string;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
/**
|
|
33
|
+
/**
|
|
34
|
+
* 内部收尾闭包类型:供运行器内部的清理注册表使用(如 `postSetup` 钩子内部累积待收尾动作),
|
|
35
|
+
* 不出现在任何公开的生命周期钩子签名里——`SandboxHook`/`AgentSetup`/`AgentTeardown` 等公开
|
|
36
|
+
* 钩子一律 `void | Promise<void>`,setup 不返回值,收尾靠成对的 teardown(见
|
|
37
|
+
* docs/runner.md「环境预置不进运行器,但按顺序调它」)。
|
|
38
|
+
*/
|
|
34
39
|
export type Cleanup = () => Promise<void> | void;
|
|
35
40
|
|
|
36
41
|
/** `ScopedFeedback.progress` 的入参:此刻正在做什么(短命状态,可被后续更新覆盖)。 */
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// cases: docs/engineering/unit-tests/reports/cases.md
|
|
2
|
+
// 「外壳、页面与 Tabs」——show 专属的索引命令拼装(showCommand)与其余页索引文案(otherPagesText)。
|
|
3
|
+
|
|
4
|
+
import { describe, expect, it } from "vitest";
|
|
5
|
+
import { showCommand } from "./command.ts";
|
|
6
|
+
import { otherPagesText } from "./render.ts";
|
|
7
|
+
|
|
8
|
+
describe("其余页索引与索引命令上下文", () => {
|
|
9
|
+
const pages = [
|
|
10
|
+
{ id: "overview", title: { en: "Overview", "zh-CN": "总览" } },
|
|
11
|
+
{ id: "exam", title: { en: "Exam", "zh-CN": "成绩单" } },
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
it("只列未渲染的页,索引命令保留当前 --results / --report 与位置参数,复制即可复现下一层视图", () => {
|
|
15
|
+
// 渲染的是 overview,其余页索引只含 exam 一行——与「渲染初始页 + 尾部附其余页索引」
|
|
16
|
+
// 的行为一致(docs/feature/reports/show/reports.md Case 2)。
|
|
17
|
+
const text = otherPagesText({
|
|
18
|
+
otherPages: pages.filter((p) => p.id !== "overview"),
|
|
19
|
+
command: { patterns: [], results: "tmp/published-results", report: "reports/site.tsx" },
|
|
20
|
+
locale: "zh-CN",
|
|
21
|
+
});
|
|
22
|
+
expect(text).toContain("其余页:");
|
|
23
|
+
expect(text).toContain("niceeval show --results tmp/published-results --report reports/site.tsx --page exam");
|
|
24
|
+
expect(text).toContain("成绩单");
|
|
25
|
+
expect(text).not.toContain("总览");
|
|
26
|
+
expect(text).not.toContain("--page overview");
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("showCommand 按序携带位置参数与 --exp / --results / --report / --page", () => {
|
|
30
|
+
expect(
|
|
31
|
+
showCommand({ patterns: ["memory/swelancer"], experiment: "dev-e2b", report: "reports/site.tsx", page: "exam" }),
|
|
32
|
+
).toBe("niceeval show memory/swelancer --exp dev-e2b --report reports/site.tsx --page exam");
|
|
33
|
+
});
|
|
34
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// show 专属的可复制命令拼装:页索引 / 组索引共用的携带规则(docs/feature/reports/show/reports.md
|
|
2
|
+
// 「索引命令携带完整上下文」)。这不是两宿主共用的报告 runtime 部分——view 走网页路由,不生成
|
|
3
|
+
// 终端命令——所以住在 show 自己这一侧,不进 src/report/runtime/host.ts。
|
|
4
|
+
|
|
5
|
+
import type { HostCommandContext } from "../report/runtime/host.ts";
|
|
6
|
+
|
|
7
|
+
/** 按上下文拼一条可复制的 show 命令(页索引 / 组索引共用的携带规则)。 */
|
|
8
|
+
export function showCommand(ctx: HostCommandContext, extra: string[] = []): string {
|
|
9
|
+
const parts = ["niceeval show", ...ctx.patterns];
|
|
10
|
+
if (ctx.experiment !== undefined) parts.push(`--exp ${ctx.experiment}`);
|
|
11
|
+
if (ctx.results !== undefined) parts.push(`--results ${ctx.results}`);
|
|
12
|
+
if (ctx.report !== undefined) parts.push(`--report ${ctx.report}`);
|
|
13
|
+
if (ctx.page !== undefined) parts.push(`--page ${ctx.page}`);
|
|
14
|
+
parts.push(...extra);
|
|
15
|
+
return parts.join(" ");
|
|
16
|
+
}
|
package/src/show/compose.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// 现刻水位 Scope(两个宿主共用)住在 ../results/select.ts;本文件只留 show 独有的时间轴计算。
|
|
7
7
|
// 数据只消费 niceeval/results 的读取面。
|
|
8
8
|
|
|
9
|
-
import { attemptCostUSD } from "../report/metrics.ts";
|
|
9
|
+
import { attemptCostUSD } from "../report/model/metrics.ts";
|
|
10
10
|
import { compactAssertionSummary, primaryAssertionSummary, summaryText } from "../scoring/display.ts";
|
|
11
11
|
import type { EvalResult, Verdict } from "../types.ts";
|
|
12
12
|
import type { AttemptHandle, Experiment } from "../results/index.ts";
|
package/src/show/index.ts
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
// 位置参数 = eval id 前缀,或 `@<locator>`(精确指名单个 attempt,见 results/locator.ts):
|
|
5
5
|
// 裸跑 / 多 eval 前缀 内建报告(niceeval/report/built-in 默认导出)的 text 面(单 eval 前缀仍进入详情)
|
|
6
6
|
// 恰好一个 eval 单 eval 详情(attempt / 断言明细,宿主本体)
|
|
7
|
-
// @<locator> 精确 attempt:无证据 flag →
|
|
7
|
+
// @<locator> 精确 attempt:无证据 flag → 当前 report 的 attempt-input page(内建 standard
|
|
8
|
+
// 或 --report 自定义);带 flag → 对应证据切面(宿主本体,不经报告管线)
|
|
8
9
|
// --source / --execution / --diff[=路径] 证据切面(宿主本体):出现即走证据室,不渲染报告槽
|
|
9
10
|
// --history 执行时间轴(逐 experimentId + evalId 分节),与 --report 互斥
|
|
10
11
|
// --report <文件> 整槽换成用户报告;位置前缀 / --results / --exp 先收窄 Scope 再注入
|
|
@@ -28,26 +29,25 @@ import {
|
|
|
28
29
|
// against — `instanceof` is keyed by declaration site, so a raw src copy and the compiled
|
|
29
30
|
// dist copy of "the same" class are two different types. The package-owned report runtime
|
|
30
31
|
// ships as precompiled ESM (dist/report/**, built by `pnpm run build:report`); all report
|
|
31
|
-
// loading/rendering goes through
|
|
32
|
-
import { ReportLoadError } from "../../dist/report/load.js";
|
|
32
|
+
// loading/rendering goes through ../report/runtime/host.ts (the shared contact surface).
|
|
33
|
+
import { ReportLoadError } from "../../dist/report/runtime/load.js";
|
|
33
34
|
import { detectLocale, t } from "../i18n/index.ts";
|
|
34
35
|
import { foldEvalVerdict } from "../shared/verdict.ts";
|
|
35
36
|
import { selectCurrentResults, filterExperiments } from "../results/select.ts";
|
|
36
37
|
import { evalPrefixPredicate } from "../shared/aggregate.ts";
|
|
37
38
|
import { attemptHistory } from "./compose.ts";
|
|
38
39
|
import {
|
|
40
|
+
buildHostReportMeta,
|
|
39
41
|
HostReportError,
|
|
40
42
|
loadHostReport,
|
|
41
|
-
reportMetaFor,
|
|
42
43
|
renderHostPageText,
|
|
43
44
|
type HostCommandContext,
|
|
44
|
-
} from "
|
|
45
|
+
} from "../report/runtime/host.ts";
|
|
45
46
|
import {
|
|
46
47
|
attemptArtifactsPath,
|
|
47
48
|
attemptEvidenceHeader,
|
|
48
49
|
attemptHistoryText,
|
|
49
50
|
attemptIndexLine,
|
|
50
|
-
attemptOverviewText,
|
|
51
51
|
attemptsOfEval,
|
|
52
52
|
diffText,
|
|
53
53
|
evalDetailText,
|
|
@@ -96,8 +96,9 @@ function clampWidth(columns: number | undefined): number {
|
|
|
96
96
|
return Math.max(40, Math.min(columns as number, 160));
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
// --report
|
|
100
|
-
|
|
99
|
+
// --report 的装载住在 ../report/runtime/host.ts(两个宿主共用的中性联系面);规范化本身是
|
|
100
|
+
// `defineReport` 自己的职责,不在宿主层重复。
|
|
101
|
+
export { loadHostReport, localizeText } from "../report/runtime/host.ts";
|
|
101
102
|
|
|
102
103
|
export async function runShow(
|
|
103
104
|
cwd: string,
|
|
@@ -163,8 +164,7 @@ async function show(
|
|
|
163
164
|
// `@<locator>` 位置参数:身份直达单个 attempt,与 eval id 前缀匹配完全不同的语义
|
|
164
165
|
// (`@` 打头对 eval id 天然无歧义,见 locator.ts),必须在下面的前缀匹配逻辑之前分流掉,
|
|
165
166
|
// 不然 "@1x7f3q" 会被当成一个谁都匹配不到的 eval id 前缀,报「no eval match」这种文不对题的
|
|
166
|
-
//
|
|
167
|
-
// 三个证据切面)——真正的 `--source`/`--execution`/`--diff` 统一 attempt 全景是后续阶段。
|
|
167
|
+
// 错误。
|
|
168
168
|
const locatorArg = patterns.find((p) => p.startsWith(ATTEMPT_LOCATOR_PREFIX));
|
|
169
169
|
if (locatorArg !== undefined) {
|
|
170
170
|
if (patterns.length !== 1) {
|
|
@@ -199,7 +199,35 @@ async function show(
|
|
|
199
199
|
io.out(blocks.join("\n\n") + "\n");
|
|
200
200
|
return;
|
|
201
201
|
}
|
|
202
|
-
|
|
202
|
+
// 无证据 flag:选中当前 report definition 里唯一的 attempt-input page,注入这份 evidence,
|
|
203
|
+
// 走与其它 page 完全相同的 resolve → validate → render 管线(docs/feature/reports/show/attempt.md;
|
|
204
|
+
// docs/feature/reports/library/attempt-detail.md「在 show 与 view 怎样渲染」)。不带 --report
|
|
205
|
+
// 时装载内建 standard,其中就带这张 page;--report 指向的自定义报告没有声明 attempt-input page
|
|
206
|
+
// 时报完整用户反馈,不回退到内建详情(三条解决路径都在错误文案里给出)。
|
|
207
|
+
const report = await loadHostReport(cwd, flags.report);
|
|
208
|
+
const attemptPage = report.pages.find((p) => p.input === "attempt");
|
|
209
|
+
if (attemptPage === undefined) {
|
|
210
|
+
const sourceLabel = flags.report ?? "the built-in report";
|
|
211
|
+
throw new ShowError(
|
|
212
|
+
`error: ${sourceLabel} has no attempt-input page — "${locatorArg}" cannot be opened without one. ` +
|
|
213
|
+
`Add one: use \`extends: standard\` (inherits its attempt page), import { standardAttemptPage } from ` +
|
|
214
|
+
`"niceeval/report/built-in" and add it to your pages list, or declare your own \`input: "attempt"\` page.\n`,
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
const locale = detectLocale();
|
|
218
|
+
const selection = selectCurrentResults(results, {});
|
|
219
|
+
const meta = await buildHostReportMeta(report, selection);
|
|
220
|
+
const text = await renderHostPageText(
|
|
221
|
+
attemptPage,
|
|
222
|
+
{
|
|
223
|
+
scope: selection,
|
|
224
|
+
results,
|
|
225
|
+
report: meta,
|
|
226
|
+
page: { id: attemptPage.id, input: "attempt", locator: attempt.locator!, evidence: attemptEvidence },
|
|
227
|
+
},
|
|
228
|
+
{ width: io.width, locale },
|
|
229
|
+
);
|
|
230
|
+
io.out(text + "\n");
|
|
203
231
|
return;
|
|
204
232
|
}
|
|
205
233
|
|
|
@@ -322,16 +350,23 @@ async function show(
|
|
|
322
350
|
};
|
|
323
351
|
const sourceLabel = flags.report ?? "the built-in report";
|
|
324
352
|
|
|
325
|
-
// 初始页 = --page
|
|
326
|
-
//
|
|
327
|
-
// 「行为约束」「本地宿主只 resolve 被打开的页」)。
|
|
328
|
-
|
|
353
|
+
// 初始页 = --page 指定的页,缺省第一张可导航页(docs/feature/reports/show/reports.md
|
|
354
|
+
// Case 2);本地宿主只 resolve 被打开的这一页——其余页只留 id / title,不触发取数(见
|
|
355
|
+
// shell.md「行为约束」「本地宿主只 resolve 被打开的页」)。navigation:false 的页(参数化
|
|
356
|
+
// attempt 详情)不参与缺省选择,也不能被 --page 直接打开——没有 locator 不能拿 Scope 强行
|
|
357
|
+
// resolve(architecture.md「Attempt 详情是一张参数化 page」)。
|
|
358
|
+
let page = report.pages.find((p) => p.navigation !== false) ?? report.pages[0];
|
|
329
359
|
if (flags.page !== undefined) {
|
|
330
360
|
const hit = report.pages.find((p) => p.id === flags.page);
|
|
331
361
|
if (!hit) {
|
|
332
362
|
// 用法错误:列出可用页 id(docs/feature/reports/show/reports.md Case 1/2 的报错样例)。
|
|
333
363
|
throw new ShowError(
|
|
334
|
-
`error: page "${flags.page}" not found in ${sourceLabel}. Available pages: ${report.pages.map((p) => p.id).join(", ")}\n`,
|
|
364
|
+
`error: page "${flags.page}" not found in ${sourceLabel}. Available pages: ${report.pages.filter((p) => p.navigation !== false).map((p) => p.id).join(", ")}\n`,
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
if (hit.input === "attempt") {
|
|
368
|
+
throw new ShowError(
|
|
369
|
+
`error: page "${hit.id}" in ${sourceLabel} is an attempt-input page and needs a locator — it cannot be opened with --page directly. Use niceeval show @<locator> instead.\n`,
|
|
335
370
|
);
|
|
336
371
|
}
|
|
337
372
|
page = hit;
|
|
@@ -339,10 +374,10 @@ async function show(
|
|
|
339
374
|
|
|
340
375
|
// attemptCommand 留给渲染管线的默认值:AttemptLocator 已经是可直接 `niceeval show @<locator>`
|
|
341
376
|
// 的真实 CLI 语法,不需要再反查 eval id 拼一条近似命令。
|
|
342
|
-
const meta =
|
|
377
|
+
const meta = await buildHostReportMeta(report, selection);
|
|
343
378
|
const text = await renderHostPageText(
|
|
344
379
|
page,
|
|
345
|
-
{ scope: selection, results, report: meta },
|
|
380
|
+
{ scope: selection, results, report: meta, page: { id: page.id, input: "scope" } },
|
|
346
381
|
{
|
|
347
382
|
width: io.width,
|
|
348
383
|
locale,
|
|
@@ -350,8 +385,9 @@ async function show(
|
|
|
350
385
|
},
|
|
351
386
|
);
|
|
352
387
|
|
|
353
|
-
// 页数大于一时尾部附「其余页」索引(
|
|
354
|
-
|
|
388
|
+
// 页数大于一时尾部附「其余页」索引(只列未渲染、且可导航的页,不倾倒内容);单页定义
|
|
389
|
+
// 没有这段;隐藏的 attempt page 不出现在「其余页」里。
|
|
390
|
+
const remaining = report.pages.filter((p) => p.id !== page.id && p.navigation !== false);
|
|
355
391
|
if (remaining.length === 0) {
|
|
356
392
|
io.out(text + "\n");
|
|
357
393
|
return;
|
package/src/show/render.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// show 的文本渲染:单 eval 详情、--history 时间轴、三个证据切面(transcript / trace / diff)。
|
|
2
|
-
// 输出形态照 docs-site/zh/
|
|
2
|
+
// 输出形态照 docs-site/zh/tutorials/viewing-results.mdx 的示例块;长内容一律截断,
|
|
3
3
|
// 但截断永远如实标注剩余数量和原始 artifact 路径 —— 输出对上下文窗口友好,事实源留在盘上。
|
|
4
4
|
// 全部纯函数(时间经 now 显式传入),证据数据由调用方 await 好了递进来。
|
|
5
5
|
|
|
6
6
|
import { join, relative } from "node:path";
|
|
7
|
-
import type { AssertionResult, DiffData, EvalResult, TimingNode, TraceSpan, Verdict } from "../types.ts";
|
|
7
|
+
import type { AssertionResult, DiffData, EvalResult, LocalizedText, TimingNode, TraceSpan, Verdict } from "../types.ts";
|
|
8
8
|
import type { AttemptEvidence, AttemptHandle, Snapshot } from "../results/index.ts";
|
|
9
9
|
import type { AnnotatedSourceLine, SendAnnotation } from "../results/index.ts";
|
|
10
10
|
import { groupIncompatibleVersionSkips } from "../results/index.ts";
|
|
@@ -12,11 +12,12 @@ import type { SkippedDir } from "../results/index.ts";
|
|
|
12
12
|
import type { ExecutionNode } from "../o11y/execution-tree.ts";
|
|
13
13
|
import { foldEvalVerdict } from "../shared/verdict.ts";
|
|
14
14
|
import { summaryText } from "../scoring/display.ts";
|
|
15
|
-
import { attemptCostUSD } from "../report/metrics.ts";
|
|
16
|
-
import { formatDurationMs, formatMetricValue, formatPlainNumber, formatUSD } from "../report/format.ts";
|
|
17
|
-
import { indentBlock, padDisplay, renderAlignedRows, wrapDisplay } from "../report/text
|
|
15
|
+
import { attemptCostUSD } from "../report/model/metrics.ts";
|
|
16
|
+
import { formatDurationMs, formatMetricValue, formatPlainNumber, formatUSD } from "../report/model/format.ts";
|
|
17
|
+
import { indentBlock, padDisplay, renderAlignedRows, wrapDisplay } from "../report/model/text-layout.ts";
|
|
18
18
|
import type { AttemptHistoryRow } from "./compose.ts";
|
|
19
|
-
import { localizeText,
|
|
19
|
+
import { localizeText, type HostCommandContext } from "../report/runtime/host.ts";
|
|
20
|
+
import { showCommand } from "./command.ts";
|
|
20
21
|
|
|
21
22
|
const MISSING = "—";
|
|
22
23
|
|
|
@@ -158,74 +159,6 @@ export function assertionLine(a: AssertionResult): string {
|
|
|
158
159
|
return head;
|
|
159
160
|
}
|
|
160
161
|
|
|
161
|
-
/** 字段行:首行 `<label>: <值首行>`,值的其余行(如 CommandResult 的 output tail)缩进原样展开。 */
|
|
162
|
-
function fieldLines(label: string, value: string): string[] {
|
|
163
|
-
const [first, ...rest] = value.split("\n");
|
|
164
|
-
return [` ${label}: ${first ?? ""}`, ...rest.map((line) => ` ${line}`)];
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/** 每条的通用行组(见 docs/feature/scoring/library/display.md「通用渲染规则」):
|
|
168
|
-
* 首行 `severity · <标题>`(有分组时标题是分组路径,随后 assertion: 行给检查方式),
|
|
169
|
-
* 之后按有则显示的顺序列 expected / received / score / reason / source。 */
|
|
170
|
-
function assertionRecordLines(a: AssertionResult): string[] {
|
|
171
|
-
const group = groupTitle(a);
|
|
172
|
-
const optional = a.optional ? "optional · " : "";
|
|
173
|
-
const title = group ?? a.name;
|
|
174
|
-
const lines: string[] = [];
|
|
175
|
-
const scoreSuffix =
|
|
176
|
-
a.outcome !== "unavailable" && (a.severity === "soft" || a.threshold !== undefined)
|
|
177
|
-
? ` ${scoreText(a.score)}${a.threshold !== undefined ? ` / ${scoreText(a.threshold)}` : ""}`
|
|
178
|
-
: "";
|
|
179
|
-
lines.push(` ${a.severity} · ${optional}${title}${scoreSuffix}`);
|
|
180
|
-
const detailLine = a.detail ?? (group ? a.name : undefined);
|
|
181
|
-
if (detailLine !== undefined && detailLine !== title) lines.push(` assertion: ${detailLine}`);
|
|
182
|
-
if (a.outcome === "unavailable") {
|
|
183
|
-
lines.push(` reason: ${a.reason}`);
|
|
184
|
-
} else {
|
|
185
|
-
if (a.expected !== undefined) lines.push(...fieldLines("expected", a.expected));
|
|
186
|
-
if (a.received !== undefined) lines.push(...fieldLines("received", a.received));
|
|
187
|
-
if (a.evidence !== undefined && a.evidence !== a.received) lines.push(...fieldLines("evidence", a.evidence));
|
|
188
|
-
}
|
|
189
|
-
if (a.loc) lines.push(` source: ${a.loc.file}:${a.loc.line}${a.loc.column ? `:${a.loc.column}` : ""}`);
|
|
190
|
-
return lines;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* 默认 Attempt 页的首要诊断:按结果分节,只逐条列出需要看的(见 display.md)——
|
|
195
|
-
* `failures:`(gate 失败,含 --strict 下改判的 soft)、`soft below threshold:`、
|
|
196
|
-
* `scores:`(无阈值 judge 的纯打分)、`unavailable:`(证据评不了的,带 reason)。
|
|
197
|
-
* 全部通过时这些节整体省略(返回 undefined),只留计数行。
|
|
198
|
-
*/
|
|
199
|
-
export function failureDiagnostics(assertions: AssertionResult[], width: number, strict?: boolean): string | undefined {
|
|
200
|
-
const failures = assertions.filter(
|
|
201
|
-
(a) => a.outcome === "failed" && (a.severity === "gate" || strict),
|
|
202
|
-
);
|
|
203
|
-
const softBelow = assertions.filter(
|
|
204
|
-
(a) => a.outcome === "failed" && a.severity === "soft" && !strict,
|
|
205
|
-
);
|
|
206
|
-
const scores = assertions.filter(
|
|
207
|
-
(a) => a.outcome === "passed" && a.severity === "soft" && a.threshold === undefined && a.score < 1,
|
|
208
|
-
);
|
|
209
|
-
const unavailableOnes = assertions.filter((a) => a.outcome === "unavailable");
|
|
210
|
-
const lines: string[] = [];
|
|
211
|
-
const section = (title: string, items: AssertionResult[]) => {
|
|
212
|
-
if (items.length === 0) return;
|
|
213
|
-
if (lines.length > 0) lines.push("");
|
|
214
|
-
lines.push(title);
|
|
215
|
-
for (const a of items) lines.push(...assertionRecordLines(a));
|
|
216
|
-
};
|
|
217
|
-
section("failures:", failures);
|
|
218
|
-
section("soft below threshold:", softBelow);
|
|
219
|
-
section("scores:", scores);
|
|
220
|
-
section("unavailable:", unavailableOnes);
|
|
221
|
-
if (lines.length === 0) return undefined;
|
|
222
|
-
return lines.flatMap((line) => {
|
|
223
|
-
if (line === "") return [line];
|
|
224
|
-
const indent = line.length - line.trimStart().length;
|
|
225
|
-
return wrapDisplay(line.trimStart(), Math.max(20, width - indent)).map((part) => `${" ".repeat(indent)}${part}`);
|
|
226
|
-
}).join("\n");
|
|
227
|
-
}
|
|
228
|
-
|
|
229
162
|
/** 详情块头部:`attempt 3 · compare/codex-gpt-5.4 · failed · 41s · 12.3k tokens · $0.04`。 */
|
|
230
163
|
export function attemptHeader(attempt: AttemptHandle): string {
|
|
231
164
|
const r = attempt.result;
|
|
@@ -242,12 +175,13 @@ export function attemptHeader(attempt: AttemptHandle): string {
|
|
|
242
175
|
}
|
|
243
176
|
|
|
244
177
|
// ───────────────────────── AttemptEvidence 共用 ─────────────────────────
|
|
245
|
-
// evalSourceText / executionText /
|
|
246
|
-
// 与 evalDetailText 的紧凑索引列共用的小件:locator
|
|
247
|
-
//
|
|
248
|
-
//
|
|
178
|
+
// evalSourceText / executionText / timingText / diffText(--source / --execution / --timing / --diff
|
|
179
|
+
// 四个证据切面)与 evalDetailText 的紧凑索引列共用的小件:locator 头、失败原因、断言计票摘要。
|
|
180
|
+
// 四个证据 renderer 都只消费同一份 AttemptEvidence,不各自读 artifact 或重新判定 capability
|
|
181
|
+
// (loadAttemptEvidence 已经算好)。无证据 flag 的默认页改由 niceeval/report 的 attempt-input
|
|
182
|
+
// page 管线渲染(docs/feature/reports/show/attempt.md),不在这里。
|
|
249
183
|
|
|
250
|
-
/**
|
|
184
|
+
/** 证据切面的头行:`@<locator> · <evalId> · <experimentId> · <verdict>`。 */
|
|
251
185
|
export function attemptEvidenceHeader(evidence: AttemptEvidence): string {
|
|
252
186
|
return [evidence.locator, evidence.identity.evalId, evidence.identity.experimentId, evidence.result.verdict].join(" · ");
|
|
253
187
|
}
|
|
@@ -427,7 +361,7 @@ export function attemptHistoryText(opts: {
|
|
|
427
361
|
* 拼接这段(单页定义没有「其余页」段);`otherPages` 不含被渲染的那一页。
|
|
428
362
|
*/
|
|
429
363
|
export function otherPagesText(opts: {
|
|
430
|
-
otherPages: { id: string; title:
|
|
364
|
+
otherPages: { id: string; title: LocalizedText }[];
|
|
431
365
|
command: HostCommandContext;
|
|
432
366
|
locale: string;
|
|
433
367
|
}): string {
|
|
@@ -619,7 +553,7 @@ function execBody(
|
|
|
619
553
|
/**
|
|
620
554
|
* action / subagent 节点渲染成两行(call + result)——节点模型把 call+result 合并成一个
|
|
621
555
|
* ExecutionNode(按 callId),但分两行读更符合「先看调用、再看结果」的阅读顺序,
|
|
622
|
-
* 与 docs-site/zh/
|
|
556
|
+
* 与 docs-site/zh/tutorials/agent-feedback-loop.mdx 的示例一致。
|
|
623
557
|
*/
|
|
624
558
|
function executionNodeLines(node: ExecutionNode, originMs: number, timingAvailable: boolean, width: number): string[] {
|
|
625
559
|
const time = node.kind !== "telemetry" && node.span ? relSeconds(node.span.startMs, originMs) : undefined;
|
|
@@ -753,10 +687,6 @@ export function executionText(
|
|
|
753
687
|
return `${header}\n\n${lines.join("\n")}\n\n${tail.join("\n")}`;
|
|
754
688
|
}
|
|
755
689
|
|
|
756
|
-
// ───────────────────────── 默认全景:AttemptEvidence 紧凑总览 ─────────────────────────
|
|
757
|
-
|
|
758
|
-
const MAX_OVERVIEW_DIFF_NAMES = 5;
|
|
759
|
-
|
|
760
690
|
/** net 效果的单字母标记(A/M/D;none = 动过但净无变化,标 ±)。 */
|
|
761
691
|
function netLetter(net: string): string {
|
|
762
692
|
switch (net) {
|
|
@@ -771,15 +701,6 @@ function netLetter(net: string): string {
|
|
|
771
701
|
}
|
|
772
702
|
}
|
|
773
703
|
|
|
774
|
-
function overviewDiffLine(diff: DiffData): string {
|
|
775
|
-
const names = Object.entries(diff.files)
|
|
776
|
-
.sort(([a], [b]) => a.localeCompare(b))
|
|
777
|
-
.map(([p, f]) => `${netLetter(f.net)} ${p}`);
|
|
778
|
-
const shown = names.slice(0, MAX_OVERVIEW_DIFF_NAMES);
|
|
779
|
-
const more = names.length > shown.length ? ` · +${names.length - shown.length} more` : "";
|
|
780
|
-
return `changes: ${names.length} ${names.length === 1 ? "file" : "files"} changed by agent · ${shown.join(" · ")}${more}`;
|
|
781
|
-
}
|
|
782
|
-
|
|
783
704
|
/** 有界行 diff(公共前后缀修剪):对单区域编辑精确,复杂编辑给出上界近似。 */
|
|
784
705
|
function lineDelta(before: string | undefined, after: string | undefined): { adds: number; dels: number } {
|
|
785
706
|
const a = before === undefined ? [] : before.split("\n");
|
|
@@ -791,131 +712,6 @@ function lineDelta(before: string | undefined, after: string | undefined): { add
|
|
|
791
712
|
return { adds: b.length - prefix - suffix, dels: a.length - prefix - suffix };
|
|
792
713
|
}
|
|
793
714
|
|
|
794
|
-
/**
|
|
795
|
-
* `niceeval show @<locator>` 不带证据 flag 时的默认面:紧凑全景,只给摘要——Eval 断言计票、
|
|
796
|
-
* 执行事件计数、可选 OTel 时间指示、工作区 diff 摘要,不复现 `--eval` 的完整源码、
|
|
797
|
-
* `--execution` 的完整事件流或 `--diff` 的完整文件列表(那些内容各自的证据 flag 才给)。
|
|
798
|
-
*/
|
|
799
|
-
/** lifecycle operation → 首页可读标签:点换空格,与 docs/feature/reports/show.md 的
|
|
800
|
-
* `phase: sandbox provision` 字面一致(不引入第二套本地化标签,show 首页用英文原样呈现)。 */
|
|
801
|
-
function operationWords(operation: string): string {
|
|
802
|
-
return operation.replace(/\./g, " ");
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
/**
|
|
806
|
-
* errored attempt 的结构化 `error:` 块(见 docs/feature/reports/show.md「errored attempt 的首页」)。
|
|
807
|
-
* 先展开 phase(= operation 的可读形态)/ code / message / cause,stack 放在块后、保持原始换行。
|
|
808
|
-
* 字段来自结构化 `AttemptError`;非 errored(`r.error === undefined`)返回 undefined。
|
|
809
|
-
*/
|
|
810
|
-
function renderErrorBlock(r: EvalResult): string | undefined {
|
|
811
|
-
const err = r.error;
|
|
812
|
-
if (err === undefined) return undefined;
|
|
813
|
-
const lines = ["error:", ` phase: ${err.phase}`, ` code: ${err.code}`, ` message: ${err.message}`];
|
|
814
|
-
if (err.cause) {
|
|
815
|
-
const c = err.cause;
|
|
816
|
-
const causeText = c.name ? `${c.name} · ${c.message}` : c.message;
|
|
817
|
-
lines.push(` cause: ${causeText}`);
|
|
818
|
-
}
|
|
819
|
-
// stack 放在 error 块之后、保持原始换行(见 docs);没有 stack(如 timeout / turn-failed)就不加空块。
|
|
820
|
-
if (err.stack && err.stack.trim() !== "") return `${lines.join("\n")}\n\n${err.stack.replace(/\n+$/, "")}`;
|
|
821
|
-
return lines.join("\n");
|
|
822
|
-
}
|
|
823
|
-
|
|
824
|
-
/**
|
|
825
|
-
* attempt 级诊断块(见 docs/feature/reports/show.md「diagnostics」)。每条一行标头
|
|
826
|
-
* `<level> · <operation> · <code>`,message 缩进在下一行;`count > 1` 时补 `(N occurrences)`。
|
|
827
|
-
* 诊断的 level 与 verdict 无关 —— passed / failed / errored 都可能带一条 cleanup / teardown
|
|
828
|
-
* warning。没有诊断返回 undefined。
|
|
829
|
-
*/
|
|
830
|
-
function renderAttemptDiagnostics(r: EvalResult): string | undefined {
|
|
831
|
-
if (!r.diagnostics || r.diagnostics.length === 0) return undefined;
|
|
832
|
-
const lines = ["diagnostics:"];
|
|
833
|
-
for (const d of r.diagnostics) {
|
|
834
|
-
lines.push(` ${d.level} · ${d.phase} · ${d.code}`);
|
|
835
|
-
const occ = d.count && d.count > 1 ? ` (${d.count} occurrences)` : "";
|
|
836
|
-
lines.push(` ${d.message}${occ}`);
|
|
837
|
-
}
|
|
838
|
-
return lines.join("\n");
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
export function attemptOverviewText(
|
|
842
|
-
evidence: AttemptEvidence,
|
|
843
|
-
opts: { header: string; artifactPath?: string; width: number },
|
|
844
|
-
): string {
|
|
845
|
-
const { header, artifactPath } = opts;
|
|
846
|
-
const r = evidence.result;
|
|
847
|
-
|
|
848
|
-
const metaParts = [`snapshot ${evidence.identity.snapshotStartedAt}`, `attempt ${evidence.identity.attempt + 1}`, formatDurationMs(r.durationMs)];
|
|
849
|
-
if (r.usage) metaParts.push(`${formatMetricValue(r.usage.inputTokens + r.usage.outputTokens)} tokens`);
|
|
850
|
-
const cost = attemptCostUSD(r);
|
|
851
|
-
if (cost !== null) metaParts.push(formatUSD(cost));
|
|
852
|
-
|
|
853
|
-
const blocks: string[] = [[header, metaParts.join(" · ")].join("\n")];
|
|
854
|
-
|
|
855
|
-
// errored attempt 的首页不靠 trace 就要能解释基础设施错误(见 docs/feature/reports/show.md
|
|
856
|
-
// 「errored attempt 的首页」):先展开结构化 error(phase/operation/code/message/cause + stack),
|
|
857
|
-
// 断言块对没有断言的 errored attempt 省略(它在评分之前就挂了)。
|
|
858
|
-
const errorBlock = renderErrorBlock(r);
|
|
859
|
-
if (errorBlock) blocks.push(errorBlock);
|
|
860
|
-
|
|
861
|
-
// errored 且没有任何断言时不打印空的 "assertions: 0 passed" —— 主因已经在 error 块里说清楚了。
|
|
862
|
-
if (r.assertions.length > 0 || r.error === undefined) {
|
|
863
|
-
blocks.push(
|
|
864
|
-
[
|
|
865
|
-
assertionSummaryLine(r.assertions),
|
|
866
|
-
evidence.evalSource
|
|
867
|
-
? `eval source: ${evidence.evalSource.sourcePath} · sha256:${evidence.evalSource.sourceSha256.slice(0, 8)}…`
|
|
868
|
-
: "eval source: unavailable (not captured for this attempt)",
|
|
869
|
-
].join("\n"),
|
|
870
|
-
);
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
const diagnostics = failureDiagnostics(r.assertions, opts.width);
|
|
874
|
-
if (diagnostics) blocks.push(diagnostics);
|
|
875
|
-
|
|
876
|
-
// attempt 级诊断(teardown/cleanup 等,与 verdict 独立;passed/failed/errored 都可能有)。
|
|
877
|
-
const attemptDiag = renderAttemptDiagnostics(r);
|
|
878
|
-
if (attemptDiag) blocks.push(attemptDiag);
|
|
879
|
-
|
|
880
|
-
if (evidence.execution) {
|
|
881
|
-
const nodes = evidence.execution.nodes.filter((node) => node.kind !== "telemetry");
|
|
882
|
-
const skillLoads = nodes.filter((n) => n.kind === "skill.loaded").length;
|
|
883
|
-
const toolCalls = nodes.filter((n) => n.kind === "action").length;
|
|
884
|
-
const aiMessages = nodes.filter((n) => n.kind === "message" && n.role === "assistant").length;
|
|
885
|
-
const counted = (count: number, singular: string, plural = `${singular}s`) =>
|
|
886
|
-
`${count} ${count === 1 ? singular : plural}`;
|
|
887
|
-
const execLines = [
|
|
888
|
-
`execution: ${counted(nodes.length, "event")} · ${counted(skillLoads, "skill load")} · ${counted(toolCalls, "tool call")} · ${counted(aiMessages, "AI message")}`,
|
|
889
|
-
];
|
|
890
|
-
const timingLine = overviewTimingLine(r);
|
|
891
|
-
if (timingLine) execLines.push(timingLine);
|
|
892
|
-
blocks.push(execLines.join("\n"));
|
|
893
|
-
} else {
|
|
894
|
-
blocks.push("execution: unavailable (no events recorded for this attempt)");
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
if (evidence.diff && evidence.capabilities.diff) {
|
|
898
|
-
blocks.push(overviewDiffLine(evidence.diff));
|
|
899
|
-
} else if (evidence.diff) {
|
|
900
|
-
blocks.push("changes: diff unavailable · this attempt did not produce workspace file changes");
|
|
901
|
-
} else {
|
|
902
|
-
blocks.push("changes: diff unavailable · no workspace diff was recorded for this attempt");
|
|
903
|
-
}
|
|
904
|
-
|
|
905
|
-
const tail: string[] = [];
|
|
906
|
-
if (artifactPath) tail.push(`artifacts: ${artifactPath}/`);
|
|
907
|
-
const available = [
|
|
908
|
-
evidence.capabilities.source ? `niceeval show ${evidence.locator} --source` : undefined,
|
|
909
|
-
evidence.capabilities.execution ? `niceeval show ${evidence.locator} --execution` : undefined,
|
|
910
|
-
evidence.capabilities.timing ? `niceeval show ${evidence.locator} --timing` : undefined,
|
|
911
|
-
evidence.capabilities.diff ? `niceeval show ${evidence.locator} --diff` : undefined,
|
|
912
|
-
].filter((command): command is string => command !== undefined);
|
|
913
|
-
if (available.length > 0) tail.push(`available:\n${available.map((command) => ` ${command}`).join("\n")}`);
|
|
914
|
-
if (tail.length > 0) blocks.push(tail.join("\n"));
|
|
915
|
-
|
|
916
|
-
return blocks.join("\n\n");
|
|
917
|
-
}
|
|
918
|
-
|
|
919
715
|
// ───────────────────────── 证据切面:diff ─────────────────────────
|
|
920
716
|
|
|
921
717
|
const MAX_DIFF_LINES = 200;
|
|
@@ -1013,26 +809,6 @@ function windowHunk(c: { status: string; before?: string; after?: string }): str
|
|
|
1013
809
|
|
|
1014
810
|
const CLOSING_PHASE_NAMES = new Set(["eval.teardown", "agent.teardown", "sandbox.teardown", "sandbox.suspend", "sandbox.stop"]);
|
|
1015
811
|
|
|
1016
|
-
// 首页只保留能回答「大头在哪」的阶段。分类账锚点与 telemetry bookkeeping 在很短时
|
|
1017
|
-
// 留给 --timing 完整树;一旦自身变成慢点或失败,仍必须抬到首页,不能被静默藏掉。
|
|
1018
|
-
const OVERVIEW_DETAIL_PHASE_NAMES = new Set(["workspace.baseline", "telemetry.configure", "telemetry.collect"]);
|
|
1019
|
-
const OVERVIEW_DETAIL_THRESHOLD_MS = 1_000;
|
|
1020
|
-
|
|
1021
|
-
/** phases 主链摘要行(首页 `timing:`):各主链阶段耗时点隔,收尾段合计以 `teardown +N` 单列。 */
|
|
1022
|
-
export function overviewTimingLine(r: EvalResult): string | undefined {
|
|
1023
|
-
if (!r.phases || r.phases.length === 0) return undefined;
|
|
1024
|
-
const main = r.phases.filter(
|
|
1025
|
-
(p) =>
|
|
1026
|
-
!CLOSING_PHASE_NAMES.has(p.name) &&
|
|
1027
|
-
(p.failed === true || !OVERVIEW_DETAIL_PHASE_NAMES.has(p.name) || p.durationMs >= OVERVIEW_DETAIL_THRESHOLD_MS),
|
|
1028
|
-
);
|
|
1029
|
-
const closing = r.phases.filter((p) => CLOSING_PHASE_NAMES.has(p.name));
|
|
1030
|
-
const parts = main.map((p) => `${p.name} ${formatDurationMs(p.durationMs)}${p.failed ? " ✗" : ""}`);
|
|
1031
|
-
const closingMs = closing.reduce((sum, p) => sum + p.durationMs, 0);
|
|
1032
|
-
if (closingMs > 0) parts.push(`teardown +${formatDurationMs(closingMs)}`);
|
|
1033
|
-
return `timing: ${parts.join(" · ")}`;
|
|
1034
|
-
}
|
|
1035
|
-
|
|
1036
812
|
const TIMING_DETAIL_NODE_BUDGET = 80;
|
|
1037
813
|
|
|
1038
814
|
interface DiagnosticTimingNode {
|