niceeval 0.10.0 → 0.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/report/components/attempt-detail/AttemptConversation.d.ts +5 -1
- package/dist/report/components/attempt-detail/AttemptConversation.js +42 -2
- package/dist/report/components/attempt-detail/AttemptSource.js +83 -13
- package/dist/report/components/attempt-detail/compute.js +44 -1
- package/dist/report/components/attempt-detail/faces.js +4 -0
- package/dist/report/components/attempt-detail/index.d.ts +2 -2
- package/dist/report/components/attempt-detail/index.js +24 -4
- package/dist/report/components/entity-lists/ExperimentList.d.ts +1 -2
- package/dist/report/components/entity-lists/ExperimentList.js +6 -5
- package/dist/report/components/entity-lists/faces.d.ts +1 -1
- package/dist/report/components/entity-lists/faces.js +10 -9
- package/dist/report/components/entity-lists/index.d.ts +5 -7
- package/dist/report/components/entity-lists/index.js +7 -3
- package/dist/report/components/metric-views/MetricScatter.js +2 -38
- package/dist/report/index.d.ts +1 -1
- package/dist/report/model/format.d.ts +5 -5
- package/dist/report/model/format.js +34 -8
- package/dist/report/model/types.d.ts +16 -2
- package/dist/report/react/index.d.ts +1 -1
- package/dist/runner/types.d.ts +2 -1
- package/dist/sandbox/e2b.d.ts +0 -1
- package/docs-site/zh/examples/integrations/ai-sdk-v7.mdx +5 -5
- package/docs-site/zh/explanation/runner.mdx +6 -2
- package/docs-site/zh/reference/cli.mdx +2 -2
- package/docs-site/zh/reference/report-components.mdx +3 -1
- package/docs-site/zh/tutorials/experiments.mdx +1 -2
- package/docs-site/zh/tutorials/viewing-results.mdx +12 -9
- package/docs-site/zh/tutorials/write-experiment.mdx +1 -3
- package/package.json +3 -4
- package/src/cli.ts +4 -2
- package/src/o11y/prices.json +176 -99
- package/src/report/assets/styles.css +822 -0
- package/src/report/components/attempt-detail/AttemptConversation.tsx +55 -7
- package/src/report/components/attempt-detail/AttemptSource.tsx +186 -42
- package/src/report/components/attempt-detail/attempt-components.test.tsx +82 -4
- package/src/report/components/attempt-detail/compute.ts +50 -1
- package/src/report/components/attempt-detail/faces.ts +3 -0
- package/src/report/components/attempt-detail/index.tsx +33 -16
- package/src/report/components/attempt-detail/validate.test.ts +19 -2
- package/src/report/components/entity-lists/ExperimentList.tsx +13 -10
- package/src/report/components/entity-lists/faces.ts +10 -9
- package/src/report/components/entity-lists/index.tsx +7 -10
- package/src/report/components/metric-views/MetricScatter.tsx +2 -38
- package/src/report/components/render.test.tsx +19 -9
- package/src/report/index.ts +2 -0
- package/src/report/model/format.ts +33 -8
- package/src/report/model/types.ts +18 -2
- package/src/report/react/index.tsx +2 -0
- package/src/report/runtime/dual-render.test.tsx +43 -10
- package/src/runner/types.ts +2 -1
- package/src/sandbox/e2b-reconcile.test.ts +125 -0
- package/src/sandbox/e2b.ts +38 -2
- package/src/view/view-report.test.ts +3 -1
package/src/sandbox/e2b.ts
CHANGED
|
@@ -14,7 +14,11 @@ import type {
|
|
|
14
14
|
SourceFiles,
|
|
15
15
|
ReadSourceFilesOptions,
|
|
16
16
|
} from "../types.ts";
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
classifyProvisionErrorFallback,
|
|
19
|
+
isRetryableProvisionError,
|
|
20
|
+
type SandboxProvisionErrorKind,
|
|
21
|
+
} from "./errors.ts";
|
|
18
22
|
import { classifySandboxIoError } from "./errors.ts";
|
|
19
23
|
import { readSourceFilesByList } from "./source-files.ts";
|
|
20
24
|
import { collectLocalFiles } from "./local-files.ts";
|
|
@@ -30,6 +34,13 @@ const DEFAULT_COMMAND_TIMEOUT_MS = 600_000;
|
|
|
30
34
|
const SESSION_TIMEOUT_MS = 1_800_000;
|
|
31
35
|
|
|
32
36
|
/** e2b 的限流错误是 SDK 原生的 RateLimitError(HTTP 429 映射而来);见 resolve.ts 的 withProvisionRetry。 */
|
|
37
|
+
// 对账本身只有一次机会:retry.ts 的 withProvisionRetry 对账失败就直接放弃重试、抛回原始
|
|
38
|
+
// create() 错误(见那边的注释)。对账走的这次 list 请求跟刚失败的 create() 往往挨得很近,
|
|
39
|
+
// 大概率处在同一段网络抖动里——不给它自己的重试,一次瞬时失败就会把本可能自愈的 attempt
|
|
40
|
+
// 判死。这里给 nextItems() 单独包一层短重试,只吃与 create() 侧同一套分类下的瞬时错误。
|
|
41
|
+
const RECONCILE_LIST_MAX_ATTEMPTS = 3;
|
|
42
|
+
const RECONCILE_LIST_RETRY_DELAY_MS = 500;
|
|
43
|
+
|
|
33
44
|
/**
|
|
34
45
|
* Provisioning 重试前的对账:按 metadata 里的 provision token 检索远端实例,查到即 kill。
|
|
35
46
|
* 检索或销毁失败必须抛出——对账是重试的硬前置,静默放行等于盲重试,会复制计费实例
|
|
@@ -43,7 +54,7 @@ export async function reconcileProvision(token: string): Promise<void> {
|
|
|
43
54
|
// 命中的实例数极少,通常一页打完。
|
|
44
55
|
const paginator = E2BSdkSandbox.list({ apiKey, query: { metadata: { "niceeval-provision-token": token } } });
|
|
45
56
|
while (paginator.hasNext) {
|
|
46
|
-
const sandboxes = await paginator
|
|
57
|
+
const sandboxes = await fetchNextItemsWithRetry(paginator);
|
|
47
58
|
for (const info of sandboxes) {
|
|
48
59
|
try {
|
|
49
60
|
await E2BSdkSandbox.kill(info.sandboxId, { apiKey });
|
|
@@ -54,6 +65,31 @@ export async function reconcileProvision(token: string): Promise<void> {
|
|
|
54
65
|
}
|
|
55
66
|
}
|
|
56
67
|
|
|
68
|
+
/**
|
|
69
|
+
* `nextItems()` 按类型契约总是 resolve 成数组(SDK 内部对空响应也兜了 `?? []`),但对账这条
|
|
70
|
+
* 路径线上真实撞见过它 resolve 成非数组的一次——没能复现出确切成因,不排它,原样让下面的
|
|
71
|
+
* `for...of` 抛出,但换一句能定位的诊断,而不是留一条裸的 "X is not iterable"。
|
|
72
|
+
*/
|
|
73
|
+
async function fetchNextItemsWithRetry(
|
|
74
|
+
paginator: ReturnType<typeof E2BSdkSandbox.list>,
|
|
75
|
+
): Promise<Awaited<ReturnType<typeof paginator.nextItems>>> {
|
|
76
|
+
for (let attempt = 0; ; attempt += 1) {
|
|
77
|
+
try {
|
|
78
|
+
const items = await paginator.nextItems();
|
|
79
|
+
if (!Array.isArray(items)) {
|
|
80
|
+
throw new Error(
|
|
81
|
+
`e2b Sandbox.list() 分页器 nextItems() 返回了非数组(${typeof items}),不是 SDK 类型契约里的 SandboxInfo[]`,
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
return items;
|
|
85
|
+
} catch (e) {
|
|
86
|
+
const kind = classifyProvisionErrorFallback(e);
|
|
87
|
+
if (attempt >= RECONCILE_LIST_MAX_ATTEMPTS - 1 || !isRetryableProvisionError(kind)) throw e;
|
|
88
|
+
await new Promise((resolve) => setTimeout(resolve, RECONCILE_LIST_RETRY_DELAY_MS * 2 ** attempt));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
57
93
|
export function classifyProvisionError(e: unknown): SandboxProvisionErrorKind {
|
|
58
94
|
// SDK 原生限流先归拒绝类;没认出的过与文件 IO 共用的保守瞬时兜底分类器
|
|
59
95
|
// (真实跑分里出现过 create 阶段 `fetch failed · other side closed`,属歧义类)。
|
|
@@ -577,7 +577,7 @@ describe("buildView · attempt/<locator>.html", () => {
|
|
|
577
577
|
expect(existsSync(join(out, "attempt", `${droppedLocator}.html`))).toBe(false);
|
|
578
578
|
});
|
|
579
579
|
|
|
580
|
-
it("
|
|
580
|
+
it("直接读取该文档即可见完整内容(cases.md Attempt 参数化 page);样式与视觉归 e2e 报告域,不在本层断言", async () => {
|
|
581
581
|
const root = await makeRoot();
|
|
582
582
|
const writer = createResultsWriter(root, { producer: { name: "niceeval", version: "1.0.0" } });
|
|
583
583
|
const snap = await writer.snapshot({ experimentId: "compare/bub", agent: "bub", startedAt: "2026-07-01T08:00:00.000Z" });
|
|
@@ -642,5 +642,7 @@ describe("buildView · attempt/<locator>.html", () => {
|
|
|
642
642
|
]) {
|
|
643
643
|
expect(enBlock, needle).toContain(needle);
|
|
644
644
|
}
|
|
645
|
+
// 有 source 时对话住在源码行内,不再渲染独立 AttemptConversation。
|
|
646
|
+
expect(enBlock).not.toContain("nre-attempt-conversation");
|
|
645
647
|
});
|
|
646
648
|
});
|