niceeval 0.9.0 → 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 +15 -7
- 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} +44 -12
- package/dist/report/{tree.js → definition/tree.js} +10 -12
- 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 +12 -2
- 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 +15 -1
- package/dist/shared/aggregate.js +32 -1
- 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 +43 -19
- 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 +11 -5
- 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 +59 -50
- 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 +77 -64
- package/src/context/types.ts +21 -21
- package/src/define.ts +13 -0
- package/src/i18n/en.ts +30 -15
- package/src/i18n/zh-CN.ts +30 -15
- 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 +136 -112
- 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} +58 -22
- 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 +54 -3
- package/src/results/select.ts +30 -6
- 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 +30 -1
- 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 +58 -22
- package/src/show/render.ts +16 -240
- package/src/show/show.test.ts +128 -35
- package/src/view/app/App.test.tsx +8 -7
- package/src/view/app/App.tsx +135 -51
- 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 +122 -89
- package/src/view/index.ts +3 -13
- package/src/view/server.ts +36 -6
- 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 +18 -806
- package/src/view/view-report.test.ts +182 -34
- 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
package/src/view/app/App.tsx
CHANGED
|
@@ -1,17 +1,41 @@
|
|
|
1
|
-
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
1
|
+
import { useCallback, useEffect, useMemo, useRef, useState, type CSSProperties } from "react";
|
|
2
2
|
import { detectLocale, makeTranslator, persistLocale, setDocumentLocale } from "./i18n.ts";
|
|
3
|
-
import type { Locale, LocalizedText, ReportSlotHtml, Tab, ViewData, ViewReportPageMeta
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
import type { Locale, LocalizedText, ReportSlotHtml, Tab, ViewData, ViewReportPageMeta } from "./types.ts";
|
|
4
|
+
import {
|
|
5
|
+
attemptHrefFor,
|
|
6
|
+
attemptLocatorFromHref,
|
|
7
|
+
hashForAttempt,
|
|
8
|
+
locatorFromHash,
|
|
9
|
+
parseAttemptDocument,
|
|
10
|
+
type AttemptDocumentContent,
|
|
11
|
+
} from "./lib/attempt-dialog.ts";
|
|
12
|
+
import { Dialog, DialogClose, DialogContent, DialogTitle } from "./components/ui/dialog.tsx";
|
|
7
13
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "./components/ui/tabs.tsx";
|
|
8
14
|
|
|
9
15
|
// 导航组成只有一条规则(docs/feature/reports/view.md「页面构成」):导航项 = 报告定义声明的页,
|
|
10
16
|
// 按声明顺序排列(路由 `#/page/<id>`,`--page <id>` 定初始页)。宿主不追加、不保留任何导航项——
|
|
11
|
-
// 裸 view 的「报告 / Attempts / 追踪」三个 tab 就是内建报告的三页。页面里的 hero
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
// (docs/feature/reports/architecture.md「宿主保留的只有机器」)。
|
|
17
|
+
// 裸 view 的「报告 / Attempts / 追踪」三个 tab 就是内建报告的三页。页面里的 hero、Scope 警告
|
|
18
|
+
// 都不是宿主渲染的:它们是页内的站点组件(Hero / ScopeWarnings)。宿主保留的是机器
|
|
19
|
+
// 加一个恒定的品牌位:管线与路由、attempt 详情路由、文档单例(<title>)、语言切换,以及
|
|
20
|
+
// 页头左端的 NiceEval 字标(docs/feature/reports/architecture.md「宿主保留的只有机器」)。
|
|
21
|
+
|
|
22
|
+
// niceeval 官网。页头品牌字标与 hero 下的 `Powered by NiceEval` 行都外链到它,
|
|
23
|
+
// utm_medium 区分点击来自哪个品牌位(shell.md「行为约束」)。
|
|
24
|
+
const BRAND_HREF = "https://niceeval.com/?utm_source=report&utm_medium=brand";
|
|
25
|
+
|
|
26
|
+
/** 屏幕阅读器可用、视觉上不占位:Radix Dialog 需要一个可访问标题,内容本身(身份 / verdict)
|
|
27
|
+
* 已经在 dialog 里可见,不需要再视觉重复一遍。 */
|
|
28
|
+
const VISUALLY_HIDDEN: CSSProperties = {
|
|
29
|
+
position: "absolute",
|
|
30
|
+
width: 1,
|
|
31
|
+
height: 1,
|
|
32
|
+
padding: 0,
|
|
33
|
+
margin: -1,
|
|
34
|
+
overflow: "hidden",
|
|
35
|
+
clip: "rect(0, 0, 0, 0)",
|
|
36
|
+
whiteSpace: "nowrap",
|
|
37
|
+
border: 0,
|
|
38
|
+
};
|
|
15
39
|
|
|
16
40
|
/**
|
|
17
41
|
* LocalizedText 的确定回退(docs/feature/reports/library/shell.md):当前 locale → en →
|
|
@@ -28,19 +52,6 @@ export function localizedText(text: LocalizedText | undefined, locale: Locale):
|
|
|
28
52
|
return undefined;
|
|
29
53
|
}
|
|
30
54
|
|
|
31
|
-
/** 初始 URL → 直接打开的 attempt:先认 #/attempt/@<locator> 深链,回退旧版 ?modal= 参数。 */
|
|
32
|
-
function modalResultFromLocation(snapshots: ViewData["snapshots"]): ViewResult | null {
|
|
33
|
-
const locator = parseAttemptHash(location.hash);
|
|
34
|
-
if (locator) {
|
|
35
|
-
const found = resolveAttemptLocator(snapshots, locator);
|
|
36
|
-
if (found) return found;
|
|
37
|
-
// 定位不到(locator 不在、快照未加载、旧格式数据):不开空 modal,页面照常渲染。
|
|
38
|
-
console.warn(unresolvedAttemptWarning(location.hash));
|
|
39
|
-
return null;
|
|
40
|
-
}
|
|
41
|
-
return resultFromUrl(snapshots);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
55
|
/**
|
|
45
56
|
* 报告槽:React 19 对 dangerouslySetInnerHTML 只比较对象身份,身份一变就无条件重设
|
|
46
57
|
* innerHTML(不再比对 __html 字符串值)。{__html} 必须 memo 住,否则 App 任意一次重渲染
|
|
@@ -64,7 +75,6 @@ function hashForTab(tab: Tab): string {
|
|
|
64
75
|
}
|
|
65
76
|
|
|
66
77
|
export function App({ data, reportPages }: { data: ViewData; reportPages: Record<string, ReportSlotHtml> }) {
|
|
67
|
-
const snapshots = data.snapshots ?? [];
|
|
68
78
|
const [locale, setLocale] = useState<Locale>(() => detectLocale());
|
|
69
79
|
const t = useMemo(() => makeTranslator(locale), [locale]);
|
|
70
80
|
|
|
@@ -75,11 +85,16 @@ export function App({ data, reportPages }: { data: ViewData; reportPages: Record
|
|
|
75
85
|
const initialPageId = data.report?.initialPageId ?? pages[0]!.id;
|
|
76
86
|
|
|
77
87
|
const [tab, setTab] = useState<Tab>(() => tabFromHash(location.hash, pages) ?? `page:${initialPageId}`);
|
|
78
|
-
|
|
79
|
-
//
|
|
88
|
+
|
|
89
|
+
// attempt 详情弹窗:内容是独立文档(attempt/<locator>.html)fetch 回来的同一份 server-rendered
|
|
90
|
+
// 片段,不维护第二份客户端渲染(docs/feature/reports/view.md「静态导出」)。dialogLocator 为
|
|
91
|
+
// null 即关闭。
|
|
92
|
+
const [dialogLocator, setDialogLocator] = useState<string | null>(null);
|
|
93
|
+
const [dialogContent, setDialogContent] = useState<AttemptDocumentContent | null>(null);
|
|
94
|
+
// 当前 dialog 的 hash 历史条目前面是否还有本页条目(点击链接 push 的 / 前进键回到的):
|
|
80
95
|
// true → UI 关闭走 history.back(),前进键还能重新打开;false(深链直接落地)→ 原地抹 hash,
|
|
81
96
|
// 免得 back 把用户弹出站外。
|
|
82
|
-
const
|
|
97
|
+
const dialogOwnsHistory = useRef(false);
|
|
83
98
|
|
|
84
99
|
useEffect(() => {
|
|
85
100
|
setDocumentLocale(locale);
|
|
@@ -94,48 +109,88 @@ export function App({ data, reportPages }: { data: ViewData; reportPages: Record
|
|
|
94
109
|
document.title = shellTitle;
|
|
95
110
|
}, [shellTitle]);
|
|
96
111
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
112
|
+
/** fetch 一个 locator 的独立文档、抠出两种语言内容并打开 dialog;定位不到就直说,不开空 dialog。 */
|
|
113
|
+
const openAttempt = useCallback(async (locator: string, ownsHistory: boolean) => {
|
|
114
|
+
try {
|
|
115
|
+
const res = await fetch(attemptHrefFor(locator));
|
|
116
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
117
|
+
const content = parseAttemptDocument(await res.text());
|
|
118
|
+
if (!content) throw new Error("response is not a recognized attempt document");
|
|
119
|
+
dialogOwnsHistory.current = ownsHistory;
|
|
120
|
+
setDialogLocator(locator);
|
|
121
|
+
setDialogContent(content);
|
|
122
|
+
} catch (e) {
|
|
123
|
+
console.warn(
|
|
124
|
+
`[niceeval view] failed to open attempt "${locator}": ${e instanceof Error ? e.message : String(e)}`,
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
}, []);
|
|
128
|
+
|
|
129
|
+
const closeDialog = useCallback(() => {
|
|
130
|
+
setDialogLocator(null);
|
|
131
|
+
setDialogContent(null);
|
|
132
|
+
if (dialogOwnsHistory.current) {
|
|
133
|
+
dialogOwnsHistory.current = false;
|
|
101
134
|
history.back();
|
|
102
135
|
return;
|
|
103
136
|
}
|
|
104
137
|
try {
|
|
105
|
-
//
|
|
106
|
-
history.replaceState(null, "", location.pathname);
|
|
138
|
+
// 深链直接落地:没有可回退的本页条目,原地还原成无 hash 的 URL。
|
|
139
|
+
history.replaceState(null, "", location.pathname + location.search);
|
|
107
140
|
} catch {
|
|
108
141
|
// 还原 URL 失败不影响关闭。
|
|
109
142
|
}
|
|
110
143
|
}, []);
|
|
111
144
|
|
|
112
|
-
//
|
|
113
|
-
//
|
|
114
|
-
// attempt
|
|
115
|
-
//
|
|
145
|
+
// 初始 URL 已经是 #/attempt/@<locator> 深链:直接打开(不经 hashchange——那只在后续变化时触发)。
|
|
146
|
+
// 有效根即收窄后的结果(view.md「打开与收窄」):这份路由对完整结果根解析,不受当前统计口径
|
|
147
|
+
// (现刻水位)限制,深链因此对历史 attempt 同样可达;收窄之外的 locator 由这份文档自身
|
|
148
|
+
// 的宿主寻址语义处理(本地越过收窄解析,导出站按证据缺失呈现,不是这里的关注点)。
|
|
149
|
+
// 空依赖数组是有意的:只在挂载时检查一次初始 hash,openAttempt 本身是 useCallback(deps: [])
|
|
150
|
+
// 的稳定引用,不会随后续渲染变化。
|
|
151
|
+
useEffect(() => {
|
|
152
|
+
const locator = locatorFromHash(location.hash);
|
|
153
|
+
if (locator) void openAttempt(locator, false);
|
|
154
|
+
}, [openAttempt]);
|
|
155
|
+
|
|
156
|
+
// 浏览器前进/后退、手改 hash、页内链接点击(经下面的点击拦截转成 hash 变化)统一从
|
|
157
|
+
// hashchange 分发:attempt hash 开证据室 dialog,页 hash 切当前 tab。
|
|
116
158
|
useEffect(() => {
|
|
117
159
|
const onHashChange = () => {
|
|
118
|
-
const locator =
|
|
160
|
+
const locator = locatorFromHash(location.hash);
|
|
119
161
|
if (locator) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
console.warn(unresolvedAttemptWarning(location.hash));
|
|
123
|
-
setModalResult(null);
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
// 经浏览器导航打开:前一条历史仍是本页,UI 关闭可以安全 back()。
|
|
127
|
-
modalOwnsHistory.current = true;
|
|
128
|
-
setModalResult(found);
|
|
162
|
+
// 经浏览器导航打开:前一条历史仍是本页,dialog 关闭可以安全 back()。
|
|
163
|
+
void openAttempt(locator, true);
|
|
129
164
|
return;
|
|
130
165
|
}
|
|
131
|
-
|
|
132
|
-
|
|
166
|
+
setDialogLocator(null);
|
|
167
|
+
setDialogContent(null);
|
|
133
168
|
const routed = tabFromHash(location.hash, pages);
|
|
134
169
|
if (routed) setTab(routed);
|
|
135
170
|
};
|
|
136
171
|
window.addEventListener("hashchange", onHashChange);
|
|
137
172
|
return () => window.removeEventListener("hashchange", onHashChange);
|
|
138
|
-
}, [
|
|
173
|
+
}, [pages, openAttempt]);
|
|
174
|
+
|
|
175
|
+
// 拦截 attempt 文档链接(报告页里的 locator 引用,attemptHref 缺省产出的
|
|
176
|
+
// `attempt/<encodeURIComponent(locator)>.html`):点击时改写成 hash 路由,交给上面的
|
|
177
|
+
// hashchange 统一打开——无 JavaScript 时这些链接原样导航到独立文档,同样完整可读。
|
|
178
|
+
// 修饰键 / 非左键点击放行,让「新标签页打开」这类浏览器原生行为不受影响。
|
|
179
|
+
useEffect(() => {
|
|
180
|
+
const onClick = (e: MouseEvent) => {
|
|
181
|
+
if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
|
|
182
|
+
const target = e.target;
|
|
183
|
+
if (!(target instanceof Element)) return;
|
|
184
|
+
const anchor = target.closest("a[href]");
|
|
185
|
+
if (!anchor) return;
|
|
186
|
+
const locator = attemptLocatorFromHref(anchor.getAttribute("href") ?? "");
|
|
187
|
+
if (!locator) return;
|
|
188
|
+
e.preventDefault();
|
|
189
|
+
location.hash = hashForAttempt(locator);
|
|
190
|
+
};
|
|
191
|
+
document.addEventListener("click", onClick);
|
|
192
|
+
return () => document.removeEventListener("click", onClick);
|
|
193
|
+
}, []);
|
|
139
194
|
|
|
140
195
|
const selectTab = useCallback((value: Tab) => {
|
|
141
196
|
setTab(value);
|
|
@@ -151,6 +206,14 @@ export function App({ data, reportPages }: { data: ViewData; reportPages: Record
|
|
|
151
206
|
return (
|
|
152
207
|
<Tabs value={tab} onValueChange={(v) => selectTab(v as Tab)}>
|
|
153
208
|
<header className="topbar">
|
|
209
|
+
{/* 页头左端是恒定的 NiceEval 品牌字标(与 Powered by 行同族的产品品牌位),
|
|
210
|
+
报告定义不能覆盖或移除,点击外链官网;报告 title 的落点是页内 hero 与浏览器标题。
|
|
211
|
+
rel 用 noopener 而非 noreferrer:保留 Referer(默认策略只发 origin),官网统计由此
|
|
212
|
+
得知点击来自哪个报告站点;utm 只负责区分品牌位。 */}
|
|
213
|
+
<a className="brand" href={BRAND_HREF} target="_blank" rel="noopener">
|
|
214
|
+
<span className="mark" aria-hidden="true" />
|
|
215
|
+
<span>NiceEval</span>
|
|
216
|
+
</a>
|
|
154
217
|
<TabsList aria-label={t("nav.label")}>
|
|
155
218
|
{pages.map((page) => (
|
|
156
219
|
<TabsTrigger key={`page:${page.id}`} value={`page:${page.id}`}>
|
|
@@ -190,7 +253,7 @@ export function App({ data, reportPages }: { data: ViewData; reportPages: Record
|
|
|
190
253
|
<TabsContent key={`page:${page.id}`} value={`page:${page.id}`} id={`tab-page-${page.id}`}>
|
|
191
254
|
{/* 报告槽:server 侧逐页渲染好的静态 HTML(含 <Style> 产物),按当前页与界面语言
|
|
192
255
|
摆放对应块;hero、品牌行、Scope 警告、批量修复 prompt 都是页内组件,壳不再渲染。
|
|
193
|
-
attempt 深链是普通 <a href="
|
|
256
|
+
attempt 深链是普通 <a href="attempt/…html">,经上面的点击拦截打开 dialog。 */}
|
|
194
257
|
<ReportSlot html={reportPages[page.id]?.[locale] || reportPages[page.id]?.en || ""} />
|
|
195
258
|
</TabsContent>
|
|
196
259
|
))}
|
|
@@ -200,7 +263,28 @@ export function App({ data, reportPages }: { data: ViewData; reportPages: Record
|
|
|
200
263
|
<span className="site-footer-text">{footerText}</span>
|
|
201
264
|
</footer>
|
|
202
265
|
) : null}
|
|
203
|
-
{
|
|
266
|
+
{dialogLocator && dialogContent ? (
|
|
267
|
+
<Dialog open onOpenChange={(o) => { if (!o) closeDialog(); }}>
|
|
268
|
+
<DialogContent aria-describedby={undefined}>
|
|
269
|
+
{/* 屏幕阅读器用的可访问标题:视觉上隐藏,身份 / verdict 等实际内容已经在下面
|
|
270
|
+
fetch 回来的片段里可见,这里不重复渲染。 */}
|
|
271
|
+
<DialogTitle style={VISUALLY_HIDDEN}>{t("dialog.attemptTitle")}</DialogTitle>
|
|
272
|
+
<div className="flex min-w-0 shrink-0 items-center justify-end border-b border-line px-7 pb-3 pt-4">
|
|
273
|
+
<DialogClose
|
|
274
|
+
aria-label={t("action.close")}
|
|
275
|
+
className="grid h-7 w-7 shrink-0 place-items-center rounded-md border border-transparent text-sm text-muted transition-colors hover:border-line hover:bg-panel-2 hover:text-text"
|
|
276
|
+
>
|
|
277
|
+
x
|
|
278
|
+
</DialogClose>
|
|
279
|
+
</div>
|
|
280
|
+
{/* 与直接打开 attempt/<locator>.html 看到的是同一份 server-rendered 片段
|
|
281
|
+
(docs/engineering/unit-tests/reports/cases.md 第 207 行),不是客户端重新渲染。 */}
|
|
282
|
+
<div className="flex-1 overflow-y-auto px-7 pb-7 pt-2">
|
|
283
|
+
<ReportSlot html={dialogContent[locale] || dialogContent.en} />
|
|
284
|
+
</div>
|
|
285
|
+
</DialogContent>
|
|
286
|
+
</Dialog>
|
|
287
|
+
) : null}
|
|
204
288
|
</Tabs>
|
|
205
289
|
);
|
|
206
290
|
}
|
package/src/view/app/i18n.ts
CHANGED
|
@@ -1,61 +1,11 @@
|
|
|
1
1
|
// view 前端 i18n:内核(插值/归一)在 src/i18n/core.ts;这里只注入
|
|
2
2
|
// localStorage + navigator 的 locale 来源与 en 默认值。字典与 CLI 侧分开维护。
|
|
3
|
-
// 词条只覆盖宿主机器(导航标签、attempt
|
|
4
|
-
// 是报告组件,文案在 niceeval/report 的组件词典里(src/report/locale.ts)。
|
|
3
|
+
// 词条只覆盖宿主机器(导航标签、attempt dialog 的关闭按钮):页面内容(hero、警告、列表、瀑布、
|
|
4
|
+
// attempt 详情)是报告组件,文案在 niceeval/report 的组件词典里(src/report/locale.ts)。
|
|
5
5
|
|
|
6
6
|
import { interpolate, normalizeLocale, type Locale, type Vars } from "../../i18n/core.ts";
|
|
7
7
|
|
|
8
|
-
export type MessageKey =
|
|
9
|
-
| "nav.label"
|
|
10
|
-
| "hero.title"
|
|
11
|
-
| "status.pass"
|
|
12
|
-
| "status.fail"
|
|
13
|
-
| "status.error"
|
|
14
|
-
| "status.skipped"
|
|
15
|
-
| "action.close"
|
|
16
|
-
| "action.copyPrompt"
|
|
17
|
-
| "action.copied"
|
|
18
|
-
| "trace.loading"
|
|
19
|
-
| "trace.loadFailed"
|
|
20
|
-
| "trace.timing"
|
|
21
|
-
| "trace.noSpans"
|
|
22
|
-
| "trace.total"
|
|
23
|
-
| "trace.spans"
|
|
24
|
-
| "trace.clickDetails"
|
|
25
|
-
| "trace.enableHint"
|
|
26
|
-
| "trace.enableHintLink"
|
|
27
|
-
| "trace.enableHintUrl"
|
|
28
|
-
| "transcript.noEvents"
|
|
29
|
-
| "transcript.user"
|
|
30
|
-
| "transcript.assistant"
|
|
31
|
-
| "transcript.thinking"
|
|
32
|
-
| "transcript.inputRequested"
|
|
33
|
-
| "transcript.awaitingInput"
|
|
34
|
-
| "transcript.contextCompacted"
|
|
35
|
-
| "transcript.skillLoaded"
|
|
36
|
-
| "transcript.rawEvent"
|
|
37
|
-
| "transcript.running"
|
|
38
|
-
| "transcript.input"
|
|
39
|
-
| "transcript.output"
|
|
40
|
-
| "transcript.empty"
|
|
41
|
-
| "code.otherAssertions"
|
|
42
|
-
| "code.noReply"
|
|
43
|
-
| "code.reply"
|
|
44
|
-
| "code.hide"
|
|
45
|
-
| "code.checks"
|
|
46
|
-
| "code.conversation"
|
|
47
|
-
| "code.noSource"
|
|
48
|
-
| "code.sourceUnavailable"
|
|
49
|
-
| "attempt.timing"
|
|
50
|
-
| "attempt.teardown"
|
|
51
|
-
| "attempt.diagnostics"
|
|
52
|
-
| "assert.pass"
|
|
53
|
-
| "assert.fail"
|
|
54
|
-
| "assert.passedCollapsed"
|
|
55
|
-
| "assert.unavailable"
|
|
56
|
-
| "assert.optional"
|
|
57
|
-
| "assert.soft"
|
|
58
|
-
| "assert.evidence";
|
|
8
|
+
export type MessageKey = "nav.label" | "hero.title" | "action.close" | "dialog.attemptTitle";
|
|
59
9
|
|
|
60
10
|
type Dictionary = Record<MessageKey, string>;
|
|
61
11
|
|
|
@@ -65,106 +15,16 @@ const dictionaries: Record<Locale, Dictionary> = {
|
|
|
65
15
|
// 标题回退链终点的内置文案(shell.md:「Eval 运行结果 / Eval Results」);
|
|
66
16
|
// 正常路径 server 侧已走完回退链,这里只兜旧数据 / 缺声明。
|
|
67
17
|
"hero.title": "Eval Results",
|
|
68
|
-
"status.pass": "pass",
|
|
69
|
-
"status.fail": "fail",
|
|
70
|
-
"status.error": "error",
|
|
71
|
-
"status.skipped": "skipped",
|
|
72
18
|
"action.close": "Close",
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
"
|
|
76
|
-
"trace.loadFailed": "load failed (static report has no server - use niceeval view):",
|
|
77
|
-
"trace.timing": "timing trace",
|
|
78
|
-
"trace.noSpans": "no spans",
|
|
79
|
-
"trace.total": "total",
|
|
80
|
-
"trace.spans": "spans",
|
|
81
|
-
"trace.clickDetails": "click a row for details",
|
|
82
|
-
"trace.enableHint": "No trace for this run. Wire up OTel to get a call waterfall — see the ",
|
|
83
|
-
"trace.enableHintLink": "OTel guide",
|
|
84
|
-
"trace.enableHintUrl": "https://niceeval.com/docs/guides/connect-otel",
|
|
85
|
-
"transcript.noEvents": "no events",
|
|
86
|
-
"transcript.user": "user",
|
|
87
|
-
"transcript.assistant": "assistant",
|
|
88
|
-
"transcript.thinking": "thinking",
|
|
89
|
-
"transcript.inputRequested": "input requested",
|
|
90
|
-
"transcript.awaitingInput": "(awaiting input)",
|
|
91
|
-
"transcript.contextCompacted": "context compacted",
|
|
92
|
-
"transcript.skillLoaded": "skill loaded",
|
|
93
|
-
"transcript.rawEvent": "unrecognized event, shown as-is",
|
|
94
|
-
"transcript.running": "running...",
|
|
95
|
-
"transcript.input": "input",
|
|
96
|
-
"transcript.output": "output",
|
|
97
|
-
"transcript.empty": "(empty)",
|
|
98
|
-
"code.otherAssertions": "other assertions",
|
|
99
|
-
"code.noReply": "(no reply)",
|
|
100
|
-
"code.reply": "reply",
|
|
101
|
-
"code.hide": "hide",
|
|
102
|
-
"code.checks": "checks",
|
|
103
|
-
"code.conversation": "conversation",
|
|
104
|
-
"code.noSource": "Source was not captured. This run may predate source-loc or the source may be unavailable. Re-run this eval to see the code view.",
|
|
105
|
-
"code.sourceUnavailable": "Source was captured for this run, but its artifact files are missing from this deployment. Re-export with `niceeval view --out <dir>` (directory mode bundles artifacts), or open the results locally with `niceeval view`.",
|
|
106
|
-
"attempt.timing": "timing",
|
|
107
|
-
"attempt.teardown": "teardown (not counted in total)",
|
|
108
|
-
"attempt.diagnostics": "diagnostics",
|
|
109
|
-
"assert.pass": "pass",
|
|
110
|
-
"assert.fail": "fail",
|
|
111
|
-
"assert.passedCollapsed": "{{count}} passed",
|
|
112
|
-
"assert.unavailable": "unavailable",
|
|
113
|
-
"assert.optional": "optional",
|
|
114
|
-
"assert.soft": "soft",
|
|
115
|
-
"assert.evidence": "What was checked",
|
|
19
|
+
// 屏幕阅读器专用(Radix Dialog 的可访问标题),视觉上不出现——内容本身的身份 / verdict
|
|
20
|
+
// 已经在 dialog 里可见。
|
|
21
|
+
"dialog.attemptTitle": "Attempt details",
|
|
116
22
|
},
|
|
117
23
|
"zh-CN": {
|
|
118
24
|
"nav.label": "报告",
|
|
119
25
|
"hero.title": "Eval 运行结果",
|
|
120
|
-
"status.pass": "通过",
|
|
121
|
-
"status.fail": "失败",
|
|
122
|
-
"status.error": "错误",
|
|
123
|
-
"status.skipped": "跳过",
|
|
124
26
|
"action.close": "关闭",
|
|
125
|
-
"
|
|
126
|
-
"action.copied": "已复制",
|
|
127
|
-
"trace.loading": "加载中...",
|
|
128
|
-
"trace.loadFailed": "加载失败(静态报告没有服务端 - 请用 niceeval view):",
|
|
129
|
-
"trace.timing": "耗时追踪",
|
|
130
|
-
"trace.noSpans": "没有 span",
|
|
131
|
-
"trace.total": "总计",
|
|
132
|
-
"trace.spans": "spans",
|
|
133
|
-
"trace.clickDetails": "点击行查看详情",
|
|
134
|
-
"trace.enableHint": "这次运行没有 trace。接入 OTel 才有调用瀑布图——看",
|
|
135
|
-
"trace.enableHintLink": "OTel 接入指南",
|
|
136
|
-
"trace.enableHintUrl": "https://niceeval.com/docs/zh/how-to/connect-otel",
|
|
137
|
-
"transcript.noEvents": "没有事件",
|
|
138
|
-
"transcript.user": "user",
|
|
139
|
-
"transcript.assistant": "assistant",
|
|
140
|
-
"transcript.thinking": "thinking",
|
|
141
|
-
"transcript.inputRequested": "请求输入",
|
|
142
|
-
"transcript.awaitingInput": "(等待输入)",
|
|
143
|
-
"transcript.contextCompacted": "上下文已压缩",
|
|
144
|
-
"transcript.skillLoaded": "已加载 Skill",
|
|
145
|
-
"transcript.rawEvent": "未识别事件,原样展示",
|
|
146
|
-
"transcript.running": "运行中...",
|
|
147
|
-
"transcript.input": "输入",
|
|
148
|
-
"transcript.output": "输出",
|
|
149
|
-
"transcript.empty": "(空)",
|
|
150
|
-
"code.otherAssertions": "其它断言",
|
|
151
|
-
"code.noReply": "(无回复)",
|
|
152
|
-
"code.reply": "回复",
|
|
153
|
-
"code.hide": "收起",
|
|
154
|
-
"code.checks": "检查",
|
|
155
|
-
"code.conversation": "会话",
|
|
156
|
-
"code.noSource": "源码未捕获。此 run 可能早于 source-loc,或源码不可读。重跑此 eval 即可看到代码视图。",
|
|
157
|
-
"code.sourceUnavailable": "此 run 捕获过源码,但当前部署里缺少它的 artifact 文件。用 `niceeval view --out <目录>` 重新导出(目录模式会带上 artifact),或在本地 `niceeval view` 查看。",
|
|
158
|
-
"attempt.timing": "耗时",
|
|
159
|
-
"attempt.teardown": "收尾(不计入总耗时)",
|
|
160
|
-
"attempt.diagnostics": "诊断",
|
|
161
|
-
"assert.pass": "通过",
|
|
162
|
-
"assert.fail": "失败",
|
|
163
|
-
"assert.passedCollapsed": "{{count}} 条通过",
|
|
164
|
-
"assert.unavailable": "评不了",
|
|
165
|
-
"assert.optional": "可缺席",
|
|
166
|
-
"assert.soft": "soft",
|
|
167
|
-
"assert.evidence": "实际被检查的内容",
|
|
27
|
+
"dialog.attemptTitle": "Attempt 详情",
|
|
168
28
|
},
|
|
169
29
|
};
|
|
170
30
|
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// cases: docs/engineering/unit-tests/reports/cases.md
|
|
2
|
+
// 第 207 行:增强脚本拦截 locator 链接后,dialog 内容与直接打开该文档的内容是同一份
|
|
3
|
+
// server-rendered 字节/DOM 片段。
|
|
4
|
+
import { describe, expect, it } from "vitest";
|
|
5
|
+
import { attemptHrefFor, attemptLocatorFromHref, hashForAttempt, locatorFromHash, parseAttemptDocument } from "./attempt-dialog.ts";
|
|
6
|
+
|
|
7
|
+
describe("attemptHrefFor / attemptLocatorFromHref", () => {
|
|
8
|
+
it("互为逆运算(编码/解码往返)", () => {
|
|
9
|
+
const locator = "@1kijjdij";
|
|
10
|
+
const href = attemptHrefFor(locator);
|
|
11
|
+
expect(href).toBe("attempt/%401kijjdij.html");
|
|
12
|
+
expect(attemptLocatorFromHref(href)).toBe(locator);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("不是 attempt 文档链接或形状不对时返回 undefined", () => {
|
|
16
|
+
expect(attemptLocatorFromHref("index.html")).toBeUndefined();
|
|
17
|
+
expect(attemptLocatorFromHref("artifact/e/run/a0/events.json")).toBeUndefined();
|
|
18
|
+
expect(attemptLocatorFromHref("attempt/not-a-locator.html")).toBeUndefined();
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe("hashForAttempt / locatorFromHash", () => {
|
|
23
|
+
it("互为逆运算", () => {
|
|
24
|
+
expect(locatorFromHash(hashForAttempt("@abc123"))).toBe("@abc123");
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("非 attempt 路由或形状不对返回 undefined", () => {
|
|
28
|
+
expect(locatorFromHash("#/page/report")).toBeUndefined();
|
|
29
|
+
expect(locatorFromHash("#/attempt/not-valid")).toBeUndefined();
|
|
30
|
+
expect(locatorFromHash("")).toBeUndefined();
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe("parseAttemptDocument", () => {
|
|
35
|
+
function fakeDocument(en: string, zh: string): string {
|
|
36
|
+
return [
|
|
37
|
+
"<!doctype html>",
|
|
38
|
+
"<html><head></head><body>",
|
|
39
|
+
`<div data-nre-locale="en">${en}</div>`,
|
|
40
|
+
`<div data-nre-locale="zh-CN" hidden>${zh}</div>`,
|
|
41
|
+
"<script>/* locale swap */</script>",
|
|
42
|
+
"</body></html>",
|
|
43
|
+
].join("\n");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
it("取出两种语言的内容片段,不含包裹的 div 标签本身", () => {
|
|
47
|
+
const html = fakeDocument("<p>hello</p>", "<p>你好</p>");
|
|
48
|
+
expect(parseAttemptDocument(html)).toEqual({ en: "<p>hello</p>", "zh-CN": "<p>你好</p>" });
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("内容里嵌套的 <div> 不会打断切分(现实里的 AttemptDetail 输出全是嵌套 div)", () => {
|
|
52
|
+
const html = fakeDocument(
|
|
53
|
+
'<div class="nre nre-col"><div class="nre-attempt-summary">stuff</div></div>',
|
|
54
|
+
'<div class="nre nre-col"><div class="nre-attempt-summary">东西</div></div>',
|
|
55
|
+
);
|
|
56
|
+
expect(parseAttemptDocument(html)).toEqual({
|
|
57
|
+
en: '<div class="nre nre-col"><div class="nre-attempt-summary">stuff</div></div>',
|
|
58
|
+
"zh-CN": '<div class="nre nre-col"><div class="nre-attempt-summary">东西</div></div>',
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("不是这份渲染器产出的文档(缺少标记)返回 null,不开空 dialog", () => {
|
|
63
|
+
expect(parseAttemptDocument("<html><body>not an attempt document</body></html>")).toBeNull();
|
|
64
|
+
});
|
|
65
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// 独立 attempt 文档(attempt/<locator>.html,site.ts 的 renderAttemptDocument)的客户端消费面:
|
|
2
|
+
// 拦截同源链接、按 locator 拼 href、把 fetch 回来的文档两种语言内容抠出来塞进 dialog——
|
|
3
|
+
// 内容片段与直接打开该文档看到的完全一致,不维护第二份客户端渲染(view.md「静态导出」)。
|
|
4
|
+
// 纯字符串切分,不用 DOMParser:结构由同一个 site.ts 的固定模板产出,不需要通用 HTML 解析。
|
|
5
|
+
|
|
6
|
+
const EN_MARK = '<div data-nre-locale="en">';
|
|
7
|
+
const ZH_MARK = '<div data-nre-locale="zh-CN"';
|
|
8
|
+
const SCRIPT_MARK = "<script>";
|
|
9
|
+
|
|
10
|
+
export interface AttemptDocumentContent {
|
|
11
|
+
en: string;
|
|
12
|
+
"zh-CN": string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** attempt/<locator>.html 链接的形状(相对于当前文档的相对 href,与 encodeURIComponent(locator) 对应)。 */
|
|
16
|
+
const ATTEMPT_HREF_PATTERN = /^attempt\/(.+)\.html$/;
|
|
17
|
+
|
|
18
|
+
/** locator 串的最小形状:`@` + 至少一个 base36 字符——与 site.ts DEFAULT_ATTEMPT_HREF 编码前的原始值同形。 */
|
|
19
|
+
const LOCATOR_SHAPE = /^@[0-9a-z]+$/;
|
|
20
|
+
|
|
21
|
+
/** 相对 href → 解码后的 locator;不是 attempt 文档链接或 locator 形状不对都返回 undefined。 */
|
|
22
|
+
export function attemptLocatorFromHref(href: string): string | undefined {
|
|
23
|
+
const match = ATTEMPT_HREF_PATTERN.exec(href);
|
|
24
|
+
if (!match) return undefined;
|
|
25
|
+
const locator = decodeURIComponent(match[1]!);
|
|
26
|
+
return LOCATOR_SHAPE.test(locator) ? locator : undefined;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** locator → 该文档的根相对 href(与 report/web.ts DEFAULT_ATTEMPT_HREF 同一编码规则)。 */
|
|
30
|
+
export function attemptHrefFor(locator: string): string {
|
|
31
|
+
return `attempt/${encodeURIComponent(locator)}.html`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const ATTEMPT_HASH_PREFIX = "#/attempt/";
|
|
35
|
+
|
|
36
|
+
/** hash → locator;不是这条路由或形状不对(旧格式深链、手打错的 hash)都返回 undefined。 */
|
|
37
|
+
export function locatorFromHash(hash: string): string | undefined {
|
|
38
|
+
if (!hash.startsWith(ATTEMPT_HASH_PREFIX)) return undefined;
|
|
39
|
+
const locator = hash.slice(ATTEMPT_HASH_PREFIX.length);
|
|
40
|
+
return LOCATOR_SHAPE.test(locator) ? locator : undefined;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function hashForAttempt(locator: string): string {
|
|
44
|
+
return `${ATTEMPT_HASH_PREFIX}${locator}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 从 attempt 文档的响应文本里取出两种语言的内容片段。两个 locale 块紧邻、且两者之间/之后
|
|
49
|
+
* 不会出现这里搜索的标记字符串(site.ts 的固定模板保证),纯字符串切分足够,不需要 DOMParser。
|
|
50
|
+
* 形状不对(不是这份渲染器产出的文档)返回 null,调用方不开空 dialog。
|
|
51
|
+
*/
|
|
52
|
+
export function parseAttemptDocument(html: string): AttemptDocumentContent | null {
|
|
53
|
+
const enStart = html.indexOf(EN_MARK);
|
|
54
|
+
if (enStart === -1) return null;
|
|
55
|
+
const afterEnOpen = html.slice(enStart + EN_MARK.length);
|
|
56
|
+
const zhMarkIdx = afterEnOpen.indexOf(ZH_MARK);
|
|
57
|
+
if (zhMarkIdx === -1) return null;
|
|
58
|
+
const en = afterEnOpen.slice(0, zhMarkIdx).replace(/<\/div>\s*$/, "");
|
|
59
|
+
|
|
60
|
+
const afterZhMark = afterEnOpen.slice(zhMarkIdx);
|
|
61
|
+
const zhTagEnd = afterZhMark.indexOf(">");
|
|
62
|
+
if (zhTagEnd === -1) return null;
|
|
63
|
+
const afterZhOpen = afterZhMark.slice(zhTagEnd + 1);
|
|
64
|
+
const scriptIdx = afterZhOpen.indexOf(SCRIPT_MARK);
|
|
65
|
+
if (scriptIdx === -1) return null;
|
|
66
|
+
const zh = afterZhOpen.slice(0, scriptIdx).replace(/<\/div>\s*$/, "");
|
|
67
|
+
|
|
68
|
+
return { en, "zh-CN": zh };
|
|
69
|
+
}
|