niceeval 0.9.1 → 0.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/INDEX.md +31 -33
- package/dist/agents/types.d.ts +20 -17
- package/dist/context/types.d.ts +21 -21
- package/dist/i18n/en.d.ts +10 -2
- package/dist/i18n/zh-CN.d.ts +14 -6
- package/dist/o11y/execution-tree.d.ts +103 -0
- package/dist/o11y/otlp/select.d.ts +22 -0
- package/dist/report/{react → assets}/colors.d.ts +8 -0
- package/dist/report/{react → assets}/colors.js +23 -0
- package/dist/report/built-in/index.d.ts +2 -2
- package/dist/report/built-in/index.js +2 -2
- package/dist/report/built-in/standard.d.ts +8 -1
- package/dist/report/built-in/standard.js +17 -7
- package/dist/report/components/attempt-detail/AttemptAssertions.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptAssertions.js +17 -0
- package/dist/report/components/attempt-detail/AttemptConversation.d.ts +10 -0
- package/dist/report/components/attempt-detail/AttemptConversation.js +74 -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 +97 -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 +297 -0
- package/dist/report/components/attempt-detail/faces.d.ts +14 -0
- package/dist/report/components/attempt-detail/faces.js +239 -0
- package/dist/report/components/attempt-detail/index.d.ts +38 -0
- package/dist/report/components/attempt-detail/index.js +547 -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 +4 -5
- package/dist/report/{react → components/entity-lists}/ExperimentList.js +12 -11
- 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 +175 -0
- package/dist/report/components/entity-lists/index.d.ts +49 -0
- package/dist/report/components/entity-lists/index.js +169 -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 +27 -60
- package/dist/report/{react → components/metric-views}/MetricTable.d.ts +3 -3
- package/dist/report/{react → components/metric-views}/MetricTable.js +4 -4
- package/dist/report/{react → components/metric-views}/Scoreboard.d.ts +2 -2
- package/dist/report/{react → components/metric-views}/Scoreboard.js +3 -3
- package/dist/report/components/metric-views/compute.d.ts +89 -0
- package/dist/report/{compute.js → components/metric-views/compute.js} +32 -415
- package/dist/report/components/metric-views/faces.d.ts +13 -0
- package/dist/report/components/metric-views/faces.js +381 -0
- package/dist/report/components/metric-views/index.d.ts +50 -0
- package/dist/report/components/metric-views/index.js +273 -0
- package/dist/report/{text → components/metric-views}/plot.js +1 -1
- package/dist/report/components/shared-compute.d.ts +22 -0
- package/dist/report/components/shared-compute.js +47 -0
- package/dist/report/components/shared-faces.d.ts +9 -0
- package/dist/report/components/shared-faces.js +26 -0
- package/dist/report/components/shared.d.ts +68 -0
- package/dist/report/components/shared.js +125 -0
- package/dist/report/{react → components/site-components}/CopyFixPrompt.d.ts +2 -2
- package/dist/report/{react → components/site-components}/CopyFixPrompt.js +2 -2
- package/dist/report/{react → components/site-components}/HeroCard.d.ts +2 -2
- package/dist/report/{react → components/site-components}/HeroCard.js +2 -2
- package/dist/report/{react → components/site-components}/ScopeWarnings.d.ts +2 -2
- package/dist/report/{react → components/site-components}/ScopeWarnings.js +3 -3
- package/dist/report/{react → components/site-components}/TraceWaterfall.d.ts +3 -3
- package/dist/report/{react → components/site-components}/TraceWaterfall.js +5 -5
- package/dist/report/components/site-components/compute.d.ts +28 -0
- package/dist/report/components/site-components/compute.js +132 -0
- package/dist/report/components/site-components/faces.d.ts +21 -0
- package/dist/report/components/site-components/faces.js +75 -0
- package/dist/report/components/site-components/index.d.ts +74 -0
- package/dist/report/components/site-components/index.js +220 -0
- package/dist/report/{scope-warnings.d.ts → components/site-components/scope-warnings.d.ts} +2 -2
- package/dist/report/{scope-warnings.js → components/site-components/scope-warnings.js} +2 -2
- package/dist/report/{react → components/summaries}/ScopeSummary.d.ts +2 -2
- package/dist/report/{react → components/summaries}/ScopeSummary.js +17 -8
- package/dist/report/components/summaries/compute.d.ts +7 -0
- package/dist/report/components/summaries/compute.js +51 -0
- package/dist/report/components/summaries/faces.d.ts +7 -0
- package/dist/report/components/summaries/faces.js +38 -0
- package/dist/report/components/summaries/index.d.ts +27 -0
- package/dist/report/components/summaries/index.js +78 -0
- package/dist/report/definition/grid-layout.d.ts +50 -0
- package/dist/report/definition/grid-layout.js +89 -0
- package/dist/report/{primitives.d.ts → definition/primitives.d.ts} +34 -4
- package/dist/report/{primitives.js → definition/primitives.js} +101 -10
- package/dist/report/{report.d.ts → definition/report.d.ts} +36 -65
- package/dist/report/{report.js → definition/report.js} +58 -90
- package/dist/report/{text/table.d.ts → definition/table-text.d.ts} +2 -2
- package/dist/report/{text/table.js → definition/table-text.js} +2 -2
- package/dist/report/{tree.d.ts → definition/tree.d.ts} +43 -11
- package/dist/report/{tree.js → definition/tree.js} +9 -11
- package/dist/report/index.d.ts +33 -18
- package/dist/report/index.js +23 -14
- package/dist/report/{aggregate.d.ts → model/aggregate.d.ts} +12 -4
- package/dist/report/{aggregate.js → model/aggregate.js} +28 -8
- package/dist/report/{flag.d.ts → model/flag.d.ts} +14 -1
- package/dist/report/{flag.js → model/flag.js} +35 -0
- package/dist/report/{format.d.ts → model/format.d.ts} +13 -7
- package/dist/report/{format.js → model/format.js} +79 -9
- 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} +154 -20
- package/dist/report/react/index.d.ts +32 -21
- package/dist/report/react/index.js +33 -21
- package/dist/report/{load.d.ts → runtime/load.d.ts} +1 -1
- package/dist/report/{load.js → runtime/load.js} +1 -1
- package/dist/report/runtime/text.d.ts +67 -0
- package/dist/report/runtime/text.js +114 -0
- package/dist/report/runtime/web.d.ts +35 -0
- package/dist/report/runtime/web.js +70 -0
- package/dist/results/annotated-source.d.ts +87 -0
- package/dist/results/attempt-evidence.d.ts +74 -0
- package/dist/results/attempt-source.d.ts +15 -0
- package/dist/results/select.d.ts +11 -1
- package/dist/results/select.js +27 -5
- package/dist/runner/eval-selection.d.ts +26 -0
- package/dist/runner/feedback/sink.d.ts +26 -1
- package/dist/runner/types.d.ts +151 -24
- package/dist/sandbox/docker.d.ts +6 -0
- package/dist/sandbox/e2b.d.ts +0 -1
- package/dist/sandbox/types.d.ts +23 -21
- package/dist/scoring/display.d.ts +7 -2
- package/dist/scoring/display.js +42 -4
- package/dist/scoring/types.d.ts +3 -3
- package/dist/shared/aggregate.d.ts +14 -0
- package/dist/shared/aggregate.js +31 -0
- package/dist/shared/types.d.ts +6 -1
- package/docs-site/images/logo-dark.svg +7 -0
- package/docs-site/images/logo.svg +6 -5
- package/docs-site/zh/README.md +8 -7
- package/docs-site/zh/examples/ai-agent-application.mdx +5 -5
- package/docs-site/zh/examples/coding-agent-extensions.mdx +4 -4
- package/docs-site/zh/examples/index.mdx +3 -3
- package/docs-site/zh/examples/integrations/ai-sdk-v7.mdx +6 -6
- 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 +17 -13
- 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 +36 -33
- 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 +195 -121
- package/docs-site/zh/reference/results-data.mdx +13 -13
- package/docs-site/zh/troubleshooting/debug-sandbox.mdx +11 -11
- package/docs-site/zh/troubleshooting/debugging.mdx +40 -16
- package/docs-site/zh/{how-to → tutorials}/agent-feedback-loop.mdx +38 -38
- package/docs-site/zh/tutorials/agent-onboarding.mdx +100 -0
- package/docs-site/zh/tutorials/authoring.mdx +211 -0
- package/docs-site/zh/{how-to → tutorials}/ci-integration.mdx +8 -8
- package/docs-site/zh/{how-to → tutorials}/connect-otel.mdx +22 -22
- package/docs-site/zh/{how-to → tutorials}/connect-your-agent.mdx +58 -63
- package/docs-site/zh/tutorials/custom-reports.mdx +453 -0
- package/docs-site/zh/{how-to → tutorials}/dataset-fanout.mdx +4 -4
- package/docs-site/zh/tutorials/experiments.mdx +102 -0
- package/docs-site/zh/{how-to → tutorials}/fixtures.mdx +9 -9
- package/docs-site/zh/{how-to → tutorials}/publish-report.mdx +6 -6
- package/docs-site/zh/tutorials/quickstart.mdx +18 -18
- package/docs-site/zh/{how-to → tutorials}/reporters.mdx +6 -6
- package/docs-site/zh/{how-to → tutorials}/sandbox-agent.mdx +6 -6
- package/docs-site/zh/{how-to → tutorials}/sandbox-providers.mdx +41 -19
- package/docs-site/zh/{how-to → tutorials}/scoring-guide.mdx +4 -4
- package/docs-site/zh/{how-to → tutorials}/viewing-results.mdx +60 -57
- package/docs-site/zh/tutorials/write-experiment.mdx +353 -0
- package/docs-site/zh/{how-to → tutorials}/write-send.mdx +40 -40
- package/package.json +10 -7
- 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 +75 -59
- package/src/context/types.ts +21 -21
- package/src/define.ts +13 -0
- package/src/i18n/en.ts +27 -12
- package/src/i18n/zh-CN.ts +27 -12
- package/src/index.ts +2 -0
- package/src/o11y/prices.json +176 -99
- package/src/report/{react → assets}/colors.ts +22 -0
- package/src/report/{react → assets}/enhance.js +1 -33
- package/src/report/assets/styles.css +1785 -0
- 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 +158 -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 +224 -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 +758 -0
- package/src/report/components/attempt-detail/compute.ts +336 -0
- package/src/report/components/attempt-detail/faces.ts +260 -0
- package/src/report/components/attempt-detail/index.tsx +600 -0
- package/src/report/components/attempt-detail/validate.test.ts +252 -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 +33 -27
- package/src/report/components/entity-lists/compute.ts +196 -0
- package/src/report/components/entity-lists/faces.ts +208 -0
- package/src/report/components/entity-lists/index.tsx +238 -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 +42 -70
- 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 +66 -26
- package/src/report/components/shared-compute.ts +59 -0
- package/src/report/components/shared-faces.ts +30 -0
- package/src/report/components/shared.ts +192 -0
- package/src/report/{react → components/site-components}/CopyFixPrompt.tsx +3 -3
- package/src/report/{react → components/site-components}/HeroCard.tsx +3 -3
- package/src/report/{react → components/site-components}/ScopeWarnings.tsx +4 -4
- package/src/report/{react → components/site-components}/TraceWaterfall.tsx +13 -10
- package/src/report/components/site-components/compute.ts +144 -0
- package/src/report/components/site-components/faces.ts +80 -0
- package/src/report/components/site-components/index.tsx +265 -0
- package/src/report/{scope-warnings.ts → components/site-components/scope-warnings.ts} +3 -3
- package/src/report/{site-components.test.tsx → components/site-components/site-components.test.tsx} +23 -21
- package/src/report/components/site-components/validate.test.ts +89 -0
- package/src/report/{react → components/summaries}/ScopeSummary.tsx +26 -11
- package/src/report/components/summaries/compute.ts +56 -0
- package/src/report/components/summaries/faces.ts +48 -0
- package/src/report/components/summaries/index.tsx +127 -0
- package/src/report/components/summaries/validate.test.ts +49 -0
- package/src/report/definition/grid-layout.test.ts +124 -0
- package/src/report/definition/grid-layout.ts +146 -0
- package/src/report/{primitives.tsx → definition/primitives.tsx} +168 -18
- package/src/report/{report.ts → definition/report.ts} +98 -165
- package/src/report/{shell-head.test.ts → definition/shell-head.test.ts} +2 -2
- package/src/report/{text/table.ts → definition/table-text.ts} +4 -4
- package/src/report/{tree.ts → definition/tree.ts} +56 -20
- package/src/report/index.ts +128 -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} +89 -10
- 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} +159 -22
- package/src/report/react/index.tsx +54 -26
- package/src/report/{dual-render.test.tsx → runtime/dual-render.test.tsx} +601 -53
- package/src/report/runtime/host.test.ts +44 -0
- package/src/report/runtime/host.ts +138 -0
- package/src/report/{load.ts → runtime/load.ts} +1 -1
- package/src/report/runtime/text.ts +192 -0
- package/src/report/runtime/web.ts +106 -0
- package/src/results/attempt-evidence.ts +5 -1
- package/src/results/host-equivalence.test.ts +53 -2
- package/src/results/select.ts +29 -5
- package/src/runner/attempt.test.ts +48 -1
- package/src/runner/attempt.ts +62 -42
- package/src/runner/cleanup-timeout.test.ts +16 -0
- package/src/runner/cleanup-timeout.ts +23 -0
- package/src/runner/discover.ts +1 -2
- package/src/runner/eval-selection.test.ts +187 -0
- package/src/runner/eval-selection.ts +109 -0
- package/src/runner/experiment-cleanup-registry.test.ts +89 -0
- package/src/runner/experiment-cleanup-registry.ts +39 -0
- package/src/runner/experiment-labels.test.ts +71 -0
- package/src/runner/feedback/agent.test.ts +33 -1
- package/src/runner/feedback/agent.ts +12 -16
- package/src/runner/feedback/ci.test.ts +35 -1
- package/src/runner/feedback/ci.ts +16 -18
- package/src/runner/feedback/coordinator.ts +28 -0
- package/src/runner/feedback/human.test.ts +108 -1
- package/src/runner/feedback/human.ts +71 -21
- package/src/runner/feedback/reducer.test.ts +64 -0
- package/src/runner/feedback/reducer.ts +26 -0
- package/src/runner/feedback/sink.ts +40 -1
- package/src/runner/fingerprint.ts +2 -1
- package/src/runner/report.test.ts +14 -0
- package/src/runner/report.ts +3 -1
- package/src/runner/run.test.ts +390 -12
- package/src/runner/run.ts +259 -26
- package/src/runner/sandbox-selection.test.ts +13 -3
- package/src/runner/sandbox-selection.ts +4 -2
- package/src/runner/types.ts +160 -24
- package/src/sandbox/docker.ts +7 -0
- package/src/sandbox/e2b-reconcile.test.ts +125 -0
- package/src/sandbox/e2b.ts +47 -12
- package/src/sandbox/types.ts +23 -21
- package/src/scoring/display.test.ts +28 -2
- package/src/scoring/display.ts +38 -5
- package/src/scoring/types.ts +3 -3
- package/src/shared/aggregate.test.ts +52 -0
- package/src/shared/aggregate.ts +29 -0
- package/src/shared/types.ts +6 -1
- package/src/show/command.test.ts +34 -0
- package/src/show/command.ts +16 -0
- package/src/show/compose.ts +1 -1
- package/src/show/index.ts +56 -20
- package/src/show/render.ts +15 -239
- package/src/show/show.test.ts +127 -34
- package/src/view/app/App.test.tsx +1 -1
- package/src/view/app/App.tsx +119 -47
- package/src/view/app/i18n.ts +7 -147
- package/src/view/app/lib/attempt-dialog.test.ts +65 -0
- package/src/view/app/lib/attempt-dialog.ts +69 -0
- package/src/view/app/main.tsx +0 -1
- package/src/view/app/types.ts +3 -86
- package/src/view/artifact-serving.test.ts +22 -38
- package/src/view/client-dist/app.css +1 -1
- package/src/view/client-dist/app.js +15 -22
- package/src/view/data.test.ts +48 -36
- package/src/view/data.ts +105 -83
- package/src/view/index.ts +1 -1
- package/src/view/server.ts +32 -2
- package/src/view/shared/types.ts +6 -31
- package/src/view/site-parity.test.ts +24 -1
- package/src/view/site.ts +144 -17
- package/src/view/styles.css +0 -805
- package/src/view/view-report.test.ts +143 -22
- package/dist/report/components.d.ts +0 -179
- package/dist/report/components.js +0 -544
- package/dist/report/compute.d.ts +0 -139
- package/dist/report/react/ExperimentComparison.d.ts +0 -10
- package/dist/report/react/ExperimentComparison.js +0 -12
- package/dist/report/react/MetricScatter.d.ts +0 -9
- package/dist/report/react/format.d.ts +0 -3
- package/dist/report/react/format.js +0 -7
- package/dist/report/text/faces.d.ts +0 -45
- package/dist/report/text/faces.js +0 -671
- package/dist/report/web.d.ts +0 -32
- package/dist/report/web.js +0 -48
- package/docs-site/zh/how-to/authoring.mdx +0 -162
- package/docs-site/zh/how-to/custom-reports.mdx +0 -414
- package/docs-site/zh/how-to/experiments.mdx +0 -86
- package/docs-site/zh/how-to/write-experiment.mdx +0 -164
- package/src/report/components.tsx +0 -925
- package/src/report/react/ExperimentComparison.tsx +0 -73
- package/src/report/react/format.ts +0 -9
- package/src/report/react/styles.css +0 -945
- 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/cli.ts
CHANGED
|
@@ -5,17 +5,20 @@
|
|
|
5
5
|
// niceeval clean 删除 .niceeval/ 历史运行 artifact
|
|
6
6
|
|
|
7
7
|
import { spawn } from "node:child_process";
|
|
8
|
-
import { createHash } from "node:crypto";
|
|
9
8
|
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
10
9
|
import { existsSync } from "node:fs";
|
|
11
10
|
import { join, relative, resolve as resolvePath } from "node:path";
|
|
12
11
|
import { pathToFileURL } from "node:url";
|
|
13
12
|
import { parseArgs as nodeParseArgs } from "node:util";
|
|
14
|
-
import { discoverEvals, discoverExperiments
|
|
13
|
+
import { discoverEvals, discoverExperiments } from "./runner/discover.ts";
|
|
14
|
+
import { browsableExperimentPaths, matchExperimentSelector } from "./shared/aggregate.ts";
|
|
15
15
|
import { runEvals, type AgentRun } from "./runner/run.ts";
|
|
16
16
|
import { planCarry } from "./runner/fingerprint.ts";
|
|
17
|
+
import { fingerprintEvalsFilter, resolveExperimentEvals, selectedEvalsForRun } from "./runner/eval-selection.ts";
|
|
17
18
|
import { failureDetailFromResult } from "./runner/feedback/failure.ts";
|
|
18
19
|
import { stopAllSandboxes, liveSandboxCount } from "./sandbox/registry.ts";
|
|
20
|
+
import { drainExperimentTeardowns } from "./runner/experiment-cleanup-registry.ts";
|
|
21
|
+
import { CLEANUP_TIMEOUT_MS } from "./runner/cleanup-timeout.ts";
|
|
19
22
|
import { evalLevelStats } from "./shared/verdict.ts";
|
|
20
23
|
import { prepareRunSandboxes, resolvedSandboxRecommendedConcurrency } from "./runner/sandbox-selection.ts";
|
|
21
24
|
import { Json, JUnit } from "./runner/reporters/json.ts";
|
|
@@ -47,14 +50,13 @@ import {
|
|
|
47
50
|
// --report 用的那份(dist/report/**,见 tsconfig.report-build.json)是同一个模块实例——
|
|
48
51
|
// `unique symbol` 品牌与 class 的 instanceof 都按声明处的模块身份判定,raw src 和编译产物
|
|
49
52
|
// 是两份不同源码位置,即使运行时同名同形,TS 类型与 instanceof 都不认。
|
|
50
|
-
import { ReportLoadError } from "../dist/report/load.js";
|
|
53
|
+
import { ReportLoadError } from "../dist/report/runtime/load.js";
|
|
51
54
|
import { runShow } from "./show/index.ts";
|
|
52
55
|
import { t } from "./i18n/index.ts";
|
|
53
56
|
import { formatThrown, upsertManagedBlock } from "./util.ts";
|
|
54
57
|
import type {
|
|
55
58
|
CompletionStatus,
|
|
56
59
|
Config,
|
|
57
|
-
DiscoveredExperiment,
|
|
58
60
|
ReporterError,
|
|
59
61
|
ReporterRegistration,
|
|
60
62
|
RunCompletion,
|
|
@@ -135,9 +137,9 @@ const FLAG_OPTIONS = {
|
|
|
135
137
|
timeout: { type: "string" },
|
|
136
138
|
/** 整次运行的预算上限(美元)。 */
|
|
137
139
|
budget: { type: "string" },
|
|
138
|
-
/** `exp` 命令专用:跑完留下 failed/errored attempt
|
|
140
|
+
/** `exp` 命令专用:跑完留下 failed/errored attempt 的 Sandbox 现场(= `--keep-sandbox=failed`);`--keep-sandbox=all` 连 passed 也留。事后用 `niceeval sandbox list/enter/stop` 查看与销毁。 */
|
|
139
141
|
"keep-sandbox": { type: "boolean" },
|
|
140
|
-
/** `sandbox stop`
|
|
142
|
+
/** `sandbox stop` 专用:销毁全部留存 Sandbox。 */
|
|
141
143
|
all: { type: "boolean" },
|
|
142
144
|
/** `sandbox diff` 专用:只看某个 send 窗口(如 `--window s1/t2`);省略输出全部窗口的串联视图。 */
|
|
143
145
|
window: { type: "string" },
|
|
@@ -155,7 +157,7 @@ const FLAG_OPTIONS = {
|
|
|
155
157
|
out: { type: "string" },
|
|
156
158
|
/** `view` 命令专用:指定本地服务器监听端口。 */
|
|
157
159
|
port: { type: "string" },
|
|
158
|
-
// show 的证据切面 / 时间轴 / 报告装载(docs-site/zh/
|
|
160
|
+
// show 的证据切面 / 时间轴 / 报告装载(docs-site/zh/tutorials/viewing-results.mdx)。
|
|
159
161
|
// 证据切面只认 `@<locator>`(或收窄到单个 eval 的前缀)选出的那一个 attempt——不再有
|
|
160
162
|
// 数字 `--attempt`,选哪个 attempt 由 locator 精确指名,不是「先选 eval 再挑第几次」。
|
|
161
163
|
/** `show` 命令专用:该 attempt 运行时保存的 Eval 源码,gate/soft 断言标回源码行(证据切面)。 */
|
|
@@ -170,7 +172,7 @@ const FLAG_OPTIONS = {
|
|
|
170
172
|
diff: { type: "boolean" },
|
|
171
173
|
/** `show` 命令专用:执行时间轴——对匹配的每个 experiment × eval 分节,逐 attempt 列时间 / verdict / 摘要 / 耗时 / 成本 / locator;与 `--report` 互斥。 */
|
|
172
174
|
history: { type: "boolean" },
|
|
173
|
-
/** `show` / `view` 命令专用:按路径段前缀收窄 experiment(与 `niceeval exp` 位置参数同一套匹配)
|
|
175
|
+
/** `show` / `view` 命令专用:按路径段前缀收窄 experiment(与 `niceeval exp` 位置参数同一套匹配);目录路径会选中其下全部配置。`view --out` 时同一收窄决定出站内容。 */
|
|
174
176
|
exp: { type: "string" },
|
|
175
177
|
/** `show` / `view` / `sandbox enter|list|stop` 共用:结果根目录(`.niceeval` 之外的另一个根,如 `copySnapshots` 产出的发布根)。 */
|
|
176
178
|
results: { type: "string" },
|
|
@@ -188,8 +190,9 @@ const FLAG_OPTIONS = {
|
|
|
188
190
|
force: { type: "boolean" },
|
|
189
191
|
/** CI 中推荐使用:让软阈值(`soft`)失败也计入整条 eval 的 verdict。 */
|
|
190
192
|
strict: { type: "boolean" },
|
|
191
|
-
/** 某个 eval 的一次 attempt 通过后,停止该 eval 剩余的 attempts。 */
|
|
193
|
+
/** 某个 eval 的一次 attempt 通过后,停止该 eval 剩余的 attempts;省略默认关(`runs` 默认跑满、测完整通过率)。 */
|
|
192
194
|
"early-exit": { type: "boolean" },
|
|
195
|
+
/** 强制关闭首过即停,即使实验文件里写了 `earlyExit: true`。 */
|
|
193
196
|
"no-early-exit": { type: "boolean" },
|
|
194
197
|
/** `view` 命令专用:启动后自动打开浏览器(默认行为)。 */
|
|
195
198
|
open: { type: "boolean" },
|
|
@@ -462,32 +465,6 @@ async function openBrowser(url: string): Promise<boolean> {
|
|
|
462
465
|
});
|
|
463
466
|
}
|
|
464
467
|
|
|
465
|
-
function evalsFilterFromExperiment(
|
|
466
|
-
evals: DiscoveredExperiment["evals"],
|
|
467
|
-
patterns: string[],
|
|
468
|
-
): (id: string) => boolean {
|
|
469
|
-
const patternFilter = makeFilter(patterns);
|
|
470
|
-
let expFilter: (id: string) => boolean = () => true;
|
|
471
|
-
if (Array.isArray(evals)) expFilter = makeFilter(evals);
|
|
472
|
-
else if (typeof evals === "function") expFilter = evals;
|
|
473
|
-
return (id) => expFilter(id) && patternFilter(id);
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
/**
|
|
477
|
-
* evals 过滤器的指纹(进 ExperimentRunInfo.evalFilterFingerprint,供「配置没变」判断):
|
|
478
|
-
* 数组按内容、函数按函数体哈希;CLI 追加的位置参数前缀一并计入。不存过滤器本身——
|
|
479
|
-
* 求值结果在 selectedEvalIds(见 runEvals)。
|
|
480
|
-
*/
|
|
481
|
-
function fingerprintEvalsFilter(evals: DiscoveredExperiment["evals"], patterns: string[]): string {
|
|
482
|
-
const basis =
|
|
483
|
-
evals === undefined || evals === "*"
|
|
484
|
-
? "*"
|
|
485
|
-
: Array.isArray(evals)
|
|
486
|
-
? JSON.stringify([...evals].sort())
|
|
487
|
-
: evals.toString();
|
|
488
|
-
return createHash("sha256").update(JSON.stringify({ basis, patterns })).digest("hex").slice(0, 16);
|
|
489
|
-
}
|
|
490
|
-
|
|
491
468
|
/**
|
|
492
469
|
* run 结束后把 coordinator 累计的诊断折成 `RunCompletion`(见 docs/feature/experiments/cli.md
|
|
493
470
|
* 「运行完成状态不只看 verdict 计数」)。只读已经真实发生过的诊断,不额外发明信号:
|
|
@@ -663,7 +640,7 @@ async function main(): Promise<void> {
|
|
|
663
640
|
|
|
664
641
|
const agentRuns: AgentRun[] = [];
|
|
665
642
|
let experimentSelection = t("cli.all");
|
|
666
|
-
let
|
|
643
|
+
let availableExperimentPaths = t("cli.none");
|
|
667
644
|
|
|
668
645
|
if (command === "exp") {
|
|
669
646
|
if (flags.agent || flags.model) {
|
|
@@ -679,16 +656,13 @@ async function main(): Promise<void> {
|
|
|
679
656
|
const expArg = positionals[0];
|
|
680
657
|
const extraPatterns = positionals.slice(1);
|
|
681
658
|
experimentSelection = positionals.join(" ") || t("cli.all");
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
const selected = expArg
|
|
686
|
-
? experiments.filter((e) => e.group === expArg || e.id === expArg || e.id.startsWith(expArg + "/"))
|
|
687
|
-
: experiments;
|
|
659
|
+
availableExperimentPaths = browsableExperimentPaths(experiments.map((e) => e.id)).join(", ") || t("cli.none");
|
|
660
|
+
const selectedIds = expArg ? new Set(matchExperimentSelector(experiments.map((e) => e.id), expArg)) : undefined;
|
|
661
|
+
const selected = selectedIds ? experiments.filter((e) => selectedIds.has(e.id)) : experiments;
|
|
688
662
|
if (selected.length === 0) {
|
|
689
663
|
process.stderr.write(t("cli.experiment.noMatch", {
|
|
690
664
|
arg: expArg ?? t("cli.all"),
|
|
691
|
-
experiments:
|
|
665
|
+
experiments: availableExperimentPaths,
|
|
692
666
|
}));
|
|
693
667
|
// show / view 是顶层命令。只有同名 experiment 确实不存在时才纠错,不能抢占合法 id。
|
|
694
668
|
if (expArg === "show" || expArg === "view") {
|
|
@@ -707,33 +681,45 @@ async function main(): Promise<void> {
|
|
|
707
681
|
}
|
|
708
682
|
for (const exp of selected) {
|
|
709
683
|
// 一个实验 = 一个配置(单 model)。跨模型对比写多个实验文件,各钉一个 model。
|
|
684
|
+
// evals 谓词在这里对本次 invocation 的候选 eval 各求值一次;下游(dry-run、sandbox 查表、
|
|
685
|
+
// fingerprint/carry、attempt 展开)只消费 selectedEvalIds,不重新调用谓词
|
|
686
|
+
// (见 docs/feature/experiments/library.md「evals」)。
|
|
687
|
+
const { selectedEvalIds } = resolveExperimentEvals({
|
|
688
|
+
experimentId: exp.id,
|
|
689
|
+
selector: exp.evals,
|
|
690
|
+
cliPatterns: extraPatterns,
|
|
691
|
+
evals,
|
|
692
|
+
});
|
|
710
693
|
agentRuns.push({
|
|
711
694
|
agent: exp.agent,
|
|
712
695
|
model: exp.model,
|
|
713
696
|
reasoningEffort: exp.reasoningEffort,
|
|
714
697
|
flags: exp.flags ?? {},
|
|
715
698
|
runs: flags.runs ?? envNumber("NICEEVAL_RUNS") ?? exp.runs ?? 1,
|
|
716
|
-
earlyExit: flags.earlyExit ?? exp.earlyExit ??
|
|
699
|
+
earlyExit: flags.earlyExit ?? exp.earlyExit ?? false,
|
|
717
700
|
sandbox: exp.sandbox ?? config.sandbox,
|
|
718
701
|
timeoutMs: flags.timeout ?? envNumber("NICEEVAL_TIMEOUT") ?? exp.timeoutMs ?? config.timeoutMs,
|
|
719
702
|
budget: flags.budget ?? envNumber("NICEEVAL_BUDGET") ?? exp.budget,
|
|
720
|
-
|
|
703
|
+
selectedEvalIds,
|
|
721
704
|
experimentId: exp.id,
|
|
722
705
|
description: exp.description,
|
|
706
|
+
labels: exp.labels,
|
|
723
707
|
evalFilterFingerprint: fingerprintEvalsFilter(exp.evals, extraPatterns),
|
|
724
708
|
strict: flags.strict,
|
|
725
709
|
// 实验级并发上限:随 AgentRun 进调度器按实验单独限流(runner 两级信号量),
|
|
726
710
|
// 不再取所有选中实验的最小值钳全局——那会让一个串行实验拖慢整批基线。
|
|
727
711
|
maxConcurrency: exp.maxConcurrency,
|
|
712
|
+
setup: exp.setup,
|
|
713
|
+
teardown: exp.teardown,
|
|
728
714
|
});
|
|
729
715
|
}
|
|
730
716
|
} else {
|
|
731
717
|
// 裸 run / `niceeval <eval>` 不再执行。运行配置必须来自 experiments/,
|
|
732
718
|
// 这样 agent/model/flags/runs/budget 与结果聚合都有可签入的身份。
|
|
733
719
|
const experiments = await discoverExperiments(cwd);
|
|
734
|
-
const
|
|
735
|
-
|
|
736
|
-
);
|
|
720
|
+
const ids = experiments.map((e) => e.id);
|
|
721
|
+
const matchedIds = new Set(positionals.flatMap((p) => matchExperimentSelector(ids, p)));
|
|
722
|
+
const asExp = experiments.filter((e) => matchedIds.has(e.id));
|
|
737
723
|
process.stderr.write(t("cli.run.experimentRequired"));
|
|
738
724
|
if (asExp.length > 0) {
|
|
739
725
|
process.stderr.write(t("cli.run.experimentRequiredHint", {
|
|
@@ -758,14 +744,14 @@ async function main(): Promise<void> {
|
|
|
758
744
|
|
|
759
745
|
// matchedByRun[i] 对应 agentRuns[i] 匹配到的 eval 集合;--dry 预览与真正开跑时的
|
|
760
746
|
// RunFeedbackPlan(总量、去重 eval 数)共用同一份计算,不重复过滤一遍。
|
|
761
|
-
const matchedByRun = agentRuns.map((run) => evals
|
|
747
|
+
const matchedByRun = agentRuns.map((run) => selectedEvalsForRun(evals, run));
|
|
762
748
|
const totalRuns = agentRuns.reduce((sum, run, i) => sum + matchedByRun[i]!.length * run.runs, 0);
|
|
763
749
|
const uniqueEvalIds = new Set(matchedByRun.flat().map((e) => e.id));
|
|
764
750
|
|
|
765
751
|
if (totalRuns === 0) {
|
|
766
752
|
process.stderr.write(t("cli.experiment.noEvalsSelected", {
|
|
767
753
|
selection: experimentSelection,
|
|
768
|
-
experiments:
|
|
754
|
+
experiments: availableExperimentPaths,
|
|
769
755
|
}));
|
|
770
756
|
process.exit(1);
|
|
771
757
|
}
|
|
@@ -873,13 +859,34 @@ async function main(): Promise<void> {
|
|
|
873
859
|
// 3 次:真不耐烦了,硬退(此时多半已无可清理的)。
|
|
874
860
|
const ctrl = new AbortController();
|
|
875
861
|
let signalCount = 0;
|
|
876
|
-
//
|
|
877
|
-
//
|
|
862
|
+
// 强清 = 加速收尾,不是绕过收尾(docs/cli.md「中断:三级响应」)。顺序:先强停沙箱(卡在
|
|
863
|
+
// 沙箱 I/O 上的收尾立刻失败返回),然后事件驱动收口——并发等待「在飞收尾链 settle」与
|
|
864
|
+
// 「实验级 teardown 注册表排空」(drain 会启动未启动的、等待在飞的同一 memoized promise),
|
|
865
|
+
// 两者都 settle 即退。兜底上限从单可调用体清理上限推导(docs 声明的不等式:provider stop 8s
|
|
866
|
+
// < 看门狗 < CLEANUP_TIMEOUT_MS ≤ 本上限),不是第 2 级的语义——settle 才是——只拦
|
|
867
|
+
// 「收尾可调用体绕过了自己的超时」的失守病态,到点放弃退出(职责同第 3 级硬退)。
|
|
868
|
+
// 只跑一次;先停 dashboard 的 tick/动态区域(coordinator.stopDynamic()),
|
|
869
|
+
// 避免硬退时终端卡在半帧 ANSI 状态。
|
|
870
|
+
const FORCE_SETTLE_CAP_MS = CLEANUP_TIMEOUT_MS * 2;
|
|
871
|
+
let runInFlight: Promise<unknown> | undefined;
|
|
878
872
|
let forcing = false;
|
|
879
873
|
const forceCleanupAndExit = (code: number) => {
|
|
880
874
|
if (forcing) return;
|
|
881
875
|
forcing = true;
|
|
882
|
-
void
|
|
876
|
+
void (async () => {
|
|
877
|
+
await Promise.all([coordinator.stopDynamic(), stopAllSandboxes()]);
|
|
878
|
+
const settled = Promise.allSettled([
|
|
879
|
+
...(runInFlight ? [runInFlight] : []),
|
|
880
|
+
drainExperimentTeardowns(),
|
|
881
|
+
]);
|
|
882
|
+
await Promise.race([
|
|
883
|
+
settled.then(() => {}),
|
|
884
|
+
new Promise<void>((r) => {
|
|
885
|
+
setTimeout(r, FORCE_SETTLE_CAP_MS).unref();
|
|
886
|
+
}),
|
|
887
|
+
]);
|
|
888
|
+
process.exit(code);
|
|
889
|
+
})();
|
|
883
890
|
};
|
|
884
891
|
for (const sig of ["SIGINT", "SIGTERM"] as const) {
|
|
885
892
|
process.on(sig, () => {
|
|
@@ -887,13 +894,16 @@ async function main(): Promise<void> {
|
|
|
887
894
|
if (signalCount === 1) {
|
|
888
895
|
reportActivity(t("cli.interruptCleanup").trimEnd());
|
|
889
896
|
ctrl.abort();
|
|
890
|
-
// 看门狗:graceful
|
|
897
|
+
// 看门狗:graceful 清理迟迟没让进程自己收口,就强清兜底。取值在 docs/cli.md 声明的
|
|
898
|
+
// 不等式链里:> provider stop 超时(8s,一次正常停容器超时后才升级,不误伤),
|
|
899
|
+
// < CLEANUP_TIMEOUT_MS(30s)。
|
|
900
|
+
const GRACEFUL_WATCHDOG_MS = 12_000;
|
|
891
901
|
setTimeout(() => {
|
|
892
902
|
if (liveSandboxCount() > 0) {
|
|
893
903
|
reportActivity(t("cli.fallbackCleanupTimeout").trimEnd());
|
|
894
904
|
forceCleanupAndExit(130);
|
|
895
905
|
}
|
|
896
|
-
},
|
|
906
|
+
}, GRACEFUL_WATCHDOG_MS).unref();
|
|
897
907
|
} else if (signalCount === 2) {
|
|
898
908
|
reportActivity(t("cli.forceCleanupExit").trimEnd());
|
|
899
909
|
forceCleanupAndExit(130);
|
|
@@ -921,7 +931,7 @@ async function main(): Promise<void> {
|
|
|
921
931
|
|
|
922
932
|
let summary: RunSummary;
|
|
923
933
|
try {
|
|
924
|
-
|
|
934
|
+
const inFlight = runEvals({
|
|
925
935
|
config,
|
|
926
936
|
evals,
|
|
927
937
|
agentRuns,
|
|
@@ -933,15 +943,20 @@ async function main(): Promise<void> {
|
|
|
933
943
|
keepSandbox: flags.keepSandbox,
|
|
934
944
|
niceevalRoot: resolvePath(cwd, ".niceeval"),
|
|
935
945
|
});
|
|
946
|
+
// 交给强清路径一个可等待的收尾句柄:二次中断/看门狗强清时先有界等它收口,让在飞的
|
|
947
|
+
// teardown 链跑完,而不是 process.exit 把它们连同进程一起杀掉。
|
|
948
|
+
runInFlight = inFlight;
|
|
949
|
+
summary = await inFlight;
|
|
936
950
|
} catch (e) {
|
|
937
951
|
// 真崩溃前先撤下 dashboard,不让半帧 ANSI 状态和下面 main().catch 打印的错误交织。
|
|
938
952
|
await coordinator.stopDynamic();
|
|
939
953
|
throw e;
|
|
940
954
|
}
|
|
941
955
|
|
|
942
|
-
// 正常返回(含被中断后走部分汇总)后再兜一刀:Scope finalizer
|
|
943
|
-
//
|
|
956
|
+
// 正常返回(含被中断后走部分汇总)后再兜一刀:Scope finalizer 没停掉的残留沙箱、没被运行
|
|
957
|
+
// 路径消费的实验级 cleanup 在这里强清。跑顺利时两份登记表都已空,是 no-op。
|
|
944
958
|
await stopAllSandboxes();
|
|
959
|
+
await drainExperimentTeardowns();
|
|
945
960
|
|
|
946
961
|
// completion 要先算好,--json/--junit 是否"这次真的写出"才有依据(见下)。
|
|
947
962
|
const completion = assembleRunCompletion(coordinator.state);
|
|
@@ -979,7 +994,8 @@ async function main(): Promise<void> {
|
|
|
979
994
|
|
|
980
995
|
main().catch(async (e) => {
|
|
981
996
|
process.stderr.write(t("cli.error", { error: formatThrown(e) }));
|
|
982
|
-
// 真·崩溃路径也别留孤儿:强清还活着的沙箱(带超时)
|
|
997
|
+
// 真·崩溃路径也别留孤儿:强清还活着的沙箱(带超时)、排空实验级 cleanup 注册表,再退。
|
|
983
998
|
await stopAllSandboxes();
|
|
999
|
+
await drainExperimentTeardowns();
|
|
984
1000
|
process.exit(2);
|
|
985
1001
|
});
|
package/src/context/types.ts
CHANGED
|
@@ -140,35 +140,35 @@ export interface RespondAnswer {
|
|
|
140
140
|
readonly text?: string;
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
-
/**
|
|
143
|
+
/** 评估用例作者可见的受限 Sandbox 视图:能执行命令 / 文件 IO / 读最终 diff,但不能 stop。 */
|
|
144
144
|
export interface SandboxHandle {
|
|
145
|
-
/**
|
|
145
|
+
/** Sandbox 内的工作目录绝对路径。 */
|
|
146
146
|
readonly workdir: string;
|
|
147
|
-
/**
|
|
147
|
+
/** 在 Sandbox 里执行一条命令(argv 形式,不经 shell)。装系统依赖等需要 root 时传 `{ root: true }`。 */
|
|
148
148
|
runCommand(cmd: string, args?: string[], opts?: CommandOptions): Promise<CommandResult>;
|
|
149
|
-
/**
|
|
149
|
+
/** 在 Sandbox 里执行一段 shell 脚本(经 shell 解释,支持管道 / 重定向)。 */
|
|
150
150
|
runShell(script: string, opts?: CommandOptions): Promise<CommandResult>;
|
|
151
|
-
/**
|
|
151
|
+
/** 读 Sandbox 内某文件此刻的文本内容(实时读,不是 diff 快照)。 */
|
|
152
152
|
readFile(path: string): Promise<string>;
|
|
153
|
-
/**
|
|
153
|
+
/** Sandbox 内某路径此刻是否存在。 */
|
|
154
154
|
fileExists(path: string): Promise<boolean>;
|
|
155
|
-
/**
|
|
155
|
+
/** 按选项批量读回 Sandbox 里的源文件,供比对 / judge 用材料。 */
|
|
156
156
|
readSourceFiles(opts?: ReadSourceFilesOptions): Promise<SourceFiles>;
|
|
157
|
-
/**
|
|
157
|
+
/** 把一组内容写进 Sandbox(路径相对 targetDir,默认 workdir)。 */
|
|
158
158
|
writeFiles(files: Record<string, string>, targetDir?: string): Promise<void>;
|
|
159
|
-
/**
|
|
159
|
+
/** 把一批内存中的文件上传进 Sandbox。 */
|
|
160
160
|
uploadFiles(files: SandboxFile[], targetDir?: string): Promise<void>;
|
|
161
|
-
/**
|
|
161
|
+
/** 把本地目录整体上传进 Sandbox;`opts.ignore` 排除指定路径。 */
|
|
162
162
|
uploadDirectory(localDir: string, targetDir?: string, opts?: { ignore?: string[] }): Promise<void>;
|
|
163
|
-
/**
|
|
163
|
+
/** 从 Sandbox 下载某文件内容。 */
|
|
164
164
|
downloadFile(path: string): Promise<Buffer>;
|
|
165
|
-
/**
|
|
165
|
+
/** 把一段内容上传成 Sandbox 里的单个文件。 */
|
|
166
166
|
uploadFile(path: string, content: Buffer): Promise<void>;
|
|
167
|
-
/**
|
|
167
|
+
/** Sandbox provider 分配的实例 id,用于排查 / 关联日志。 */
|
|
168
168
|
readonly sandboxId: string;
|
|
169
169
|
/** 相对 git 基线的最终 diff 视图(test() 跑完、finalize 前才落定)。 */
|
|
170
170
|
readonly diff: DiffView;
|
|
171
|
-
/**
|
|
171
|
+
/** 取 Sandbox 内某文件的最终内容,占位延迟到 finalize 才真正读取;只能配合 t.check 使用。 */
|
|
172
172
|
file(path: string): string;
|
|
173
173
|
/** 断言 diff 里某路径发生了变化(新增或修改)。 */
|
|
174
174
|
fileChanged(path: string): AssertionHandle;
|
|
@@ -176,7 +176,7 @@ export interface SandboxHandle {
|
|
|
176
176
|
fileDeleted(path: string): AssertionHandle;
|
|
177
177
|
/** 断言 diff 的路径与内容都不匹配给定正则(常用来否定式检查不该出现的改动)。 */
|
|
178
178
|
notInDiff(re: RegExp): AssertionHandle;
|
|
179
|
-
/**
|
|
179
|
+
/** 断言 Sandbox 里执行过的命令都没有失败退出。 */
|
|
180
180
|
noFailedShellCommands(): AssertionHandle;
|
|
181
181
|
}
|
|
182
182
|
|
|
@@ -285,7 +285,7 @@ export interface TestContext {
|
|
|
285
285
|
/** 本次 attempt 生效的实验 flags(experiment.flags 的只读视图;实验条件,非命令行开关)。 */
|
|
286
286
|
readonly flags: Readonly<Record<string, unknown>>;
|
|
287
287
|
/**
|
|
288
|
-
*
|
|
288
|
+
* 作用域反馈:报告评估用例自己执行的长步骤(上传 Fixture、跑构建……)。短命状态,scope 固定
|
|
289
289
|
* 为 `eval.run`;只报告不断言(见 docs/feature/eval/library/context.md「向运行反馈长步骤」)。
|
|
290
290
|
*/
|
|
291
291
|
progress(update: ProgressUpdate): void;
|
|
@@ -296,18 +296,18 @@ export interface TestContext {
|
|
|
296
296
|
diagnostic(input: DiagnosticInput): void;
|
|
297
297
|
/** `progress({ message: msg })` 的别名(调试日志),不出现在最终结果里。 */
|
|
298
298
|
log(msg: string): void;
|
|
299
|
-
/**
|
|
299
|
+
/** 立即中止本评估用例并标记为 skipped(verdict / EvalResult.skipReason),reason 不能为空。 */
|
|
300
300
|
skip(reason: string): never;
|
|
301
301
|
|
|
302
302
|
// 值断言
|
|
303
303
|
/**
|
|
304
304
|
* 对任意值跑一个 ValueAssertion,返回可链 `.gate()` / `.atLeast()` 的 AssertionHandle。
|
|
305
|
-
*
|
|
306
|
-
*
|
|
305
|
+
* 打分延迟到评估用例结束后统一 finalize,调用本身同步、不抛错——不通过只是记一条失败断言,
|
|
306
|
+
* 不会中止后续代码。要「不满足就立即中止评估用例」用 require。
|
|
307
307
|
*/
|
|
308
308
|
check(value: unknown, assertion: ValueAssertion): AssertionHandle;
|
|
309
309
|
/**
|
|
310
|
-
* 对任意值跑一个 ValueAssertion,立即(await 时)
|
|
310
|
+
* 对任意值跑一个 ValueAssertion,立即(await 时)求值;不满足就抛错中止整个评估用例剩余步骤
|
|
311
311
|
* (仍会把这条断言计入报告,不影响已记录的其它断言)。跟 check 的区别:check 只记录、
|
|
312
312
|
* 从不抛错,打分留到最后统一算;require 当场判定、失败即中止,适合「前置条件不满足,
|
|
313
313
|
* 后面写了也没意义」的场景。
|
|
@@ -352,7 +352,7 @@ export interface TestContext {
|
|
|
352
352
|
eventsSatisfy(label: string, predicate: (events: readonly StreamEvent[]) => boolean): AssertionHandle;
|
|
353
353
|
|
|
354
354
|
// 工作区 / 沙箱
|
|
355
|
-
/**
|
|
355
|
+
/** 受限 Sandbox 视图:能执行命令 / 读写文件 / 看最终 diff,不能 stop Sandbox 本身(见 SandboxHandle)。 */
|
|
356
356
|
readonly sandbox: SandboxHandle;
|
|
357
357
|
|
|
358
358
|
// 效率 / 成本
|
package/src/define.ts
CHANGED
|
@@ -67,6 +67,11 @@ export function defineExperiment(def: ExperimentDef): ExperimentDef {
|
|
|
67
67
|
throw new Error(t("define.experimentIdRejected"));
|
|
68
68
|
}
|
|
69
69
|
if (!def.agent) throw new Error(t("define.experimentAgentRequired"));
|
|
70
|
+
// setup 是实验级生命周期钩子(整场一次,宿主机侧,见 runner/types.ts 的 ExperimentDef.setup);
|
|
71
|
+
// 传成非函数(如误把 sandbox 钩子对象塞进来)在解析时就报,不等到调度才炸。
|
|
72
|
+
if (def.setup !== undefined && typeof def.setup !== "function") {
|
|
73
|
+
throw new Error(t("define.experimentSetupNotFunction"));
|
|
74
|
+
}
|
|
70
75
|
// flags 必须可 JSON 序列化(进结果快照的 ExperimentRunInfo.flags):解析时即校验,
|
|
71
76
|
// 非 JSON 值(函数 / undefined / 循环引用 / bigint)直接报错,不等到落盘才炸。
|
|
72
77
|
if (def.flags !== undefined) {
|
|
@@ -76,6 +81,14 @@ export function defineExperiment(def: ExperimentDef): ExperimentDef {
|
|
|
76
81
|
}
|
|
77
82
|
}
|
|
78
83
|
}
|
|
84
|
+
// labels 是报告归类坐标(进 ExperimentRunInfo.labels,不透传 ctx/t):值域 string | number,
|
|
85
|
+
// 解析时即校验,布尔 / 对象 / NaN 直接报错,不等到落盘或报告分组才炸。
|
|
86
|
+
if (def.labels !== undefined) {
|
|
87
|
+
for (const [key, value] of Object.entries(def.labels)) {
|
|
88
|
+
const ok = typeof value === "string" || (typeof value === "number" && Number.isFinite(value));
|
|
89
|
+
if (!ok) throw new Error(t("define.experimentLabelInvalid", { key }));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
79
92
|
return def;
|
|
80
93
|
}
|
|
81
94
|
|
package/src/i18n/en.ts
CHANGED
|
@@ -60,9 +60,16 @@ export const en = {
|
|
|
60
60
|
"cli.flag.invalidOutput": "Flag --output expects one of auto|human|agent|ci, got \"{{value}}\".\n",
|
|
61
61
|
"runner.budgetUnenforceable":
|
|
62
62
|
"budget for {{budgetKey}}: several attempts completed without any cost data (agent reports no usage and the model is not in the price table) — the budget cannot be enforced for this agent; continuing without the guard.\n",
|
|
63
|
+
"runner.experimentTeardownFailed":
|
|
64
|
+
"teardown for experiment {{experimentId}} failed: {{message}}. Results are unaffected, but host-side resources started by this experiment may not have been released; check manually.\n",
|
|
65
|
+
"runner.cleanupTimeout": "cleanup timed out after {{timeoutMs}}ms\n",
|
|
66
|
+
"runner.setupReturnedCleanup":
|
|
67
|
+
"{{layer}} returned a function. setup does not carry cleanup and the returned value will not be executed — put the cleanup in the paired teardown of the same layer ({{hint}}); see the experiments tutorial on docs-site or docs/runner.md.\n",
|
|
68
|
+
"runner.experimentTeardownLate":
|
|
69
|
+
"experiment {{experimentId}}'s teardown was not triggered by the normal countdown path; it has been executed by the end-of-run sweep instead. Results are unaffected; seeing this line means an unlocated intermittent scheduling issue fired — please record this run in the memory ledger.\n",
|
|
63
70
|
"judge.modelMissing":
|
|
64
71
|
"No judge model configured. Set it in defineConfig({ judge: { model: \"...\" } }), the eval's judge config, or the NICEEVAL_JUDGE_MODEL environment variable (there is no built-in default model).\n" +
|
|
65
|
-
" Docs: node_modules/niceeval/docs-site/zh/
|
|
72
|
+
" Docs: node_modules/niceeval/docs-site/zh/tutorials/scoring-guide.mdx",
|
|
66
73
|
"loaders.yamlMissing":
|
|
67
74
|
"loadYaml(\"{{path}}\") needs a YAML parser: run `pnpm add yaml` first (or switch to loadJson with a JSON dataset).",
|
|
68
75
|
"cli.flag.parseError": "{{message}}\nRun `niceeval --help` for usage.\n",
|
|
@@ -70,7 +77,7 @@ export const en = {
|
|
|
70
77
|
"cli.help":
|
|
71
78
|
"niceeval — agent-native evals\n\n" +
|
|
72
79
|
"Usage:\n" +
|
|
73
|
-
" niceeval exp [
|
|
80
|
+
" niceeval exp [path|experiment] [eval-id-prefix…] run experiments\n" +
|
|
74
81
|
" niceeval show [eval-id-prefix… | @<locator>] read results in the terminal\n" +
|
|
75
82
|
" bare: current verdicts per experiment (composed across runs), each row\n" +
|
|
76
83
|
" with a compact attempt index (locator + failure reason)\n" +
|
|
@@ -121,10 +128,14 @@ export const en = {
|
|
|
121
128
|
"cli.eval.noMatchKnown": "Discovered {{count}} evals: {{evals}}\n",
|
|
122
129
|
"cli.exp.agentModelFlagUnsupported": "experiment runs do not support --agent / --model. Add or copy an experiment file and change its model instead.\n",
|
|
123
130
|
"cli.exp.viewerFlagUnsupported": "`{{flag}}` only applies to niceeval {{command}}, not niceeval exp.\n",
|
|
124
|
-
"cli.experiment.noMatch":
|
|
131
|
+
"cli.experiment.noMatch":
|
|
132
|
+
"No experiment matched: {{arg}}. Available paths: {{experiments}}.\n" +
|
|
133
|
+
"Run `niceeval exp <path> --dry` to preview a plan.\n",
|
|
125
134
|
"cli.experiment.viewerCommandHint": "Did you mean: niceeval {{command}}{{args}}\n",
|
|
126
|
-
"cli.experiment.noEvalsSelected":
|
|
127
|
-
|
|
135
|
+
"cli.experiment.noEvalsSelected":
|
|
136
|
+
"No evals selected: {{selection}} matched 0 evals. Available eval prefixes: {{experiments}}.\n" +
|
|
137
|
+
"Run `niceeval exp {{selection}} --dry` to see what it covers, or drop the eval filter to run every eval selected by those experiments.\n",
|
|
138
|
+
"cli.experimentGroup": " path",
|
|
128
139
|
"cli.fallbackCleanupTimeout": "\ngraceful cleanup timed out; force-cleaning sandboxes...\n",
|
|
129
140
|
"cli.forceCleanupExit": "\nForce-cleaning sandboxes and exiting...\n",
|
|
130
141
|
"cli.init.done": "Ready: evals/, niceeval.config.ts, and the niceeval agent-rules block in AGENTS.md (points coding agents at node_modules/niceeval/docs-site/zh).\n",
|
|
@@ -135,8 +146,8 @@ export const en = {
|
|
|
135
146
|
"cli.pressCtrlC": "Press Ctrl+C to exit.\n",
|
|
136
147
|
"cli.resultsPath": "Structured results: {{path}} (snapshot.json + per-attempt result.json / events.json / trace.json / diff.json)\n",
|
|
137
148
|
"cli.run.experimentRequired":
|
|
138
|
-
"Run evals through an experiment: use `niceeval exp [
|
|
139
|
-
" Docs: node_modules/niceeval/docs-site/zh/
|
|
149
|
+
"Run evals through an experiment: use `niceeval exp [path|config] [eval id prefix]`.\n" +
|
|
150
|
+
" Docs: node_modules/niceeval/docs-site/zh/tutorials/write-experiment.mdx\n",
|
|
140
151
|
"cli.run.experimentRequiredHint": "Hint: \"{{pattern}}\" is an experiment{{kind}}; you probably meant: niceeval exp {{pattern}}\n",
|
|
141
152
|
"cli.run.experimentRequiredKnown": "Discovered experiments: {{experiments}}\n",
|
|
142
153
|
"cli.unimplemented": "Command \"{{command}}\" is not implemented yet (MVP).\n",
|
|
@@ -147,7 +158,7 @@ export const en = {
|
|
|
147
158
|
"cli.view.url": "niceeval view: {{url}}\n",
|
|
148
159
|
"context.capabilityMissing":
|
|
149
160
|
"Agent \"{{agent}}\" is not sandbox-backed (built with defineSandboxAgent), so t.{{method}} is unavailable. Use an agent built with defineSandboxAgent, or drop this assertion.\n" +
|
|
150
|
-
" Docs: node_modules/niceeval/docs-site/zh/
|
|
161
|
+
" Docs: node_modules/niceeval/docs-site/zh/tutorials/sandbox-agent.mdx",
|
|
151
162
|
"context.skipEmpty": "skip() requires a non-empty reason.",
|
|
152
163
|
"context.turnFailed": "This send returned failed (turn status = failed): {{message}}",
|
|
153
164
|
"context.turnFailedDefault": "This send returned failed (turn status = failed)",
|
|
@@ -157,6 +168,8 @@ export const en = {
|
|
|
157
168
|
"define.evalTestRequired": "defineEval requires an async test(t) function.",
|
|
158
169
|
"define.experimentAgentRequired": "defineExperiment requires agent.",
|
|
159
170
|
"define.experimentFlagNotJson": "experiment.flags.{{key}} is not JSON-serializable (functions / undefined / cycles / bigint are not allowed); flags are persisted verbatim into result snapshots and must be plain JSON.",
|
|
171
|
+
"define.experimentLabelInvalid": "experiment.labels.{{key}} must be a string or a finite number; labels are report-side grouping coordinates persisted verbatim into result snapshots.",
|
|
172
|
+
"define.experimentSetupNotFunction": "experiment.setup must be a function ((ctx) => void); use experiment.teardown for cleanup; to prepare the in-sandbox environment per experiment, chain .setup() hooks on the sandbox spec instead.",
|
|
160
173
|
"define.experimentIdRejected": "defineExperiment does not accept id; ids are derived from file paths.",
|
|
161
174
|
"define.sandboxAgentNameRequired": "defineSandboxAgent requires name.",
|
|
162
175
|
"define.sandboxCreateRequired": "defineSandbox requires a create() function.",
|
|
@@ -169,7 +182,7 @@ export const en = {
|
|
|
169
182
|
"docker.unsupportedRuntime": "Unsupported runtime: {{runtime}}",
|
|
170
183
|
"feedback.human.active": "ACTIVE",
|
|
171
184
|
"feedback.human.budgetExhausted": "budget exhausted for {{experimentId}} (spent {{spent}}, unstarted {{unstarted}})",
|
|
172
|
-
"feedback.human.compare": "Compare: niceeval view
|
|
185
|
+
"feedback.human.compare": "Compare: niceeval view",
|
|
173
186
|
"feedback.human.counts": "{{total}} total · {{reused}} reused · {{running}} running · {{queued}} queued · {{completed}} completed",
|
|
174
187
|
"feedback.human.diffHint": "Diff: niceeval show {{locator}} --diff",
|
|
175
188
|
"feedback.human.evalHint": "Eval: niceeval show {{locator}} --source",
|
|
@@ -194,6 +207,10 @@ export const en = {
|
|
|
194
207
|
"feedback.phase.evalSetup": "eval setup",
|
|
195
208
|
"feedback.phase.sandboxCreate": "creating sandbox",
|
|
196
209
|
"feedback.phase.sandboxQueue": "queued for sandbox",
|
|
210
|
+
"feedback.phase.experimentSetup": "experiment setup",
|
|
211
|
+
"feedback.phase.experimentTeardown": "experiment teardown",
|
|
212
|
+
"feedback.human.hookDone": "done",
|
|
213
|
+
"feedback.human.hookFailed": "failed",
|
|
197
214
|
"feedback.phase.sandboxSetup": "sandbox setup",
|
|
198
215
|
"feedback.phase.scoring": "scoring",
|
|
199
216
|
"feedback.phase.teardown": "cleaning up",
|
|
@@ -273,13 +290,11 @@ export const en = {
|
|
|
273
290
|
"runner.startSandboxTeardown": "sandbox teardown (environment provisioning hooks)...",
|
|
274
291
|
"runner.timeout": "attempt timed out ({{timeoutMs}}ms)\nRecent progress:\n{{recentLogs}}",
|
|
275
292
|
"runner.traceSelected": " -> kept {{count}} semantic spans",
|
|
276
|
-
"runner.resumeCarry": " · reusing {{carried}} settled results from last run, re-running {{retry}} evals\n",
|
|
277
|
-
"runner.resumeCarryDetail": " carried [{{experiment}}] {{evals}}\n",
|
|
278
293
|
"runner.useRemoteAgent": "using remote agent (no sandbox created)...",
|
|
279
294
|
"sandbox.providerNotImplemented": "{{provider}} sandbox provider is not implemented; use docker, vercel, or e2b",
|
|
280
295
|
"sandbox.missingSpec":
|
|
281
296
|
"sandbox agent needs a sandbox, but none was given. niceeval no longer picks a default — set `sandbox` in defineExperiment()/defineConfig() to dockerSandbox() / vercelSandbox() / e2bSandbox() (import from \"niceeval/sandbox\").\n" +
|
|
282
|
-
" Docs: node_modules/niceeval/docs-site/zh/
|
|
297
|
+
" Docs: node_modules/niceeval/docs-site/zh/tutorials/sandbox-providers.mdx",
|
|
283
298
|
"sandbox.dependencyMissing.docker": "Docker sandbox requires 'dockerode'. Install it with: pnpm add dockerode @types/dockerode",
|
|
284
299
|
"sandbox.dependencyMissing.e2b": "E2B sandbox requires 'e2b'. Install it with: pnpm add e2b",
|
|
285
300
|
"sandbox.dependencyMissing.vercel": "Vercel sandbox requires '@vercel/sandbox'. Install it with: pnpm add @vercel/sandbox",
|