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
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { defineComponent } from "../../definition/tree.js";
|
|
3
|
+
import { Col } from "../../definition/primitives.js";
|
|
4
|
+
import { arrayProblem, dataShapeError, isObject } from "../shared.js";
|
|
5
|
+
import { attemptAssertionsData, attemptConversationData, attemptDiagnosticsData, attemptDiffData, attemptErrorData, attemptFixPromptData, attemptSourceData, attemptSummaryData, attemptTimelineData, attemptTraceData, attemptUsageData, } from "./compute.js";
|
|
6
|
+
import { attemptAssertionsText, attemptConversationText, attemptDiagnosticsText, attemptDiffText, attemptErrorText, attemptFixPromptText, attemptSourceText, attemptSummaryText, attemptTimelineText, attemptTraceText, attemptUsageText, } from "./faces.js";
|
|
7
|
+
import { AttemptSummary as AttemptSummaryWeb } from "./AttemptSummary.js";
|
|
8
|
+
import { AttemptError as AttemptErrorWeb } from "./AttemptError.js";
|
|
9
|
+
import { AttemptAssertions as AttemptAssertionsWeb } from "./AttemptAssertions.js";
|
|
10
|
+
import { AttemptSource as AttemptSourceWeb } from "./AttemptSource.js";
|
|
11
|
+
import { AttemptFixPrompt as AttemptFixPromptWeb } from "./AttemptFixPrompt.js";
|
|
12
|
+
import { AttemptTimeline as AttemptTimelineWeb } from "./AttemptTimeline.js";
|
|
13
|
+
import { AttemptConversation as AttemptConversationWeb } from "./AttemptConversation.js";
|
|
14
|
+
import { AttemptDiagnostics as AttemptDiagnosticsWeb } from "./AttemptDiagnostics.js";
|
|
15
|
+
import { AttemptUsage as AttemptUsageWeb } from "./AttemptUsage.js";
|
|
16
|
+
import { AttemptTrace as AttemptTraceWeb } from "./AttemptTrace.js";
|
|
17
|
+
import { AttemptDiff as AttemptDiffWeb } from "./AttemptDiff.js";
|
|
18
|
+
/**
|
|
19
|
+
* 11 个叶子组件共用的装配:resolve 决定 evidence 来源(显式 data > 显式 input > 当前
|
|
20
|
+
* attempt-input page 注入的 evidence),不在 scope-input page 上凭空工作;两面渲染前都
|
|
21
|
+
* 校验 data 结构,版本漂移时报完整用户反馈而不是静默展示错误字段。
|
|
22
|
+
*/
|
|
23
|
+
function makeAttemptComponent(def) {
|
|
24
|
+
const assertData = (data) => {
|
|
25
|
+
if (data === null)
|
|
26
|
+
return null;
|
|
27
|
+
const problem = def.validate(data);
|
|
28
|
+
if (problem !== null)
|
|
29
|
+
throw dataShapeError(def.name, def.dataFnName, def.shapeName, problem);
|
|
30
|
+
return data;
|
|
31
|
+
};
|
|
32
|
+
const resolve = (props, ctx) => {
|
|
33
|
+
if (props.data !== undefined) {
|
|
34
|
+
if (props.input !== undefined) {
|
|
35
|
+
throw new Error(`<${def.name}> got both \`data\` and \`input\` — the two evidence sources are exclusive and niceeval will not silently pick one. ` +
|
|
36
|
+
`Keep \`data\` (precomputed with ${def.dataFnName}()) and drop \`input\`, or drop \`data\` and let the pipeline compute it from the evidence.`);
|
|
37
|
+
}
|
|
38
|
+
assertData(props.data);
|
|
39
|
+
return { data: props.data, className: props.className };
|
|
40
|
+
}
|
|
41
|
+
const evidence = props.input ?? (ctx.page.input === "attempt" ? ctx.page.evidence : undefined);
|
|
42
|
+
if (evidence === undefined) {
|
|
43
|
+
throw new Error(`<${def.name}> needs an attempt: the current page has no locator to derive evidence from (it is a scope-input page, or no page context is active). ` +
|
|
44
|
+
`Move it to an attempt-input page (\`input: "attempt"\`), or pass \`input\` explicitly with an AttemptEvidence.`);
|
|
45
|
+
}
|
|
46
|
+
return { data: def.dataFn(evidence), className: props.className };
|
|
47
|
+
};
|
|
48
|
+
const component = defineComponent({
|
|
49
|
+
resolve,
|
|
50
|
+
web: (props, ctx) => {
|
|
51
|
+
assertData(props.data);
|
|
52
|
+
return def.web(props, ctx);
|
|
53
|
+
},
|
|
54
|
+
text: (props, ctx) => {
|
|
55
|
+
assertData(props.data);
|
|
56
|
+
return def.text(props, ctx);
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
component.displayName = def.name;
|
|
60
|
+
return component;
|
|
61
|
+
}
|
|
62
|
+
// ───────────────────────── 跨叶子复用的字段路径校验 ─────────────────────────
|
|
63
|
+
/** SourceLoc(src/shared/types.ts):`t.send` / 断言在 eval 源码里的调用点。 */
|
|
64
|
+
function sourceLocProblem(value, path) {
|
|
65
|
+
if (!isObject(value))
|
|
66
|
+
return `"${path}" must be a SourceLoc { file, line }`;
|
|
67
|
+
if (typeof value.file !== "string")
|
|
68
|
+
return `"${path}.file" must be a string`;
|
|
69
|
+
if (typeof value.line !== "number")
|
|
70
|
+
return `"${path}.line" must be a number`;
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
/** AssertionResult(src/scoring/types.ts):按 outcome 判别的联合,passed/failed 要 score,unavailable 要 reason。 */
|
|
74
|
+
function assertionResultProblem(value, path) {
|
|
75
|
+
if (!isObject(value))
|
|
76
|
+
return `"${path}" must be an AssertionResult object`;
|
|
77
|
+
if (typeof value.name !== "string")
|
|
78
|
+
return `"${path}.name" must be a string`;
|
|
79
|
+
if (value.severity !== "gate" && value.severity !== "soft")
|
|
80
|
+
return `"${path}.severity" must be "gate" or "soft"`;
|
|
81
|
+
if (value.outcome === "passed" || value.outcome === "failed") {
|
|
82
|
+
if (typeof value.score !== "number")
|
|
83
|
+
return `"${path}.score" must be a number`;
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
if (value.outcome === "unavailable") {
|
|
87
|
+
if (typeof value.reason !== "string")
|
|
88
|
+
return `"${path}.reason" must be a string`;
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
return `"${path}.outcome" must be "passed" | "failed" | "unavailable"`;
|
|
92
|
+
}
|
|
93
|
+
/** TraceSpan(src/o11y/types.ts):AttemptTimeline 的 trace 与 AttemptTrace 的 spans 共用。 */
|
|
94
|
+
function traceSpanProblem(value, path) {
|
|
95
|
+
if (!isObject(value))
|
|
96
|
+
return `"${path}" must be a TraceSpan { traceId, spanId, name, startMs, endMs }`;
|
|
97
|
+
if (typeof value.traceId !== "string")
|
|
98
|
+
return `"${path}.traceId" must be a string`;
|
|
99
|
+
if (typeof value.spanId !== "string")
|
|
100
|
+
return `"${path}.spanId" must be a string`;
|
|
101
|
+
if (typeof value.name !== "string")
|
|
102
|
+
return `"${path}.name" must be a string`;
|
|
103
|
+
if (typeof value.startMs !== "number")
|
|
104
|
+
return `"${path}.startMs" must be a number`;
|
|
105
|
+
if (typeof value.endMs !== "number")
|
|
106
|
+
return `"${path}.endMs" must be a number`;
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
// ───────────────────────── AttemptSummary(恒非空) ─────────────────────────
|
|
110
|
+
/** AttemptIdentity(src/results/locator.ts):locator 派生自的不可变身份元组。 */
|
|
111
|
+
function attemptIdentityProblem(value, path) {
|
|
112
|
+
if (!isObject(value)) {
|
|
113
|
+
return `"${path}" must be an AttemptIdentity { experimentId, snapshotStartedAt, evalId, attempt }`;
|
|
114
|
+
}
|
|
115
|
+
if (typeof value.experimentId !== "string")
|
|
116
|
+
return `"${path}.experimentId" must be a string`;
|
|
117
|
+
if (typeof value.snapshotStartedAt !== "string")
|
|
118
|
+
return `"${path}.snapshotStartedAt" must be a string`;
|
|
119
|
+
if (typeof value.evalId !== "string")
|
|
120
|
+
return `"${path}.evalId" must be a string`;
|
|
121
|
+
if (typeof value.attempt !== "number")
|
|
122
|
+
return `"${path}.attempt" must be a number`;
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
/** AttemptEvidenceCapabilities(src/results/attempt-evidence.ts):四个证据切面开关。 */
|
|
126
|
+
function capabilitiesProblem(value, path) {
|
|
127
|
+
if (!isObject(value))
|
|
128
|
+
return `"${path}" must be an object { source, execution, timing, diff }`;
|
|
129
|
+
for (const key of ["source", "execution", "timing", "diff"]) {
|
|
130
|
+
if (typeof value[key] !== "boolean")
|
|
131
|
+
return `"${path}.${key}" must be a boolean`;
|
|
132
|
+
}
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
export function validateSummaryData(data) {
|
|
136
|
+
if (!isObject(data))
|
|
137
|
+
return "expected an object";
|
|
138
|
+
if (typeof data.locator !== "string")
|
|
139
|
+
return 'missing "locator" (string)';
|
|
140
|
+
const identityProblem = attemptIdentityProblem(data.identity, "identity");
|
|
141
|
+
if (identityProblem !== null)
|
|
142
|
+
return identityProblem;
|
|
143
|
+
if (typeof data.verdict !== "string")
|
|
144
|
+
return 'missing "verdict" (string)';
|
|
145
|
+
if (typeof data.durationMs !== "number")
|
|
146
|
+
return '"durationMs" must be a number';
|
|
147
|
+
if (!(data.costUSD === null || typeof data.costUSD === "number"))
|
|
148
|
+
return '"costUSD" must be a number or null';
|
|
149
|
+
return capabilitiesProblem(data.capabilities, "capabilities");
|
|
150
|
+
}
|
|
151
|
+
export const AttemptSummary = makeAttemptComponent({
|
|
152
|
+
name: "AttemptSummary",
|
|
153
|
+
dataFnName: "attemptSummaryData",
|
|
154
|
+
shapeName: "AttemptSummaryData",
|
|
155
|
+
dataFn: attemptSummaryData,
|
|
156
|
+
validate: validateSummaryData,
|
|
157
|
+
web: (props, ctx) => _jsx(AttemptSummaryWeb, { data: props.data, locale: ctx.locale, className: props.className }),
|
|
158
|
+
text: (props, ctx) => attemptSummaryText(props.data, ctx),
|
|
159
|
+
});
|
|
160
|
+
// ───────────────────────── AttemptError ─────────────────────────
|
|
161
|
+
export function validateErrorData(data) {
|
|
162
|
+
if (!isObject(data))
|
|
163
|
+
return "expected an object";
|
|
164
|
+
if (typeof data.code !== "string")
|
|
165
|
+
return '"code" must be a string';
|
|
166
|
+
if (typeof data.message !== "string")
|
|
167
|
+
return '"message" must be a string';
|
|
168
|
+
if (typeof data.phase !== "string")
|
|
169
|
+
return '"phase" must be a string';
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
export const AttemptError = makeAttemptComponent({
|
|
173
|
+
name: "AttemptError",
|
|
174
|
+
dataFnName: "attemptErrorData",
|
|
175
|
+
shapeName: "AttemptErrorData",
|
|
176
|
+
dataFn: attemptErrorData,
|
|
177
|
+
validate: validateErrorData,
|
|
178
|
+
web: (props, ctx) => _jsx(AttemptErrorWeb, { data: props.data, className: props.className }),
|
|
179
|
+
text: (props, ctx) => attemptErrorText(props.data, ctx),
|
|
180
|
+
});
|
|
181
|
+
// ───────────────────────── AttemptAssertions ─────────────────────────
|
|
182
|
+
export function validateAssertionsData(data) {
|
|
183
|
+
if (!isObject(data))
|
|
184
|
+
return "expected an object";
|
|
185
|
+
const attentionProblem = arrayProblem(data.attention, "attention", assertionResultProblem);
|
|
186
|
+
if (attentionProblem !== null)
|
|
187
|
+
return attentionProblem;
|
|
188
|
+
return arrayProblem(data.passedGroups, "passedGroups", (group, path) => {
|
|
189
|
+
if (!isObject(group) || typeof group.group !== "string") {
|
|
190
|
+
return `"${path}" must be an object with a string "group"`;
|
|
191
|
+
}
|
|
192
|
+
return arrayProblem(group.items, `${path}.items`, assertionResultProblem);
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
export const AttemptAssertions = makeAttemptComponent({
|
|
196
|
+
name: "AttemptAssertions",
|
|
197
|
+
dataFnName: "attemptAssertionsData",
|
|
198
|
+
shapeName: "AttemptAssertionsData",
|
|
199
|
+
dataFn: attemptAssertionsData,
|
|
200
|
+
validate: validateAssertionsData,
|
|
201
|
+
web: (props, ctx) => _jsx(AttemptAssertionsWeb, { data: props.data, className: props.className }),
|
|
202
|
+
text: (props, ctx) => attemptAssertionsText(props.data, ctx),
|
|
203
|
+
});
|
|
204
|
+
// ───────────────────────── AttemptSource ─────────────────────────
|
|
205
|
+
/** AnnotatedSourceLine(src/results/annotated-source.ts):一行源码 + 映射到这一行的断言 / send 标注。 */
|
|
206
|
+
function annotatedSourceLineProblem(value, path) {
|
|
207
|
+
if (!isObject(value))
|
|
208
|
+
return `"${path}" must be an AnnotatedSourceLine { line, text, assertions, sends }`;
|
|
209
|
+
if (typeof value.line !== "number")
|
|
210
|
+
return `"${path}.line" must be a number`;
|
|
211
|
+
if (typeof value.text !== "string")
|
|
212
|
+
return `"${path}.text" must be a string`;
|
|
213
|
+
const assertionsProblem = arrayProblem(value.assertions, `${path}.assertions`, assertionResultProblem);
|
|
214
|
+
if (assertionsProblem !== null)
|
|
215
|
+
return assertionsProblem;
|
|
216
|
+
if (!Array.isArray(value.sends))
|
|
217
|
+
return `"${path}.sends" must be an array`;
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
/** AnnotatedEvalSourceSummary(src/results/annotated-source.ts):全是计数字段。 */
|
|
221
|
+
function sourceSummaryProblem(value, path) {
|
|
222
|
+
if (!isObject(value))
|
|
223
|
+
return `"${path}" must be an AnnotatedEvalSourceSummary`;
|
|
224
|
+
for (const key of [
|
|
225
|
+
"totalAssertions",
|
|
226
|
+
"mappedAssertions",
|
|
227
|
+
"unmappedAssertions",
|
|
228
|
+
"passed",
|
|
229
|
+
"failed",
|
|
230
|
+
"gate",
|
|
231
|
+
"soft",
|
|
232
|
+
"totalLines",
|
|
233
|
+
"annotatedLines",
|
|
234
|
+
]) {
|
|
235
|
+
if (typeof value[key] !== "number")
|
|
236
|
+
return `"${path}.${key}" must be a number`;
|
|
237
|
+
}
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
export function validateSourceData(data) {
|
|
241
|
+
if (!isObject(data))
|
|
242
|
+
return "expected an object";
|
|
243
|
+
if (typeof data.locator !== "string")
|
|
244
|
+
return 'missing "locator" (string)';
|
|
245
|
+
if (typeof data.sourcePath !== "string")
|
|
246
|
+
return 'missing "sourcePath" (string)';
|
|
247
|
+
const linesProblem = arrayProblem(data.lines, "lines", annotatedSourceLineProblem);
|
|
248
|
+
if (linesProblem !== null)
|
|
249
|
+
return linesProblem;
|
|
250
|
+
const unmappedProblem = arrayProblem(data.unmapped, "unmapped", assertionResultProblem);
|
|
251
|
+
if (unmappedProblem !== null)
|
|
252
|
+
return unmappedProblem;
|
|
253
|
+
return sourceSummaryProblem(data.summary, "summary");
|
|
254
|
+
}
|
|
255
|
+
export const AttemptSource = makeAttemptComponent({
|
|
256
|
+
name: "AttemptSource",
|
|
257
|
+
dataFnName: "attemptSourceData",
|
|
258
|
+
shapeName: "AttemptSourceData",
|
|
259
|
+
dataFn: attemptSourceData,
|
|
260
|
+
validate: validateSourceData,
|
|
261
|
+
web: (props, ctx) => _jsx(AttemptSourceWeb, { data: props.data, className: props.className }),
|
|
262
|
+
text: (props, ctx) => attemptSourceText(props.data, ctx),
|
|
263
|
+
});
|
|
264
|
+
// ───────────────────────── AttemptFixPrompt ─────────────────────────
|
|
265
|
+
export function validateFixPromptData(data) {
|
|
266
|
+
if (!isObject(data))
|
|
267
|
+
return "expected an object";
|
|
268
|
+
if (typeof data.prompt !== "string")
|
|
269
|
+
return 'missing "prompt" (string)';
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
export const AttemptFixPrompt = makeAttemptComponent({
|
|
273
|
+
name: "AttemptFixPrompt",
|
|
274
|
+
dataFnName: "attemptFixPromptData",
|
|
275
|
+
shapeName: "AttemptFixPromptData",
|
|
276
|
+
dataFn: attemptFixPromptData,
|
|
277
|
+
validate: validateFixPromptData,
|
|
278
|
+
web: (props, ctx) => _jsx(AttemptFixPromptWeb, { data: props.data, className: props.className }),
|
|
279
|
+
text: (props, ctx) => attemptFixPromptText(props.data, ctx),
|
|
280
|
+
});
|
|
281
|
+
// ───────────────────────── AttemptTimeline ─────────────────────────
|
|
282
|
+
/** PhaseTiming(src/runner/types.ts):runner 阶段计时,按执行顺序。 */
|
|
283
|
+
function phaseTimingProblem(value, path) {
|
|
284
|
+
if (!isObject(value))
|
|
285
|
+
return `"${path}" must be a PhaseTiming { name, durationMs }`;
|
|
286
|
+
if (typeof value.name !== "string")
|
|
287
|
+
return `"${path}.name" must be a string`;
|
|
288
|
+
if (typeof value.durationMs !== "number")
|
|
289
|
+
return `"${path}.durationMs" must be a number`;
|
|
290
|
+
return null;
|
|
291
|
+
}
|
|
292
|
+
export function validateTimelineData(data) {
|
|
293
|
+
if (!isObject(data))
|
|
294
|
+
return "expected an object";
|
|
295
|
+
if (typeof data.locator !== "string")
|
|
296
|
+
return 'missing "locator" (string)';
|
|
297
|
+
const phasesProblem = arrayProblem(data.phases, "phases", phaseTimingProblem);
|
|
298
|
+
if (phasesProblem !== null)
|
|
299
|
+
return phasesProblem;
|
|
300
|
+
if (data.trace !== null)
|
|
301
|
+
return arrayProblem(data.trace, "trace", traceSpanProblem);
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
export const AttemptTimeline = makeAttemptComponent({
|
|
305
|
+
name: "AttemptTimeline",
|
|
306
|
+
dataFnName: "attemptTimelineData",
|
|
307
|
+
shapeName: "AttemptTimelineData",
|
|
308
|
+
dataFn: attemptTimelineData,
|
|
309
|
+
validate: validateTimelineData,
|
|
310
|
+
web: (props, ctx) => _jsx(AttemptTimelineWeb, { data: props.data, className: props.className }),
|
|
311
|
+
text: (props, ctx) => attemptTimelineText(props.data, ctx),
|
|
312
|
+
});
|
|
313
|
+
// ───────────────────────── AttemptConversation ─────────────────────────
|
|
314
|
+
const CONVERSATION_REPLY_KINDS = [
|
|
315
|
+
"assistant",
|
|
316
|
+
"user",
|
|
317
|
+
"thinking",
|
|
318
|
+
"error",
|
|
319
|
+
"tool",
|
|
320
|
+
"skill",
|
|
321
|
+
"subagent",
|
|
322
|
+
"input",
|
|
323
|
+
"compaction",
|
|
324
|
+
"raw",
|
|
325
|
+
];
|
|
326
|
+
/**
|
|
327
|
+
* AttemptConversationReply(src/report/model/types.ts):按 `kind` 判别的联合,每支自己的必填
|
|
328
|
+
* 字段各自校验——`raw` 是未识别事件类型的兜底分支,不吞没其余 kind 的校验。
|
|
329
|
+
*/
|
|
330
|
+
function conversationReplyProblem(value, path) {
|
|
331
|
+
if (!isObject(value))
|
|
332
|
+
return `"${path}" must be an AttemptConversationReply object`;
|
|
333
|
+
switch (value.kind) {
|
|
334
|
+
case "assistant":
|
|
335
|
+
case "user":
|
|
336
|
+
case "thinking":
|
|
337
|
+
case "error":
|
|
338
|
+
if (typeof value.text !== "string")
|
|
339
|
+
return `"${path}.text" must be a string`;
|
|
340
|
+
return null;
|
|
341
|
+
case "tool":
|
|
342
|
+
if (typeof value.callId !== "string")
|
|
343
|
+
return `"${path}.callId" must be a string`;
|
|
344
|
+
if (typeof value.name !== "string")
|
|
345
|
+
return `"${path}.name" must be a string`;
|
|
346
|
+
if (!("input" in value))
|
|
347
|
+
return `"${path}.input" is required`;
|
|
348
|
+
return null;
|
|
349
|
+
case "skill":
|
|
350
|
+
if (typeof value.skill !== "string")
|
|
351
|
+
return `"${path}.skill" must be a string`;
|
|
352
|
+
return null;
|
|
353
|
+
case "subagent":
|
|
354
|
+
if (typeof value.callId !== "string")
|
|
355
|
+
return `"${path}.callId" must be a string`;
|
|
356
|
+
if (typeof value.name !== "string")
|
|
357
|
+
return `"${path}.name" must be a string`;
|
|
358
|
+
return null;
|
|
359
|
+
case "input":
|
|
360
|
+
if (!isObject(value.request))
|
|
361
|
+
return `"${path}.request" must be an InputRequest object`;
|
|
362
|
+
return null;
|
|
363
|
+
case "compaction":
|
|
364
|
+
return null;
|
|
365
|
+
case "raw":
|
|
366
|
+
if (!("raw" in value))
|
|
367
|
+
return `"${path}.raw" is required`;
|
|
368
|
+
return null;
|
|
369
|
+
default:
|
|
370
|
+
return `"${path}.kind" must be one of ${JSON.stringify(CONVERSATION_REPLY_KINDS)}`;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
function conversationRoundProblem(value, path) {
|
|
374
|
+
if (!isObject(value))
|
|
375
|
+
return `"${path}" must be an AttemptConversationRound { sentText, replies }`;
|
|
376
|
+
if (typeof value.sentText !== "string")
|
|
377
|
+
return `"${path}.sentText" must be a string`;
|
|
378
|
+
if (value.loc !== undefined) {
|
|
379
|
+
const locProblem = sourceLocProblem(value.loc, `${path}.loc`);
|
|
380
|
+
if (locProblem !== null)
|
|
381
|
+
return locProblem;
|
|
382
|
+
}
|
|
383
|
+
return arrayProblem(value.replies, `${path}.replies`, conversationReplyProblem);
|
|
384
|
+
}
|
|
385
|
+
export function validateConversationData(data) {
|
|
386
|
+
if (!isObject(data))
|
|
387
|
+
return "expected an object";
|
|
388
|
+
if (typeof data.locator !== "string")
|
|
389
|
+
return 'missing "locator" (string)';
|
|
390
|
+
return arrayProblem(data.rounds, "rounds", conversationRoundProblem);
|
|
391
|
+
}
|
|
392
|
+
export const AttemptConversation = makeAttemptComponent({
|
|
393
|
+
name: "AttemptConversation",
|
|
394
|
+
dataFnName: "attemptConversationData",
|
|
395
|
+
shapeName: "AttemptConversationData",
|
|
396
|
+
dataFn: attemptConversationData,
|
|
397
|
+
validate: validateConversationData,
|
|
398
|
+
web: (props, ctx) => _jsx(AttemptConversationWeb, { data: props.data, className: props.className }),
|
|
399
|
+
text: (props, ctx) => attemptConversationText(props.data, ctx),
|
|
400
|
+
});
|
|
401
|
+
// ───────────────────────── AttemptDiagnostics ─────────────────────────
|
|
402
|
+
/** DiagnosticRecord(src/runner/types.ts):`level` 是消息严重度,不是 verdict 的别名。 */
|
|
403
|
+
function diagnosticRecordProblem(value, path) {
|
|
404
|
+
if (!isObject(value))
|
|
405
|
+
return `"${path}" must be a DiagnosticRecord { code, level, message, phase }`;
|
|
406
|
+
if (typeof value.code !== "string")
|
|
407
|
+
return `"${path}.code" must be a string`;
|
|
408
|
+
if (value.level !== "warning" && value.level !== "error")
|
|
409
|
+
return `"${path}.level" must be "warning" or "error"`;
|
|
410
|
+
if (typeof value.message !== "string")
|
|
411
|
+
return `"${path}.message" must be a string`;
|
|
412
|
+
if (typeof value.phase !== "string")
|
|
413
|
+
return `"${path}.phase" must be a string`;
|
|
414
|
+
return null;
|
|
415
|
+
}
|
|
416
|
+
export function validateDiagnosticsData(data) {
|
|
417
|
+
if (!isObject(data))
|
|
418
|
+
return "expected an object";
|
|
419
|
+
return arrayProblem(data.groups, "groups", (group, path) => {
|
|
420
|
+
if (!isObject(group) || typeof group.phase !== "string") {
|
|
421
|
+
return `"${path}" must be an object with a string "phase"`;
|
|
422
|
+
}
|
|
423
|
+
return arrayProblem(group.items, `${path}.items`, diagnosticRecordProblem);
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
export const AttemptDiagnostics = makeAttemptComponent({
|
|
427
|
+
name: "AttemptDiagnostics",
|
|
428
|
+
dataFnName: "attemptDiagnosticsData",
|
|
429
|
+
shapeName: "AttemptDiagnosticsData",
|
|
430
|
+
dataFn: attemptDiagnosticsData,
|
|
431
|
+
validate: validateDiagnosticsData,
|
|
432
|
+
web: (props, ctx) => _jsx(AttemptDiagnosticsWeb, { data: props.data, className: props.className }),
|
|
433
|
+
text: (props, ctx) => attemptDiagnosticsText(props.data, ctx),
|
|
434
|
+
});
|
|
435
|
+
// ───────────────────────── AttemptUsage ─────────────────────────
|
|
436
|
+
/** Usage(src/o11y/types.ts):cacheReadTokens / cacheWriteTokens / requests / costUSD 均可选。 */
|
|
437
|
+
function usageProblem(value, path) {
|
|
438
|
+
if (!isObject(value))
|
|
439
|
+
return `"${path}" must be a Usage { inputTokens, outputTokens }`;
|
|
440
|
+
if (typeof value.inputTokens !== "number")
|
|
441
|
+
return `"${path}.inputTokens" must be a number`;
|
|
442
|
+
if (typeof value.outputTokens !== "number")
|
|
443
|
+
return `"${path}.outputTokens" must be a number`;
|
|
444
|
+
return null;
|
|
445
|
+
}
|
|
446
|
+
export function validateUsageData(data) {
|
|
447
|
+
if (!isObject(data))
|
|
448
|
+
return "expected an object";
|
|
449
|
+
const usageProb = usageProblem(data.usage, "usage");
|
|
450
|
+
if (usageProb !== null)
|
|
451
|
+
return usageProb;
|
|
452
|
+
if (!(data.costUSD === null || typeof data.costUSD === "number"))
|
|
453
|
+
return '"costUSD" must be a number or null';
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
456
|
+
export const AttemptUsage = makeAttemptComponent({
|
|
457
|
+
name: "AttemptUsage",
|
|
458
|
+
dataFnName: "attemptUsageData",
|
|
459
|
+
shapeName: "AttemptUsageData",
|
|
460
|
+
dataFn: attemptUsageData,
|
|
461
|
+
validate: validateUsageData,
|
|
462
|
+
web: (props, ctx) => _jsx(AttemptUsageWeb, { data: props.data, className: props.className }),
|
|
463
|
+
text: (props, ctx) => attemptUsageText(props.data, ctx),
|
|
464
|
+
});
|
|
465
|
+
// ───────────────────────── AttemptTrace ─────────────────────────
|
|
466
|
+
export function validateTraceData(data) {
|
|
467
|
+
if (!isObject(data))
|
|
468
|
+
return "expected an object";
|
|
469
|
+
if (typeof data.locator !== "string")
|
|
470
|
+
return 'missing "locator" (string)';
|
|
471
|
+
return arrayProblem(data.spans, "spans", traceSpanProblem);
|
|
472
|
+
}
|
|
473
|
+
export const AttemptTrace = makeAttemptComponent({
|
|
474
|
+
name: "AttemptTrace",
|
|
475
|
+
dataFnName: "attemptTraceData",
|
|
476
|
+
shapeName: "AttemptTraceData",
|
|
477
|
+
dataFn: attemptTraceData,
|
|
478
|
+
validate: validateTraceData,
|
|
479
|
+
web: (props, ctx) => _jsx(AttemptTraceWeb, { data: props.data, className: props.className }),
|
|
480
|
+
text: (props, ctx) => attemptTraceText(props.data, ctx),
|
|
481
|
+
});
|
|
482
|
+
// ───────────────────────── AttemptDiff ─────────────────────────
|
|
483
|
+
const DIFF_FILE_NET = ["added", "modified", "deleted"];
|
|
484
|
+
/** AttemptDiffFileEntry(src/report/model/types.ts):`net` 恒 !== "none"(净无变化不进这份列表)。 */
|
|
485
|
+
function diffFileEntryProblem(value, path) {
|
|
486
|
+
if (!isObject(value))
|
|
487
|
+
return `"${path}" must be an AttemptDiffFileEntry { path, net, lines, windows }`;
|
|
488
|
+
if (typeof value.path !== "string")
|
|
489
|
+
return `"${path}.path" must be a string`;
|
|
490
|
+
if (typeof value.net !== "string" || !DIFF_FILE_NET.includes(value.net)) {
|
|
491
|
+
return `"${path}.net" must be one of ${JSON.stringify(DIFF_FILE_NET)}`;
|
|
492
|
+
}
|
|
493
|
+
if (!isObject(value.lines) || typeof value.lines.added !== "number" || typeof value.lines.deleted !== "number") {
|
|
494
|
+
return `"${path}.lines" must be an object { added, deleted }`;
|
|
495
|
+
}
|
|
496
|
+
if (!Array.isArray(value.windows))
|
|
497
|
+
return `"${path}.windows" must be an array`;
|
|
498
|
+
return null;
|
|
499
|
+
}
|
|
500
|
+
export function validateDiffData(data) {
|
|
501
|
+
if (!isObject(data))
|
|
502
|
+
return "expected an object";
|
|
503
|
+
if (typeof data.locator !== "string")
|
|
504
|
+
return 'missing "locator" (string)';
|
|
505
|
+
return arrayProblem(data.files, "files", diffFileEntryProblem);
|
|
506
|
+
}
|
|
507
|
+
export const AttemptDiff = makeAttemptComponent({
|
|
508
|
+
name: "AttemptDiff",
|
|
509
|
+
dataFnName: "attemptDiffData",
|
|
510
|
+
shapeName: "AttemptDiffData",
|
|
511
|
+
dataFn: attemptDiffData,
|
|
512
|
+
validate: validateDiffData,
|
|
513
|
+
web: (props, ctx) => _jsx(AttemptDiffWeb, { data: props.data, className: props.className }),
|
|
514
|
+
text: (props, ctx) => attemptDiffText(props.data, ctx),
|
|
515
|
+
});
|
|
516
|
+
// ───────────────────────── 两个普通组合组件 ─────────────────────────
|
|
517
|
+
/** source / assertions fallback:有 source 放 AttemptSource,否则放 AttemptAssertions。 */
|
|
518
|
+
export const AttemptAssessment = defineComponent((_props, ctx) => {
|
|
519
|
+
if (ctx.page.input !== "attempt") {
|
|
520
|
+
throw new Error("AttemptAssessment requires an attempt-input page (input: \"attempt\") — it reads ctx.page.evidence to choose between AttemptSource and AttemptAssertions.");
|
|
521
|
+
}
|
|
522
|
+
return (_jsxs(Col, { children: [_jsx(AttemptError, {}), ctx.page.evidence.capabilities.source ? _jsx(AttemptSource, {}) : _jsx(AttemptAssertions, {})] }));
|
|
523
|
+
});
|
|
524
|
+
AttemptAssessment.displayName = "AttemptAssessment";
|
|
525
|
+
/** 内建排列顺序;不产生新的 data 或渲染面,用户可以在自己的 attempt-input page 里重排这些叶子。 */
|
|
526
|
+
export const AttemptDetail = defineComponent(() => (_jsxs(Col, { children: [_jsx(AttemptSummary, {}), _jsx(AttemptAssessment, {}), _jsx(AttemptFixPrompt, {}), _jsx(AttemptTimeline, {}), _jsx(AttemptDiagnostics, {}), _jsx(AttemptUsage, {}), _jsx(AttemptConversation, {}), _jsx(AttemptTrace, {}), _jsx(AttemptDiff, {})] })));
|
|
527
|
+
AttemptDetail.displayName = "AttemptDetail";
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { ReactElement } from "react";
|
|
2
|
-
import type { MetricCell } from "../types.ts";
|
|
2
|
+
import type { MetricCell } from "../model/types.ts";
|
|
3
3
|
import type { AttemptLocator } from "../../results/locator.ts";
|
|
4
|
-
import { type ReportLocale } from "../locale.ts";
|
|
5
|
-
export declare function MetricCellView({ cell, attemptHref, locale, }: {
|
|
4
|
+
import { type ReportLocale } from "../model/locale.ts";
|
|
5
|
+
export declare function MetricCellView({ cell, attemptHref, locale, showCoverage, }: {
|
|
6
6
|
cell: MetricCell;
|
|
7
7
|
attemptHref?: (locator: AttemptLocator) => string;
|
|
8
8
|
locale?: ReportLocale;
|
|
9
|
+
/** 默认用紧凑角标显示覆盖率;已有展开说明的摘要卡可关闭角标。 */
|
|
10
|
+
showCoverage?: boolean;
|
|
9
11
|
}): ReactElement;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { DEFAULT_REPORT_LOCALE, localeText, resolveLocalizedText } from "../locale.js";
|
|
3
|
-
export function MetricCellView({ cell, attemptHref, locale = DEFAULT_REPORT_LOCALE, }) {
|
|
2
|
+
import { DEFAULT_REPORT_LOCALE, localeText, resolveLocalizedText } from "../model/locale.js";
|
|
3
|
+
export function MetricCellView({ cell, attemptHref, locale = DEFAULT_REPORT_LOCALE, showCoverage = true, }) {
|
|
4
4
|
// 全 null(没有任何有效样本)→ 缺数据文案,绝不画 0;total 仍如实入 title
|
|
5
5
|
if (cell.value === null) {
|
|
6
6
|
return (_jsx("span", { className: "nre-cell nre-cell-missing", children: _jsx("span", { className: "nre-missing", title: localeText(locale, "cell.noneMeasurableTitle", { total: cell.total }), children: localeText(locale, "cell.missing") }) }));
|
|
7
7
|
}
|
|
8
|
-
return (_jsxs("span", { className: "nre-cell", children: [_jsx("span", { className: "nre-value", title: localeText(locale, "cell.measuredTitle", { samples: cell.samples, total: cell.total }), children: resolveLocalizedText(cell.display, locale) }), cell.samples < cell.total && (_jsxs("sup", { className: "nre-coverage", title: localeText(locale, "cell.coverageTitle", { samples: cell.samples, total: cell.total }), children: [cell.samples, "/", cell.total] })), attemptHref && cell.refs && cell.refs.length > 0 && (_jsx("span", { className: "nre-refs", children: cell.refs.map((locator, i) => (_jsxs("a", { className: "nre-ref", href: attemptHref(locator), children: ["#", i + 1] }, locator))) }))] }));
|
|
8
|
+
return (_jsxs("span", { className: "nre-cell", children: [_jsx("span", { className: "nre-value", title: localeText(locale, "cell.measuredTitle", { samples: cell.samples, total: cell.total }), children: resolveLocalizedText(cell.display, locale) }), showCoverage && cell.samples < cell.total && (_jsxs("sup", { className: "nre-coverage", title: localeText(locale, "cell.coverageTitle", { samples: cell.samples, total: cell.total }), children: [cell.samples, "/", cell.total] })), attemptHref && cell.refs && cell.refs.length > 0 && (_jsx("span", { className: "nre-refs", children: cell.refs.map((locator, i) => (_jsxs("a", { className: "nre-ref", href: attemptHref(locator), children: ["#", i + 1] }, locator))) }))] }));
|
|
9
9
|
}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import type { ReactElement } from "react";
|
|
2
|
-
import type { AttemptListItem } from "
|
|
3
|
-
import type { AttemptLocator } from "
|
|
4
|
-
import { type ReportLocale } from "
|
|
5
|
-
/**
|
|
2
|
+
import type { AttemptListItem } from "../../model/types.ts";
|
|
3
|
+
import type { AttemptLocator } from "../../../results/locator.ts";
|
|
4
|
+
import { type ReportLocale } from "../../model/locale.ts";
|
|
5
|
+
/**
|
|
6
|
+
* locator + 判定符,AttemptList/EvalList/ExperimentList 共用。没有 target(当前报告没有
|
|
7
|
+
* declare attempt-input page,也没有显式 attemptHref)时是纯文本,不生成空 href 或假链接
|
|
8
|
+
* (docs/feature/reports/architecture.md「Attempt 详情是一张参数化 page」)。
|
|
9
|
+
*/
|
|
6
10
|
export declare function AttemptLocatorBadge({ item, attemptHref, }: {
|
|
7
11
|
item: Pick<AttemptListItem, "locator" | "verdict">;
|
|
8
12
|
attemptHref?: (locator: AttemptLocator) => string;
|
|
@@ -1,11 +1,17 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { DEFAULT_REPORT_LOCALE, countText, localeText } from "
|
|
3
|
-
import { colorClassForKey } from "
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { DEFAULT_REPORT_LOCALE, countText, localeText } from "../../model/locale.js";
|
|
3
|
+
import { colorClassForKey } from "../../assets/colors.js";
|
|
4
|
+
import { formatDurationMs, formatUSD, verdictMark } from "../../model/format.js";
|
|
5
|
+
import { cx } from "../shared.js";
|
|
6
|
+
/**
|
|
7
|
+
* locator + 判定符,AttemptList/EvalList/ExperimentList 共用。没有 target(当前报告没有
|
|
8
|
+
* declare attempt-input page,也没有显式 attemptHref)时是纯文本,不生成空 href 或假链接
|
|
9
|
+
* (docs/feature/reports/architecture.md「Attempt 详情是一张参数化 page」)。
|
|
10
|
+
*/
|
|
11
|
+
export function AttemptLocatorBadge({ item, attemptHref, }) {
|
|
12
|
+
const className = cx("nre-locator", `nre-verdict-${item.verdict}`);
|
|
13
|
+
const content = (_jsxs(_Fragment, { children: [item.locator, _jsx("span", { className: "nre-locator-mark", children: verdictMark(item.verdict) })] }));
|
|
14
|
+
return attemptHref ? (_jsx("a", { className: className, href: attemptHref(item.locator), children: content })) : (_jsx("span", { className: className, children: content }));
|
|
9
15
|
}
|
|
10
16
|
/** failureSummary + moreFailures 的展示形态:摘要原样,+N 计数由 moreFailures 驱动。 */
|
|
11
17
|
export function failureSummaryText(item, locale) {
|
|
@@ -16,11 +22,11 @@ export function failureSummaryText(item, locale) {
|
|
|
16
22
|
: item.failureSummary;
|
|
17
23
|
}
|
|
18
24
|
/** 一条 Attempt 的比较卡片;完整 assertions 通过 locator 下钻,不在列表内展开。 */
|
|
19
|
-
export function AttemptRow({ item, attemptHref
|
|
25
|
+
export function AttemptRow({ item, attemptHref, locale = DEFAULT_REPORT_LOCALE, }) {
|
|
20
26
|
const reason = failureSummaryText(item, locale);
|
|
21
27
|
return (_jsxs("li", { className: cx("nre-attempt", `nre-attempt-${item.verdict}`), "data-nre-verdict": item.verdict, children: [_jsxs("div", { className: "nre-attempt-head", children: [_jsx(AttemptLocatorBadge, { item: item, attemptHref: attemptHref }), _jsx("span", { className: "nre-attempt-eval", children: item.evalId }), _jsx("span", { className: "nre-attempt-experiment", children: item.experimentId }), _jsx("span", { className: cx("nre-attempt-agent", "nre-key", colorClassForKey(item.agent)), children: item.agent }), _jsx("span", { className: "nre-attempt-duration", children: formatDurationMs(item.durationMs) }), item.costUSD !== null && _jsx("span", { className: "nre-attempt-cost", children: formatUSD(item.costUSD) })] }), reason && _jsx("p", { className: "nre-attempt-result", children: reason })] }));
|
|
22
28
|
}
|
|
23
|
-
export function AttemptList({ data, total, filter = false, attemptHref
|
|
29
|
+
export function AttemptList({ data, total, filter = false, attemptHref, className, locale = DEFAULT_REPORT_LOCALE, }) {
|
|
24
30
|
const remaining = (total ?? data.length) - data.length;
|
|
25
31
|
return (_jsxs("section", { className: cx("nre", "nre-attempt-list", className), children: [filter && (_jsx("input", { className: "nre-filter", "data-nre-attempt-filter": "", type: "search", placeholder: localeText(locale, "attemptList.filterPlaceholder") })), data.length === 0 && _jsx("p", { className: "nre-attempt-list-empty", children: localeText(locale, "attemptList.empty") }), _jsx("ul", { className: "nre-attempts", children: data.map((item) => (_jsx(AttemptRow, { item: item, attemptHref: attemptHref, locale: locale }, item.locator))) }), remaining > 0 && (_jsx("p", { className: "nre-truncated", children: localeText(locale, "attemptList.truncated", { n: remaining }) }))] }));
|
|
26
32
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ReactElement } from "react";
|
|
2
|
-
import type { AttemptLocator } from "
|
|
3
|
-
import type { EvalListItem } from "
|
|
4
|
-
import { type ReportLocale } from "
|
|
2
|
+
import type { AttemptLocator } from "../../../results/locator.ts";
|
|
3
|
+
import type { EvalListItem } from "../../model/types.ts";
|
|
4
|
+
import { type ReportLocale } from "../../model/locale.ts";
|
|
5
5
|
export declare function EvalList({ data, attemptHref, className, locale, }: {
|
|
6
6
|
data: readonly EvalListItem[];
|
|
7
7
|
attemptHref?: (locator: AttemptLocator) => string;
|
|
Binary file
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ReactElement } from "react";
|
|
2
|
-
import type { AttemptLocator } from "
|
|
3
|
-
import type { ExperimentListItem } from "
|
|
4
|
-
import { type ReportLocale } from "
|
|
2
|
+
import type { AttemptLocator } from "../../../results/locator.ts";
|
|
3
|
+
import type { ExperimentListItem } from "../../model/types.ts";
|
|
4
|
+
import { type ReportLocale } from "../../model/locale.ts";
|
|
5
5
|
export declare function ExperimentList({ data, attemptHref, filter, className, locale, relativeTo, }: {
|
|
6
6
|
data: readonly ExperimentListItem[];
|
|
7
7
|
attemptHref?: (locator: AttemptLocator) => string;
|