niceeval 0.5.4 → 0.6.1
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 +55 -0
- package/dist/agents/types.d.ts +321 -0
- package/dist/context/types.d.ts +335 -0
- package/dist/i18n/core.d.ts +11 -0
- package/dist/i18n/en.d.ts +181 -0
- package/dist/i18n/index.d.ts +5 -0
- package/dist/i18n/zh-CN.d.ts +183 -0
- package/dist/o11y/otlp/parse.d.ts +3 -0
- package/dist/o11y/otlp/receiver.d.ts +21 -0
- package/dist/o11y/otlp/turn-otel.d.ts +43 -0
- package/dist/o11y/types.d.ts +190 -0
- package/dist/report/aggregate.d.ts +57 -0
- package/dist/report/aggregate.js +212 -0
- package/dist/report/built-ins/experiment-comparison.d.ts +1 -0
- package/dist/report/built-ins/experiment-comparison.js +13 -0
- package/dist/report/built-ins/index.d.ts +1 -0
- package/dist/report/built-ins/index.js +2 -0
- package/dist/report/components.d.ts +180 -0
- package/dist/report/components.js +132 -0
- package/dist/report/compute.d.ts +106 -0
- package/dist/report/compute.js +600 -0
- package/dist/report/flag.d.ts +6 -0
- package/dist/report/flag.js +8 -0
- package/dist/report/format.d.ts +26 -0
- package/dist/report/format.js +109 -0
- package/dist/report/index.d.ts +19 -0
- package/dist/report/index.js +30 -0
- package/dist/report/load.d.ts +12 -0
- package/dist/report/load.js +56 -0
- package/dist/report/locale.d.ts +94 -0
- package/dist/report/locale.js +183 -0
- package/dist/report/metrics.d.ts +21 -0
- package/dist/report/metrics.js +115 -0
- package/dist/report/primitives.d.ts +64 -0
- package/dist/report/primitives.js +113 -0
- package/dist/report/react/AttemptList.d.ts +23 -0
- package/dist/report/react/AttemptList.js +19 -0
- package/dist/report/react/DeltaTable.d.ts +8 -0
- package/dist/report/react/DeltaTable.js +24 -0
- package/dist/report/react/EvalList.d.ts +10 -0
- package/dist/report/react/EvalList.js +0 -0
- package/dist/report/react/ExperimentList.d.ts +11 -0
- package/dist/report/react/ExperimentList.js +67 -0
- package/dist/report/react/GroupSummary.d.ts +8 -0
- package/dist/report/react/GroupSummary.js +8 -0
- package/dist/report/react/MetricBars.d.ts +10 -0
- package/dist/report/react/MetricBars.js +48 -0
- package/dist/report/react/MetricLine.d.ts +9 -0
- package/dist/report/react/MetricLine.js +68 -0
- package/dist/report/react/MetricMatrix.d.ts +10 -0
- package/dist/report/react/MetricMatrix.js +23 -0
- package/dist/report/react/MetricScatter.d.ts +9 -0
- package/dist/report/react/MetricScatter.js +96 -0
- package/dist/report/react/MetricTable.d.ts +11 -0
- package/dist/report/react/MetricTable.js +26 -0
- package/dist/report/react/RunOverview.d.ts +8 -0
- package/dist/report/react/RunOverview.js +12 -0
- package/dist/report/react/Scoreboard.d.ts +8 -0
- package/dist/report/react/Scoreboard.js +26 -0
- package/dist/report/react/cell.d.ts +9 -0
- package/dist/report/react/cell.js +9 -0
- package/dist/report/react/chart-math.d.ts +14 -0
- package/dist/report/react/chart-math.js +68 -0
- package/dist/report/react/colors.d.ts +14 -0
- package/dist/report/react/colors.js +45 -0
- package/dist/report/react/fixtures.d.ts +19 -0
- package/dist/report/react/fixtures.js +398 -0
- package/dist/report/react/format.d.ts +3 -0
- package/dist/report/react/format.js +7 -0
- package/dist/report/react/index.d.ts +6 -0
- package/dist/report/react/index.js +14 -0
- package/dist/report/report.d.ts +24 -0
- package/dist/report/report.js +49 -0
- package/dist/report/text/faces.d.ts +29 -0
- package/dist/report/text/faces.js +541 -0
- package/dist/report/text/layout.d.ts +75 -0
- package/dist/report/text/layout.js +173 -0
- package/dist/report/text/plot.d.ts +37 -0
- package/dist/report/text/plot.js +81 -0
- package/dist/report/text/table.d.ts +8 -0
- package/dist/report/text/table.js +86 -0
- package/dist/report/tree.d.ts +99 -0
- package/dist/report/tree.js +228 -0
- package/dist/report/types.d.ts +408 -0
- package/dist/report/types.js +4 -0
- package/dist/report/web.d.ts +15 -0
- package/dist/report/web.js +37 -0
- package/dist/results/locator.d.ts +75 -0
- package/dist/results/locator.js +0 -0
- package/dist/results/select.d.ts +45 -0
- package/dist/results/select.js +230 -0
- package/dist/results/source-hash.d.ts +9 -0
- package/dist/results/types.d.ts +204 -0
- package/dist/runner/eval-source.d.ts +22 -0
- package/dist/runner/fingerprint.d.ts +23 -0
- package/dist/runner/types.d.ts +332 -0
- package/dist/sandbox/docker-stream.d.ts +24 -0
- package/dist/sandbox/docker.d.ts +84 -0
- package/dist/sandbox/e2b.d.ts +31 -0
- package/dist/sandbox/errors.d.ts +4 -0
- package/dist/sandbox/local-files.d.ts +6 -0
- package/dist/sandbox/paths.d.ts +4 -0
- package/dist/sandbox/registry.d.ts +13 -0
- package/dist/sandbox/resolve.d.ts +40 -0
- package/dist/sandbox/retry.d.ts +10 -0
- package/dist/sandbox/shell.d.ts +11 -0
- package/dist/sandbox/source-files.d.ts +13 -0
- package/dist/sandbox/types.d.ts +203 -0
- package/dist/sandbox/vercel.d.ts +37 -0
- package/dist/scoring/types.d.ts +70 -0
- package/dist/shared/aggregate.d.ts +29 -0
- package/dist/shared/aggregate.js +55 -0
- package/dist/shared/types.d.ts +27 -0
- package/dist/shared/verdict.d.ts +27 -0
- package/dist/shared/verdict.js +34 -0
- package/dist/tty-line.d.ts +6 -0
- package/dist/types.d.ts +7 -0
- package/dist/util.d.ts +22 -0
- package/docs-site/zh/concepts/adapter.mdx +7 -7
- package/docs-site/zh/concepts/assert.mdx +19 -18
- package/docs-site/zh/concepts/evals.mdx +12 -11
- package/docs-site/zh/concepts/experiment.mdx +2 -2
- package/docs-site/zh/concepts/judge.mdx +9 -9
- package/docs-site/zh/concepts/overview.mdx +3 -3
- package/docs-site/zh/concepts/tier.mdx +6 -6
- package/docs-site/zh/example/claude-code-codex-plugin.mdx +9 -5
- package/docs-site/zh/example/claude-code-codex-skill.mdx +12 -7
- package/docs-site/zh/guides/agent-feedback-loop.mdx +237 -78
- package/docs-site/zh/guides/connect-otel.mdx +3 -3
- package/docs-site/zh/guides/connect-your-agent.mdx +2 -2
- package/docs-site/zh/guides/custom-reports.mdx +196 -36
- package/docs-site/zh/guides/experiments.mdx +7 -1
- package/docs-site/zh/guides/fixtures.mdx +4 -2
- package/docs-site/zh/guides/official-adapters.mdx +31 -9
- package/docs-site/zh/guides/publish-report.mdx +4 -4
- package/docs-site/zh/guides/report-components.mdx +194 -55
- package/docs-site/zh/guides/reporters.mdx +2 -2
- package/docs-site/zh/guides/results-data.mdx +61 -34
- package/docs-site/zh/guides/runner.mdx +2 -2
- package/docs-site/zh/guides/sandbox-agent.mdx +3 -2
- package/docs-site/zh/guides/{sandbox-backends.mdx → sandbox-providers.mdx} +12 -12
- package/docs-site/zh/guides/viewing-results.mdx +200 -79
- package/docs-site/zh/guides/write-experiment.mdx +9 -1
- package/docs-site/zh/guides/write-send.mdx +4 -3
- package/docs-site/zh/index.mdx +4 -3
- package/docs-site/zh/introduction.mdx +2 -1
- package/docs-site/zh/quickstart.mdx +5 -3
- package/docs-site/zh/reference/builtin-agents.mdx +38 -13
- package/docs-site/zh/reference/capabilities.mdx +1 -1
- package/docs-site/zh/reference/cli.mdx +36 -13
- package/docs-site/zh/reference/define-agent.mdx +15 -13
- package/docs-site/zh/reference/define-config.mdx +1 -1
- package/docs-site/zh/reference/define-eval.mdx +3 -3
- package/docs-site/zh/reference/events.mdx +9 -3
- package/docs-site/zh/reference/expect.mdx +13 -8
- package/package.json +10 -6
- package/src/agents/bub.ts +129 -42
- package/src/agents/claude-code.test.ts +152 -0
- package/src/agents/claude-code.ts +127 -13
- package/src/agents/codex.test.ts +192 -0
- package/src/agents/codex.ts +151 -12
- package/src/agents/index.ts +9 -3
- package/src/agents/manifest.ts +41 -0
- package/src/agents/plugin-config.test.ts +94 -0
- package/src/agents/shared.ts +33 -2
- package/src/agents/skills.test.ts +214 -0
- package/src/agents/skills.ts +304 -0
- package/src/agents/streaming.ts +5 -5
- package/src/agents/types.ts +59 -3
- package/src/cli.ts +74 -47
- package/src/context/context.test.ts +20 -2
- package/src/context/control-flow.ts +1 -1
- package/src/context/session.test.ts +92 -1
- package/src/context/session.ts +23 -1
- package/src/context/types.ts +4 -4
- package/src/define.test.ts +8 -8
- package/src/define.ts +11 -11
- package/src/expect/index.ts +5 -5
- package/src/i18n/en.ts +33 -15
- package/src/i18n/zh-CN.ts +37 -21
- package/src/index.ts +1 -1
- package/src/o11y/execution-tree.test.ts +452 -0
- package/src/o11y/execution-tree.ts +367 -0
- package/src/o11y/otlp/receiver.ts +1 -1
- package/src/o11y/otlp/select.ts +7 -3
- package/src/o11y/parsers/claude-code.test.ts +175 -0
- package/src/o11y/parsers/claude-code.ts +32 -0
- package/src/o11y/types.ts +6 -1
- package/src/report/aggregate.ts +26 -20
- package/src/report/built-in-user-parity.test.tsx +640 -0
- package/src/report/built-ins/experiment-comparison.tsx +19 -0
- package/src/report/built-ins/index.ts +2 -0
- package/src/report/components.tsx +225 -50
- package/src/report/compute.ts +335 -54
- package/src/report/dual-render.test.tsx +1129 -0
- package/src/report/flag.ts +1 -1
- package/src/report/format.ts +35 -2
- package/src/report/index.ts +47 -14
- package/src/report/load.ts +1 -1
- package/src/report/locale.ts +229 -0
- package/src/report/metrics.ts +39 -15
- package/src/report/primitives.tsx +100 -2
- package/src/report/react/AttemptList.tsx +113 -0
- package/src/report/react/DeltaTable.tsx +9 -6
- package/src/report/react/EvalList.tsx +0 -0
- package/src/report/react/ExperimentList.tsx +220 -0
- package/src/report/react/GroupSummary.tsx +66 -0
- package/src/report/react/MetricBars.tsx +24 -13
- package/src/report/react/MetricLine.tsx +21 -21
- package/src/report/react/MetricMatrix.tsx +8 -4
- package/src/report/react/MetricScatter.tsx +126 -96
- package/src/report/react/MetricTable.tsx +115 -15
- package/src/report/react/RunOverview.tsx +48 -26
- package/src/report/react/Scoreboard.tsx +15 -7
- package/src/report/react/cell.tsx +18 -9
- package/src/{view/app/lib/chart.ts → report/react/chart-math.ts} +2 -6
- package/src/report/react/colors.ts +24 -15
- package/src/report/react/enhance.js +168 -0
- package/src/report/react/fixtures.ts +169 -34
- package/src/report/react/format.ts +1 -1
- package/src/report/react/index.tsx +21 -6
- package/src/report/react/render.test.tsx +180 -31
- package/src/report/react/styles.css +432 -213
- package/src/report/report.test.ts +398 -94
- package/src/report/report.ts +29 -11
- package/src/report/text/faces.ts +344 -96
- package/src/report/text/layout.ts +76 -11
- package/src/report/text/table.ts +98 -0
- package/src/report/tree.ts +103 -20
- package/src/report/types.ts +184 -25
- package/src/report/web.ts +46 -19
- package/src/results/annotated-source.test.ts +146 -0
- package/src/results/annotated-source.ts +128 -0
- package/src/results/attempt-evidence.test.ts +158 -0
- package/src/results/attempt-evidence.ts +158 -0
- package/src/results/attempt-source.ts +48 -0
- package/src/results/copy.ts +154 -139
- package/src/results/format.ts +54 -47
- package/src/results/host-equivalence.test.ts +527 -0
- package/src/results/index.ts +56 -14
- package/src/results/locator.test.ts +198 -0
- package/src/results/locator.ts +0 -0
- package/src/results/open.ts +392 -175
- package/src/results/results.test.ts +943 -494
- package/src/results/select.ts +129 -23
- package/src/results/skipped-notice.ts +0 -0
- package/src/results/source-hash.ts +28 -0
- package/src/results/types.ts +98 -62
- package/src/results/writer.ts +304 -176
- package/src/runner/attempt.test.ts +170 -0
- package/src/runner/attempt.ts +48 -13
- package/src/runner/discover.test.ts +65 -0
- package/src/runner/discover.ts +6 -2
- package/src/runner/eval-source.test.ts +121 -0
- package/src/runner/eval-source.ts +45 -0
- package/src/runner/fingerprint.ts +54 -1
- package/src/runner/remote-sandbox.ts +1 -1
- package/src/runner/report.test.ts +6 -8
- package/src/runner/report.ts +5 -4
- package/src/runner/reporters/artifacts.ts +27 -41
- package/src/runner/reporters/braintrust.test.ts +3 -3
- package/src/runner/reporters/braintrust.ts +1 -1
- package/src/runner/reporters/console.ts +6 -6
- package/src/runner/reporters/json.ts +4 -4
- package/src/runner/reporters/live.test.ts +56 -0
- package/src/runner/reporters/live.ts +36 -10
- package/src/runner/reporters/quiet.test.ts +66 -0
- package/src/runner/reporters/quiet.ts +49 -0
- package/src/runner/reporters/shared.ts +5 -5
- package/src/runner/reporters/table.ts +17 -17
- package/src/runner/run.ts +61 -102
- package/src/runner/types.ts +43 -32
- package/src/sandbox/checkpoint.ts +1 -1
- package/src/sandbox/docker.ts +15 -2
- package/src/sandbox/e2b.ts +11 -5
- package/src/sandbox/errors.ts +13 -0
- package/src/sandbox/index.ts +3 -3
- package/src/sandbox/local-files.ts +1 -1
- package/src/sandbox/registry.ts +6 -5
- package/src/sandbox/resolve.ts +38 -23
- package/src/sandbox/retry.test.ts +68 -0
- package/src/sandbox/retry.ts +44 -0
- package/src/sandbox/types.ts +24 -24
- package/src/sandbox/vercel.ts +17 -3
- package/src/scoring/collector.ts +3 -3
- package/src/scoring/judge.ts +2 -2
- package/src/scoring/scoped.ts +13 -1
- package/src/scoring/types.ts +7 -7
- package/src/scoring/verdict.ts +4 -4
- package/src/shared/aggregate.ts +23 -3
- package/src/shared/types.ts +2 -2
- package/src/shared/{outcome.ts → verdict.ts} +15 -15
- package/src/show/compose.ts +12 -132
- package/src/show/index.ts +119 -95
- package/src/show/render.ts +462 -159
- package/src/show/show.test.ts +471 -253
- package/src/source-loc.ts +1 -1
- package/src/tty-line.ts +32 -0
- package/src/types.ts +2 -2
- package/src/util.test.ts +21 -1
- package/src/util.ts +5 -1
- package/src/view/app/App.tsx +33 -198
- package/src/view/app/components/AttemptModal.tsx +6 -6
- package/src/view/app/components/CodeView.tsx +2 -2
- package/src/view/app/components/CopyControls.tsx +32 -36
- package/src/view/app/components/SkippedRunsBanner.tsx +140 -0
- package/src/view/app/components/ui/badge.tsx +1 -1
- package/src/view/app/components/ui/dialog.tsx +2 -2
- package/src/view/app/i18n.ts +40 -31
- package/src/view/app/lib/artifact-url.ts +1 -1
- package/src/view/app/lib/attempt-route.test.ts +37 -47
- package/src/view/app/lib/attempt-route.ts +26 -26
- package/src/view/app/lib/rows.ts +9 -112
- package/src/view/app/lib/verdict.ts +25 -0
- package/src/view/app/main.tsx +9 -12
- package/src/view/app/pages/RunsPage.tsx +4 -4
- package/src/view/app/pages/TracesPage.tsx +3 -3
- package/src/view/app/shared.ts +0 -1
- package/src/view/app/types.ts +6 -51
- package/src/view/artifact-serving.test.ts +136 -0
- package/src/view/client-dist/app.css +1 -1
- package/src/view/client-dist/app.js +21 -21
- package/src/view/data.test.ts +214 -143
- package/src/view/data.ts +166 -120
- package/src/view/index.ts +23 -11
- package/src/view/server.ts +52 -17
- package/src/view/shared/types.ts +22 -31
- package/src/view/styles.css +117 -327
- package/src/view/view-report.test.ts +146 -98
- package/src/report/default-report.tsx +0 -222
- package/src/report/dual-face.test.tsx +0 -527
- package/src/report/react/CaseList.tsx +0 -70
- package/src/view/app/components/CostScoreChart.tsx +0 -147
- package/src/view/app/components/ExperimentTable.tsx +0 -284
- package/src/view/app/components/GroupSelector.tsx +0 -61
- package/src/view/app/components/primitives.tsx +0 -43
- package/src/view/app/lib/outcome.ts +0 -82
package/src/report/compute.ts
CHANGED
|
@@ -1,19 +1,24 @@
|
|
|
1
|
-
//
|
|
1
|
+
// 计算函数:Selection → 一份组件数据。跑在 Node 侧,产物是算好的、可序列化的普通 JSON
|
|
2
2
|
// (终值 + 渲染提示,不含公式);渲染面(web/text)只做展示。
|
|
3
3
|
//
|
|
4
4
|
// 这些函数不做顶层导出,而是挂在对应组件上(MetricTable.data / Scoreboard.data …,
|
|
5
5
|
// 见 components.tsx):配对打点即发现,泛化名不占顶层导出。
|
|
6
6
|
//
|
|
7
|
-
// 共同约定(docs/reports.md
|
|
7
|
+
// 共同约定(docs/feature/reports/architecture.md「指标聚合不变量」):
|
|
8
8
|
// - 第一参收 Selection | Snapshot[];收 Selection 时 warnings 随行进 OverviewData;
|
|
9
9
|
// - 聚合前按身份键去重(dedupeAttempts;missing-startedAt 不去重、如实保留、不透出警告);
|
|
10
10
|
// - null ≠ 0:缺数据不编数,覆盖率经 samples/total 如实暴露;
|
|
11
11
|
// - core 中立:只认 Metric / Dimension 接口,不出现具体 agent 名的分支。
|
|
12
12
|
|
|
13
13
|
import type {
|
|
14
|
-
|
|
14
|
+
AttemptListItem,
|
|
15
|
+
AttemptLocator,
|
|
15
16
|
DeltaData,
|
|
16
17
|
DimensionInput,
|
|
18
|
+
EvalListItem,
|
|
19
|
+
ExperimentListEvalRow,
|
|
20
|
+
ExperimentListItem,
|
|
21
|
+
GroupSummaryData,
|
|
17
22
|
LineData,
|
|
18
23
|
MatrixData,
|
|
19
24
|
Metric,
|
|
@@ -23,7 +28,11 @@ import type {
|
|
|
23
28
|
ScatterData,
|
|
24
29
|
ScoreboardData,
|
|
25
30
|
TableData,
|
|
31
|
+
TableRowMeta,
|
|
26
32
|
} from "./types.ts";
|
|
33
|
+
import type { AssertionResult, EvalResult } from "../types.ts";
|
|
34
|
+
import type { AttemptHandle } from "../results/types.ts";
|
|
35
|
+
import { evalLevelStats, foldEvalVerdict } from "../shared/verdict.ts";
|
|
27
36
|
import {
|
|
28
37
|
applyAggregator,
|
|
29
38
|
assertUniqueMetricNames,
|
|
@@ -40,13 +49,14 @@ import {
|
|
|
40
49
|
filterItems,
|
|
41
50
|
groupItems,
|
|
42
51
|
flagAxisValue,
|
|
52
|
+
locatorOf,
|
|
43
53
|
resolveInput,
|
|
44
54
|
snapshotKeyOf,
|
|
45
55
|
toColumn,
|
|
46
56
|
type Item,
|
|
47
57
|
type SnapshotsInput,
|
|
48
58
|
} from "./aggregate.ts";
|
|
49
|
-
import { attemptCostUSD, examScore } from "./metrics.ts";
|
|
59
|
+
import { attemptCostUSD, costUSD, durationMs, examScore, passRate, tokens } from "./metrics.ts";
|
|
50
60
|
import { formatMetricValue, formatPlainNumber } from "./format.ts";
|
|
51
61
|
|
|
52
62
|
// ───────────────────────── MetricTable.data ─────────────────────────
|
|
@@ -62,6 +72,132 @@ export interface TableDataOptions<M extends readonly Metric[]> {
|
|
|
62
72
|
evals?: string | string[];
|
|
63
73
|
}
|
|
64
74
|
|
|
75
|
+
// 一组 Item 的 eval 全身份键:experimentId + eval id。单 experiment 场景(如 experimentRowMeta,
|
|
76
|
+
// 一组本就只有一个 experimentId)下退化为只按 eval id 折叠,与旧行为一致;多 experiment 场景
|
|
77
|
+
// (GroupSummary 的组可能跨多个 experiment)下避免两个 experiment 里同名 eval 被误合并成一道题。
|
|
78
|
+
// 分隔符是 NUL(同 aggregate.ts 的 KEY_SEP 手法):不会出现在 eval id / experimentId 里,拼接键不串味。
|
|
79
|
+
const GROUP_KEY_SEP = "\u0000";
|
|
80
|
+
function fullEvalKey(item: Item): string {
|
|
81
|
+
return `${experimentIdOf(item)}${GROUP_KEY_SEP}${evalIdOf(item)}`;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* 一批 Item 的组级统计:eval 级折叠计票(evalLevelStats,与 view 榜单 / `TableRowMeta.verdicts`
|
|
86
|
+
* 同一套 foldEvalVerdict 口径,按完整身份键折叠)、experiment/eval/attempt 数量、总成本
|
|
87
|
+
* (null-safe 求和)、最后运行时间(组内快照 startedAt 的最大值)。`experimentRowMeta` 与
|
|
88
|
+
* `groupSummaryData` 共用这一份实现,不各自拼装 evalLevelStats。
|
|
89
|
+
*
|
|
90
|
+
* 内部纯函数,不导出、不进 index.ts:对外只经 `experimentRowMeta`(挑 verdicts)与
|
|
91
|
+
* `groupSummaryData`(挑全部字段,包成 `GroupSummaryData`)暴露,调用方拿不到 `Item[]`
|
|
92
|
+
* 本身,所以这里也不用担心被越权复用。
|
|
93
|
+
*/
|
|
94
|
+
function summarizeItems(items: Item[]): {
|
|
95
|
+
experiments: number;
|
|
96
|
+
evals: number;
|
|
97
|
+
attempts: number;
|
|
98
|
+
verdicts: { passed: number; failed: number; errored: number; skipped: number };
|
|
99
|
+
/** 折叠后代表每个「已跑」(非 skipped)eval 的一条 attempt 引用,与 ran 同序同数。 */
|
|
100
|
+
refs: AttemptLocator[];
|
|
101
|
+
/** 计入通过率分母的 eval 数(passed + failed + errored,不含 skipped)。 */
|
|
102
|
+
ran: number;
|
|
103
|
+
totalCostUSD: number | null;
|
|
104
|
+
lastRunAt: string | undefined;
|
|
105
|
+
} {
|
|
106
|
+
const experimentIds = new Set<string>();
|
|
107
|
+
for (const item of items) experimentIds.add(experimentIdOf(item));
|
|
108
|
+
|
|
109
|
+
const byEval = new Map<string, Item[]>();
|
|
110
|
+
for (const item of items) {
|
|
111
|
+
const key = fullEvalKey(item);
|
|
112
|
+
const list = byEval.get(key);
|
|
113
|
+
if (list) list.push(item);
|
|
114
|
+
else byEval.set(key, [item]);
|
|
115
|
+
}
|
|
116
|
+
const stats = evalLevelStats(
|
|
117
|
+
items.map((item) => ({ verdict: item.attempt.result.verdict, key: fullEvalKey(item) })),
|
|
118
|
+
(r) => r.key,
|
|
119
|
+
);
|
|
120
|
+
// 折叠代表 attempt:每个已跑的 eval 挑一条与折叠判定一致的 attempt 做证据引用,
|
|
121
|
+
// skipped 的 eval 不进分母、不出证据。
|
|
122
|
+
const refs: AttemptLocator[] = [];
|
|
123
|
+
for (const group of byEval.values()) {
|
|
124
|
+
const verdict = foldEvalVerdict(group.map((item) => item.attempt.result));
|
|
125
|
+
if (verdict === "skipped") continue;
|
|
126
|
+
const rep = group.find((item) => item.attempt.result.verdict === verdict) ?? group[0]!;
|
|
127
|
+
refs.push(locatorOf(rep));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
let totalCostUSD: number | null = null;
|
|
131
|
+
for (const item of items) {
|
|
132
|
+
const cost = attemptCostUSD(item.attempt.result);
|
|
133
|
+
if (cost !== null) totalCostUSD = (totalCostUSD ?? 0) + cost;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
let lastRunAt: string | undefined;
|
|
137
|
+
for (const item of items) {
|
|
138
|
+
const startedAt = item.snapshot.startedAt;
|
|
139
|
+
if (lastRunAt === undefined || startedAt > lastRunAt) lastRunAt = startedAt;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
experiments: experimentIds.size,
|
|
144
|
+
evals: stats.evals,
|
|
145
|
+
attempts: items.length,
|
|
146
|
+
verdicts: { passed: stats.passed, failed: stats.failed, errored: stats.errored, skipped: stats.skipped },
|
|
147
|
+
refs,
|
|
148
|
+
ran: stats.passed + stats.failed + stats.errored,
|
|
149
|
+
totalCostUSD,
|
|
150
|
+
lastRunAt,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* experiment 行的元信息:agent/model 身份(组内去重后拼接)+ eval 级折叠计票 + eval/attempt
|
|
156
|
+
* 数量与最后运行时间(summarizeItems,即 view 榜单 / ExperimentList 的同一套 foldEvalVerdict
|
|
157
|
+
* 口径)。其它行维度(agent/eval/自定义…)没有唯一身份,不携带。
|
|
158
|
+
*/
|
|
159
|
+
function experimentRowMeta(group: Item[]): TableRowMeta {
|
|
160
|
+
const agents = new Set<string>();
|
|
161
|
+
const models = new Set<string>();
|
|
162
|
+
for (const item of group) {
|
|
163
|
+
agents.add(item.attempt.result.agent);
|
|
164
|
+
const model = item.attempt.result.model ?? item.snapshot.model;
|
|
165
|
+
if (model !== undefined) models.add(model);
|
|
166
|
+
}
|
|
167
|
+
const stats = summarizeItems(group);
|
|
168
|
+
return {
|
|
169
|
+
...(agents.size > 0 ? { agent: [...agents].join(", ") } : {}),
|
|
170
|
+
...(models.size > 0 ? { model: [...models].join(", ") } : {}),
|
|
171
|
+
verdicts: stats.verdicts,
|
|
172
|
+
evals: stats.evals,
|
|
173
|
+
attempts: stats.attempts,
|
|
174
|
+
...(stats.lastRunAt !== undefined ? { lastRunAt: stats.lastRunAt } : {}),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* 一次 attempt 未通过的 gate 断言,原始声明顺序不变;soft 断言不参与判定,不算「失败原因」,
|
|
180
|
+
* 只影响得分,永不出现在这份列表里。`EvalList` / `ExperimentList` 的失败诊断与 `AttemptList`
|
|
181
|
+
* 的断言列表共用这同一份材料,保证同一个 attempt 在各处给出同一个原因。
|
|
182
|
+
*/
|
|
183
|
+
export function failingGateAssertions(result: EvalResult): AssertionResult[] {
|
|
184
|
+
return result.assertions.filter((a) => !a.passed && a.severity === "gate");
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* 一次 attempt 的失败原因文案,按优先级取第一个在场的:
|
|
189
|
+
* `error` → `skipReason` → 未通过的 gate 断言(原始声明顺序,`name`,detail 在场则
|
|
190
|
+
* `"name: detail"`,多条用「, 」连接)→ 都缺席则无原因(如某道题恰好没有失败信号)。
|
|
191
|
+
* soft 断言永不进入这份原因文案,soft 得分是独立概念,不与 reason 混用同一个字段。
|
|
192
|
+
*/
|
|
193
|
+
export function reasonFor(result: EvalResult): string | undefined {
|
|
194
|
+
if (result.error !== undefined) return result.error;
|
|
195
|
+
if (result.skipReason !== undefined) return result.skipReason;
|
|
196
|
+
const gates = failingGateAssertions(result);
|
|
197
|
+
if (gates.length === 0) return undefined;
|
|
198
|
+
return gates.map((a) => (a.detail ? `${a.name}: ${a.detail}` : a.name)).join(", ");
|
|
199
|
+
}
|
|
200
|
+
|
|
65
201
|
export async function tableData<const M extends readonly Metric[]>(
|
|
66
202
|
input: SnapshotsInput,
|
|
67
203
|
opts: TableDataOptions<M>,
|
|
@@ -70,7 +206,7 @@ export async function tableData<const M extends readonly Metric[]>(
|
|
|
70
206
|
const { snapshots } = resolveInput(input);
|
|
71
207
|
const items = filterItems(collectItems(snapshots), opts.evals);
|
|
72
208
|
const groups = groupItems(items, opts.rows);
|
|
73
|
-
const rows:
|
|
209
|
+
const rows: TableData["rows"] = [];
|
|
74
210
|
const sortCells = new Map<string, MetricCell>();
|
|
75
211
|
for (const [key, group] of groups) {
|
|
76
212
|
const cells: Record<string, MetricCell> = {};
|
|
@@ -79,7 +215,12 @@ export async function tableData<const M extends readonly Metric[]>(
|
|
|
79
215
|
// sort 指标不在 columns 里时单独算一遍,只用于排序、不进输出
|
|
80
216
|
sortCells.set(key, cells[opts.sort.name] ?? (await computeCell(opts.sort, group)));
|
|
81
217
|
}
|
|
82
|
-
rows
|
|
218
|
+
const meta: TableRowMeta = opts.rows === "experiment" ? experimentRowMeta(group) : {};
|
|
219
|
+
rows.push({
|
|
220
|
+
key,
|
|
221
|
+
cells,
|
|
222
|
+
...(Object.keys(meta).length > 0 ? { meta } : {}),
|
|
223
|
+
});
|
|
83
224
|
}
|
|
84
225
|
if (opts.sort) {
|
|
85
226
|
const better = opts.sort.better ?? "higher";
|
|
@@ -99,6 +240,147 @@ export async function tableData<const M extends readonly Metric[]>(
|
|
|
99
240
|
} as TableData<M[number]["name"]>;
|
|
100
241
|
}
|
|
101
242
|
|
|
243
|
+
// ───────────────────────── ExperimentList.data / EvalList.data / AttemptList.data ─────────────────────────
|
|
244
|
+
//
|
|
245
|
+
// 三个实体列表逐级下钻(experiment → experimentId × eval → attempt),固定展示实体事实,
|
|
246
|
+
// 没有列配置;过滤是报告作者对返回数组调用 .filter()/.slice() 的事,不进这里
|
|
247
|
+
// (docs/feature/reports/library.md「实体列表」)。AttemptListItem 是三者共用的叶子形状——
|
|
248
|
+
// ExperimentList / EvalList 的下钻数组直接复用它,不各自精简一份。
|
|
249
|
+
|
|
250
|
+
/** 自由文本(error / 断言 detail / evidence)的发布消毒钩子;身份字段(name/severity/loc)不经它。 */
|
|
251
|
+
function redactAssertions(assertions: AssertionResult[], redact: (text: string) => string): AssertionResult[] {
|
|
252
|
+
if (assertions.length === 0) return assertions;
|
|
253
|
+
return assertions.map((a) => ({
|
|
254
|
+
...a,
|
|
255
|
+
...(a.detail !== undefined ? { detail: redact(a.detail) } : {}),
|
|
256
|
+
...(a.evidence !== undefined ? { evidence: redact(a.evidence) } : {}),
|
|
257
|
+
}));
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/** AttemptList / ExperimentList / EvalList 共用的叶子构造:一个 Item → 一个 AttemptListItem。 */
|
|
261
|
+
function attemptListItemOf(item: Item, redact: (text: string) => string): AttemptListItem {
|
|
262
|
+
const result = item.attempt.result;
|
|
263
|
+
const cost = attemptCostUSD(result);
|
|
264
|
+
return {
|
|
265
|
+
evalId: evalIdOf(item),
|
|
266
|
+
experimentId: experimentIdOf(item),
|
|
267
|
+
attempt: result.attempt,
|
|
268
|
+
agent: result.agent,
|
|
269
|
+
verdict: result.verdict,
|
|
270
|
+
...(result.error !== undefined ? { error: redact(result.error) } : {}),
|
|
271
|
+
assertions: redactAssertions(result.assertions, redact),
|
|
272
|
+
durationMs: result.durationMs,
|
|
273
|
+
...(cost !== null ? { costUSD: cost } : {}),
|
|
274
|
+
locator: locatorOf(item),
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const identityRedact = (text: string): string => text;
|
|
279
|
+
|
|
280
|
+
export interface AttemptListDataOptions {
|
|
281
|
+
/** 发布消毒:error / 断言 detail / evidence 经这个钩子;身份字段(experimentId/evalId/locator…)不经它。 */
|
|
282
|
+
redact?: (text: string) => string;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/** `AttemptList.data(selection)`:每个 Attempt 一项,顺序取自 Selection 展平顺序(不重排)。 */
|
|
286
|
+
export async function attemptListData(
|
|
287
|
+
input: SnapshotsInput,
|
|
288
|
+
opts?: AttemptListDataOptions,
|
|
289
|
+
): Promise<AttemptListItem[]> {
|
|
290
|
+
const { snapshots } = resolveInput(input);
|
|
291
|
+
const redact = opts?.redact ?? identityRedact;
|
|
292
|
+
const items = collectItems(snapshots);
|
|
293
|
+
return items.map((item) => attemptListItemOf(item, redact));
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/** `EvalList.data(selection)`:每个 `experimentId + evalId` 一项,按 evalId 再按 experimentId 升序。 */
|
|
297
|
+
export async function evalListData(input: SnapshotsInput): Promise<EvalListItem[]> {
|
|
298
|
+
const { snapshots } = resolveInput(input);
|
|
299
|
+
const items = collectItems(snapshots);
|
|
300
|
+
const groups = new Map<string, Item[]>();
|
|
301
|
+
for (const item of items) {
|
|
302
|
+
const key = fullEvalKey(item);
|
|
303
|
+
const list = groups.get(key);
|
|
304
|
+
if (list) list.push(item);
|
|
305
|
+
else groups.set(key, [item]);
|
|
306
|
+
}
|
|
307
|
+
const out: EvalListItem[] = [];
|
|
308
|
+
for (const group of groups.values()) {
|
|
309
|
+
const sorted = [...group].sort((a, b) => a.attempt.result.attempt - b.attempt.result.attempt);
|
|
310
|
+
const verdict = foldEvalVerdict(sorted.map((item) => item.attempt.result));
|
|
311
|
+
const representative = sorted.find((item) => item.attempt.result.verdict === verdict) ?? sorted[0]!;
|
|
312
|
+
const attempts = sorted.map((item) => attemptListItemOf(item, identityRedact));
|
|
313
|
+
out.push({
|
|
314
|
+
evalId: evalIdOf(sorted[0]!),
|
|
315
|
+
experimentId: experimentIdOf(sorted[0]!),
|
|
316
|
+
verdict,
|
|
317
|
+
reason: reasonFor(representative.attempt.result),
|
|
318
|
+
score: await computeCell(examScore, sorted),
|
|
319
|
+
duration: await computeCell(durationMs, sorted),
|
|
320
|
+
cost: await computeCell(costUSD, sorted),
|
|
321
|
+
attempts,
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
out.sort((a, b) => a.evalId.localeCompare(b.evalId) || a.experimentId.localeCompare(b.experimentId));
|
|
325
|
+
return out;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/** `ExperimentList.data(selection)`:每个 experiment 一项,按 experimentId 升序;展开到每道 Eval。 */
|
|
329
|
+
export async function experimentListData(input: SnapshotsInput): Promise<ExperimentListItem[]> {
|
|
330
|
+
const { snapshots } = resolveInput(input);
|
|
331
|
+
const items = collectItems(snapshots);
|
|
332
|
+
const groups = groupItems(items, "experiment");
|
|
333
|
+
const out: ExperimentListItem[] = [];
|
|
334
|
+
for (const [experimentId, group] of groups) {
|
|
335
|
+
const stats = summarizeItems(group);
|
|
336
|
+
const newest = [...group].sort((a, b) => b.snapshot.startedAt.localeCompare(a.snapshot.startedAt))[0]!;
|
|
337
|
+
const evalGroups = groupItems(group, "eval");
|
|
338
|
+
const evalRows: ExperimentListEvalRow[] = [];
|
|
339
|
+
for (const [evalId, evalItems] of evalGroups) {
|
|
340
|
+
const sorted = [...evalItems].sort((a, b) => a.attempt.result.attempt - b.attempt.result.attempt);
|
|
341
|
+
const verdict = foldEvalVerdict(sorted.map((item) => item.attempt.result));
|
|
342
|
+
const representative = sorted.find((item) => item.attempt.result.verdict === verdict) ?? sorted[0]!;
|
|
343
|
+
const attempts = sorted.map((item) => attemptListItemOf(item, identityRedact));
|
|
344
|
+
evalRows.push({
|
|
345
|
+
evalId,
|
|
346
|
+
verdict,
|
|
347
|
+
reason: reasonFor(representative.attempt.result),
|
|
348
|
+
duration: await computeCell(durationMs, sorted),
|
|
349
|
+
cost: await computeCell(costUSD, sorted),
|
|
350
|
+
attempts,
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
evalRows.sort((a, b) => a.evalId.localeCompare(b.evalId));
|
|
354
|
+
const experiment = newest.snapshot.experiment ?? newest.attempt.result.experiment;
|
|
355
|
+
out.push({
|
|
356
|
+
experimentId,
|
|
357
|
+
agent: newest.snapshot.agent,
|
|
358
|
+
...((newest.attempt.result.model ?? newest.snapshot.model) !== undefined
|
|
359
|
+
? { model: newest.attempt.result.model ?? newest.snapshot.model }
|
|
360
|
+
: {}),
|
|
361
|
+
...(experiment?.flags ? { flags: experiment.flags } : {}),
|
|
362
|
+
verdicts: stats.verdicts,
|
|
363
|
+
passRate: await computeCell(passRate, group),
|
|
364
|
+
cost: await computeCell(costUSD, group),
|
|
365
|
+
duration: await computeCell(durationMs, group),
|
|
366
|
+
tokens: await computeCell(tokens, group),
|
|
367
|
+
evals: stats.evals,
|
|
368
|
+
attempts: stats.attempts,
|
|
369
|
+
lastRunAt: stats.lastRunAt!,
|
|
370
|
+
evalRows,
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
// ExperimentList 是默认实验比较表:初始态按成功率从高到低,缺数据沉底;
|
|
374
|
+
// 同分时按 experiment id 稳定排序。web 增强可临时重排,text 面沿用同一基准顺序。
|
|
375
|
+
out.sort((a, b) => {
|
|
376
|
+
if (a.passRate.value === null && b.passRate.value === null) return a.experimentId.localeCompare(b.experimentId);
|
|
377
|
+
if (a.passRate.value === null) return 1;
|
|
378
|
+
if (b.passRate.value === null) return -1;
|
|
379
|
+
return b.passRate.value - a.passRate.value || a.experimentId.localeCompare(b.experimentId);
|
|
380
|
+
});
|
|
381
|
+
return out;
|
|
382
|
+
}
|
|
383
|
+
|
|
102
384
|
// ───────────────────────── MetricMatrix.data(= MetricBars.data)─────────────────────────
|
|
103
385
|
|
|
104
386
|
export interface MatrixDataOptions {
|
|
@@ -326,7 +608,7 @@ export async function lineData(input: SnapshotsInput, opts: LineDataOptions): Pr
|
|
|
326
608
|
|
|
327
609
|
// ───────────────────────── RunOverview.data ─────────────────────────
|
|
328
610
|
|
|
329
|
-
/**
|
|
611
|
+
/** Selection 的 warnings 随行进 OverviewData,RunOverview 直接渲染 —— 诚实不靠使用者记得接线。 */
|
|
330
612
|
export async function overviewData(input: SnapshotsInput): Promise<OverviewData> {
|
|
331
613
|
const { snapshots, warnings } = resolveInput(input);
|
|
332
614
|
const items = collectItems(snapshots);
|
|
@@ -340,7 +622,7 @@ export async function overviewData(input: SnapshotsInput): Promise<OverviewData>
|
|
|
340
622
|
for (const item of items) {
|
|
341
623
|
const result = item.attempt.result;
|
|
342
624
|
evalIds.add(evalIdOf(item));
|
|
343
|
-
switch (result.
|
|
625
|
+
switch (result.verdict) {
|
|
344
626
|
case "passed":
|
|
345
627
|
passed += 1;
|
|
346
628
|
break;
|
|
@@ -358,6 +640,9 @@ export async function overviewData(input: SnapshotsInput): Promise<OverviewData>
|
|
|
358
640
|
const cost = attemptCostUSD(result);
|
|
359
641
|
if (cost !== null) costUSD = (costUSD ?? 0) + cost;
|
|
360
642
|
}
|
|
643
|
+
// 通过率的唯一官方口径:两级聚合(computeCell),不是从上面四个 verdict 计票现场重算——
|
|
644
|
+
// 一道题内 attempt 部分通过要算部分 credit,不是二元投票。
|
|
645
|
+
const passRateCell = await computeCell(passRate, items);
|
|
361
646
|
return {
|
|
362
647
|
snapshots: snapshots.map((s) => ({
|
|
363
648
|
experimentId: s.experimentId,
|
|
@@ -365,11 +650,52 @@ export async function overviewData(input: SnapshotsInput): Promise<OverviewData>
|
|
|
365
650
|
model: s.model,
|
|
366
651
|
startedAt: s.startedAt,
|
|
367
652
|
})),
|
|
368
|
-
totals: {
|
|
653
|
+
totals: {
|
|
654
|
+
evals: evalIds.size,
|
|
655
|
+
attempts: items.length,
|
|
656
|
+
passed,
|
|
657
|
+
failed,
|
|
658
|
+
errored,
|
|
659
|
+
skipped,
|
|
660
|
+
passRate: passRateCell,
|
|
661
|
+
costUSD,
|
|
662
|
+
durationMs,
|
|
663
|
+
},
|
|
369
664
|
warnings: [...warnings],
|
|
370
665
|
};
|
|
371
666
|
}
|
|
372
667
|
|
|
668
|
+
// ───────────────────────── GroupSummary.data ─────────────────────────
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* 一组 experiment 的摘要:experiment/eval/attempt 数量、eval 级折叠计票、通过率(旧
|
|
672
|
+
* `GroupSelector` 卡片口径,见 summarizeItems)、总成本(null-safe 求和)、最后运行时间
|
|
673
|
+
* (组内快照 startedAt 最大值)。`input` 就是调用方已经收窄好的组 Selection(如自定义报告
|
|
674
|
+
* 按 experiment 组前缀 filter 出来的那份)——本函数不再自己分组。
|
|
675
|
+
*/
|
|
676
|
+
export async function groupSummaryData(input: SnapshotsInput): Promise<GroupSummaryData> {
|
|
677
|
+
const { snapshots } = resolveInput(input);
|
|
678
|
+
const items = collectItems(snapshots);
|
|
679
|
+
const summary = summarizeItems(items);
|
|
680
|
+
const ratio = summary.ran > 0 ? summary.verdicts.passed / summary.ran : null; // 分母为 0 → 缺数据,不编 0%
|
|
681
|
+
const passRateCell: MetricCell = {
|
|
682
|
+
value: ratio,
|
|
683
|
+
display: ratio === null ? "—" : formatMetricValue(ratio, "%"),
|
|
684
|
+
samples: summary.ran,
|
|
685
|
+
total: summary.evals,
|
|
686
|
+
refs: summary.refs,
|
|
687
|
+
};
|
|
688
|
+
return {
|
|
689
|
+
experiments: summary.experiments,
|
|
690
|
+
evals: summary.evals,
|
|
691
|
+
attempts: summary.attempts,
|
|
692
|
+
verdicts: summary.verdicts,
|
|
693
|
+
passRate: passRateCell,
|
|
694
|
+
totalCostUSD: summary.totalCostUSD,
|
|
695
|
+
...(summary.lastRunAt !== undefined ? { lastRunAt: summary.lastRunAt } : {}),
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
|
|
373
699
|
// ───────────────────────── DeltaTable.data ─────────────────────────
|
|
374
700
|
|
|
375
701
|
export interface DeltaPair {
|
|
@@ -423,48 +749,3 @@ function deltaDisplay(metric: Metric, delta: number | null): string {
|
|
|
423
749
|
const text = displayValue(metric, delta); // 负号由格式化自带
|
|
424
750
|
return delta > 0 ? `+${text}` : text;
|
|
425
751
|
}
|
|
426
|
-
|
|
427
|
-
// ───────────────────────── CaseList.data ─────────────────────────
|
|
428
|
-
|
|
429
|
-
export interface CaseListDataOptions {
|
|
430
|
-
/** 要列出的判决;默认 failed + errored。 */
|
|
431
|
-
outcomes?: ("failed" | "errored")[];
|
|
432
|
-
/** 超出如实报 truncated,不静默截断。 */
|
|
433
|
-
limit?: number;
|
|
434
|
-
/** 自由文本(error / 断言 detail / judge evidence)的发布消毒钩子;身份字段不经它。 */
|
|
435
|
-
redact?: (text: string) => string;
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
export async function caseListData(input: SnapshotsInput, opts?: CaseListDataOptions): Promise<CaseListData> {
|
|
439
|
-
const { snapshots } = resolveInput(input);
|
|
440
|
-
const wanted = new Set<"failed" | "errored">(opts?.outcomes ?? ["failed", "errored"]);
|
|
441
|
-
const redact = opts?.redact ?? ((text: string) => text);
|
|
442
|
-
const selected = collectItems(snapshots).filter((item) => {
|
|
443
|
-
const outcome = item.attempt.result.outcome;
|
|
444
|
-
return (outcome === "failed" || outcome === "errored") && wanted.has(outcome);
|
|
445
|
-
});
|
|
446
|
-
const shown = opts?.limit === undefined ? selected : selected.slice(0, opts.limit);
|
|
447
|
-
const rows: CaseListData["rows"] = shown.map((item) => {
|
|
448
|
-
const result = item.attempt.result;
|
|
449
|
-
const cost = attemptCostUSD(result);
|
|
450
|
-
return {
|
|
451
|
-
eval: evalIdOf(item),
|
|
452
|
-
experimentId: experimentIdOf(item),
|
|
453
|
-
agent: result.agent,
|
|
454
|
-
outcome: result.outcome as "failed" | "errored",
|
|
455
|
-
error: result.error === undefined ? undefined : redact(result.error),
|
|
456
|
-
failedAssertions: result.assertions
|
|
457
|
-
.filter((assertion) => !assertion.passed)
|
|
458
|
-
.map((assertion) => ({
|
|
459
|
-
name: assertion.name,
|
|
460
|
-
score: assertion.score,
|
|
461
|
-
detail: assertion.detail === undefined ? undefined : redact(assertion.detail),
|
|
462
|
-
evidence: assertion.evidence === undefined ? undefined : redact(assertion.evidence),
|
|
463
|
-
})),
|
|
464
|
-
durationMs: result.durationMs,
|
|
465
|
-
costUSD: cost ?? undefined,
|
|
466
|
-
ref: item.attempt.ref,
|
|
467
|
-
};
|
|
468
|
-
});
|
|
469
|
-
return { rows, truncated: selected.length - shown.length };
|
|
470
|
-
}
|