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/view/app/lib/rows.ts
CHANGED
|
@@ -1,73 +1,17 @@
|
|
|
1
|
-
// viewData
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
1
|
+
// viewData 快照明细 → 证据室页面模型的纯拼接,零聚合口径:Runs / Traces 的平铺列表
|
|
2
|
+
// 来自全部快照(server 侧已跨快照去重);统计口径整体住在报告槽的静态 HTML 里,
|
|
3
|
+
// 前端不再拼榜单行。修复 prompt 的失败清单(CopyControls 的 fixPromptEntries)
|
|
4
|
+
// 也从这份快照明细现算。
|
|
5
5
|
|
|
6
|
-
import type {
|
|
7
|
-
import type {
|
|
8
|
-
import
|
|
9
|
-
import { evalLevelStats } from "../../../shared/outcome.ts";
|
|
10
|
-
import { displayExperimentName, fallbackExperimentLabel, sumMaybe } from "../../../shared/aggregate.ts";
|
|
11
|
-
import { formatConfigValue } from "./format.ts";
|
|
6
|
+
import type { RowRun } from "../shared.ts";
|
|
7
|
+
import type { ViewResult, ViewSnapshot } from "../types.ts";
|
|
8
|
+
import { displayExperimentName } from "../../../shared/aggregate.ts";
|
|
12
9
|
|
|
13
|
-
/**
|
|
14
|
-
export
|
|
15
|
-
passRate: "pass-rate",
|
|
16
|
-
duration: "duration",
|
|
17
|
-
tokens: "tokens",
|
|
18
|
-
cost: "cost",
|
|
19
|
-
} as const;
|
|
20
|
-
|
|
21
|
-
/** 快照的展示标签:experiment id 的末段;合成键退化到 agent/model 兜底。 */
|
|
22
|
-
function snapshotLabel(snapshot: ViewSnapshot): string {
|
|
23
|
-
if (snapshot.synthetic) {
|
|
24
|
-
const first = snapshot.results[0];
|
|
25
|
-
return first ? fallbackExperimentLabel(first) : snapshot.experimentId;
|
|
26
|
-
}
|
|
10
|
+
/** 快照的展示标签:experiment id 的末段。 */
|
|
11
|
+
export function snapshotLabel(snapshot: ViewSnapshot): string {
|
|
27
12
|
return displayExperimentName(snapshot.experimentId) ?? snapshot.experimentId;
|
|
28
13
|
}
|
|
29
14
|
|
|
30
|
-
function experimentGroup(snapshot: ViewSnapshot): string | undefined {
|
|
31
|
-
if (snapshot.synthetic || !snapshot.experimentId.includes("/")) return undefined;
|
|
32
|
-
return snapshot.experimentId.split("/").slice(0, -1).join("/");
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/** 官方 TableData 行 × latest 快照 → 榜单行。行集合以官方表为准,快照只补证据室明细。 */
|
|
36
|
-
export function buildRows(data: ViewData): ViewRow[] {
|
|
37
|
-
const latestByExperiment = new Map<string, ViewSnapshot>();
|
|
38
|
-
for (const snapshot of data.snapshots) {
|
|
39
|
-
if (snapshot.latest) latestByExperiment.set(snapshot.experimentId, snapshot);
|
|
40
|
-
}
|
|
41
|
-
const rows: ViewRow[] = [];
|
|
42
|
-
for (const tableRow of data.table.rows) {
|
|
43
|
-
const snapshot = latestByExperiment.get(tableRow.key);
|
|
44
|
-
if (!snapshot) continue; // 官方表与快照来自同一次挑选,理论上不缺;缺了宁可不渲染半行
|
|
45
|
-
const results = snapshot.results;
|
|
46
|
-
const stats = evalLevelStats(results, (r) => r.id);
|
|
47
|
-
rows.push({
|
|
48
|
-
key: tableRow.key,
|
|
49
|
-
experimentId: snapshot.experimentId,
|
|
50
|
-
...(snapshot.synthetic ? { synthetic: true } : {}),
|
|
51
|
-
experiment: results[0]?.experiment,
|
|
52
|
-
group: experimentGroup(snapshot),
|
|
53
|
-
label: snapshotLabel(snapshot),
|
|
54
|
-
agent: snapshot.agent,
|
|
55
|
-
model: snapshot.model,
|
|
56
|
-
lastRunAt: snapshot.startedAt,
|
|
57
|
-
cells: tableRow.cells,
|
|
58
|
-
runs: results.length,
|
|
59
|
-
evals: stats.evals,
|
|
60
|
-
passed: stats.passed,
|
|
61
|
-
failed: stats.failed,
|
|
62
|
-
errored: stats.errored,
|
|
63
|
-
skipped: stats.skipped,
|
|
64
|
-
totalCostUSD: sumMaybe(results.map((r) => r.usage?.costUSD ?? r.estimatedCostUSD)),
|
|
65
|
-
results,
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
return rows;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
15
|
/** Runs / Traces 的平铺列表:全部快照(含历史)的 attempt,带所属实验的展示标注。 */
|
|
72
16
|
export function flattenAttempts(snapshots: ViewSnapshot[]): RowRun[] {
|
|
73
17
|
return snapshots.flatMap((snapshot) => {
|
|
@@ -94,50 +38,3 @@ export function resultFromUrl(snapshots: ViewSnapshot[]): ViewResult | null {
|
|
|
94
38
|
}
|
|
95
39
|
return null;
|
|
96
40
|
}
|
|
97
|
-
|
|
98
|
-
export function buildGroupMap(rows: ViewRow[]): Map<string, ViewRow[]> {
|
|
99
|
-
const map = new Map<string, ViewRow[]>();
|
|
100
|
-
for (const row of rows) {
|
|
101
|
-
if (!row.group) continue;
|
|
102
|
-
if (!map.has(row.group)) map.set(row.group, []);
|
|
103
|
-
map.get(row.group)?.push(row);
|
|
104
|
-
}
|
|
105
|
-
return map;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export function compareRows(a: ViewRow, b: ViewRow, key: SortKey): number {
|
|
109
|
-
const av = valueFor(a, key);
|
|
110
|
-
const bv = valueFor(b, key);
|
|
111
|
-
if (typeof av === "string" || typeof bv === "string") return String(av).localeCompare(String(bv));
|
|
112
|
-
// 缺数据(null)沉底,与官方组件「缺数据不画 0」同一姿势。
|
|
113
|
-
if (av === null && bv === null) return 0;
|
|
114
|
-
if (av === null) return 1;
|
|
115
|
-
if (bv === null) return -1;
|
|
116
|
-
return Number(av) - Number(bv);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
export function valueFor(row: ViewRow, key: SortKey): string | number | null {
|
|
120
|
-
if (key === "experiment") return row.label;
|
|
121
|
-
if (key === "model") return row.model || "";
|
|
122
|
-
if (key === "agent") return row.agent;
|
|
123
|
-
const cellKey =
|
|
124
|
-
key === "passRate" ? CELL_KEYS.passRate : key === "duration" ? CELL_KEYS.duration : key === "tokens" ? CELL_KEYS.tokens : CELL_KEYS.cost;
|
|
125
|
-
return row.cells[cellKey]?.value ?? null;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
export function configChips(row: ViewRow, t: T): [string, ReactNode][] {
|
|
129
|
-
const exp = row.experiment || {};
|
|
130
|
-
const flags = exp.flags && Object.keys(exp.flags).length
|
|
131
|
-
? Object.entries(exp.flags).map(([k, v]) => k + "=" + formatConfigValue(v)).join(", ")
|
|
132
|
-
: t("config.flagsNone");
|
|
133
|
-
return [
|
|
134
|
-
[t("config.experiment"), row.synthetic ? row.label : row.experimentId],
|
|
135
|
-
[t("table.model"), row.model || t("config.default")],
|
|
136
|
-
["agent", row.agent],
|
|
137
|
-
["runs", exp.runs ?? row.runs],
|
|
138
|
-
["earlyExit", exp.earlyExit === undefined ? t("config.notApplicable") : String(exp.earlyExit)],
|
|
139
|
-
["sandbox", exp.sandbox || t("config.default")],
|
|
140
|
-
["budget", exp.budget === undefined ? t("config.none") : "$" + exp.budget],
|
|
141
|
-
["flags", flags],
|
|
142
|
-
];
|
|
143
|
-
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Assertion, Verdict, ViewResult } from "../types.ts";
|
|
2
|
+
import type { T } from "../shared.ts";
|
|
3
|
+
|
|
4
|
+
export function verdictClass(verdict: Verdict): string {
|
|
5
|
+
return verdict === "passed" ? "good" : verdict === "errored" ? "infra-err" : verdict === "failed" ? "bad" : "warn";
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function verdictLabel(verdict: Verdict, t: T): string {
|
|
9
|
+
if (verdict === "passed") return t("status.pass");
|
|
10
|
+
if (verdict === "failed") return t("status.fail");
|
|
11
|
+
if (verdict === "errored") return t("status.error");
|
|
12
|
+
if (verdict === "skipped") return t("status.skipped");
|
|
13
|
+
return verdict || "—";
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Only gate-severity failures are eval "failure reasons"; soft failures show as scores
|
|
17
|
+
export function failingAssertions(result: ViewResult): Assertion[] {
|
|
18
|
+
return (result.assertions || []).filter((a: Assertion) => !a.passed && a.severity === "gate");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function reasonFor(result: ViewResult, failedGates: Assertion[]): string {
|
|
22
|
+
if (result.error) return result.error;
|
|
23
|
+
if (result.skipReason) return result.skipReason;
|
|
24
|
+
return failedGates.map((a: Assertion) => (a.detail ? `${a.name}: ${a.detail}` : a.name)).join(", ");
|
|
25
|
+
}
|
package/src/view/app/main.tsx
CHANGED
|
@@ -1,26 +1,23 @@
|
|
|
1
1
|
import { createRoot } from "react-dom/client";
|
|
2
|
-
import type { ViewData } from "./types.ts";
|
|
2
|
+
import type { ReportSlotHtml, ViewData } from "./types.ts";
|
|
3
3
|
import { App } from "./App.tsx";
|
|
4
4
|
import "../styles.css";
|
|
5
5
|
|
|
6
|
-
// 没有烘焙数据(比如直接打开裸产物)
|
|
6
|
+
// 没有烘焙数据(比如直接打开裸产物)时的空页面兜底。
|
|
7
7
|
const emptyViewData: ViewData = {
|
|
8
8
|
composedRuns: 0,
|
|
9
|
-
overview: {
|
|
10
|
-
snapshots: [],
|
|
11
|
-
totals: { evals: 0, attempts: 0, passed: 0, failed: 0, errored: 0, skipped: 0, costUSD: null, durationMs: 0 },
|
|
12
|
-
warnings: [],
|
|
13
|
-
},
|
|
14
|
-
table: { dimension: "experiment", columns: [], rows: [] },
|
|
15
|
-
overall: { dimension: "overall", columns: [], rows: [] },
|
|
16
9
|
snapshots: [],
|
|
17
10
|
};
|
|
18
11
|
|
|
19
12
|
const initialData: ViewData = window.__NICEEVAL_VIEW_DATA__ ?? emptyViewData;
|
|
20
13
|
|
|
21
|
-
//
|
|
22
|
-
// (__NICEEVAL_VIEW_DATA__
|
|
23
|
-
|
|
14
|
+
// 报告槽:server 把报告树的静态 HTML 烘成 en / zh-CN 两个 <template> 静态块
|
|
15
|
+
// (__NICEEVAL_VIEW_DATA__ 旁)。前端按当前界面语言把对应块摆进报告槽位置,
|
|
16
|
+
// 切语言即换块;不解析、不 hydrate。
|
|
17
|
+
const reportHtml: ReportSlotHtml = {
|
|
18
|
+
en: document.getElementById("niceeval-report-en")?.innerHTML ?? "",
|
|
19
|
+
"zh-CN": document.getElementById("niceeval-report-zh-CN")?.innerHTML ?? "",
|
|
20
|
+
};
|
|
24
21
|
|
|
25
22
|
const rootEl = document.getElementById("root");
|
|
26
23
|
if (!rootEl) throw new Error("Missing #root element");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useState } from "react";
|
|
2
2
|
import type { RowRun, T } from "../shared.ts";
|
|
3
|
-
import {
|
|
3
|
+
import { verdictClass, verdictLabel } from "../lib/verdict.ts";
|
|
4
4
|
import { formatCost, formatDateTime, formatDuration, formatTokens, totalTokens } from "../lib/format.ts";
|
|
5
5
|
|
|
6
6
|
/** 全部历史 attempt 打平成一张表;列表在 App 里由 flattenAttempts(全部快照,已跨快照去重)算好。 */
|
|
@@ -35,7 +35,7 @@ export function RunsView({ attempts, t }: { attempts: RowRun[]; t: T }) {
|
|
|
35
35
|
<tr>
|
|
36
36
|
<th>{t("table.evalId")}</th>
|
|
37
37
|
<th>{t("table.experiment")}</th>
|
|
38
|
-
<th>{t("table.
|
|
38
|
+
<th>{t("table.verdict")}</th>
|
|
39
39
|
<th>{t("table.agent")}</th>
|
|
40
40
|
<th>{t("table.model")}</th>
|
|
41
41
|
<th>{t("metric.duration")}</th>
|
|
@@ -47,14 +47,14 @@ export function RunsView({ attempts, t }: { attempts: RowRun[]; t: T }) {
|
|
|
47
47
|
<tbody>
|
|
48
48
|
{filtered.length ? (
|
|
49
49
|
filtered.map((r: RowRun) => {
|
|
50
|
-
const
|
|
50
|
+
const verdict = r.verdict;
|
|
51
51
|
return (
|
|
52
52
|
<tr key={`${r.id}-${r.rowLabel}-${r.attempt}`}>
|
|
53
53
|
<td>
|
|
54
54
|
<span className="name">{r.id}</span>
|
|
55
55
|
</td>
|
|
56
56
|
<td>{r.rowLabel}</td>
|
|
57
|
-
<td className={
|
|
57
|
+
<td className={verdictClass(verdict)}>{verdictLabel(verdict, t)}</td>
|
|
58
58
|
<td>{r.rowAgent}</td>
|
|
59
59
|
<td>{r.rowModel || t("config.default")}</td>
|
|
60
60
|
<td className="num">{formatDuration(r.durationMs)}</td>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { RowRun, T } from "../shared.ts";
|
|
2
|
-
import {
|
|
2
|
+
import { verdictClass, verdictLabel } from "../lib/verdict.ts";
|
|
3
3
|
import { formatDuration } from "../lib/format.ts";
|
|
4
4
|
import { LazyArtifact } from "../components/LazyArtifact.tsx";
|
|
5
5
|
|
|
@@ -15,11 +15,11 @@ export function TracesView({ attempts, t }: { attempts: RowRun[]; t: T }) {
|
|
|
15
15
|
<div className="empty">{t("empty.traces")}</div>
|
|
16
16
|
) : (
|
|
17
17
|
traceable.map((r: RowRun) => {
|
|
18
|
-
const
|
|
18
|
+
const verdict = r.verdict;
|
|
19
19
|
return (
|
|
20
20
|
<div className="traces-entry" key={`${r.id}-${r.rowLabel}-${r.attempt}`}>
|
|
21
21
|
<div className="traces-entry-head">
|
|
22
|
-
<span className={`${
|
|
22
|
+
<span className={`${verdictClass(verdict)} traces-verdict`}>{verdictLabel(verdict, t)}</span>
|
|
23
23
|
<span className="eval-id">{r.id}</span>
|
|
24
24
|
<span className="traces-exp">{r.rowLabel}</span>
|
|
25
25
|
<span className="num traces-dur">{formatDuration(r.durationMs)}</span>
|
package/src/view/app/shared.ts
CHANGED
|
@@ -3,7 +3,6 @@ import type { MessageKey } from "./i18n.ts";
|
|
|
3
3
|
import type { CodeSource, TranscriptEvent, ViewJson, ViewResult } from "./types.ts";
|
|
4
4
|
|
|
5
5
|
export type T = (key: MessageKey, vars?: Vars) => string;
|
|
6
|
-
export type OpenModal = (result: ViewResult) => void;
|
|
7
6
|
export type ArtifactLoadState =
|
|
8
7
|
| { sources: CodeSource[] | null; events: TranscriptEvent[] | null; status: "loading" | "ready" | "none" };
|
|
9
8
|
export type RowRun = ViewResult & { rowLabel: string; rowAgent: string; rowModel?: string };
|
package/src/view/app/types.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
AssertionResult,
|
|
3
|
-
ExperimentRunInfo,
|
|
4
3
|
JsonValue,
|
|
5
4
|
LocalizedText,
|
|
6
5
|
SourceArtifact,
|
|
@@ -9,71 +8,27 @@ import type {
|
|
|
9
8
|
TraceSpan,
|
|
10
9
|
Usage,
|
|
11
10
|
} from "../../types.ts";
|
|
12
|
-
import type {
|
|
11
|
+
import type { ViewData, ViewEvalResult } from "../shared/types.ts";
|
|
13
12
|
|
|
14
13
|
export type { LocalizedText };
|
|
15
14
|
// Locale 只在 i18n 内核声明一次;页面数据形状与 server 共用 shared/types.ts 的声明。
|
|
16
15
|
export type { Locale } from "../../i18n/core.ts";
|
|
17
16
|
export type {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
MetricColumn,
|
|
21
|
-
OverviewData,
|
|
22
|
-
SelectionWarning,
|
|
17
|
+
AttemptLocator,
|
|
18
|
+
ReportSlotHtml,
|
|
23
19
|
SkippedRunNotice,
|
|
24
|
-
TableData,
|
|
25
20
|
ViewData,
|
|
26
21
|
ViewSnapshot,
|
|
27
22
|
} from "../shared/types.ts";
|
|
28
23
|
|
|
29
|
-
export type Tab = "report" | "
|
|
30
|
-
export type SortKey = "experiment" | "model" | "agent" | "duration" | "passRate" | "tokens" | "cost";
|
|
31
|
-
export type SortDir = 1 | -1;
|
|
24
|
+
export type Tab = "report" | "runs" | "traces";
|
|
32
25
|
|
|
33
|
-
/**
|
|
34
|
-
* Experiments 榜单一行 = 一个实验的最新快照(latest 口径)。
|
|
35
|
-
* 统计数字(cells)是官方 MetricTable.data 的产物,前端只渲染;
|
|
36
|
-
* 明细(results / 判决计数)是该快照的证据室数据,由 buildRows 从 viewData 拼接。
|
|
37
|
-
*/
|
|
38
|
-
export interface ViewRow {
|
|
39
|
-
/** = experimentId(官方 TableData 行键)。 */
|
|
40
|
-
key: string;
|
|
41
|
-
experimentId: string;
|
|
42
|
-
/** experimentId 是 "<agent>/<model>" 合成键时为 true(落盘缺 experimentId)。 */
|
|
43
|
-
synthetic?: boolean;
|
|
44
|
-
experiment?: ExperimentRunInfo;
|
|
45
|
-
group?: string;
|
|
46
|
-
label: string;
|
|
47
|
-
agent: string;
|
|
48
|
-
model?: string;
|
|
49
|
-
/** 该快照的 startedAt(ISO)—— 榜单「每行带判决产生的时间」。 */
|
|
50
|
-
lastRunAt: string;
|
|
51
|
-
/** 默认报告格子(pass-rate / duration / tokens / cost),display 已格式化。 */
|
|
52
|
-
cells: Partial<Record<string, MetricCell>>;
|
|
53
|
-
/** 快照内 attempt 总数。 */
|
|
54
|
-
runs: number;
|
|
55
|
-
/** 折叠后的 eval 判决计数(钻取明细的展示口径,与 CLI 共用同一份折叠实现)。 */
|
|
56
|
-
evals: number;
|
|
57
|
-
passed: number;
|
|
58
|
-
failed: number;
|
|
59
|
-
errored: number;
|
|
60
|
-
skipped: number;
|
|
61
|
-
/** 快照内成本合计(证据室口径的总花费;榜单列是官方的「平均每 eval 成本」格子)。 */
|
|
62
|
-
totalCostUSD?: number;
|
|
63
|
-
results: ViewEvalResult[];
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export interface SortState {
|
|
67
|
-
key: SortKey;
|
|
68
|
-
dir: SortDir;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/** 前端拿到的单条 attempt 结果就是瘦身后的 EvalResult(attemptRef / artifactBase 由 loader 注入)。 */
|
|
26
|
+
/** 前端拿到的单条 attempt 结果就是瘦身后的 EvalResult(locator / artifactBase 由 loader 注入)。 */
|
|
72
27
|
export type ViewResult = ViewEvalResult;
|
|
73
28
|
|
|
74
29
|
export type Assertion = AssertionResult;
|
|
75
30
|
|
|
76
|
-
export type
|
|
31
|
+
export type Verdict = "passed" | "failed" | "errored" | "skipped" | string;
|
|
77
32
|
|
|
78
33
|
export interface SourceTurn {
|
|
79
34
|
loc?: SourceLoc;
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// 回归覆盖:src/results/open.ts 把 sources.json 从「全量 SourceArtifact[]」改成两层去重存储
|
|
2
|
+
// (attempt 级引用 `{path, sha256}[]` + 快照根 `sources/<sha256>.json` 内容仓库,见 memory/
|
|
3
|
+
// attempt-locator-and-source-dedup.md)之后,view 的两条 artifact 出口(server.ts 的
|
|
4
|
+
// serveArtifact、index.ts 的 copyFetchedArtifacts)曾各自继续把落盘的引用格式原样吐给浏览器
|
|
5
|
+
// (readFile 直传 / copyFile 直拷),而不是走 AttemptHandle.sources() 解引用。浏览器端
|
|
6
|
+
// isCodeSource 守卫要求 `content: string` 字段,引用格式没有,代码视图因此静默判空——
|
|
7
|
+
// 即便 sources 明明被捕获了(memory/static-site-export-drops-sources.md 记录的是同一症状的
|
|
8
|
+
// 更早一次事故,根因不同:那次是文件压根没导出,这次是导出了但格式不对)。
|
|
9
|
+
//
|
|
10
|
+
// 两个测试都特意让两个 attempt 共享字节相同的 eval 源码,确保真的走了去重路径(sha256 引用 +
|
|
11
|
+
// 共享 blob),而不是巧合地测到「本来就没有第二层」的场景。
|
|
12
|
+
|
|
13
|
+
import { mkdtemp, readFile, readdir, rm } from "node:fs/promises";
|
|
14
|
+
import { tmpdir } from "node:os";
|
|
15
|
+
import { join } from "node:path";
|
|
16
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
17
|
+
import { createResultsWriter } from "../results/index.ts";
|
|
18
|
+
import { loadViewScan } from "./data.ts";
|
|
19
|
+
import { buildView } from "./index.ts";
|
|
20
|
+
import { startViewServer, type ViewServer } from "./server.ts";
|
|
21
|
+
import { artifactUrl } from "./app/lib/artifact-url.ts";
|
|
22
|
+
|
|
23
|
+
const SHARED_CONTENT = "export default { test() {} };\n";
|
|
24
|
+
const SHARED_PATH = "evals/shared.eval.ts";
|
|
25
|
+
|
|
26
|
+
const roots: string[] = [];
|
|
27
|
+
async function makeRoot(): Promise<string> {
|
|
28
|
+
const root = await mkdtemp(join(tmpdir(), "niceeval-artifact-serving-"));
|
|
29
|
+
roots.push(root);
|
|
30
|
+
return root;
|
|
31
|
+
}
|
|
32
|
+
afterEach(async () => {
|
|
33
|
+
await Promise.all(roots.splice(0).map((r) => rm(r, { recursive: true, force: true })));
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 写一份真实快照:两个 attempt(q1/q2)共享字节相同的 eval 源码触发去重仓库,
|
|
38
|
+
* 外加一个 attempt(q3)带 events.json(用于确认 events/trace 这类 artifact 不受影响)。
|
|
39
|
+
* 走真实 writer(createResultsWriter),不手写落盘格式——这正是 sources 去重两层存储
|
|
40
|
+
* 唯一的生产写入路径。
|
|
41
|
+
*/
|
|
42
|
+
async function seedDedupedSnapshot(root: string): Promise<{ snapDir: string }> {
|
|
43
|
+
const writer = createResultsWriter(root, { producer: { name: "niceeval", version: "1.0.0" } });
|
|
44
|
+
const snap = await writer.snapshot({ experimentId: "e", agent: "bub", startedAt: "2026-07-01T08:00:00.000Z" });
|
|
45
|
+
await snap.writeAttempt(
|
|
46
|
+
{ id: "q1", verdict: "passed", attempt: 0, durationMs: 1, assertions: [] },
|
|
47
|
+
{ sources: [{ path: SHARED_PATH, content: SHARED_CONTENT }] },
|
|
48
|
+
);
|
|
49
|
+
await snap.writeAttempt(
|
|
50
|
+
{ id: "q2", verdict: "passed", attempt: 0, durationMs: 1, assertions: [] },
|
|
51
|
+
{ sources: [{ path: SHARED_PATH, content: SHARED_CONTENT }] },
|
|
52
|
+
);
|
|
53
|
+
await snap.writeAttempt(
|
|
54
|
+
{ id: "q3", verdict: "passed", attempt: 0, durationMs: 1, assertions: [] },
|
|
55
|
+
{ events: [{ type: "message", role: "user", text: "hi" }] },
|
|
56
|
+
);
|
|
57
|
+
await writer.finish();
|
|
58
|
+
|
|
59
|
+
// 前提断言:去重真的生效了(否则这份 fixture 没有覆盖到引用/仓库两层结构,回归测试会
|
|
60
|
+
// 在「碰巧没有第二层」的场景下通过,失去意义)。
|
|
61
|
+
const storeFiles = await readdir(join(snap.dir, "sources"));
|
|
62
|
+
expect(storeFiles).toHaveLength(1);
|
|
63
|
+
const q1Ref = JSON.parse(await readFile(join(snap.dir, "q1", "a0", "sources.json"), "utf-8"));
|
|
64
|
+
expect(q1Ref).toEqual([{ path: SHARED_PATH, sha256: expect.any(String) }]);
|
|
65
|
+
expect(q1Ref[0]).not.toHaveProperty("content"); // 落盘就是引用,不含内容——下面断言浏览器出口必须解引用
|
|
66
|
+
|
|
67
|
+
return { snapDir: snap.dir };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
describe("server.ts · serveArtifact 对 sources.json 解引用", () => {
|
|
71
|
+
let server: ViewServer | undefined;
|
|
72
|
+
afterEach(async () => {
|
|
73
|
+
await server?.close();
|
|
74
|
+
server = undefined;
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("HTTP /artifact/<base>/sources.json 返回解引用后的 {path,content}[],不是落盘的 {path,sha256}[] 引用", async () => {
|
|
78
|
+
const root = await makeRoot();
|
|
79
|
+
await seedDedupedSnapshot(root);
|
|
80
|
+
|
|
81
|
+
const scan = await loadViewScan(root);
|
|
82
|
+
const byId = new Map(scan.viewData.snapshots.flatMap((s) => s.results.map((r) => [r.id, r])));
|
|
83
|
+
const q1 = byId.get("q1")!;
|
|
84
|
+
const q2 = byId.get("q2")!;
|
|
85
|
+
expect(q1.hasSources).toBe(true);
|
|
86
|
+
expect(q2.hasSources).toBe(true);
|
|
87
|
+
|
|
88
|
+
server = await startViewServer({ input: root });
|
|
89
|
+
|
|
90
|
+
for (const result of [q1, q2]) {
|
|
91
|
+
const url = new URL(artifactUrl(`${result.artifactBase}/sources.json`), server.url);
|
|
92
|
+
const res = await fetch(url);
|
|
93
|
+
expect(res.status).toBe(200);
|
|
94
|
+
const body = await res.json();
|
|
95
|
+
expect(body).toEqual([{ path: SHARED_PATH, content: SHARED_CONTENT }]);
|
|
96
|
+
expect(body[0]).not.toHaveProperty("sha256");
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("events.json 仍走原文件直传,不受 sources 解引用改动影响", async () => {
|
|
101
|
+
const root = await makeRoot();
|
|
102
|
+
await seedDedupedSnapshot(root);
|
|
103
|
+
const scan = await loadViewScan(root);
|
|
104
|
+
const q3 = scan.viewData.snapshots.flatMap((s) => s.results).find((r) => r.id === "q3")!;
|
|
105
|
+
expect(q3.hasEvents).toBe(true);
|
|
106
|
+
|
|
107
|
+
server = await startViewServer({ input: root });
|
|
108
|
+
const url = new URL(artifactUrl(`${q3.artifactBase}/events.json`), server.url);
|
|
109
|
+
const res = await fetch(url);
|
|
110
|
+
expect(res.status).toBe(200);
|
|
111
|
+
expect(await res.json()).toEqual([{ type: "message", role: "user", text: "hi" }]);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
describe("index.ts · copyFetchedArtifacts(--out 静态导出)对 sources.json 解引用", () => {
|
|
116
|
+
it("导出目录里的 sources.json 是解引用后的 {path,content}[],不是落盘引用;events.json 仍原字节复制", async () => {
|
|
117
|
+
const root = await makeRoot();
|
|
118
|
+
await seedDedupedSnapshot(root);
|
|
119
|
+
|
|
120
|
+
const out = join(root, "site");
|
|
121
|
+
await buildView({ input: root, out });
|
|
122
|
+
|
|
123
|
+
const scan = await loadViewScan(root);
|
|
124
|
+
const byId = new Map(scan.viewData.snapshots.flatMap((s) => s.results.map((r) => [r.id, r])));
|
|
125
|
+
for (const id of ["q1", "q2"]) {
|
|
126
|
+
const base = byId.get(id)!.artifactBase!;
|
|
127
|
+
const exported = JSON.parse(await readFile(join(out, "artifact", base, "sources.json"), "utf-8"));
|
|
128
|
+
expect(exported).toEqual([{ path: SHARED_PATH, content: SHARED_CONTENT }]);
|
|
129
|
+
expect(exported[0]).not.toHaveProperty("sha256");
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const q3Base = byId.get("q3")!.artifactBase!;
|
|
133
|
+
const exportedEvents = JSON.parse(await readFile(join(out, "artifact", q3Base, "events.json"), "utf-8"));
|
|
134
|
+
expect(exportedEvents).toEqual([{ type: "message", role: "user", text: "hi" }]);
|
|
135
|
+
});
|
|
136
|
+
});
|