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
|
@@ -3,16 +3,42 @@
|
|
|
3
3
|
// metric.display 可整体覆盖;这里只负责默认。
|
|
4
4
|
import { DISPLAY_LOCALES } from "./locale.js";
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* 一组 id 的显示名:每个 id 缩成在这组里唯一的最短路径后缀,重名逐步加长到能区分为止
|
|
7
|
+
* (与 `MetricScatter` 点标签同一算法,两处共用本函数以保证同一份 experiment id 在散点和
|
|
8
|
+
* 列表里缩成同一个显示名)。单个 id、或所有 id 深度不同时也照常缩到各自的最短唯一后缀。
|
|
9
|
+
* 完整 id 不受影响,调用方仍用它做排序 / 过滤 / 折叠的身份键,这里只产出显示名。
|
|
10
10
|
*/
|
|
11
|
-
export function
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
export function shortestUniqueLabels(ids) {
|
|
12
|
+
const segsOf = (id) => id.split("/").filter(Boolean);
|
|
13
|
+
const depth = new Map(ids.map((id) => [id, 1]));
|
|
14
|
+
for (;;) {
|
|
15
|
+
const byLabel = new Map();
|
|
16
|
+
for (const id of ids) {
|
|
17
|
+
const segs = segsOf(id);
|
|
18
|
+
const label = segs.slice(-Math.min(depth.get(id), segs.length)).join("/") || id;
|
|
19
|
+
byLabel.set(label, [...(byLabel.get(label) ?? []), id]);
|
|
20
|
+
}
|
|
21
|
+
let grew = false;
|
|
22
|
+
for (const group of byLabel.values()) {
|
|
23
|
+
if (group.length < 2)
|
|
24
|
+
continue;
|
|
25
|
+
for (const id of group) {
|
|
26
|
+
const segs = segsOf(id);
|
|
27
|
+
if (depth.get(id) < segs.length) {
|
|
28
|
+
depth.set(id, depth.get(id) + 1);
|
|
29
|
+
grew = true;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (!grew) {
|
|
34
|
+
const out = new Map();
|
|
35
|
+
for (const id of ids) {
|
|
36
|
+
const segs = segsOf(id);
|
|
37
|
+
out.set(id, segs.slice(-Math.min(depth.get(id), segs.length)).join("/") || id);
|
|
38
|
+
}
|
|
39
|
+
return out;
|
|
40
|
+
}
|
|
14
41
|
}
|
|
15
|
-
return experimentId;
|
|
16
42
|
}
|
|
17
43
|
/** 一位小数、去掉无意义的 ".0" 尾巴。 */
|
|
18
44
|
function trimmed(n) {
|
|
@@ -423,13 +423,27 @@ export interface AttemptAssertionsData {
|
|
|
423
423
|
items: AssertionResult[];
|
|
424
424
|
}[];
|
|
425
425
|
}
|
|
426
|
-
/** `AttemptSource`
|
|
426
|
+
/** `AttemptSource` 源码行内的一轮执行:send 头事实 + 标准事件流归并出的完整回复。 */
|
|
427
|
+
export interface AttemptSourceTurn {
|
|
428
|
+
label: string;
|
|
429
|
+
status: "completed" | "failed" | "waiting";
|
|
430
|
+
durationMs?: number;
|
|
431
|
+
sentText: string;
|
|
432
|
+
replies: AttemptConversationReply[];
|
|
433
|
+
}
|
|
434
|
+
/** AnnotatedSourceLine 加上 web 源码视图需要的行内执行轮。 */
|
|
435
|
+
export interface AttemptSourceLineData extends AnnotatedSourceLine {
|
|
436
|
+
turns: AttemptSourceTurn[];
|
|
437
|
+
}
|
|
438
|
+
/** `AttemptSource` 的 data:AnnotatedEvalSource + 按 loc 投影的标准事件流;没有 source 时 null。 */
|
|
427
439
|
export interface AttemptSourceData {
|
|
428
440
|
/** text 面拼 `niceeval show <locator> --source` 下钻命令用;web 面不需要。 */
|
|
429
441
|
locator: AttemptLocator;
|
|
430
442
|
sourcePath: string;
|
|
431
|
-
lines:
|
|
443
|
+
lines: AttemptSourceLineData[];
|
|
432
444
|
unmapped: AssertionResult[];
|
|
445
|
+
/** 没有 loc、指向其它文件或越界的轮次;不能静默丢弃,放在源码块末尾。 */
|
|
446
|
+
unlocatedTurns: AttemptSourceTurn[];
|
|
433
447
|
summary: AnnotatedEvalSourceSummary;
|
|
434
448
|
}
|
|
435
449
|
/** `AttemptFixPrompt` 的 data:单条 attempt 的复制修复 prompt;passed/skipped 或无可操作失败时 null。 */
|
|
@@ -25,7 +25,7 @@ export { AttemptDiagnostics } from "../components/attempt-detail/AttemptDiagnost
|
|
|
25
25
|
export { AttemptUsage } from "../components/attempt-detail/AttemptUsage.tsx";
|
|
26
26
|
export { AttemptTrace } from "../components/attempt-detail/AttemptTrace.tsx";
|
|
27
27
|
export { AttemptDiff } from "../components/attempt-detail/AttemptDiff.tsx";
|
|
28
|
-
export type { AttemptAssertionsData, AttemptConversationData, AttemptConversationReply, AttemptConversationRound, AttemptDiagnosticsData, AttemptDiffData, AttemptDiffFileEntry, AttemptErrorData, AttemptFixPromptData, AttemptListItem, AttemptLocator, AttemptSourceData, AttemptSummaryData, AttemptTimelineData, AttemptTraceData, AttemptUsageData, CopyFixPromptData, DeltaData, EvalListItem, ExperimentListEvalRow, ExperimentListItem, HeroData, LineData, MatrixData, MetricCell, MetricColumn, ScatterData, ScopeSummaryData, ScopeWarning, ScoreboardData, TableData, TraceSpanSummary, TraceWaterfallRow, VerdictTally, } from "../model/types.ts";
|
|
28
|
+
export type { AttemptAssertionsData, AttemptConversationData, AttemptConversationReply, AttemptConversationRound, AttemptDiagnosticsData, AttemptDiffData, AttemptDiffFileEntry, AttemptErrorData, AttemptFixPromptData, AttemptListItem, AttemptLocator, AttemptSourceData, AttemptSourceLineData, AttemptSourceTurn, AttemptSummaryData, AttemptTimelineData, AttemptTraceData, AttemptUsageData, CopyFixPromptData, DeltaData, EvalListItem, ExperimentListEvalRow, ExperimentListItem, HeroData, LineData, MatrixData, MetricCell, MetricColumn, ScatterData, ScopeSummaryData, ScopeWarning, ScoreboardData, TableData, TraceSpanSummary, TraceWaterfallRow, VerdictTally, } from "../model/types.ts";
|
|
29
29
|
export type { AttemptEvidence, AttemptEvidenceCapabilities } from "../../results/attempt-evidence.ts";
|
|
30
30
|
export { DEFAULT_REPORT_LOCALE, resolveLocalizedText, resolveMetricLabel } from "../model/locale.ts";
|
|
31
31
|
export type { LocalizedText, ReportLocale } from "../model/locale.ts";
|
package/dist/runner/types.d.ts
CHANGED
|
@@ -410,7 +410,8 @@ export interface ExperimentDef {
|
|
|
410
410
|
labels?: Record<string, string | number>;
|
|
411
411
|
/** 同一 eval 重复跑几次(结果各计一条 attempt);省略/CLI `--runs` 覆盖时默认 1。 */
|
|
412
412
|
runs?: number;
|
|
413
|
-
/** 一次重复(runs > 1)里某次 attempt
|
|
413
|
+
/** 一次重复(runs > 1)里某次 attempt 通过后是否跳过剩余重复;省略默认 false(`runs` 跑满、测完整通过率),
|
|
414
|
+
* 显式打开用于「只想知道能不能过」的省钱场景。 */
|
|
414
415
|
earlyExit?: boolean;
|
|
415
416
|
/**
|
|
416
417
|
* 这个实验覆盖哪些 eval:`"*"` 全部、字符串数组按 id 前缀、或自定义谓词(逐条收到发现并扇出后的
|
package/dist/sandbox/e2b.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { Sandbox, CommandResult, CommandOptions, SandboxFile, SourceFiles, ReadSourceFilesOptions } from "../types.ts";
|
|
2
2
|
import { type SandboxProvisionErrorKind } from "./errors.ts";
|
|
3
|
-
/** e2b 的限流错误是 SDK 原生的 RateLimitError(HTTP 429 映射而来);见 resolve.ts 的 withProvisionRetry。 */
|
|
4
3
|
/**
|
|
5
4
|
* Provisioning 重试前的对账:按 metadata 里的 provision token 检索远端实例,查到即 kill。
|
|
6
5
|
* 检索或销毁失败必须抛出——对账是重试的硬前置,静默放行等于盲重试,会复制计费实例
|
|
@@ -19,7 +19,7 @@ description: "一个 AI SDK v7 聊天应用,对着它的 HTTP 接口无侵入
|
|
|
19
19
|
|
|
20
20
|
接入的全部代码变更(生成时从两个目录实测统计):
|
|
21
21
|
|
|
22
|
-
<table className="gd-summary"><tbody><tr><th>{"类别"}</th><th>{"文件数"}</th><th>{"行数"}</th></tr><tr><td>{"应用侧配置(必要:依赖声明)"}</td><td>{"3"}</td><td>{"+5 −1"}</td></tr><tr><td>{"adapter(必要:传输粘合,协议映射在官方包里)"}</td><td>{"2"}</td><td>{"+27"}</td></tr><tr><td>{"evals 与 experiments(评测内容,按需增长)"}</td><td>{"8"}</td><td>{"+
|
|
22
|
+
<table className="gd-summary"><tbody><tr><th>{"类别"}</th><th>{"文件数"}</th><th>{"行数"}</th></tr><tr><td>{"应用侧配置(必要:依赖声明)"}</td><td>{"3"}</td><td>{"+5 −1"}</td></tr><tr><td>{"adapter(必要:传输粘合,协议映射在官方包里)"}</td><td>{"2"}</td><td>{"+27"}</td></tr><tr><td>{"evals 与 experiments(评测内容,按需增长)"}</td><td>{"8"}</td><td>{"+139"}</td></tr><tr className="gd-total"><td>{"合计"}</td><td>{"13"}</td><td>{"+171 −1"}</td></tr></tbody></table>
|
|
23
23
|
|
|
24
24
|
## 文件清单
|
|
25
25
|
|
|
@@ -126,15 +126,15 @@ ai-sdk-v7/
|
|
|
126
126
|
</div>
|
|
127
127
|
|
|
128
128
|
<div className="gd-file">
|
|
129
|
-
<div className="gd-head"><span className="gd-name">{"experiments/compare-models/deepseek-v4-flash.ts"}</span><span className="gd-stats"><span className="gd-plus">{"+
|
|
129
|
+
<div className="gd-head"><span className="gd-name">{"experiments/compare-models/deepseek-v4-flash.ts"}</span><span className="gd-stats"><span className="gd-plus">{"+12"}</span></span></div>
|
|
130
130
|
<div className="gd-body">
|
|
131
|
-
<table className="gd-table"><tbody><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"1"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt4">{"import"}</span><span className="gdt0">{" { defineExperiment } "}</span><span className="gdt4">{"from"}</span><span className="gdt0">{" "}</span><span className="gdt2">{"\"niceeval\""}</span><span className="gdt0">{";"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"2"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt4">{"import"}</span><span className="gdt0">{" agent "}</span><span className="gdt4">{"from"}</span><span className="gdt0">{" "}</span><span className="gdt2">{"\"../../agents/ai-sdk-v7.ts\""}</span><span className="gdt0">{";"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"3"}</td><td className="gd-sign">{"+"}</td><td className="gd-code">{" "}</td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"4"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt6">{"// compare-models 组的一格:deepseek-v4-flash。一文件一配置(单 model),model 经 ctx.model"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"5"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt6">{"// 走请求体传给应用,同一个 server 实例服务所有 model,不用重启进程。"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"6"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt4">{"export"}</span><span className="gdt0">{" "}</span><span className="gdt4">{"default"}</span><span className="gdt0">{" "}</span><span className="gdt5">{"defineExperiment"}</span><span className="gdt0">{"({"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"7"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" description: "}</span><span className="gdt2">{"\"deepseek-v4-flash: 对比模型\""}</span><span className="gdt0">{","}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"8"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" agent,"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"9"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" model: "}</span><span className="gdt2">{"\"deepseek-v4-flash\""}</span><span className="gdt0">{","}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"10"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" runs: "}</span><span className="gdt1">{"2"}</span><span className="gdt0">{","}</span
|
|
131
|
+
<table className="gd-table"><tbody><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"1"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt4">{"import"}</span><span className="gdt0">{" { defineExperiment } "}</span><span className="gdt4">{"from"}</span><span className="gdt0">{" "}</span><span className="gdt2">{"\"niceeval\""}</span><span className="gdt0">{";"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"2"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt4">{"import"}</span><span className="gdt0">{" agent "}</span><span className="gdt4">{"from"}</span><span className="gdt0">{" "}</span><span className="gdt2">{"\"../../agents/ai-sdk-v7.ts\""}</span><span className="gdt0">{";"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"3"}</td><td className="gd-sign">{"+"}</td><td className="gd-code">{" "}</td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"4"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt6">{"// compare-models 组的一格:deepseek-v4-flash。一文件一配置(单 model),model 经 ctx.model"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"5"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt6">{"// 走请求体传给应用,同一个 server 实例服务所有 model,不用重启进程。"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"6"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt4">{"export"}</span><span className="gdt0">{" "}</span><span className="gdt4">{"default"}</span><span className="gdt0">{" "}</span><span className="gdt5">{"defineExperiment"}</span><span className="gdt0">{"({"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"7"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" description: "}</span><span className="gdt2">{"\"deepseek-v4-flash: 对比模型\""}</span><span className="gdt0">{","}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"8"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" agent,"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"9"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" model: "}</span><span className="gdt2">{"\"deepseek-v4-flash\""}</span><span className="gdt0">{","}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"10"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" runs: "}</span><span className="gdt1">{"2"}</span><span className="gdt0">{", "}</span><span className="gdt6">{"// 跑满 2 次,才能比较 model 间的通过率"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"11"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" budget: "}</span><span className="gdt1">{"2"}</span><span className="gdt0">{","}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"12"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{"});"}</span></td></tr></tbody></table>
|
|
132
132
|
</div>
|
|
133
133
|
</div>
|
|
134
134
|
|
|
135
135
|
<div className="gd-file">
|
|
136
|
-
<div className="gd-head"><span className="gd-name">{"experiments/compare-models/deepseek-v4-pro.ts"}</span><span className="gd-stats"><span className="gd-plus">{"+
|
|
136
|
+
<div className="gd-head"><span className="gd-name">{"experiments/compare-models/deepseek-v4-pro.ts"}</span><span className="gd-stats"><span className="gd-plus">{"+11"}</span></span></div>
|
|
137
137
|
<div className="gd-body">
|
|
138
|
-
<table className="gd-table"><tbody><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"1"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt4">{"import"}</span><span className="gdt0">{" { defineExperiment } "}</span><span className="gdt4">{"from"}</span><span className="gdt0">{" "}</span><span className="gdt2">{"\"niceeval\""}</span><span className="gdt0">{";"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"2"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt4">{"import"}</span><span className="gdt0">{" agent "}</span><span className="gdt4">{"from"}</span><span className="gdt0">{" "}</span><span className="gdt2">{"\"../../agents/ai-sdk-v7.ts\""}</span><span className="gdt0">{";"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"3"}</td><td className="gd-sign">{"+"}</td><td className="gd-code">{" "}</td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"4"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt6">{"// compare-models 组的一格:deepseek-v4-pro。与 deepseek-v4-flash.ts 钉住一切、只差 model。"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"5"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt4">{"export"}</span><span className="gdt0">{" "}</span><span className="gdt4">{"default"}</span><span className="gdt0">{" "}</span><span className="gdt5">{"defineExperiment"}</span><span className="gdt0">{"({"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"6"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" description: "}</span><span className="gdt2">{"\"deepseek-v4-pro: 对比模型\""}</span><span className="gdt0">{","}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"7"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" agent,"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"8"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" model: "}</span><span className="gdt2">{"\"deepseek-v4-pro\""}</span><span className="gdt0">{","}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"9"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" runs: "}</span><span className="gdt1">{"2"}</span><span className="gdt0">{","}</span
|
|
138
|
+
<table className="gd-table"><tbody><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"1"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt4">{"import"}</span><span className="gdt0">{" { defineExperiment } "}</span><span className="gdt4">{"from"}</span><span className="gdt0">{" "}</span><span className="gdt2">{"\"niceeval\""}</span><span className="gdt0">{";"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"2"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt4">{"import"}</span><span className="gdt0">{" agent "}</span><span className="gdt4">{"from"}</span><span className="gdt0">{" "}</span><span className="gdt2">{"\"../../agents/ai-sdk-v7.ts\""}</span><span className="gdt0">{";"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"3"}</td><td className="gd-sign">{"+"}</td><td className="gd-code">{" "}</td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"4"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt6">{"// compare-models 组的一格:deepseek-v4-pro。与 deepseek-v4-flash.ts 钉住一切、只差 model。"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"5"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt4">{"export"}</span><span className="gdt0">{" "}</span><span className="gdt4">{"default"}</span><span className="gdt0">{" "}</span><span className="gdt5">{"defineExperiment"}</span><span className="gdt0">{"({"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"6"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" description: "}</span><span className="gdt2">{"\"deepseek-v4-pro: 对比模型\""}</span><span className="gdt0">{","}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"7"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" agent,"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"8"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" model: "}</span><span className="gdt2">{"\"deepseek-v4-pro\""}</span><span className="gdt0">{","}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"9"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" runs: "}</span><span className="gdt1">{"2"}</span><span className="gdt0">{", "}</span><span className="gdt6">{"// 跑满 2 次,才能比较 model 间的通过率"}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"10"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{" budget: "}</span><span className="gdt1">{"2"}</span><span className="gdt0">{","}</span></td></tr><tr className="gd-add"><td className="gd-ln"></td><td className="gd-ln">{"11"}</td><td className="gd-sign">{"+"}</td><td className="gd-code"><span className="gdt0">{"});"}</span></td></tr></tbody></table>
|
|
139
139
|
</div>
|
|
140
140
|
</div>
|
|
@@ -39,14 +39,18 @@ npx niceeval exp local --max-concurrency 8
|
|
|
39
39
|
|
|
40
40
|
远程 HTTP agent 可以用更高并发;本地 Docker Sandbox 通常需要更低并发,避免 CPU、内存和磁盘竞争。
|
|
41
41
|
|
|
42
|
+
把跑得慢的实验和跑得快的实验混在一次命令里跑是安全的:并发名额优先给「要跑最多轮才能跑完」的实验(比如 `maxConcurrency: 1` 又有几十个 Attempt 的那个),快的实验见缝插针补空位。慢实验有一段 `setup`(起隧道、起共享服务)时也一样——它等 `setup` 的这段时间不占名额,别的实验照常跑,`setup` 一好它就拿下一个空出来的名额,不用为它单开一次命令。
|
|
43
|
+
|
|
42
44
|
## runs 与 early-exit
|
|
43
45
|
|
|
44
46
|
```bash
|
|
45
47
|
npx niceeval exp local fixtures/button --runs 5
|
|
46
|
-
npx niceeval exp local fixtures/button --runs 5 --
|
|
48
|
+
npx niceeval exp local fixtures/button --runs 5 --early-exit
|
|
47
49
|
```
|
|
48
50
|
|
|
49
|
-
`runs` 用于测 pass rate
|
|
51
|
+
`runs` 用于测 pass rate,默认跑满 `runs` 次,给出完整的通过率分布。首过即停默认关闭;只想知道"这题能不能过"、不在乎完整分布时,用 `--early-exit` 打开——某个 Attempt 通过后,同一 eval 的剩余 Attempt 会被停止。
|
|
52
|
+
|
|
53
|
+
`runs` 的多次 Attempt 默认并发派发,并发上限由 `--max-concurrency`(或该实验的 `maxConcurrency`)决定——不会等上一次的结果出来再决定要不要派发下一次。首过即停能省下的,只是还没抢到并发名额、原本要排队的那些 Attempt;已经在跑的不受影响。想要"跑一次,过了就停、没过才跑下一次"这种一个接一个的效果,把该实验的 `maxConcurrency` 设成 1 并开启 `earlyExit`:并发名额只有一个时,同一个 eval 的 Attempt 只能排队依次跑,首过即停自然就能在下一次派发前生效。
|
|
50
54
|
|
|
51
55
|
## 缓存
|
|
52
56
|
|
|
@@ -105,7 +105,7 @@ npx niceeval exp models weather
|
|
|
105
105
|
| `--output` | string | 反馈 profile:`auto`(默认)按环境自动选择,`human` / `agent` / `ci` 强制指定;只改变终端展示,不改变选择、调度、判定、artifact 或退出码。`auto` 依次判定:stderr 是 TTY → human;否则 `CI`(或其它常见 CI 平台环境变量)存在 → ci;否则 → agent。 |
|
|
106
106
|
| `--force` | boolean | 忽略上次运行结果,不跳过已通过的 (experiment, eval) 组合,强制全部重跑。 |
|
|
107
107
|
| `--strict` | boolean | CI 中推荐使用:让软阈值(`soft`)失败也计入整条 eval 的 verdict。 |
|
|
108
|
-
| `--early-exit` / `--no-early-exit` | boolean | 某个 eval 的一次 attempt 通过后,停止该 eval 剩余的 attempts。 |
|
|
108
|
+
| `--early-exit` / `--no-early-exit` | boolean | 某个 eval 的一次 attempt 通过后,停止该 eval 剩余的 attempts;省略默认关(`runs` 默认跑满、测完整通过率)。 |
|
|
109
109
|
| `--open` / `--no-open` | boolean | `view` 命令专用:启动后自动打开浏览器(默认行为)。 |
|
|
110
110
|
| `--help` | boolean | 打印用法说明并退出。 |
|
|
111
111
|
| `--version` | boolean | 打印 niceeval 的版本号并退出。 |
|
|
@@ -174,7 +174,7 @@ npx niceeval show weather/brooklyn --history
|
|
|
174
174
|
|
|
175
175
|
## `--early-exit` 与 `--strict`
|
|
176
176
|
|
|
177
|
-
`--early-exit`
|
|
177
|
+
`--early-exit` 默认关闭:`--runs` > 1 时默认把每次 attempt 都跑完,给出真实通过率——这是 NiceEval 衡量 agent 稳不稳的核心指标,默认不该被无声截断。只想知道"这题能不能过"、不在乎完整分布时,显式加 `--early-exit`:某个评估用例的一次 attempt 通过后,自动停止该评估用例剩余的 attempts(省钱)。实验文件里写了 `earlyExit: true` 时,用 `--no-early-exit` 强制关掉它。
|
|
178
178
|
|
|
179
179
|
`--strict` 不是"更严格地报错",而是改变软阈值断言的判定:`.atLeast(n)` 这类软阈值(`soft` severity)平时失败不会把整条评估用例判为 `failed`(只是记一条不达标的断言),加了 `--strict` 之后,软阈值没达标也会让整条评估用例的 verdict 计为 `failed`。CI 中推荐加上,避免"断言分数不够但评估用例显示通过"的情况被放过。
|
|
180
180
|
|
|
@@ -152,6 +152,8 @@ description: "报告文件里能摆的全部官方双面组件:每个组件回
|
|
|
152
152
|
|
|
153
153
|
每个 experiment 的评估用例集合来自快照记录的 `selectedEvalIds`。网页面与终端面都显示完整 Scope;需要子集时用 `--exp` 收窄,或在自定义报告里对 Scope 先 `.filter()` 再传给 `input`。
|
|
154
154
|
|
|
155
|
+
实验列表行的显示名不需要额外配置:experiment id 共享公共目录前缀时(比如都在 `compare/` 下),行标签自动缩成各自的最短唯一后缀,不显示重复的前缀;末段撞名的 id 会自动加长到能互相区分为止。完整 id 始终是排序、过滤和折叠展开用的身份键,只是显示文字变短了。
|
|
156
|
+
|
|
155
157
|
组卡使用 `Pass rate / 通过率`、`Experiments / 实验`、`Evals / Eval`、`Attempts / Attempt`、`Eval results / Eval 结果`、`Total cost / 总成本` 这套字段标签,不在标签里重复“数”“次”或“计票”。时间显示为本地化到分钟的 `Last run / 最近运行` 或 `Run range / 运行范围`,不直接暴露 ISO 字符串;成本数据覆盖不全时写明“`63/72 次有成本数据`”,不显示没有上下文的 `63/72`。六项 KPI 在宽卡片保持同一行,空间不足时按三项或两项一组换行,避免总成本单独掉到下一行。
|
|
156
158
|
|
|
157
159
|
下面的 `MetricScatter`、`ScopeSummary` 和 `ExperimentList` 同样忠实消费调用方传入的数据,不推导隐藏范围。它等价于把三个组件按下面这样手工摆放——想自定义顺序或搭配其它组件时,照这个形状写:
|
|
@@ -194,7 +196,7 @@ const CompareSummary = defineComponent((_props, ctx) => (
|
|
|
194
196
|
|
|
195
197
|
每项固定代表一个 experiment。主行显示 experiment id、agent、model、flags、评估用例判定构成、通过率、Tokens、成本和耗时。默认 `ExperimentComparison` 把当前 Scope 的全部条目交给它;组件本身不猜边界。
|
|
196
198
|
|
|
197
|
-
|
|
199
|
+
行标签默认缩成 experiment id 在当前列表里的最短唯一后缀——末段唯一就只显示末段,撞名的 id 各自向前多取一段直到能区分为止(与 `MetricScatter` 散点的点标签同一算法)。排序、过滤和折叠展开始终用完整 id,不受显示名影响。中文副行用“`8 个 Eval`”而不是“`8 道题`”。
|
|
198
200
|
|
|
199
201
|
```tsx
|
|
200
202
|
<ExperimentList filter />
|
|
@@ -25,8 +25,7 @@ export default defineExperiment({
|
|
|
25
25
|
description: "gpt-5.4: 对比模型",
|
|
26
26
|
agent: webAgent({ baseUrl: "http://127.0.0.1:5188" }),
|
|
27
27
|
model: "gpt-5.4", // 单个字符串;另一个模型就复制一份文件改这一行
|
|
28
|
-
runs: 2,
|
|
29
|
-
earlyExit: true,
|
|
28
|
+
runs: 2, // earlyExit 默认关,跑满 2 次才能比较 pass rate
|
|
30
29
|
});
|
|
31
30
|
```
|
|
32
31
|
|
|
@@ -9,21 +9,24 @@ description: "用 niceeval show 在终端按 @<locator> 查看 Attempt 的评估
|
|
|
9
9
|
## 控制台输出
|
|
10
10
|
|
|
11
11
|
```text
|
|
12
|
-
|
|
12
|
+
╭─ PLAN ─────────────────────────────────────────────────────────────────────────╮
|
|
13
|
+
│ 45 attempts · 9 evals × 5 configs · concurrency 19 │
|
|
14
|
+
│ 6 of 45 carried in from cache · 39 to run │
|
|
15
|
+
╰────────────────────────────────────────────────────────────────────────────────╯
|
|
13
16
|
✗ @12h8m4k1 fixtures/button [compare/claude-e2b] errored · sandbox.create
|
|
14
17
|
sandbox-rate-limit: E2B sandbox allocation failed after 5 attempts
|
|
15
18
|
Inspect: niceeval show @12h8m4k1
|
|
16
19
|
|
|
17
|
-
niceeval exp compare
|
|
18
|
-
45 total · 6 reused · 19 running · 12 queued · 8 completed
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
● memory/agent-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
╭─ niceeval exp compare ──────────────────────────────────────────────── 2m 14s ─╮
|
|
21
|
+
│ 45 total · 6 reused · 19 running · 12 queued · 8 completed │
|
|
22
|
+
├─ ACTIVE ───────────────────────────────────────────────────────────────────────┤
|
|
23
|
+
│ ● memory/agent-029-use-cache compare/bub-e2b 1m 42s running tests │
|
|
24
|
+
│ ● memory/agent-030-app-route compare/codex 1m 18s editing src/app.ts │
|
|
25
|
+
│ … 17 more active │
|
|
26
|
+
╰──────────────────────────────────────────────────────────────────────── $0.84 ─╯
|
|
24
27
|
```
|
|
25
28
|
|
|
26
|
-
|
|
29
|
+
在终端里跑的时候,运行中的那个框会原位刷新总数和 active slots,不把历史帧推进 scrollback;失败、错误和去重后的 diagnostic 以无框行留下来,都带 locator。输出被管道接走或终端太窄时,同样的内容会退化成不带框的纯文本。跑完框会换成结论、失败清单和下一步命令三块。完整的运行、读取、修复与重跑流程见 [Coding Agent 反馈闭环](/zh/tutorials/agent-feedback-loop)。
|
|
27
30
|
|
|
28
31
|
## `.niceeval/<experiment>/<快照>/`
|
|
29
32
|
|
|
@@ -38,7 +38,6 @@ export default defineExperiment({
|
|
|
38
38
|
promptVariant: "v1",
|
|
39
39
|
},
|
|
40
40
|
runs: 1,
|
|
41
|
-
earlyExit: true,
|
|
42
41
|
budget: 5,
|
|
43
42
|
});
|
|
44
43
|
```
|
|
@@ -58,7 +57,6 @@ export default defineExperiment({
|
|
|
58
57
|
promptVariant: "v1",
|
|
59
58
|
},
|
|
60
59
|
runs: 1,
|
|
61
|
-
earlyExit: true,
|
|
62
60
|
budget: 5,
|
|
63
61
|
});
|
|
64
62
|
```
|
|
@@ -120,7 +118,7 @@ npx niceeval exp prompts/concise
|
|
|
120
118
|
| `reasoningEffort` | 单个推理努力程度(如 `"high"`),经 `ctx.reasoningEffort` / `t.reasoningEffort` 透传,归属与 `model` 一致 |
|
|
121
119
|
| `flags` | 实验条件(A/B 里的 feature flag),任意 JSON 对象,经 `ctx.flags` / `t.flags` 透传 |
|
|
122
120
|
| `runs` | 每个评估用例 × 配置最多跑几次 |
|
|
123
|
-
| `earlyExit` |
|
|
121
|
+
| `earlyExit` | 多次运行里通过一次就提前停止;默认关,`runs` 默认跑满测完整通过率 |
|
|
124
122
|
| `evals` | `"*"`、id 前缀数组,或遍历只读 eval 描述并返回 boolean 的函数 |
|
|
125
123
|
| `timeoutMs` | 单个 attempt 的超时 |
|
|
126
124
|
| `budget` | 这一格配置的预算上限 |
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "niceeval",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.2",
|
|
4
4
|
"description": "Agent-native eval tool — eval agents, services, functions, and coding-agent fixtures",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
|
-
"url": "https://github.com/
|
|
9
|
+
"url": "git+https://github.com/NiceEval/NiceEval.git"
|
|
10
10
|
},
|
|
11
11
|
"engines": {
|
|
12
12
|
"node": ">=18"
|
|
@@ -105,14 +105,13 @@
|
|
|
105
105
|
"clsx": "^2.1.1",
|
|
106
106
|
"dockerode": "^4.0.2",
|
|
107
107
|
"e2b": "^2.31.0",
|
|
108
|
-
"jsdom": "^29.1.1",
|
|
109
108
|
"lucide-react": "^1.21.0",
|
|
110
109
|
"mixpanel-browser": "^2.80.0",
|
|
111
110
|
"next": "16.2.10",
|
|
112
111
|
"prism-react-renderer": "^2.4.1",
|
|
113
112
|
"react": "^19.2.7",
|
|
114
113
|
"react-dom": "^19.2.7",
|
|
115
|
-
"react-grab": "^0.1.
|
|
114
|
+
"react-grab": "^0.1.48",
|
|
116
115
|
"shiki": "^4.3.0",
|
|
117
116
|
"tailwind-merge": "^3.6.0",
|
|
118
117
|
"tailwindcss": "^4.3.1",
|
package/src/cli.ts
CHANGED
|
@@ -190,8 +190,9 @@ const FLAG_OPTIONS = {
|
|
|
190
190
|
force: { type: "boolean" },
|
|
191
191
|
/** CI 中推荐使用:让软阈值(`soft`)失败也计入整条 eval 的 verdict。 */
|
|
192
192
|
strict: { type: "boolean" },
|
|
193
|
-
/** 某个 eval 的一次 attempt 通过后,停止该 eval 剩余的 attempts。 */
|
|
193
|
+
/** 某个 eval 的一次 attempt 通过后,停止该 eval 剩余的 attempts;省略默认关(`runs` 默认跑满、测完整通过率)。 */
|
|
194
194
|
"early-exit": { type: "boolean" },
|
|
195
|
+
/** 强制关闭首过即停,即使实验文件里写了 `earlyExit: true`。 */
|
|
195
196
|
"no-early-exit": { type: "boolean" },
|
|
196
197
|
/** `view` 命令专用:启动后自动打开浏览器(默认行为)。 */
|
|
197
198
|
open: { type: "boolean" },
|
|
@@ -695,7 +696,7 @@ async function main(): Promise<void> {
|
|
|
695
696
|
reasoningEffort: exp.reasoningEffort,
|
|
696
697
|
flags: exp.flags ?? {},
|
|
697
698
|
runs: flags.runs ?? envNumber("NICEEVAL_RUNS") ?? exp.runs ?? 1,
|
|
698
|
-
earlyExit: flags.earlyExit ?? exp.earlyExit ??
|
|
699
|
+
earlyExit: flags.earlyExit ?? exp.earlyExit ?? false,
|
|
699
700
|
sandbox: exp.sandbox ?? config.sandbox,
|
|
700
701
|
timeoutMs: flags.timeout ?? envNumber("NICEEVAL_TIMEOUT") ?? exp.timeoutMs ?? config.timeoutMs,
|
|
701
702
|
budget: flags.budget ?? envNumber("NICEEVAL_BUDGET") ?? exp.budget,
|
|
@@ -709,6 +710,7 @@ async function main(): Promise<void> {
|
|
|
709
710
|
// 不再取所有选中实验的最小值钳全局——那会让一个串行实验拖慢整批基线。
|
|
710
711
|
maxConcurrency: exp.maxConcurrency,
|
|
711
712
|
setup: exp.setup,
|
|
713
|
+
teardown: exp.teardown,
|
|
712
714
|
});
|
|
713
715
|
}
|
|
714
716
|
} else {
|