niceeval 0.12.0 → 0.12.1-canary.22
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.
|
@@ -42,6 +42,13 @@ description: "按被测对象选择可运行的 NiceEval 示例:Agent Framewor
|
|
|
42
42
|
- [查看在线报告](https://niceeval.com/showcase/memory)
|
|
43
43
|
- [查看项目源码](https://github.com/CorrectRoadH/coding-agent-memory-evals)
|
|
44
44
|
|
|
45
|
+
### Terminal-Bench
|
|
46
|
+
|
|
47
|
+
[terminal-bench](https://github.com/NiceEval/terminal-bench) 用 NiceEval 跑经过审核的 Harbor Terminal-Bench 题包,判分仍走原版 `run-tests.sh`,并把各 coding agent 与模型并排比较。
|
|
48
|
+
|
|
49
|
+
- [查看在线报告](https://niceeval.com/showcase/terminal-bench)
|
|
50
|
+
- [查看项目源码](https://github.com/NiceEval/terminal-bench)
|
|
51
|
+
|
|
45
52
|
## Examples 的收录边界
|
|
46
53
|
|
|
47
54
|
- 单段 API 用法放进任务教程或 Reference。
|
package/package.json
CHANGED
|
@@ -186,14 +186,24 @@ describe("acceptPreparedAttempt", () => {
|
|
|
186
186
|
roots.push(root);
|
|
187
187
|
const currentManifest = { algorithmVersion: 2, coverageVersion: 1, config: {}, source: {}, data: {} };
|
|
188
188
|
const sources = [makeSource(root, { id: "e" }), makeSource(root, { id: "f" })];
|
|
189
|
+
// 模拟 prepareAcceptTarget 的真实形状:每条 locator 的 currentExperiment.selectedEvalIds
|
|
190
|
+
// 只有自己那一题(cliPatterns 单 id),sandboxPlansByEval 也只有自己。
|
|
189
191
|
const prepared = await Promise.all(sources.map((source) => prepareAcceptedAttempt({
|
|
190
192
|
recordRoot: root,
|
|
191
193
|
source,
|
|
192
194
|
pair: makePair({}, source.evalId),
|
|
193
195
|
currentFingerprint: `current-${source.evalId}`,
|
|
194
196
|
currentManifest,
|
|
195
|
-
currentConfigHash:
|
|
196
|
-
|
|
197
|
+
currentConfigHash: "shared-config",
|
|
198
|
+
currentExperiment: {
|
|
199
|
+
attempts: 1,
|
|
200
|
+
earlyExit: true,
|
|
201
|
+
selectedEvalIds: [source.evalId],
|
|
202
|
+
sandboxLayer: {},
|
|
203
|
+
sandboxPlansByEval: { [source.evalId]: { plan: source.evalId } },
|
|
204
|
+
agentInstalls: [],
|
|
205
|
+
},
|
|
206
|
+
knownEvalIds: [source.evalId],
|
|
197
207
|
now: () => "2026-01-02T00:00:00.000Z",
|
|
198
208
|
})));
|
|
199
209
|
|
|
@@ -206,6 +216,22 @@ describe("acceptPreparedAttempt", () => {
|
|
|
206
216
|
accepted.map((entry) => entry.sourceLocator),
|
|
207
217
|
);
|
|
208
218
|
expect(accepted[0]!.record.experiments[0]!.runs).toHaveLength(1);
|
|
219
|
+
|
|
220
|
+
// 快照级覆盖声明必须是整组,不能只剩 groupFirst 的单题——否则 currentSample / view 塌成 1 题。
|
|
221
|
+
const runMeta = JSON.parse(
|
|
222
|
+
await readFile(join(accepted[0]!.run.dir, "run.json"), "utf-8"),
|
|
223
|
+
) as {
|
|
224
|
+
experiment?: { selectedEvalIds?: string[]; sandboxPlansByEval?: globalThis.Record<string, unknown> };
|
|
225
|
+
knownEvalIds?: string[];
|
|
226
|
+
};
|
|
227
|
+
expect(runMeta.experiment?.selectedEvalIds?.slice().sort()).toEqual(["e", "f"]);
|
|
228
|
+
expect(Object.keys(runMeta.experiment?.sandboxPlansByEval ?? {}).sort()).toEqual(["e", "f"]);
|
|
229
|
+
expect(runMeta.knownEvalIds?.slice().sort()).toEqual(["e", "f"]);
|
|
230
|
+
|
|
231
|
+
// 读面:currentSample 必须看到两条,不能按错误收窄的 selectedEvalIds 只留第一条。
|
|
232
|
+
const { currentSample } = await import("../sample/index.ts");
|
|
233
|
+
const sample = currentSample(accepted[0]!.record);
|
|
234
|
+
expect(sample.attempts.map((a) => a.evalId).sort()).toEqual(["e", "f"]);
|
|
209
235
|
});
|
|
210
236
|
|
|
211
237
|
it("批量 prepare 中任一条失败时不创建 snapshot", async () => {
|
package/src/runner/accept.ts
CHANGED
|
@@ -520,7 +520,7 @@ export async function writeAcceptedAttempts(
|
|
|
520
520
|
// 返回值必须按调用方传入的 preparedAttempts 顺序还原;分组打乱了处理顺序,用引用做索引。
|
|
521
521
|
const locatorByPrepared = new Map<PreparedAcceptedAttempt, AttemptLocator>();
|
|
522
522
|
for (const [experimentId, group] of groups) {
|
|
523
|
-
// 快照级字段(agent/model/configHash/
|
|
523
|
+
// 快照级字段(agent/model/configHash/name)按本组取,不再从全批 first 拿——
|
|
524
524
|
// 否则跨 experiment 批次会把另一个 experiment 的身份写进这个 experiment 的 run.json。
|
|
525
525
|
const groupFirst = group[0]!;
|
|
526
526
|
// manifests 是这个 experiment 自己的袋子;不同 experiment 各自独立,不共享同一个对象,
|
|
@@ -529,15 +529,21 @@ export async function writeAcceptedAttempts(
|
|
|
529
529
|
const knownEvalIds = new Set<string>();
|
|
530
530
|
for (const prepared of group) {
|
|
531
531
|
manifests[prepared.source.evalId] = prepared.currentManifest;
|
|
532
|
+
knownEvalIds.add(prepared.source.evalId);
|
|
532
533
|
for (const evalId of prepared.knownEvalIds ?? []) knownEvalIds.add(evalId);
|
|
533
534
|
}
|
|
535
|
+
// prepare 阶段每条 locator 单独按「只选中自己那一题」重算指纹,currentExperiment.selectedEvalIds
|
|
536
|
+
// 因此只有一个 id。快照级覆盖声明必须是本 experiment 组**全部**接受的题——currentSample
|
|
537
|
+
// 按 selectedEvalIds 过滤贡献范围,只写 groupFirst 会让批量 accept 的 view/show 塌成 1 题
|
|
538
|
+
// (MemoryBench: 36 条 result.json 在盘、首页 1/36)。
|
|
539
|
+
const experiment = experimentForAcceptedGroup(group);
|
|
534
540
|
const snapshot = await writer.run({
|
|
535
541
|
experimentId,
|
|
536
542
|
agent: groupFirst.pair.run.agent.name,
|
|
537
543
|
...(groupFirst.pair.run.model !== undefined ? { model: groupFirst.pair.run.model } : {}),
|
|
538
544
|
startedAt: now,
|
|
539
545
|
configHash: groupFirst.currentConfigHash,
|
|
540
|
-
...(
|
|
546
|
+
...(experiment === undefined ? {} : { experiment }),
|
|
541
547
|
...(knownEvalIds.size === 0 ? {} : { knownEvalIds: [...knownEvalIds] }),
|
|
542
548
|
manifests,
|
|
543
549
|
...(groupFirst.name === undefined ? {} : { name: groupFirst.name }),
|
|
@@ -570,6 +576,39 @@ export async function writeAcceptedAttempts(
|
|
|
570
576
|
});
|
|
571
577
|
}
|
|
572
578
|
|
|
579
|
+
/**
|
|
580
|
+
* 把同 experiment 一批 accept 的覆盖声明合成快照级 `ExperimentRunInfo`。
|
|
581
|
+
* 单条 prepare 的 `currentExperiment.selectedEvalIds` / `sandboxPlansByEval` 只含自己那题
|
|
582
|
+
* (指纹重算需要);封口时必须扩成整组,否则 Sample 读面按 selectedEvalIds 过滤会丢掉其余题。
|
|
583
|
+
*/
|
|
584
|
+
function experimentForAcceptedGroup(
|
|
585
|
+
group: readonly PreparedAcceptedAttempt[],
|
|
586
|
+
): EvalResult["experiment"] | undefined {
|
|
587
|
+
const bases = group
|
|
588
|
+
.map((prepared) => prepared.currentExperiment)
|
|
589
|
+
.filter((experiment): experiment is NonNullable<typeof experiment> => experiment !== undefined);
|
|
590
|
+
const base = bases[0];
|
|
591
|
+
if (base === undefined) return undefined;
|
|
592
|
+
|
|
593
|
+
const selectedEvalIds = new Set<string>(base.selectedEvalIds);
|
|
594
|
+
const sandboxPlansByEval: globalThis.Record<string, JsonValue> = {
|
|
595
|
+
...base.sandboxPlansByEval,
|
|
596
|
+
};
|
|
597
|
+
for (const prepared of group) {
|
|
598
|
+
selectedEvalIds.add(prepared.source.evalId);
|
|
599
|
+
const plans = prepared.currentExperiment?.sandboxPlansByEval;
|
|
600
|
+
if (plans === undefined) continue;
|
|
601
|
+
for (const [evalId, plan] of Object.entries(plans)) {
|
|
602
|
+
sandboxPlansByEval[evalId] = plan;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
return {
|
|
606
|
+
...base,
|
|
607
|
+
selectedEvalIds: [...selectedEvalIds],
|
|
608
|
+
sandboxPlansByEval,
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
|
|
573
612
|
function acceptedResultFor(
|
|
574
613
|
prepared: PreparedAcceptedAttempt,
|
|
575
614
|
locator: AttemptLocator,
|