niceeval 0.11.4-canary.21 → 0.12.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.
Files changed (54) hide show
  1. package/dist/report/assets/series-encoding.d.ts +36 -0
  2. package/dist/report/assets/series-encoding.js +104 -0
  3. package/dist/report/components/entity-lists/compute.js +5 -17
  4. package/dist/report/components/entity-lists/content.d.ts +2 -0
  5. package/dist/report/components/entity-lists/content.js +22 -7
  6. package/dist/report/definition/cell.d.ts +4 -0
  7. package/dist/report/definition/cell.js +5 -1
  8. package/dist/report/definition/primitives/chart.js +125 -25
  9. package/dist/report/definition/primitives.js +3 -2
  10. package/dist/report/definition/tree.d.ts +5 -2
  11. package/dist/report/definition/tree.js +1 -0
  12. package/dist/report/index.d.ts +1 -1
  13. package/dist/report/model/aggregate.d.ts +10 -1
  14. package/dist/report/model/aggregate.js +24 -0
  15. package/dist/report/model/locale.d.ts +0 -1
  16. package/dist/report/model/locale.js +0 -2
  17. package/dist/report/model/types.d.ts +8 -0
  18. package/dist/report/presentation.d.ts +53 -6
  19. package/dist/report/presentation.js +40 -5
  20. package/dist/report/react/index.d.ts +1 -1
  21. package/dist/report/slices/compute.js +52 -4
  22. package/dist/report/slices/content.d.ts +2 -1
  23. package/dist/report/slices/content.js +48 -15
  24. package/dist/report/slices/validate.js +23 -0
  25. package/package.json +1 -1
  26. package/src/report/assets/series-encoding.tsx +177 -0
  27. package/src/report/assets/styles.css +47 -5
  28. package/src/report/components/compute.test.ts +15 -9
  29. package/src/report/components/entity-lists/compute.ts +5 -19
  30. package/src/report/components/entity-lists/content.test.ts +13 -8
  31. package/src/report/components/entity-lists/content.ts +24 -7
  32. package/src/report/definition/cell.ts +8 -1
  33. package/src/report/definition/primitives/chart.tsx +197 -32
  34. package/src/report/definition/primitives.tsx +8 -3
  35. package/src/report/definition/tree.ts +6 -2
  36. package/src/report/index.ts +11 -1
  37. package/src/report/model/aggregate.ts +26 -0
  38. package/src/report/model/locale.ts +0 -2
  39. package/src/report/model/types.ts +8 -0
  40. package/src/report/presentation.test.tsx +179 -19
  41. package/src/report/presentation.ts +114 -11
  42. package/src/report/react/index.tsx +11 -1
  43. package/src/report/slices/compute.ts +59 -4
  44. package/src/report/slices/content.ts +55 -16
  45. package/src/report/slices/delta-table.test.ts +138 -3
  46. package/src/report/slices/validate.test.ts +29 -0
  47. package/src/report/slices/validate.ts +16 -0
  48. package/src/runner/gate-lease.test.ts +38 -14
  49. package/src/runner/lock.test.ts +40 -16
  50. package/src/sandbox/compose.ts +3 -1
  51. package/src/sandbox/dockerfile-build.ts +4 -1
  52. package/src/sandbox/runtime.ts +4 -1
  53. package/src/sandbox/vercel.test.ts +12 -3
  54. package/src/show/index.ts +10 -9
@@ -3,7 +3,7 @@
3
3
  // download-directory.test.ts;这里只证明 vercel provider 自己的接线,不重新验证模板本身的
4
4
  // ignore/剥离/写盘逻辑)。fake `vsb.runCommand` / `vsb.readFileToBuffer`,不连真实 Vercel
5
5
  // API——真实 Vercel Sandbox 行为归 E2E(../../docs/engineering/testing/e2e/README.md)。
6
- import { afterEach, describe, expect, it } from "vitest";
6
+ import { afterEach, describe, expect, it, vi } from "vitest";
7
7
  import { mkdtemp, readFile, rm } from "node:fs/promises";
8
8
  import { tmpdir } from "node:os";
9
9
  import { join } from "node:path";
@@ -11,6 +11,7 @@ import { VercelSandbox } from "./vercel.ts";
11
11
 
12
12
  let roots: string[] = [];
13
13
  afterEach(async () => {
14
+ vi.useRealTimers();
14
15
  await Promise.all(roots.map((r) => rm(r, { recursive: true, force: true })));
15
16
  roots = [];
16
17
  });
@@ -124,6 +125,8 @@ describe("VercelSandbox.runCommand · 执行身份", () => {
124
125
  // cases: docs/engineering/testing/unit/sandbox.md「Sandbox 复用」——能力归属。
125
126
  // 寿命只从当前 session 的远端元数据读(createdAt + timeout),续期走 SDK 的 extendTimeout;
126
127
  // plan 上限拒绝续期时如实报 ready:false,不把请求值当成已生效(见 vercel.ts 的注释)。
128
+ // remainingMs = createdAt + timeout - Date.now();CI 并行下 wall clock 在 before/after 两次
129
+ // 读数之间只要前进就会把刚好够的续期压到 ready:false——用假时钟固定 Date.now。
127
130
  describe("VercelSandbox.ensureLifetime", () => {
128
131
  function sessionFake(opts: { timeout: number; createdAtOffsetMs?: number; extend?: (ms: number) => void }) {
129
132
  const session = { createdAt: new Date(Date.now() - (opts.createdAtOffsetMs ?? 0)), timeout: opts.timeout };
@@ -138,6 +141,8 @@ describe("VercelSandbox.ensureLifetime", () => {
138
141
  }
139
142
 
140
143
  it("当前 session 的远端剩余时间够时直接确认,不调 extendTimeout", async () => {
144
+ vi.useFakeTimers();
145
+ vi.setSystemTime(new Date("2026-01-01T00:00:00.000Z"));
141
146
  let extended = 0;
142
147
  const sandbox = makeSandbox(sessionFake({ timeout: 1_200_000, extend: () => (extended += 1) }));
143
148
 
@@ -148,7 +153,10 @@ describe("VercelSandbox.ensureLifetime", () => {
148
153
  });
149
154
 
150
155
  it("剩余不够时按缺口续期,续到够就确认", async () => {
156
+ vi.useFakeTimers();
157
+ vi.setSystemTime(new Date("2026-01-01T00:00:00.000Z"));
151
158
  const asked: number[] = [];
159
+ // timeout 10min,已活 9min → 剩余 1min;要 2min → 按缺口续 1min。
152
160
  const sandbox = makeSandbox(
153
161
  sessionFake({ timeout: 600_000, createdAtOffsetMs: 540_000, extend: (ms) => asked.push(ms) }),
154
162
  );
@@ -156,11 +164,12 @@ describe("VercelSandbox.ensureLifetime", () => {
156
164
  const result = await sandbox.ensureLifetime(120_000);
157
165
 
158
166
  expect(result.ready).toBe(true);
159
- expect(asked).toHaveLength(1);
160
- expect(asked[0]).toBeGreaterThan(0);
167
+ expect(asked).toEqual([60_000]);
161
168
  });
162
169
 
163
170
  it("plan 拒绝续期(HTTP 400)时报 ready:false 并带上 provider 理由", async () => {
171
+ vi.useFakeTimers();
172
+ vi.setSystemTime(new Date("2026-01-01T00:00:00.000Z"));
164
173
  const sandbox = makeSandbox(sessionFake({ timeout: 600_000, createdAtOffsetMs: 590_000 }));
165
174
 
166
175
  const result = await sandbox.ensureLifetime(1_800_000);
package/src/show/index.ts CHANGED
@@ -38,7 +38,7 @@ import { detectLocale, t } from "../i18n/index.ts";
38
38
  import { currentSample, filterExperiments } from "../sample/index.ts";
39
39
  import { matchExperimentSelector } from "../shared/aggregate.ts";
40
40
  import { panelCapabilityOf } from "../report/model/panel.ts";
41
- import { formatMetricValue, formatUSD, verdictMark } from "../report/model/format.ts";
41
+ import { formatMetricValue, formatTimeDistance, formatUSD, verdictMark } from "../report/model/format.ts";
42
42
  import { renderAlignedRows, type ColumnAlign } from "../report/model/text-layout.ts";
43
43
  import {
44
44
  annotatedSourceResult,
@@ -396,7 +396,7 @@ async function renderCompareSlice(
396
396
  id: "compare",
397
397
  title: "Compare",
398
398
  navigation: true,
399
- render: () => ({ type: Table, props: { data: deltaTableContent(data) } }),
399
+ render: () => ({ type: Table, props: { data: deltaTableContent(data, io.locale) } }),
400
400
  };
401
401
  const table = await renderHostPageText(
402
402
  page,
@@ -570,9 +570,10 @@ function usageMatrixCellOf(rows: readonly UsageResult[]): UsageMatrixCell {
570
570
  };
571
571
  }
572
572
 
573
- /** 一格的 7 列文案;`historical` 时在最后一列(成本)追加 `↩`——与 `DeltaTable` 把 `↩` 叠在
574
- * 同一格末尾同一条排版纪律(faces.ts `deltaConditionCellText`),不是新发明的标注位置。 */
575
- function usageMatrixCellText(cell: UsageMatrixCell, historical: boolean): string[] {
573
+ /** 一格的 7 列文案;`staleSinceMs` 在场时在最后一列(成本)追加相对时距(experiment-table.md
574
+ * 「时效不写字」——时距是这一格的数据,不是叠加的装饰符号),与 `DeltaTable` 的历史执行标注
575
+ * 同一条排版纪律、同一份 `formatTimeDistance` 入口。 */
576
+ function usageMatrixCellText(cell: UsageMatrixCell, staleSinceMs: number | undefined): string[] {
576
577
  const cost = cell.costUSD !== undefined ? formatUSD(cell.costUSD) : MISSING_MARK;
577
578
  return [
578
579
  cell.turns !== undefined ? String(cell.turns) : MISSING_MARK,
@@ -581,15 +582,15 @@ function usageMatrixCellText(cell: UsageMatrixCell, historical: boolean): string
581
582
  cell.cacheRead !== undefined ? formatMetricValue(cell.cacheRead) : MISSING_MARK,
582
583
  cell.out !== undefined ? formatMetricValue(cell.out) : MISSING_MARK,
583
584
  cell.requests !== undefined ? String(cell.requests) : MISSING_MARK,
584
- historical ? `${cost} ↩` : cost,
585
+ staleSinceMs !== undefined ? `${cost} ${formatTimeDistance(staleSinceMs)}` : cost,
585
586
  ];
586
587
  }
587
588
 
588
589
  /**
589
590
  * `--usage` 在对照范围(`--exp` 出现两次以上)下的逐 eval 用量矩阵
590
591
  * (docs/feature/reports/show/usage.md「范围化的用量表」)。配对身份、缺席占位、跨快照携带的
591
- * `↩` 时效标注,复用 `deltaTableData` 已经算好的这份判定——与 `renderCompareSlice` 消费同一次
592
- * 计算(条件解析、eval id 配对、watermark/carried 派生的 `historical`),不重新实现一遍;这个
592
+ * 相对时距标注,复用 `deltaTableData` 已经算好的这份判定——与 `renderCompareSlice` 消费同一次
593
+ * 计算(条件解析、eval id 配对、watermark/carried 派生的 `historical`/`staleSinceMs`),不重新实现一遍;这个
593
594
  * pivot 只在它之上叠一层用量字段的逐条件合计,数据源是 `usageRowsOf` 已有的逐 attempt 行,按
594
595
  * experimentId + evalId 分组求和。每个条件一组 7 列(`USAGE_MATRIX_METRIC_COLUMNS`),缺席
595
596
  * 条件整组落 `—`。
@@ -618,7 +619,7 @@ async function renderUsageCompareSlice(
618
619
  const deltaCell = row.cells[condition];
619
620
  if (!deltaCell) return USAGE_MATRIX_METRIC_COLUMNS.map(() => MISSING_MARK);
620
621
  const usageCell = usageMatrixCellOf(byConditionByEval.get(`${condition}\u0000${row.key}`) ?? []);
621
- return usageMatrixCellText(usageCell, deltaCell.historical);
622
+ return usageMatrixCellText(usageCell, deltaCell.historical ? deltaCell.staleSinceMs : undefined);
622
623
  });
623
624
  return [row.key, ...cellsText];
624
625
  });