niceeval 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/INDEX.md +31 -33
- package/dist/agents/types.d.ts +20 -17
- package/dist/context/types.d.ts +21 -21
- package/dist/i18n/en.d.ts +10 -2
- package/dist/i18n/zh-CN.d.ts +15 -7
- package/dist/o11y/execution-tree.d.ts +103 -0
- package/dist/o11y/otlp/select.d.ts +22 -0
- package/dist/report/{react → assets}/colors.d.ts +8 -0
- package/dist/report/{react → assets}/colors.js +23 -0
- package/dist/report/built-in/index.d.ts +2 -2
- package/dist/report/built-in/index.js +2 -2
- package/dist/report/built-in/standard.d.ts +8 -1
- package/dist/report/built-in/standard.js +17 -7
- package/dist/report/components/attempt-detail/AttemptAssertions.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptAssertions.js +17 -0
- package/dist/report/components/attempt-detail/AttemptConversation.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptConversation.js +34 -0
- package/dist/report/components/attempt-detail/AttemptDiagnostics.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptDiagnostics.js +7 -0
- package/dist/report/components/attempt-detail/AttemptDiff.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptDiff.js +11 -0
- package/dist/report/components/attempt-detail/AttemptError.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptError.js +16 -0
- package/dist/report/components/attempt-detail/AttemptFixPrompt.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptFixPrompt.js +7 -0
- package/dist/report/components/attempt-detail/AttemptSource.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptSource.js +27 -0
- package/dist/report/components/attempt-detail/AttemptSummary.d.ts +8 -0
- package/dist/report/components/attempt-detail/AttemptSummary.js +20 -0
- package/dist/report/components/attempt-detail/AttemptTimeline.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptTimeline.js +28 -0
- package/dist/report/components/attempt-detail/AttemptTrace.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptTrace.js +29 -0
- package/dist/report/components/attempt-detail/AttemptUsage.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptUsage.js +20 -0
- package/dist/report/components/attempt-detail/compute.d.ts +24 -0
- package/dist/report/components/attempt-detail/compute.js +254 -0
- package/dist/report/components/attempt-detail/faces.d.ts +14 -0
- package/dist/report/components/attempt-detail/faces.js +235 -0
- package/dist/report/components/attempt-detail/index.d.ts +38 -0
- package/dist/report/components/attempt-detail/index.js +527 -0
- package/dist/report/{react → components}/cell.d.ts +5 -3
- package/dist/report/{react → components}/cell.js +3 -3
- package/dist/report/{react → components/entity-lists}/AttemptList.d.ts +8 -4
- package/dist/report/{react → components/entity-lists}/AttemptList.js +16 -10
- package/dist/report/{react → components/entity-lists}/EvalList.d.ts +3 -3
- package/dist/report/{react → components/entity-lists}/EvalList.js +0 -0
- package/dist/report/{react → components/entity-lists}/ExperimentList.d.ts +3 -3
- package/dist/report/{react → components/entity-lists}/ExperimentList.js +10 -10
- package/dist/report/components/entity-lists/compute.d.ts +23 -0
- package/dist/report/components/entity-lists/compute.js +171 -0
- package/dist/report/components/entity-lists/faces.d.ts +5 -0
- package/dist/report/components/entity-lists/faces.js +174 -0
- package/dist/report/components/entity-lists/index.d.ts +51 -0
- package/dist/report/components/entity-lists/index.js +165 -0
- package/dist/report/{react → components}/fixtures.d.ts +2 -2
- package/dist/report/{react → components}/fixtures.js +1 -1
- package/dist/report/{react → components/metric-views}/DeltaTable.d.ts +2 -2
- package/dist/report/{react → components/metric-views}/DeltaTable.js +4 -4
- package/dist/report/{react → components/metric-views}/MetricBars.d.ts +3 -3
- package/dist/report/{react → components/metric-views}/MetricBars.js +3 -3
- package/dist/report/{react → components/metric-views}/MetricLine.d.ts +2 -2
- package/dist/report/{react → components/metric-views}/MetricLine.js +8 -5
- package/dist/report/{react → components/metric-views}/MetricMatrix.d.ts +3 -3
- package/dist/report/{react → components/metric-views}/MetricMatrix.js +4 -4
- package/dist/report/components/metric-views/MetricScatter.d.ts +11 -0
- package/dist/report/{react → components/metric-views}/MetricScatter.js +26 -23
- package/dist/report/{react → components/metric-views}/MetricTable.d.ts +3 -3
- package/dist/report/{react → components/metric-views}/MetricTable.js +4 -4
- package/dist/report/{react → components/metric-views}/Scoreboard.d.ts +2 -2
- package/dist/report/{react → components/metric-views}/Scoreboard.js +3 -3
- package/dist/report/components/metric-views/compute.d.ts +89 -0
- package/dist/report/{compute.js → components/metric-views/compute.js} +32 -415
- package/dist/report/components/metric-views/faces.d.ts +13 -0
- package/dist/report/components/metric-views/faces.js +381 -0
- package/dist/report/components/metric-views/index.d.ts +50 -0
- package/dist/report/components/metric-views/index.js +273 -0
- package/dist/report/{text → components/metric-views}/plot.js +1 -1
- package/dist/report/components/shared-compute.d.ts +22 -0
- package/dist/report/components/shared-compute.js +47 -0
- package/dist/report/components/shared-faces.d.ts +9 -0
- package/dist/report/components/shared-faces.js +26 -0
- package/dist/report/components/shared.d.ts +68 -0
- package/dist/report/components/shared.js +125 -0
- package/dist/report/{react → components/site-components}/CopyFixPrompt.d.ts +2 -2
- package/dist/report/{react → components/site-components}/CopyFixPrompt.js +2 -2
- package/dist/report/{react → components/site-components}/HeroCard.d.ts +2 -2
- package/dist/report/{react → components/site-components}/HeroCard.js +2 -2
- package/dist/report/{react → components/site-components}/ScopeWarnings.d.ts +2 -2
- package/dist/report/{react → components/site-components}/ScopeWarnings.js +3 -3
- package/dist/report/{react → components/site-components}/TraceWaterfall.d.ts +3 -3
- package/dist/report/{react → components/site-components}/TraceWaterfall.js +5 -5
- package/dist/report/components/site-components/compute.d.ts +28 -0
- package/dist/report/components/site-components/compute.js +132 -0
- package/dist/report/components/site-components/faces.d.ts +21 -0
- package/dist/report/components/site-components/faces.js +75 -0
- package/dist/report/components/site-components/index.d.ts +74 -0
- package/dist/report/components/site-components/index.js +220 -0
- package/dist/report/{scope-warnings.d.ts → components/site-components/scope-warnings.d.ts} +2 -2
- package/dist/report/{scope-warnings.js → components/site-components/scope-warnings.js} +2 -2
- package/dist/report/{react → components/summaries}/ScopeSummary.d.ts +2 -2
- package/dist/report/{react → components/summaries}/ScopeSummary.js +17 -8
- package/dist/report/components/summaries/compute.d.ts +7 -0
- package/dist/report/components/summaries/compute.js +51 -0
- package/dist/report/components/summaries/faces.d.ts +7 -0
- package/dist/report/components/summaries/faces.js +38 -0
- package/dist/report/components/summaries/index.d.ts +27 -0
- package/dist/report/components/summaries/index.js +78 -0
- package/dist/report/definition/grid-layout.d.ts +50 -0
- package/dist/report/definition/grid-layout.js +89 -0
- package/dist/report/{primitives.d.ts → definition/primitives.d.ts} +34 -4
- package/dist/report/{primitives.js → definition/primitives.js} +101 -10
- package/dist/report/{report.d.ts → definition/report.d.ts} +36 -65
- package/dist/report/{report.js → definition/report.js} +58 -90
- package/dist/report/{text/table.d.ts → definition/table-text.d.ts} +2 -2
- package/dist/report/{text/table.js → definition/table-text.js} +2 -2
- package/dist/report/{tree.d.ts → definition/tree.d.ts} +44 -12
- package/dist/report/{tree.js → definition/tree.js} +10 -12
- package/dist/report/index.d.ts +33 -18
- package/dist/report/index.js +23 -14
- package/dist/report/{aggregate.d.ts → model/aggregate.d.ts} +12 -4
- package/dist/report/{aggregate.js → model/aggregate.js} +28 -8
- package/dist/report/{flag.d.ts → model/flag.d.ts} +14 -1
- package/dist/report/{flag.js → model/flag.js} +35 -0
- package/dist/report/{format.d.ts → model/format.d.ts} +11 -5
- package/dist/report/{format.js → model/format.js} +48 -4
- package/dist/report/{locale.d.ts → model/locale.d.ts} +17 -17
- package/dist/report/{locale.js → model/locale.js} +31 -33
- package/dist/report/{metrics.d.ts → model/metrics.d.ts} +2 -2
- package/dist/report/{metrics.js → model/metrics.js} +2 -2
- package/dist/report/{types.d.ts → model/types.d.ts} +140 -20
- package/dist/report/react/index.d.ts +32 -21
- package/dist/report/react/index.js +33 -21
- package/dist/report/{load.d.ts → runtime/load.d.ts} +1 -1
- package/dist/report/{load.js → runtime/load.js} +1 -1
- package/dist/report/runtime/text.d.ts +67 -0
- package/dist/report/runtime/text.js +114 -0
- package/dist/report/runtime/web.d.ts +35 -0
- package/dist/report/runtime/web.js +70 -0
- package/dist/results/annotated-source.d.ts +87 -0
- package/dist/results/attempt-evidence.d.ts +74 -0
- package/dist/results/attempt-source.d.ts +15 -0
- package/dist/results/select.d.ts +12 -2
- package/dist/results/select.js +27 -5
- package/dist/runner/eval-selection.d.ts +26 -0
- package/dist/runner/feedback/sink.d.ts +26 -1
- package/dist/runner/types.d.ts +149 -23
- package/dist/sandbox/docker.d.ts +6 -0
- package/dist/sandbox/types.d.ts +23 -21
- package/dist/scoring/display.d.ts +7 -2
- package/dist/scoring/display.js +42 -4
- package/dist/scoring/types.d.ts +3 -3
- package/dist/shared/aggregate.d.ts +15 -1
- package/dist/shared/aggregate.js +32 -1
- package/dist/shared/types.d.ts +6 -1
- package/docs-site/images/logo-dark.svg +7 -0
- package/docs-site/images/logo.svg +6 -5
- package/docs-site/zh/README.md +8 -7
- package/docs-site/zh/examples/ai-agent-application.mdx +5 -5
- package/docs-site/zh/examples/coding-agent-extensions.mdx +4 -4
- package/docs-site/zh/examples/index.mdx +3 -3
- package/docs-site/zh/examples/integrations/ai-sdk-v7.mdx +1 -1
- package/docs-site/zh/examples/integrations/claude-sdk.mdx +3 -3
- package/docs-site/zh/examples/integrations/codex-sdk.mdx +3 -3
- package/docs-site/zh/examples/integrations/langgraph.mdx +3 -3
- package/docs-site/zh/examples/integrations/pi-sdk.mdx +2 -2
- package/docs-site/zh/explanation/adapter.mdx +19 -19
- package/docs-site/zh/explanation/assert.mdx +9 -9
- package/docs-site/zh/explanation/drive.mdx +4 -4
- package/docs-site/zh/explanation/evals.mdx +8 -8
- package/docs-site/zh/explanation/experiment.mdx +8 -8
- package/docs-site/zh/explanation/hitl.mdx +13 -13
- package/docs-site/zh/explanation/judge.mdx +3 -3
- package/docs-site/zh/explanation/overview.mdx +7 -7
- package/docs-site/zh/explanation/runner.mdx +11 -11
- package/docs-site/zh/explanation/tier.mdx +6 -6
- package/docs-site/zh/index.mdx +14 -14
- package/docs-site/zh/introduction.mdx +13 -16
- package/docs-site/zh/reference/builtin-agents.mdx +61 -28
- package/docs-site/zh/reference/capabilities.mdx +7 -27
- package/docs-site/zh/reference/cli.mdx +35 -32
- package/docs-site/zh/reference/define-agent.mdx +32 -31
- package/docs-site/zh/reference/define-config.mdx +6 -6
- package/docs-site/zh/reference/define-eval.mdx +33 -20
- package/docs-site/zh/reference/events.mdx +4 -4
- package/docs-site/zh/reference/expect.mdx +3 -3
- package/docs-site/zh/reference/official-adapters.mdx +17 -17
- package/docs-site/zh/reference/report-components.mdx +193 -121
- package/docs-site/zh/reference/results-data.mdx +13 -13
- package/docs-site/zh/troubleshooting/debug-sandbox.mdx +11 -11
- package/docs-site/zh/troubleshooting/debugging.mdx +43 -19
- package/docs-site/zh/{how-to → tutorials}/agent-feedback-loop.mdx +38 -38
- package/docs-site/zh/tutorials/agent-onboarding.mdx +100 -0
- package/docs-site/zh/tutorials/authoring.mdx +211 -0
- package/docs-site/zh/{how-to → tutorials}/ci-integration.mdx +8 -8
- package/docs-site/zh/{how-to → tutorials}/connect-otel.mdx +22 -22
- package/docs-site/zh/{how-to → tutorials}/connect-your-agent.mdx +58 -63
- package/docs-site/zh/tutorials/custom-reports.mdx +453 -0
- package/docs-site/zh/{how-to → tutorials}/dataset-fanout.mdx +4 -4
- package/docs-site/zh/tutorials/experiments.mdx +103 -0
- package/docs-site/zh/{how-to → tutorials}/fixtures.mdx +9 -9
- package/docs-site/zh/{how-to → tutorials}/publish-report.mdx +11 -5
- package/docs-site/zh/tutorials/quickstart.mdx +18 -18
- package/docs-site/zh/{how-to → tutorials}/reporters.mdx +6 -6
- package/docs-site/zh/{how-to → tutorials}/sandbox-agent.mdx +6 -6
- package/docs-site/zh/{how-to → tutorials}/sandbox-providers.mdx +41 -19
- package/docs-site/zh/{how-to → tutorials}/scoring-guide.mdx +4 -4
- package/docs-site/zh/{how-to → tutorials}/viewing-results.mdx +59 -50
- package/docs-site/zh/tutorials/write-experiment.mdx +355 -0
- package/docs-site/zh/{how-to → tutorials}/write-send.mdx +40 -40
- package/package.json +8 -4
- package/src/agents/bub.ts +21 -8
- package/src/agents/claude-code.ts +23 -10
- package/src/agents/codex.test.ts +22 -7
- package/src/agents/codex.ts +22 -9
- package/src/agents/index.ts +2 -2
- package/src/agents/openai-compat.ts +1 -1
- package/src/agents/post-setup.ts +45 -22
- package/src/agents/streaming.ts +1 -1
- package/src/agents/types.ts +20 -17
- package/src/agents/ui-message-stream.test.ts +10 -0
- package/src/agents/ui-message-stream.ts +12 -1
- package/src/cli.ts +77 -64
- package/src/context/types.ts +21 -21
- package/src/define.ts +13 -0
- package/src/i18n/en.ts +30 -15
- package/src/i18n/zh-CN.ts +30 -15
- package/src/index.ts +2 -0
- package/src/report/{react → assets}/colors.ts +22 -0
- package/src/report/{react → assets}/enhance.js +1 -33
- package/src/report/{react → assets}/styles.css +136 -112
- package/src/report/built-in/index.tsx +2 -2
- package/src/report/built-in/standard.tsx +18 -6
- package/src/report/components/attempt-detail/AttemptAssertions.tsx +67 -0
- package/src/report/components/attempt-detail/AttemptConversation.tsx +110 -0
- package/src/report/components/attempt-detail/AttemptDiagnostics.tsx +38 -0
- package/src/report/components/attempt-detail/AttemptDiff.tsx +35 -0
- package/src/report/components/attempt-detail/AttemptError.tsx +31 -0
- package/src/report/components/attempt-detail/AttemptFixPrompt.tsx +26 -0
- package/src/report/components/attempt-detail/AttemptSource.tsx +80 -0
- package/src/report/components/attempt-detail/AttemptSummary.tsx +74 -0
- package/src/report/components/attempt-detail/AttemptTimeline.tsx +108 -0
- package/src/report/components/attempt-detail/AttemptTrace.tsx +63 -0
- package/src/report/components/attempt-detail/AttemptUsage.tsx +29 -0
- package/src/report/components/attempt-detail/attempt-components.test.tsx +680 -0
- package/src/report/components/attempt-detail/compute.ts +287 -0
- package/src/report/components/attempt-detail/faces.ts +257 -0
- package/src/report/components/attempt-detail/index.tsx +583 -0
- package/src/report/components/attempt-detail/validate.test.ts +235 -0
- package/src/report/{react → components}/cell.tsx +6 -3
- package/src/report/{report.test.ts → components/compute.test.ts} +316 -46
- package/src/report/{react → components/entity-lists}/AttemptList.tsx +24 -16
- package/src/report/{react → components/entity-lists}/EvalList.tsx +0 -0
- package/src/report/{react → components/entity-lists}/ExperimentList.tsx +23 -20
- package/src/report/components/entity-lists/compute.ts +196 -0
- package/src/report/components/entity-lists/faces.ts +207 -0
- package/src/report/components/entity-lists/index.tsx +241 -0
- package/src/report/components/entity-lists/validate.test.ts +114 -0
- package/src/report/{react → components}/fixtures.ts +2 -2
- package/src/report/{react → components/metric-views}/DeltaTable.tsx +5 -5
- package/src/report/{react → components/metric-views}/MetricBars.tsx +5 -5
- package/src/report/{react → components/metric-views}/MetricLine.tsx +9 -6
- package/src/report/{react → components/metric-views}/MetricMatrix.tsx +6 -6
- package/src/report/{react → components/metric-views}/MetricScatter.tsx +41 -33
- package/src/report/{react → components/metric-views}/MetricTable.tsx +6 -6
- package/src/report/{react → components/metric-views}/Scoreboard.tsx +4 -4
- package/src/report/{compute.ts → components/metric-views/compute.ts} +51 -477
- package/src/report/components/metric-views/faces.ts +423 -0
- package/src/report/components/metric-views/index.tsx +352 -0
- package/src/report/{text → components/metric-views}/plot.ts +1 -1
- package/src/report/components/metric-views/validate.test.ts +211 -0
- package/src/report/{react → components}/render.test.tsx +47 -17
- package/src/report/components/shared-compute.ts +59 -0
- package/src/report/components/shared-faces.ts +30 -0
- package/src/report/components/shared.ts +192 -0
- package/src/report/{react → components/site-components}/CopyFixPrompt.tsx +3 -3
- package/src/report/{react → components/site-components}/HeroCard.tsx +3 -3
- package/src/report/{react → components/site-components}/ScopeWarnings.tsx +4 -4
- package/src/report/{react → components/site-components}/TraceWaterfall.tsx +13 -10
- package/src/report/components/site-components/compute.ts +144 -0
- package/src/report/components/site-components/faces.ts +80 -0
- package/src/report/components/site-components/index.tsx +265 -0
- package/src/report/{scope-warnings.ts → components/site-components/scope-warnings.ts} +3 -3
- package/src/report/{site-components.test.tsx → components/site-components/site-components.test.tsx} +23 -21
- package/src/report/components/site-components/validate.test.ts +89 -0
- package/src/report/{react → components/summaries}/ScopeSummary.tsx +26 -11
- package/src/report/components/summaries/compute.ts +56 -0
- package/src/report/components/summaries/faces.ts +48 -0
- package/src/report/components/summaries/index.tsx +127 -0
- package/src/report/components/summaries/validate.test.ts +49 -0
- package/src/report/definition/grid-layout.test.ts +124 -0
- package/src/report/definition/grid-layout.ts +146 -0
- package/src/report/{primitives.tsx → definition/primitives.tsx} +168 -18
- package/src/report/{report.ts → definition/report.ts} +98 -165
- package/src/report/{shell-head.test.ts → definition/shell-head.test.ts} +2 -2
- package/src/report/{text/table.ts → definition/table-text.ts} +4 -4
- package/src/report/{tree.ts → definition/tree.ts} +58 -22
- package/src/report/index.ts +126 -66
- package/src/report/{aggregate.ts → model/aggregate.ts} +31 -9
- package/src/report/{flag.ts → model/flag.ts} +39 -1
- package/src/report/{format.ts → model/format.ts} +59 -5
- package/src/report/{locale.ts → model/locale.ts} +34 -36
- package/src/report/{metrics.ts → model/metrics.ts} +3 -3
- package/src/report/{types.ts → model/types.ts} +143 -22
- package/src/report/react/index.tsx +52 -26
- package/src/report/{dual-render.test.tsx → runtime/dual-render.test.tsx} +564 -49
- package/src/report/runtime/host.test.ts +44 -0
- package/src/report/runtime/host.ts +138 -0
- package/src/report/{load.ts → runtime/load.ts} +1 -1
- package/src/report/runtime/text.ts +192 -0
- package/src/report/runtime/web.ts +106 -0
- package/src/results/attempt-evidence.ts +5 -1
- package/src/results/host-equivalence.test.ts +54 -3
- package/src/results/select.ts +30 -6
- package/src/runner/attempt.test.ts +48 -1
- package/src/runner/attempt.ts +62 -42
- package/src/runner/cleanup-timeout.test.ts +16 -0
- package/src/runner/cleanup-timeout.ts +23 -0
- package/src/runner/discover.ts +1 -2
- package/src/runner/eval-selection.test.ts +187 -0
- package/src/runner/eval-selection.ts +109 -0
- package/src/runner/experiment-cleanup-registry.test.ts +89 -0
- package/src/runner/experiment-cleanup-registry.ts +39 -0
- package/src/runner/experiment-labels.test.ts +71 -0
- package/src/runner/feedback/agent.test.ts +33 -1
- package/src/runner/feedback/agent.ts +12 -16
- package/src/runner/feedback/ci.test.ts +35 -1
- package/src/runner/feedback/ci.ts +16 -18
- package/src/runner/feedback/coordinator.ts +28 -0
- package/src/runner/feedback/human.test.ts +108 -1
- package/src/runner/feedback/human.ts +71 -21
- package/src/runner/feedback/reducer.test.ts +64 -0
- package/src/runner/feedback/reducer.ts +26 -0
- package/src/runner/feedback/sink.ts +40 -1
- package/src/runner/fingerprint.ts +2 -1
- package/src/runner/report.test.ts +14 -0
- package/src/runner/report.ts +3 -1
- package/src/runner/run.test.ts +390 -12
- package/src/runner/run.ts +259 -26
- package/src/runner/sandbox-selection.test.ts +13 -3
- package/src/runner/sandbox-selection.ts +4 -2
- package/src/runner/types.ts +158 -23
- package/src/sandbox/docker.ts +7 -0
- package/src/sandbox/e2b.ts +12 -13
- package/src/sandbox/types.ts +23 -21
- package/src/scoring/display.test.ts +28 -2
- package/src/scoring/display.ts +38 -5
- package/src/scoring/types.ts +3 -3
- package/src/shared/aggregate.test.ts +52 -0
- package/src/shared/aggregate.ts +30 -1
- package/src/shared/types.ts +6 -1
- package/src/show/command.test.ts +34 -0
- package/src/show/command.ts +16 -0
- package/src/show/compose.ts +1 -1
- package/src/show/index.ts +58 -22
- package/src/show/render.ts +16 -240
- package/src/show/show.test.ts +128 -35
- package/src/view/app/App.test.tsx +8 -7
- package/src/view/app/App.tsx +135 -51
- package/src/view/app/i18n.ts +7 -147
- package/src/view/app/lib/attempt-dialog.test.ts +65 -0
- package/src/view/app/lib/attempt-dialog.ts +69 -0
- package/src/view/app/main.tsx +0 -1
- package/src/view/app/types.ts +3 -86
- package/src/view/artifact-serving.test.ts +22 -38
- package/src/view/client-dist/app.css +1 -1
- package/src/view/client-dist/app.js +15 -22
- package/src/view/data.test.ts +48 -36
- package/src/view/data.ts +122 -89
- package/src/view/index.ts +3 -13
- package/src/view/server.ts +36 -6
- package/src/view/shared/types.ts +6 -31
- package/src/view/site-parity.test.ts +24 -1
- package/src/view/site.ts +144 -17
- package/src/view/styles.css +18 -806
- package/src/view/view-report.test.ts +182 -34
- package/dist/report/components.d.ts +0 -179
- package/dist/report/components.js +0 -544
- package/dist/report/compute.d.ts +0 -139
- package/dist/report/react/ExperimentComparison.d.ts +0 -10
- package/dist/report/react/ExperimentComparison.js +0 -12
- package/dist/report/react/MetricScatter.d.ts +0 -9
- package/dist/report/react/format.d.ts +0 -3
- package/dist/report/react/format.js +0 -7
- package/dist/report/text/faces.d.ts +0 -45
- package/dist/report/text/faces.js +0 -671
- package/dist/report/web.d.ts +0 -32
- package/dist/report/web.js +0 -48
- package/docs-site/zh/how-to/authoring.mdx +0 -162
- package/docs-site/zh/how-to/custom-reports.mdx +0 -414
- package/docs-site/zh/how-to/experiments.mdx +0 -86
- package/docs-site/zh/how-to/write-experiment.mdx +0 -164
- package/src/report/components.tsx +0 -925
- package/src/report/react/ExperimentComparison.tsx +0 -73
- package/src/report/react/format.ts +0 -9
- package/src/report/text/faces.ts +0 -767
- package/src/report/web.ts +0 -77
- package/src/show/report-host.test.ts +0 -205
- package/src/show/report-host.ts +0 -389
- package/src/view/app/components/AttemptModal.tsx +0 -496
- package/src/view/app/components/CodeView.test.tsx +0 -142
- package/src/view/app/components/CodeView.tsx +0 -310
- package/src/view/app/components/CopyControls.tsx +0 -106
- package/src/view/app/components/Trace.tsx +0 -100
- package/src/view/app/components/Transcript.tsx +0 -157
- package/src/view/app/components/ui/badge.tsx +0 -21
- package/src/view/app/lib/artifact-url.ts +0 -17
- package/src/view/app/lib/attempt-route.test.ts +0 -80
- package/src/view/app/lib/attempt-route.ts +0 -52
- package/src/view/app/lib/format.ts +0 -70
- package/src/view/app/lib/guards.test.ts +0 -108
- package/src/view/app/lib/guards.ts +0 -71
- package/src/view/app/lib/rows.ts +0 -22
- package/src/view/app/lib/transcript-data.tsx +0 -151
- package/src/view/app/lib/verdict.ts +0 -23
- package/src/view/app/shared.ts +0 -8
- /package/dist/report/{react → components/metric-views}/chart-math.d.ts +0 -0
- /package/dist/report/{react → components/metric-views}/chart-math.js +0 -0
- /package/dist/report/{text → components/metric-views}/plot.d.ts +0 -0
- /package/dist/report/{react → components/site-components}/PoweredBy.d.ts +0 -0
- /package/dist/report/{react → components/site-components}/PoweredBy.js +0 -0
- /package/dist/report/{text/layout.d.ts → model/text-layout.d.ts} +0 -0
- /package/dist/report/{text/layout.js → model/text-layout.js} +0 -0
- /package/dist/report/{types.js → model/types.js} +0 -0
- /package/src/report/{react → components/metric-views}/chart-math.test.ts +0 -0
- /package/src/report/{react → components/metric-views}/chart-math.ts +0 -0
- /package/src/report/{react → components/site-components}/PoweredBy.tsx +0 -0
- /package/src/report/{text/layout.ts → model/text-layout.ts} +0 -0
package/src/runner/run.ts
CHANGED
|
@@ -8,13 +8,15 @@ import { probeJudge } from "../scoring/judge.ts";
|
|
|
8
8
|
import { t } from "../i18n/index.ts";
|
|
9
9
|
import { cacheKey, planCarry } from "./fingerprint.ts";
|
|
10
10
|
import { OtelReceiverPool } from "../o11y/otlp/turn-otel.ts";
|
|
11
|
-
import { runAttemptEffect } from "./attempt.ts";
|
|
11
|
+
import { errorFromThrown, experimentRunInfo, runAttemptEffect } from "./attempt.ts";
|
|
12
12
|
import { runReporter, emitReporterEvent, scopeReporter, summarize } from "./report.ts";
|
|
13
13
|
import {
|
|
14
14
|
reportActivity,
|
|
15
15
|
reportAttemptLifecycle,
|
|
16
16
|
reportBudgetExhausted,
|
|
17
17
|
reportDiagnostic,
|
|
18
|
+
reportExperimentHook,
|
|
19
|
+
reportExperimentProgress,
|
|
18
20
|
reportFailure,
|
|
19
21
|
reportInterrupted,
|
|
20
22
|
} from "./feedback/sink.ts";
|
|
@@ -22,8 +24,11 @@ import { failureDetailFromResult } from "./feedback/failure.ts";
|
|
|
22
24
|
import { encodeAttemptLocator, type AttemptLocator } from "../results/locator.ts";
|
|
23
25
|
import { runWho } from "./types.ts";
|
|
24
26
|
import { prepareRunSandboxes, sandboxForEval } from "./sandbox-selection.ts";
|
|
27
|
+
import { selectedEvalsForRun } from "./eval-selection.ts";
|
|
28
|
+
import { registerExperimentTeardown, unregisterExperimentTeardown } from "./experiment-cleanup-registry.ts";
|
|
29
|
+
import { withCleanupTimeout } from "./cleanup-timeout.ts";
|
|
25
30
|
import type { Agent, EvalResult, JudgeConfig, Reporter, ReporterRegistration, RunShape, RunSummary } from "../types.ts";
|
|
26
|
-
import type { AgentRun, Attempt, LifecyclePhase, AttemptRef, RunOptions } from "./types.ts";
|
|
31
|
+
import type { AgentRun, Attempt, ExperimentHookContext, LifecyclePhase, AttemptRef, RunOptions } from "./types.ts";
|
|
27
32
|
|
|
28
33
|
/** 反馈层的 attempt 身份 + 展示 label,两个 sink.ts lifecycle 调用点共用,避免各自手写
|
|
29
34
|
* 同一组字段(见 memory 的 live-who-key-mismatch-freezes-rows —— 手写副本漏改是真实事故源)。 */
|
|
@@ -113,10 +118,10 @@ export async function runEvals(opts: RunOptions): Promise<RunSummary> {
|
|
|
113
118
|
// 首个通过会 abort 同 key 其余 attempt,且它们的结果被下面的去重检查丢弃,不会重复计入。
|
|
114
119
|
const attempts: Attempt[] = [];
|
|
115
120
|
for (const run of opts.agentRuns) {
|
|
116
|
-
|
|
117
|
-
//
|
|
118
|
-
//
|
|
119
|
-
|
|
121
|
+
// selectedEvalIds 已由 CLI 在构造 AgentRun 时对候选 eval 各求值一次算好(见
|
|
122
|
+
// eval-selection.ts 的 resolveExperimentEvals());这里只按 resolved id 取 eval,
|
|
123
|
+
// 不重新调用用户谓词(见 docs/feature/results/architecture.md「selectedEvalIds」)。
|
|
124
|
+
const evals = selectedEvalsForRun(opts.evals, run);
|
|
120
125
|
for (let i = 0; i < run.runs; i++) {
|
|
121
126
|
for (const evalDef of evals) {
|
|
122
127
|
const carryKey = `${run.experimentId ?? ""}|${evalDef.id}`;
|
|
@@ -150,6 +155,30 @@ export async function runEvals(opts: RunOptions): Promise<RunSummary> {
|
|
|
150
155
|
}
|
|
151
156
|
}
|
|
152
157
|
|
|
158
|
+
// 派发顺序:瓶颈优先(docs/runner.md「派发顺序:瓶颈优先,追求最小总墙钟时间」)。
|
|
159
|
+
// 单次 attempt 耗时未知且假设同批内大致均匀,轮次数(该 run 的 attempt 数 / 有效并发宽度,
|
|
160
|
+
// 向上取整)就是耗时的代理指标——把 identical-machine 调度的 LPT 规则推广到 moldable job
|
|
161
|
+
// 场景:轮次多的 run 是关键路径瓶颈,让它先抢到并发位,总时长才接近瓶颈自身的串行耗时,
|
|
162
|
+
// 而不是「瓶颈耗时 + 排在它前面的其它 run 先跑完的耗时」。只在建 attempt 列表时算一次,
|
|
163
|
+
// 不随 earlyExit / fail-fast / budget 实际提前收尾而重算(动态调整不值得为一个尽力而为的
|
|
164
|
+
// 启发式引入)。只重排派发顺序,不改两级信号量本身;结果仍按发现顺序输出(见下方 sort)。
|
|
165
|
+
{
|
|
166
|
+
const byRun = new Map<AgentRun, Attempt[]>();
|
|
167
|
+
for (const a of attempts) {
|
|
168
|
+
let group = byRun.get(a.run);
|
|
169
|
+
if (!group) byRun.set(a.run, (group = []));
|
|
170
|
+
group.push(a);
|
|
171
|
+
}
|
|
172
|
+
const rounds = (run: AgentRun, count: number): number => {
|
|
173
|
+
const width = Math.min(run.maxConcurrency ?? opts.maxConcurrency, opts.maxConcurrency);
|
|
174
|
+
return Math.ceil(count / width);
|
|
175
|
+
};
|
|
176
|
+
const groups = [...byRun.entries()];
|
|
177
|
+
groups.sort((a, b) => rounds(b[0], b[1].length) - rounds(a[0], a[1].length));
|
|
178
|
+
attempts.length = 0;
|
|
179
|
+
for (const [, group] of groups) attempts.push(...group);
|
|
180
|
+
}
|
|
181
|
+
|
|
153
182
|
// 预检 judge:验证 API key + 端点可达,避免跑完 agent 才发现 judge 不通。
|
|
154
183
|
// 放在 attempts 展开之后,fail fast 只对会真正触发 judge 的运行生效
|
|
155
184
|
// (目标收集逻辑见 judgeProbeTargets;全部结果携入、attempts 为空时也自然跳过)。
|
|
@@ -169,26 +198,14 @@ export async function runEvals(opts: RunOptions): Promise<RunSummary> {
|
|
|
169
198
|
}
|
|
170
199
|
}
|
|
171
200
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
reportActivity(t("runner.resumeCarry", { carried: carriedResults.length, retry: retryCount }).trimEnd());
|
|
175
|
-
// 按 experiment 分组列出被复用(跳过)的 eval:不列清单的话,用户只看到数量,
|
|
176
|
-
// 无法核对「跳过的是不是我以为已经过了的那些」。同一 key 多个 run 去重。
|
|
177
|
-
const carriedByExperiment = new Map<string, Set<string>>();
|
|
178
|
-
for (const r of carriedResults) {
|
|
179
|
-
const ids = carriedByExperiment.get(r.experimentId!) ?? new Set<string>();
|
|
180
|
-
ids.add(r.id);
|
|
181
|
-
carriedByExperiment.set(r.experimentId!, ids);
|
|
182
|
-
}
|
|
183
|
-
for (const [experiment, ids] of [...carriedByExperiment].sort(([a], [b]) => a.localeCompare(b))) {
|
|
184
|
-
reportActivity(t("runner.resumeCarryDetail", { experiment, evals: [...ids].sort().join(", ") }).trimEnd());
|
|
185
|
-
}
|
|
186
|
-
}
|
|
201
|
+
// 缓存携入只在 plan 的 Reuse 行给数量,不逐条铺 eval id 清单(见 cli.md「人在终端里怎么用」:
|
|
202
|
+
// 哪些 eval 复用、哪些重跑属于 --dry 与 niceeval view,不占 human 的 scrollback)。
|
|
187
203
|
|
|
188
204
|
// onRunStart 报「本次实际要跑的 eval」(过滤 + 去重),不是发现到的全部 —— 否则计数误导。
|
|
189
205
|
const runningIds = new Set(attempts.map((a) => a.evalDef.id));
|
|
190
206
|
const runningEvals = [...runningIds].map((id) => ({ id }));
|
|
191
207
|
const firstAgent = opts.agentRuns[0]?.agent;
|
|
208
|
+
const firstModel = opts.agentRuns[0]?.model;
|
|
192
209
|
const shape: RunShape = {
|
|
193
210
|
evals: runningEvals.length,
|
|
194
211
|
configs: opts.agentRuns.length,
|
|
@@ -269,7 +286,7 @@ export async function runEvals(opts: RunOptions): Promise<RunSummary> {
|
|
|
269
286
|
// budget 护栏:只按「已完成 attempt 的实测花费」判断,不做预测性节流。之前的实现会按
|
|
270
287
|
// 「平均成本 × 在飞数」预扣,快到顶就让还没起飞的 attempt 排队等——这在探测阶段(还没有任何
|
|
271
288
|
// 成本样本时)等价于把同一 budgetKey 的并发摁到一个很小的数,且完全没有文档承诺过这个副作用
|
|
272
|
-
// (`docs-site/zh/
|
|
289
|
+
// (`docs-site/zh/tutorials/write-experiment.mdx` 对 `budget` 的描述只有一句「这一格配置的预算
|
|
273
290
|
// 上限」)。新语义:已完成 attempt 的花费加总一旦到顶,就不再放新 attempt 起飞(已经在飞的
|
|
274
291
|
// 照常跑完,不会被中途打断);到顶之前不做任何预测性限流,并发完全由 globalSem / runSem 决定。
|
|
275
292
|
// 代价是「已花 + 在飞未结算」的总花费可能短暂超出 budget——这是有意识的取舍:budget 是防止
|
|
@@ -320,6 +337,150 @@ export async function runEvals(opts: RunOptions): Promise<RunSummary> {
|
|
|
320
337
|
opts = { ...opts, otelPool: new OtelReceiverPool(opts.config.telemetry?.port) };
|
|
321
338
|
}
|
|
322
339
|
|
|
340
|
+
// 实验级生命周期(见 docs/feature/experiments/architecture.md「实验级生命周期」):
|
|
341
|
+
// setup 整场至多一次——第一个通过派发许可(preflight)的 attempt 触发,后续 attempt 等同一个
|
|
342
|
+
// memoized promise;等待发生在 gated 里、globalSem 之外,不占全局并发位。teardown 是
|
|
343
|
+
// ExperimentDef.teardown 字段,按 per-run 剩余 attempt 计数在最后一个 attempt 收尾后执行,
|
|
344
|
+
// 当且仅当 setup 时点走到过(triggered)——setup 抛错不豁免,未声明 setup 不影响触发;
|
|
345
|
+
// 计数递减挂在 Effect.ensuring 上,中断路径同样递减,teardown 因此必跑。
|
|
346
|
+
interface ExperimentLifecycle {
|
|
347
|
+
/** memoized 的 setup 执行;undefined = 还没有 attempt 触发过。未声明 setup 时为已决议 promise。 */
|
|
348
|
+
setupPromise?: Promise<void>;
|
|
349
|
+
/** setup 时点已走到(第一个通过派发许可的 attempt 已触发本实验生命周期;未声明 setup 也置位)。 */
|
|
350
|
+
triggered: boolean;
|
|
351
|
+
/** setup 抛过错(用独立布尔标记,不能拿 error 值判断——throw undefined 也是失败)。 */
|
|
352
|
+
setupFailed: boolean;
|
|
353
|
+
setupError?: unknown;
|
|
354
|
+
/** teardown 的 memoized 执行体:谁先到(计数归零 / 扫尾 / 强清 drain)谁启动,后到者等
|
|
355
|
+
* 同一个 promise——「恰好一次 + 在飞可等待」的全部机制。undefined = 还没人启动。 */
|
|
356
|
+
teardownPromise?: Promise<void>;
|
|
357
|
+
/** 本实验还没收尾的 attempt 数;归零即触发 teardown。 */
|
|
358
|
+
remaining: number;
|
|
359
|
+
/** 本实验的收尾时点已走到(计数归零 / run 收尾扫尾)。 */
|
|
360
|
+
tornDown: boolean;
|
|
361
|
+
}
|
|
362
|
+
const expLifecycles = new Map<AgentRun, ExperimentLifecycle>();
|
|
363
|
+
for (const a of attempts) {
|
|
364
|
+
if (!a.run.setup && !a.run.teardown) continue;
|
|
365
|
+
let lc = expLifecycles.get(a.run);
|
|
366
|
+
if (!lc)
|
|
367
|
+
expLifecycles.set(a.run, (lc = { triggered: false, setupFailed: false, remaining: 0, tornDown: false }));
|
|
368
|
+
lc.remaining += 1;
|
|
369
|
+
}
|
|
370
|
+
const makeExperimentHookContext = (run: AgentRun): ExperimentHookContext => {
|
|
371
|
+
const experimentId = run.experimentId ?? run.agent.name;
|
|
372
|
+
return {
|
|
373
|
+
experimentId: run.experimentId ?? "",
|
|
374
|
+
selectedEvalIds: run.selectedEvalIds,
|
|
375
|
+
signal: opts.signal,
|
|
376
|
+
// progress 是短命状态:只更新本实验运行级行的 detail,不属于任何 attempt 的 active 条目。
|
|
377
|
+
progress: (u) => {
|
|
378
|
+
const suffix = u.current !== undefined && u.total !== undefined ? ` (${u.current}/${u.total})` : "";
|
|
379
|
+
reportExperimentProgress({ experimentId, detail: `${u.message}${suffix}` });
|
|
380
|
+
},
|
|
381
|
+
// diagnostic 进运行级永久事件流;实验级钩子不属于任何单个 attempt,不落 result.json。
|
|
382
|
+
diagnostic: (input) =>
|
|
383
|
+
reportDiagnostic({
|
|
384
|
+
key: input.dedupeKey ?? `${input.code}:experiment:${experimentId}`,
|
|
385
|
+
severity: input.level,
|
|
386
|
+
message: input.message,
|
|
387
|
+
data: { experimentId, ...(input.data ?? {}) },
|
|
388
|
+
}),
|
|
389
|
+
};
|
|
390
|
+
};
|
|
391
|
+
const runExperimentTeardown = (run: AgentRun, lc: ExperimentLifecycle): Promise<void> => {
|
|
392
|
+
// memoized 一次性执行体(docs/cli.md「中断:三级响应」):正常路径(计数归零 / run 收尾
|
|
393
|
+
// 扫尾)、强清 drain、崩溃路径谁先到都启动同一个 promise,后到者等到同一个结果——
|
|
394
|
+
// 不双跑、也不空转;注册表条目在 settle 后注销,drain 因此能等待在飞中的 teardown。
|
|
395
|
+
lc.teardownPromise ??= (async () => {
|
|
396
|
+
const experimentId = run.experimentId ?? run.agent.name;
|
|
397
|
+
try {
|
|
398
|
+
// 触发规则(docs/feature/experiments/architecture.md):setup 时点没走到(一个 attempt
|
|
399
|
+
// 都没通过派发许可)则跳过;setup 抛错不豁免——半初始化现场同样要扫尾。
|
|
400
|
+
if (!lc.triggered || !run.teardown) return;
|
|
401
|
+
// 与 setup 串行:setup 仍在飞(极端时序:全部 attempt 在 setup 完成前被中断收尾)时等它
|
|
402
|
+
// settle 再收尾;setupPromise 自带 catch(失败收进 setupFailed),这里不会 reject。
|
|
403
|
+
await lc.setupPromise;
|
|
404
|
+
// 起止由 runner 发布,不依赖钩子自己调 progress(见 cli.md「实验级钩子的显示」)。
|
|
405
|
+
reportExperimentHook({ experimentId, hook: "teardown", status: "started" });
|
|
406
|
+
const startedAt = Date.now();
|
|
407
|
+
try {
|
|
408
|
+
// 有界执行(docs/cli.md 的有界性前提):挂起的 teardown 到点按失败收束,不能无限拖住
|
|
409
|
+
// 退出;超时后遗留的 promise 悬空,随进程退出消亡。
|
|
410
|
+
const ctx = makeExperimentHookContext(run);
|
|
411
|
+
await withCleanupTimeout(() => run.teardown!(ctx));
|
|
412
|
+
reportExperimentHook({ experimentId, hook: "teardown", status: "done", durationMs: Date.now() - startedAt });
|
|
413
|
+
} catch (e) {
|
|
414
|
+
reportExperimentHook({ experimentId, hook: "teardown", status: "failed", durationMs: Date.now() - startedAt });
|
|
415
|
+
// teardown 失败只作运行级诊断,不改任何已产出的 verdict(与 sandbox.teardown 的
|
|
416
|
+
// teardown-failed 同一语义);资源可能泄漏,所以要说出来。
|
|
417
|
+
reportDiagnostic({
|
|
418
|
+
key: `experiment-teardown-failed:${experimentId}`,
|
|
419
|
+
severity: "warning",
|
|
420
|
+
message: t("runner.experimentTeardownFailed", {
|
|
421
|
+
experimentId,
|
|
422
|
+
message: e instanceof Error ? e.message : String(e),
|
|
423
|
+
}).trimEnd(),
|
|
424
|
+
data: { experimentId },
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
} finally {
|
|
428
|
+
// settle 后才注销:drain 的「启动全部未启动 + 等待全部未 settle」依赖条目在飞期间仍可见。
|
|
429
|
+
unregisterExperimentTeardown(experimentId);
|
|
430
|
+
}
|
|
431
|
+
})();
|
|
432
|
+
return lc.teardownPromise;
|
|
433
|
+
};
|
|
434
|
+
const ensureExperimentSetup = (a: Attempt): Promise<void> => {
|
|
435
|
+
const lc = expLifecycles.get(a.run)!;
|
|
436
|
+
if (!lc.setupPromise) {
|
|
437
|
+
const run = a.run;
|
|
438
|
+
const experimentId = run.experimentId ?? run.agent.name;
|
|
439
|
+
lc.triggered = true;
|
|
440
|
+
// teardown 从触发时点起就静态可达:立即登记进宿主机侧兜底表,setup 挂起 / 抛错都不会
|
|
441
|
+
// 丢收尾——强清退出(二次中断/看门狗/崩溃路径)时 cli 由此排空未被运行路径消费的
|
|
442
|
+
// teardown(docs/cli.md「中断:三级响应」)。
|
|
443
|
+
if (run.teardown) registerExperimentTeardown(experimentId, () => runExperimentTeardown(run, lc));
|
|
444
|
+
if (!run.setup) {
|
|
445
|
+
lc.setupPromise = Promise.resolve();
|
|
446
|
+
return lc.setupPromise;
|
|
447
|
+
}
|
|
448
|
+
const ctx = makeExperimentHookContext(run);
|
|
449
|
+
lc.setupPromise = (async () => {
|
|
450
|
+
// 起止由 runner 发布(见 cli.md「实验级钩子的显示」):一个什么都不调的 setup 也必须
|
|
451
|
+
// 可见,不能让「0 running · N queued 长时间不动」看起来像调度卡死。
|
|
452
|
+
reportExperimentHook({ experimentId, hook: "setup", status: "started" });
|
|
453
|
+
const startedAt = Date.now();
|
|
454
|
+
try {
|
|
455
|
+
const returned = (await run.setup!(ctx)) as unknown;
|
|
456
|
+
if (typeof returned === "function") {
|
|
457
|
+
// 迁移护栏:tsx 用户没有类型检查,旧式「setup 返回 cleanup」会被静默忽略而泄漏资源。
|
|
458
|
+
// best-effort 执行一次返回的函数(把已起的资源收掉),然后按 setup 失败报清晰错误。
|
|
459
|
+
try {
|
|
460
|
+
await withCleanupTimeout(returned as () => unknown);
|
|
461
|
+
} catch {
|
|
462
|
+
// 迁移护栏里的旧式 cleanup 失败不再叠加报错,主错误(下一行)已指明修法。
|
|
463
|
+
}
|
|
464
|
+
throw new Error(
|
|
465
|
+
t("runner.setupReturnedCleanup", {
|
|
466
|
+
layer: `ExperimentDef.setup (${experimentId})`,
|
|
467
|
+
hint: "ExperimentDef.teardown",
|
|
468
|
+
}).trimEnd(),
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
} catch (e) {
|
|
472
|
+
reportExperimentHook({ experimentId, hook: "setup", status: "failed", durationMs: Date.now() - startedAt });
|
|
473
|
+
throw e;
|
|
474
|
+
}
|
|
475
|
+
reportExperimentHook({ experimentId, hook: "setup", status: "done", durationMs: Date.now() - startedAt });
|
|
476
|
+
})().catch((e) => {
|
|
477
|
+
lc.setupFailed = true;
|
|
478
|
+
lc.setupError = e;
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
return lc.setupPromise;
|
|
482
|
+
};
|
|
483
|
+
|
|
323
484
|
// earlyExit:为每个 key 各建一个 AbortController。某 attempt 通过或 errored 时 abort 它,
|
|
324
485
|
// 让并发进行中的同 key attempt 通过 signal 尽早退出,而不只是等排队的才能被跳过。
|
|
325
486
|
const evalAbortControllers = new Map<string, AbortController>();
|
|
@@ -469,7 +630,29 @@ export async function runEvals(opts: RunOptions): Promise<RunSummary> {
|
|
|
469
630
|
who: feedbackWho(a),
|
|
470
631
|
phase: initialPhase,
|
|
471
632
|
});
|
|
472
|
-
|
|
633
|
+
// 实验级 setup 失败:不派发 agent,为这条 attempt 合成结构化 errored 结果
|
|
634
|
+
// (code experiment-setup-failed、phase experiment.setup),走与真实结果完全相同的
|
|
635
|
+
// 下游路径(locator / 反馈事件 / reporter / 落盘)——环境起不来是每条 eval 都没跑成
|
|
636
|
+
// 的事实,要逐条进报告,不是一条一次性日志(见 docs/feature/experiments/
|
|
637
|
+
// architecture.md「实验级生命周期」)。
|
|
638
|
+
const expLc = expLifecycles.get(a.run);
|
|
639
|
+
const result = expLc?.setupFailed
|
|
640
|
+
? ({
|
|
641
|
+
id: a.evalDef.id,
|
|
642
|
+
description: a.evalDef.description,
|
|
643
|
+
experimentId: a.run.experimentId,
|
|
644
|
+
experiment: experimentRunInfo(a.run, opts.config.sandbox),
|
|
645
|
+
agent: a.run.agent.name,
|
|
646
|
+
model: a.run.model,
|
|
647
|
+
verdict: "errored",
|
|
648
|
+
fingerprint: a.fingerprint,
|
|
649
|
+
attempt: a.attempt,
|
|
650
|
+
startedAt: new Date().toISOString(),
|
|
651
|
+
durationMs: 0,
|
|
652
|
+
assertions: [],
|
|
653
|
+
error: { ...errorFromThrown(expLc.setupError, "experiment.setup"), code: "experiment-setup-failed" },
|
|
654
|
+
} satisfies EvalResult)
|
|
655
|
+
: yield* runAttemptEffect(a, opts, sandboxSem, attemptSignal);
|
|
473
656
|
// locator 在这里确定 —— 早于本 attempt 触发的任何 reporter 回调 / 事件
|
|
474
657
|
// (onEvalComplete、eval:complete),所以每一个观察者看到的都已经是最终值,
|
|
475
658
|
// 和落盘 result.json 完全一致(writer.ts 的 entry.locator ?? 兜底分支因此
|
|
@@ -530,9 +713,11 @@ export async function runEvals(opts: RunOptions): Promise<RunSummary> {
|
|
|
530
713
|
// 并发情况:同 key 另一个 attempt 已通过后本 attempt 才完成(被 abort 后产出
|
|
531
714
|
// errored),不计入结果。
|
|
532
715
|
return;
|
|
533
|
-
} else if (result.verdict === "errored") {
|
|
716
|
+
} else if (result.verdict === "errored" && !expLc?.setupFailed) {
|
|
534
717
|
// errored 不中止其余样本(基建可能自愈);只有同一错误 code 连续复现才判定为
|
|
535
718
|
// 确定性错误,进 run 级 fail-fast 停止派发(不 abort 已在飞的 attempt)。
|
|
719
|
+
// 实验级 setup 失败的合成结果不进 fail-fast:它不派发 agent、零成本,
|
|
720
|
+
// 契约是本实验「所有」attempt 逐条记 errored 进报告,不该被止损机制截短。
|
|
536
721
|
const code = result.error?.code ?? "unexpected-error";
|
|
537
722
|
const prev = lastErrorCode.get(a.key);
|
|
538
723
|
const streak = prev?.code === code ? prev.streak + 1 : 1;
|
|
@@ -576,10 +761,29 @@ export async function runEvals(opts: RunOptions): Promise<RunSummary> {
|
|
|
576
761
|
const gated = Effect.gen(function* () {
|
|
577
762
|
const proceed = yield* preflight;
|
|
578
763
|
if (!proceed) return;
|
|
764
|
+
// 实验级 setup:第一个走到这里的 attempt 真正执行,其余等同一个 memoized promise
|
|
765
|
+
// (它从不 reject——失败收进 lc.setupFailed,由 body 合成 errored 结果);等待发生在
|
|
766
|
+
// globalSem 之外,慢启动的 setup 不占全局并发位。
|
|
767
|
+
if (a.run.setup || a.run.teardown) yield* Effect.promise(() => ensureExperimentSetup(a));
|
|
579
768
|
yield* globalSem.withPermits(1)(body);
|
|
580
769
|
});
|
|
581
770
|
const runSem = runSems.get(a.run);
|
|
582
|
-
|
|
771
|
+
const withRunSem = runSem ? runSem.withPermits(1)(gated) : gated;
|
|
772
|
+
if (!a.run.setup && !a.run.teardown) return withRunSem;
|
|
773
|
+
// 实验级 teardown 计数:每个 attempt 收尾(含被 preflight 跳过、被中断的)都递减,
|
|
774
|
+
// 归零触发 ExperimentDef.teardown。ensuring 在中断路径同样执行,teardown 因此必跑。
|
|
775
|
+
return withRunSem.pipe(
|
|
776
|
+
Effect.ensuring(
|
|
777
|
+
Effect.promise(async () => {
|
|
778
|
+
const lc = expLifecycles.get(a.run)!;
|
|
779
|
+
lc.remaining -= 1;
|
|
780
|
+
if (lc.remaining === 0 && !lc.tornDown) {
|
|
781
|
+
lc.tornDown = true;
|
|
782
|
+
await runExperimentTeardown(a.run, lc);
|
|
783
|
+
}
|
|
784
|
+
}),
|
|
785
|
+
),
|
|
786
|
+
);
|
|
583
787
|
},
|
|
584
788
|
{ concurrency: "unbounded", discard: true },
|
|
585
789
|
).pipe(
|
|
@@ -596,15 +800,44 @@ export async function runEvals(opts: RunOptions): Promise<RunSummary> {
|
|
|
596
800
|
{ signal: opts.signal },
|
|
597
801
|
);
|
|
598
802
|
await opts.otelPool?.close();
|
|
803
|
+
|
|
804
|
+
// 实验级 teardown 兜底扫尾:正常路径由 per-attempt ensuring 的计数归零触发(见上),但一次
|
|
805
|
+
// 真实批跑观察到过计数路径未触发的间歇现象(根因未定位,排查记录见 memory 的
|
|
806
|
+
// experiment-teardown-missed-once-in-batch)。走到这里时 forEach 的全部 fiber 连同 finalizer
|
|
807
|
+
// 都已结算,任何 tornDown 仍为 false 的实验都意味着泄漏;在此强制收尾并报警示诊断——
|
|
808
|
+
// 扫尾幂等(cleanup 消费一次性),宁可多一道兜底,不把宿主机资源(隧道/容器)留给用户手拆。
|
|
809
|
+
// 真·缺陷抛出前同样要扫(finalizer 语义,见 docs/feature/experiments/architecture.md
|
|
810
|
+
// 「实验级生命周期」);cli 的 main().catch() 只兜沙箱,不知道实验级 cleanup 的存在。
|
|
811
|
+
const sweepExperimentTeardowns = async (): Promise<void> => {
|
|
812
|
+
for (const [run, lc] of expLifecycles) {
|
|
813
|
+
if (lc.tornDown) continue;
|
|
814
|
+
lc.tornDown = true;
|
|
815
|
+
// 无事可扫:没触发过 / 没声明 teardown——静默跳过。
|
|
816
|
+
if (!lc.triggered || !run.teardown) continue;
|
|
817
|
+
// 只有扫尾是启动者时才报「late」诊断;已在飞的(如强清 drain 先到)只等 settle,不算漏。
|
|
818
|
+
if (!lc.teardownPromise) {
|
|
819
|
+
const experimentId = run.experimentId ?? run.agent.name;
|
|
820
|
+
reportDiagnostic({
|
|
821
|
+
key: `experiment-teardown-late:${experimentId}`,
|
|
822
|
+
severity: "warning",
|
|
823
|
+
message: t("runner.experimentTeardownLate", { experimentId }).trimEnd(),
|
|
824
|
+
data: { experimentId, remaining: lc.remaining },
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
await runExperimentTeardown(run, lc);
|
|
828
|
+
}
|
|
829
|
+
};
|
|
599
830
|
if (Exit.isFailure(exit)) {
|
|
600
831
|
// signal abort 或 cause 含中断 → 当作用户中断,走部分汇总;否则是真·缺陷,照常抛出。
|
|
601
832
|
if (opts.signal?.aborted || Cause.isInterrupted(exit.cause)) {
|
|
602
833
|
interrupted = true;
|
|
603
834
|
} else {
|
|
835
|
+
await sweepExperimentTeardowns();
|
|
604
836
|
throw Cause.squash(exit.cause);
|
|
605
837
|
}
|
|
606
838
|
}
|
|
607
839
|
if (interrupted) reportInterrupted();
|
|
840
|
+
await sweepExperimentTeardowns();
|
|
608
841
|
|
|
609
842
|
// 稳定排序:按发现顺序 + attempt;携带结果并入后一起排
|
|
610
843
|
const order = new Map(opts.evals.map((e, i) => [e.id, i]));
|
|
@@ -616,7 +849,7 @@ export async function runEvals(opts: RunOptions): Promise<RunSummary> {
|
|
|
616
849
|
a.attempt - b.attempt,
|
|
617
850
|
);
|
|
618
851
|
|
|
619
|
-
const summary = summarize(allResults, firstAgent?.name ?? "", startedAt, Date.now() - t0, opts.config.name);
|
|
852
|
+
const summary = summarize(allResults, firstAgent?.name ?? "", startedAt, Date.now() - t0, opts.config.name, firstModel);
|
|
620
853
|
await emitReporterEvent(reporters, { type: "run:summary", summary });
|
|
621
854
|
for (const reg of reporters) {
|
|
622
855
|
// required reporter(默认 artifacts、显式 --json/--junit)在这一步失败,不能中断其它
|
|
@@ -45,7 +45,7 @@ function run(overrides: Partial<AgentRun> = {}): AgentRun {
|
|
|
45
45
|
flags: {},
|
|
46
46
|
runs: 1,
|
|
47
47
|
earlyExit: true,
|
|
48
|
-
|
|
48
|
+
selectedEvalIds: [],
|
|
49
49
|
experimentId: "profiles/run",
|
|
50
50
|
...overrides,
|
|
51
51
|
};
|
|
@@ -64,6 +64,7 @@ describe("eval-level sandbox selection", () => {
|
|
|
64
64
|
"node-18-legacy": { template: "niceeval-node18" },
|
|
65
65
|
},
|
|
66
66
|
}),
|
|
67
|
+
selectedEvalIds: ["astropy/old", "legacy/node", "weather/basic"],
|
|
67
68
|
});
|
|
68
69
|
|
|
69
70
|
prepareRunSandboxes([py39, node18, plain], [selected]);
|
|
@@ -93,7 +94,10 @@ describe("eval-level sandbox selection", () => {
|
|
|
93
94
|
|
|
94
95
|
const missingA = await evalDef("astropy/old", "python-3.9-astropy-4.2");
|
|
95
96
|
const missingB = await evalDef("legacy/node", "node-18-legacy");
|
|
96
|
-
const bare = run({
|
|
97
|
+
const bare = run({
|
|
98
|
+
sandbox: e2bSandbox({ template: "niceeval-agents" }),
|
|
99
|
+
selectedEvalIds: ["astropy/old", "legacy/node"],
|
|
100
|
+
});
|
|
97
101
|
let thrown: Error | undefined;
|
|
98
102
|
try {
|
|
99
103
|
prepareRunSandboxes([missingA, missingB], [bare]);
|
|
@@ -114,14 +118,20 @@ describe("eval-level sandbox selection", () => {
|
|
|
114
118
|
template: "niceeval-agents",
|
|
115
119
|
environments: { "python-3.9-astropy-4.2": { template: "niceeval-py39-astropy42" } },
|
|
116
120
|
}),
|
|
121
|
+
selectedEvalIds: ["astropy/old", "weather/basic"],
|
|
122
|
+
});
|
|
123
|
+
const vercelRun = run({
|
|
124
|
+
experimentId: "profiles/vercel",
|
|
125
|
+
sandbox: vercelSandbox({ snapshotId: "snap_base" }),
|
|
126
|
+
selectedEvalIds: ["weather/basic"],
|
|
117
127
|
});
|
|
118
|
-
const vercelRun = run({ experimentId: "profiles/vercel", sandbox: vercelSandbox({ snapshotId: "snap_base" }) });
|
|
119
128
|
expect(resolvedSandboxRecommendedConcurrency([item, plain], [e2bRun])).toBe(20);
|
|
120
129
|
expect(resolvedSandboxRecommendedConcurrency([plain], [e2bRun, vercelRun])).toBe(1);
|
|
121
130
|
|
|
122
131
|
const remote = run({
|
|
123
132
|
agent: agent("remote"),
|
|
124
133
|
sandbox: e2bSandbox({ template: "niceeval-agents" }),
|
|
134
|
+
selectedEvalIds: ["astropy/old"],
|
|
125
135
|
});
|
|
126
136
|
expect(() => prepareRunSandboxes([item], [remote])).not.toThrow();
|
|
127
137
|
expect(resolvedSandboxRecommendedConcurrency([item], [remote])).toBe(10);
|
|
@@ -54,9 +54,10 @@ export function prepareRunSandboxes(evals: DiscoveredEval[], runs: AgentRun[], f
|
|
|
54
54
|
if (run.agent.kind !== "sandbox") continue;
|
|
55
55
|
const spec = run.sandbox ?? fallback;
|
|
56
56
|
if (spec === undefined) continue; // 缺 spec 的错误由既有 resolveSandbox 路径按原文案报
|
|
57
|
+
const selectedIds = new Set(run.selectedEvalIds);
|
|
57
58
|
const missing: Array<readonly [string, string]> = [];
|
|
58
59
|
for (const evalDef of evals) {
|
|
59
|
-
if (!
|
|
60
|
+
if (!selectedIds.has(evalDef.id) || evalDef.environment === undefined) continue;
|
|
60
61
|
if (run.resolvedSandboxes?.has(evalDef.id)) continue;
|
|
61
62
|
const derived = deriveSpec(spec, evalDef.environment);
|
|
62
63
|
if (derived === undefined) {
|
|
@@ -101,8 +102,9 @@ export function resolvedSandboxRecommendedConcurrency(
|
|
|
101
102
|
const recommendations: number[] = [];
|
|
102
103
|
for (const run of runs) {
|
|
103
104
|
if (run.agent.kind !== "sandbox") continue;
|
|
105
|
+
const selectedIds = new Set(run.selectedEvalIds);
|
|
104
106
|
for (const evalDef of evals) {
|
|
105
|
-
if (!
|
|
107
|
+
if (!selectedIds.has(evalDef.id)) continue;
|
|
106
108
|
recommendations.push(sandboxRecommendedConcurrency(sandboxForEval(run, evalDef, fallback)));
|
|
107
109
|
}
|
|
108
110
|
}
|