niceeval 0.11.1 → 0.11.3
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 +1 -0
- package/README.md +4 -2
- package/README.zh.md +4 -2
- package/dist/agents/types.d.ts +2 -2
- package/dist/o11y/types.d.ts +3 -3
- package/dist/report/assets/colors.d.ts +1 -1
- package/dist/report/assets/colors.js +1 -1
- package/dist/report/components/attempt-detail/AttemptSource.js +1 -2
- package/dist/report/components/attempt-detail/compute.d.ts +4 -4
- package/dist/report/components/attempt-detail/compute.js +6 -6
- package/dist/report/components/attempt-detail/faces.d.ts +1 -1
- package/dist/report/components/attempt-detail/faces.js +3 -3
- package/dist/report/components/entity-lists/AttemptList.d.ts +3 -3
- package/dist/report/components/entity-lists/AttemptList.js +3 -3
- package/dist/report/components/entity-lists/ExperimentList.js +2 -2
- package/dist/report/components/entity-lists/compute.js +1 -1
- package/dist/report/components/entity-lists/faces.js +1 -1
- package/dist/report/components/entity-lists/index.d.ts +1 -1
- package/dist/report/components/entity-lists/index.js +1 -1
- package/dist/report/components/metric-views/chart-math.d.ts +2 -2
- package/dist/report/components/metric-views/chart-math.js +2 -2
- package/dist/report/components/metric-views/compute.d.ts +2 -3
- package/dist/report/components/metric-views/compute.js +3 -4
- package/dist/report/components/shared.d.ts +1 -1
- package/dist/report/components/site-components/compute.d.ts +5 -5
- package/dist/report/components/site-components/compute.js +6 -6
- package/dist/report/components/site-components/index.d.ts +7 -7
- package/dist/report/components/site-components/index.js +7 -7
- package/dist/report/components/site-components/scope-warnings.d.ts +1 -1
- package/dist/report/components/site-components/scope-warnings.js +3 -3
- package/dist/report/components/site-components/snapshot-diagnostics.js +1 -1
- package/dist/report/components/summaries/compute.d.ts +1 -1
- package/dist/report/components/summaries/compute.js +2 -2
- package/dist/report/components/summaries/faces.js +1 -1
- package/dist/report/components/summaries/index.d.ts +1 -1
- package/dist/report/components/summaries/index.js +1 -1
- package/dist/report/index.js +1 -1
- package/dist/report/model/aggregate.d.ts +2 -2
- package/dist/report/model/aggregate.js +2 -2
- package/dist/report/model/format.d.ts +2 -2
- package/dist/report/model/format.js +2 -2
- package/dist/report/model/metrics.js +3 -3
- package/dist/report/model/types.d.ts +11 -12
- package/dist/results/locator.js +1 -1
- package/dist/results/select.d.ts +4 -4
- package/dist/results/select.js +5 -5
- package/dist/results/types.d.ts +8 -8
- package/dist/runner/types.d.ts +14 -14
- package/dist/sandbox/resolve.d.ts +1 -1
- package/dist/sandbox/types.d.ts +1 -1
- package/docs-site/zh/reference/builtin-agents.mdx +21 -0
- package/docs-site/zh/reference/define-agent.mdx +1 -1
- package/docs-site/zh/reference/report-components.mdx +156 -74
- package/docs-site/zh/tutorials/custom-reports.mdx +112 -22
- package/docs-site/zh/tutorials/sandbox-agent.mdx +3 -4
- package/docs-site/zh/tutorials/sandbox-providers.mdx +15 -15
- package/docs-site/zh/tutorials/theming.mdx +169 -0
- package/package.json +2 -1
- package/src/agents/ai-sdk.test.ts +1 -1
- package/src/agents/bub-install-spec.ts +26 -8
- package/src/agents/bub.ts +40 -15
- package/src/agents/builtin.ts +11 -1
- package/src/agents/coding-cli-versions.ts +67 -0
- package/src/agents/hermes.ts +235 -0
- package/src/agents/index.ts +4 -0
- package/src/agents/langgraph.test.ts +1 -1
- package/src/agents/manifest.ts +1 -1
- package/src/agents/openai-compat.test.ts +1 -1
- package/src/agents/openclaw.ts +103 -24
- package/src/agents/opencode.ts +183 -0
- package/src/agents/sdk-streams.test.ts +1 -1
- package/src/agents/shared.ts +13 -2
- package/src/agents/types.ts +2 -2
- package/src/context/session.test.ts +1 -1
- package/src/context/session.ts +1 -1
- package/src/o11y/cost.ts +1 -1
- package/src/o11y/parsers/bub.test.ts +1 -1
- package/src/o11y/parsers/bub.ts +1 -1
- package/src/o11y/parsers/codex.test.ts +1 -1
- package/src/o11y/parsers/codex.ts +1 -1
- package/src/o11y/parsers/hermes.ts +198 -0
- package/src/o11y/parsers/openclaw.ts +25 -3
- package/src/o11y/parsers/opencode.ts +295 -0
- package/src/o11y/types.ts +3 -3
- package/src/report/assets/colors.ts +1 -1
- package/src/report/assets/styles.css +17 -15
- package/src/report/components/attempt-detail/AttemptAssertions.tsx +1 -1
- package/src/report/components/attempt-detail/AttemptConversation.tsx +1 -1
- package/src/report/components/attempt-detail/AttemptDiagnostics.tsx +1 -1
- package/src/report/components/attempt-detail/AttemptDiff.tsx +1 -1
- package/src/report/components/attempt-detail/AttemptError.tsx +1 -1
- package/src/report/components/attempt-detail/AttemptFixPrompt.tsx +1 -1
- package/src/report/components/attempt-detail/AttemptSource.tsx +2 -3
- package/src/report/components/attempt-detail/AttemptSummary.tsx +1 -1
- package/src/report/components/attempt-detail/AttemptTimeline.tsx +1 -1
- package/src/report/components/attempt-detail/AttemptTrace.tsx +1 -1
- package/src/report/components/attempt-detail/UsageTable.tsx +1 -1
- package/src/report/components/attempt-detail/attempt-components.test.tsx +1 -1
- package/src/report/components/attempt-detail/compute.ts +6 -6
- package/src/report/components/attempt-detail/faces.ts +3 -3
- package/src/report/components/attempt-detail/index.tsx +1 -1
- package/src/report/components/entity-lists/AttemptList.tsx +3 -3
- package/src/report/components/entity-lists/ExperimentList.tsx +2 -2
- package/src/report/components/entity-lists/compute.ts +1 -1
- package/src/report/components/entity-lists/faces.ts +1 -1
- package/src/report/components/entity-lists/index.tsx +1 -1
- package/src/report/components/metric-views/DeltaTable.tsx +1 -2
- package/src/report/components/metric-views/StabilityMatrix.tsx +1 -1
- package/src/report/components/metric-views/chart-math.test.ts +1 -1
- package/src/report/components/metric-views/chart-math.ts +2 -2
- package/src/report/components/metric-views/compute.ts +3 -4
- package/src/report/components/shared.ts +1 -1
- package/src/report/components/site-components/CopyFixPrompt.tsx +1 -1
- package/src/report/components/site-components/HeroCard.tsx +1 -1
- package/src/report/components/site-components/PoweredBy.tsx +1 -1
- package/src/report/components/site-components/ScopeWarnings.tsx +1 -1
- package/src/report/components/site-components/SnapshotDiagnostics.tsx +1 -1
- package/src/report/components/site-components/TraceWaterfall.tsx +1 -1
- package/src/report/components/site-components/compute.ts +6 -6
- package/src/report/components/site-components/index.tsx +7 -7
- package/src/report/components/site-components/scope-warnings.ts +3 -3
- package/src/report/components/site-components/snapshot-diagnostics.ts +1 -1
- package/src/report/components/summaries/ScopeSummary.tsx +2 -2
- package/src/report/components/summaries/compute.ts +2 -2
- package/src/report/components/summaries/faces.ts +1 -1
- package/src/report/components/summaries/index.tsx +1 -1
- package/src/report/index.ts +1 -1
- package/src/report/model/aggregate.ts +2 -2
- package/src/report/model/format.ts +2 -2
- package/src/report/model/metrics.ts +3 -3
- package/src/report/model/types.ts +12 -13
- package/src/results/annotated-source.test.ts +1 -1
- package/src/results/attempt-evidence.test.ts +1 -1
- package/src/results/copy.ts +3 -3
- package/src/results/format.ts +3 -3
- package/src/results/host-equivalence.test.ts +6 -6
- package/src/results/index.ts +1 -1
- package/src/results/locator.test.ts +2 -2
- package/src/results/locator.ts +1 -1
- package/src/results/open.ts +2 -2
- package/src/results/publish.ts +1 -1
- package/src/results/results.test.ts +6 -323
- package/src/results/select.test.ts +438 -0
- package/src/results/select.ts +6 -6
- package/src/results/skipped-notice.ts +1 -1
- package/src/results/truncate.ts +2 -2
- package/src/results/types.ts +9 -9
- package/src/results/writer.ts +4 -4
- package/src/runner/attempt.test.ts +2 -2
- package/src/runner/attempt.ts +6 -6
- package/src/runner/eval-source.test.ts +1 -1
- package/src/runner/eval-source.ts +1 -1
- package/src/runner/reporters/artifacts.ts +1 -1
- package/src/runner/run.ts +2 -2
- package/src/runner/timing.ts +1 -1
- package/src/runner/types.ts +14 -14
- package/src/sandbox/docker-agent-image.ts +36 -0
- package/src/sandbox/e2b-agent-template.test.ts +6 -21
- package/src/sandbox/e2b-agent-template.ts +44 -11
- package/src/sandbox/index.ts +8 -0
- package/src/sandbox/official-baselines.test.ts +175 -0
- package/src/sandbox/resolve.ts +1 -1
- package/src/sandbox/types.ts +1 -1
- package/src/scoring/diff.ts +1 -1
- package/src/scoring/types.ts +1 -1
- package/src/shared/facts.ts +1 -1
- package/src/show/index.ts +1 -1
- package/src/show/render.ts +1 -1
- package/src/show/show.test.ts +3 -3
- package/src/view/app/App.tsx +3 -1
- package/src/view/client-dist/app.css +1 -1
- package/src/view/client-dist/app.js +1 -1
- package/src/view/data.test.ts +2 -2
- package/src/view/data.ts +1 -1
- package/src/view/site-base.test.ts +51 -0
- package/src/view/site.ts +16 -0
- package/src/view/styles.css +1 -2
- package/src/view/template.html +1 -0
- package/src/view/view-report.test.ts +1 -1
|
@@ -64,11 +64,11 @@ export default defineReport(
|
|
|
64
64
|
<Col>
|
|
65
65
|
<ExperimentComparison />
|
|
66
66
|
<Section title="考试成绩单">
|
|
67
|
-
<Scoreboard
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
<Scoreboard score={examScore}>
|
|
68
|
+
<Rows dimension="agent" />
|
|
69
|
+
<Question id="security/sql-injection" />
|
|
70
|
+
<Question id="correctness/retry" />
|
|
71
|
+
</Scoreboard>
|
|
72
72
|
</Section>
|
|
73
73
|
</Col>,
|
|
74
74
|
);
|
|
@@ -146,8 +146,16 @@ niceeval view --report ./reports/exam.tsx # 本次换成另一份文件
|
|
|
146
146
|
|
|
147
147
|
```tsx
|
|
148
148
|
<Row>
|
|
149
|
-
<Scoreboard
|
|
150
|
-
|
|
149
|
+
<Scoreboard score={examScore}>
|
|
150
|
+
<Rows dimension="agent" />
|
|
151
|
+
<Question id="security/sql-injection" />
|
|
152
|
+
<Question id="correctness/retry" />
|
|
153
|
+
</Scoreboard>
|
|
154
|
+
<MetricTable>
|
|
155
|
+
<Rows dimension="agent" />
|
|
156
|
+
<Column metric={endToEndPassRate} />
|
|
157
|
+
<Column metric={costUSD} />
|
|
158
|
+
</MetricTable>
|
|
151
159
|
</Row>
|
|
152
160
|
```
|
|
153
161
|
|
|
@@ -218,11 +226,12 @@ const changedLines = defineMetric({
|
|
|
218
226
|
});
|
|
219
227
|
|
|
220
228
|
export default defineReport(
|
|
221
|
-
<MetricTable
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
229
|
+
<MetricTable>
|
|
230
|
+
<Rows dimension="agent" sort={endToEndPassRate} />
|
|
231
|
+
<Column metric={endToEndPassRate} />
|
|
232
|
+
<Column metric={changedLines} />
|
|
233
|
+
<Column metric={costUSD} />
|
|
234
|
+
</MetricTable>,
|
|
226
235
|
);
|
|
227
236
|
```
|
|
228
237
|
|
|
@@ -264,7 +273,11 @@ const vendor: CustomDimension = {
|
|
|
264
273
|
};
|
|
265
274
|
|
|
266
275
|
export default defineReport(
|
|
267
|
-
<MetricTable
|
|
276
|
+
<MetricTable>
|
|
277
|
+
<Rows dimension={vendor} />
|
|
278
|
+
<Column metric={endToEndPassRate} />
|
|
279
|
+
<Column metric={costUSD} />
|
|
280
|
+
</MetricTable>,
|
|
268
281
|
);
|
|
269
282
|
```
|
|
270
283
|
|
|
@@ -289,16 +302,23 @@ export default defineExperiment({
|
|
|
289
302
|
});
|
|
290
303
|
```
|
|
291
304
|
|
|
292
|
-
|
|
305
|
+
分组用不加 `numeric` 前缀的版本按声明值分类;当坐标轴用的变量(`<XAxis numeric={...} />`)必须用 `numericFlag()` / `numericLabel()` / `numericRunConfig()`,因为刻度要求真实数值:
|
|
293
306
|
|
|
294
307
|
```tsx
|
|
295
|
-
import {
|
|
308
|
+
import {
|
|
309
|
+
Line, LineChart, XAxis, YAxis,
|
|
310
|
+
defineReport, endToEndPassRate, flag, numericFlag,
|
|
311
|
+
} from "niceeval/report";
|
|
296
312
|
|
|
297
313
|
const agents = flag("agents", { label: "并行 agent 数" });
|
|
298
314
|
const latency = numericFlag("latencyMs", { label: "Simulated latency", unit: "ms" });
|
|
299
315
|
|
|
300
316
|
export default defineReport(
|
|
301
|
-
<
|
|
317
|
+
<LineChart>
|
|
318
|
+
<XAxis numeric={latency} />
|
|
319
|
+
<YAxis metric={endToEndPassRate} />
|
|
320
|
+
<Line metric={endToEndPassRate} by={agents} />
|
|
321
|
+
</LineChart>,
|
|
302
322
|
);
|
|
303
323
|
```
|
|
304
324
|
|
|
@@ -326,11 +346,6 @@ const CostBoard = defineComponent(async (_props: {}, ctx) => {
|
|
|
326
346
|
});
|
|
327
347
|
return (
|
|
328
348
|
<Table
|
|
329
|
-
columns={[
|
|
330
|
-
{ key: "agent", header: "Agent" },
|
|
331
|
-
{ key: "pass", header: "通过率", align: "right" },
|
|
332
|
-
{ key: "cost", header: "成本", align: "right" },
|
|
333
|
-
]}
|
|
334
349
|
rows={board.rows.map((r) => ({
|
|
335
350
|
key: r.key,
|
|
336
351
|
cells: {
|
|
@@ -340,7 +355,11 @@ const CostBoard = defineComponent(async (_props: {}, ctx) => {
|
|
|
340
355
|
cost: r.cells[costUSD.name].value === null ? null : r.cells[costUSD.name].display,
|
|
341
356
|
},
|
|
342
357
|
}))}
|
|
343
|
-
|
|
358
|
+
>
|
|
359
|
+
<Column key="agent" header="Agent" />
|
|
360
|
+
<Column key="pass" header="通过率" align="right" />
|
|
361
|
+
<Column key="cost" header="成本" align="right" />
|
|
362
|
+
</Table>
|
|
344
363
|
);
|
|
345
364
|
});
|
|
346
365
|
|
|
@@ -465,6 +484,77 @@ codex ████████████████░░░░ 80%
|
|
|
465
484
|
- **样式随树带走。** 静态导出不打包你的代码,`className` 引用的 CSS 用内置原语 `<Style>{css}</Style>` 放进页面树——web 面吐 `<style>` 标签,text 面渲染为空,上面的例子就是这么给 `.passbars` 上样式的。
|
|
466
485
|
- **诚实契约同样适用。** 缺数据渲染 `—` 不补 0,截断如实标注剩余数量——上面例子里 `克劳德` 没有样本就是 `—`。
|
|
467
486
|
|
|
487
|
+
### 让自己的组件和官方组件叫同一个名字、上同一个颜色
|
|
488
|
+
|
|
489
|
+
同一页里,`compare/with-memory` 在官方榜单上缩成 `with-memory`、配一个蓝色;自己写的组件要显示成同一个样子,用这两个出口,不要自己实现一遍:
|
|
490
|
+
|
|
491
|
+
```tsx
|
|
492
|
+
import { defineComponent, experimentListData, shortestUniqueLabels } from "niceeval/report";
|
|
493
|
+
|
|
494
|
+
const MyLegend = defineComponent<{ ids: string[] }>({
|
|
495
|
+
web({ ids }, ctx) {
|
|
496
|
+
const labels = shortestUniqueLabels(ids); // 整组一起传,不逐个调
|
|
497
|
+
return (
|
|
498
|
+
<ul>
|
|
499
|
+
{ids.map((id) => (
|
|
500
|
+
// 取色用完整 id(键),显示用缩写(名字)
|
|
501
|
+
<li key={id} className={ctx.seriesColor("experiment", id).className}>
|
|
502
|
+
{labels.get(id)}
|
|
503
|
+
</li>
|
|
504
|
+
))}
|
|
505
|
+
</ul>
|
|
506
|
+
);
|
|
507
|
+
},
|
|
508
|
+
text({ ids }) {
|
|
509
|
+
const labels = shortestUniqueLabels(ids);
|
|
510
|
+
return ids.map((id) => labels.get(id)).join(" "); // 终端不上色
|
|
511
|
+
},
|
|
512
|
+
});
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
`ctx.seriesColor(维度, 维度值)` 读的是这一页已经分好的色:官方图表的图例、实体列表的行、你这个组件,同一个 id 拿到的是同一个色槽。传进去的必须是完整的维度值,不是缩写后的显示名。
|
|
516
|
+
|
|
517
|
+
在自己的 React 页面里嵌 `niceeval/report/react` 组件时没有这一页,自己把要上色的键一次交给 `seriesColors(keys)` 拿映射——一次传全集,逐个键调用得不到同页消解撞色的结果。
|
|
518
|
+
|
|
519
|
+
### 钉住某个值的颜色
|
|
520
|
+
|
|
521
|
+
自动分配保证同一页里同一个值恒同色,但它不知道你的语义:发布图上「baseline 恒中性、我们的方案恒蓝」是你的判断。写在报告外壳的 `seriesPins` 里:
|
|
522
|
+
|
|
523
|
+
```tsx
|
|
524
|
+
export default defineReport({
|
|
525
|
+
title: "MemoryBench",
|
|
526
|
+
seriesPins: {
|
|
527
|
+
memory: { baseline: 3, mempal: 1, nowledge: 5 }, // 维度名 → 值 → 色板下标
|
|
528
|
+
},
|
|
529
|
+
content: <Leaderboard />,
|
|
530
|
+
});
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
钉的是六色色板的**下标**,不是颜色——所以深浅外观、对比度和[换主题](/zh/tutorials/theming)全都照常跟随;要改颜色本身,改的是主题的 `series` 六色。被钉住的值原样占位,其余值在剩下的槽里自动分配。
|
|
534
|
+
|
|
535
|
+
钉色写在报告里而不是主题里,是因为它说的是数据含义:换一套配色不该让基线和候选方案对调身份。它也是整站字段,所以同一个记忆机制在总览页和分科页必须同色。
|
|
536
|
+
|
|
537
|
+
### 让自己的组件跟随任何主题
|
|
538
|
+
|
|
539
|
+
自己写的组件要能装上任何一份主题——包括别人分发给你的那一份。颜色只有三个来源,没有第四条路:
|
|
540
|
+
|
|
541
|
+
| 要表达 | 用什么 |
|
|
542
|
+
|---|---|
|
|
543
|
+
| 好 / 坏 / 警告 | `Stat` 的 `tone`,或 `var(--nre-positive)` / `var(--nre-negative)` / `var(--nre-warning)` |
|
|
544
|
+
| 某个维度值的分类身份 | `ctx.seriesColor(维度, 维度值)` |
|
|
545
|
+
| 其余一切(文字、表面、分隔、圆角、字体) | `var(--nre-*)` 令牌 |
|
|
546
|
+
|
|
547
|
+
```css
|
|
548
|
+
.nre .acme-panel {
|
|
549
|
+
background: var(--nre-surface);
|
|
550
|
+
border: 1px solid var(--nre-border);
|
|
551
|
+
border-radius: var(--nre-radius); /* 零圆角主题下自动变直角 */
|
|
552
|
+
color: var(--nre-text);
|
|
553
|
+
}
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
三条纪律:写死的颜色值在深色分支会失灵、换主题不跟随;自己的 class 用自己的前缀,不占 `nre-`(那是主题瞄准的公开语义面);宽度、位置这类由数据算出来的几何量可以写成行内样式,颜色不行。令牌全集与更多写法见[给报告换主题](/zh/tutorials/theming)。
|
|
557
|
+
|
|
468
558
|
## 发布:导出即静态站
|
|
469
559
|
|
|
470
560
|
发布自定义报告和发布官方查看器是同一个动作——`--out` 加上 `--report`:
|
|
@@ -53,17 +53,16 @@ npx niceeval exp local fixtures/button --runs 10
|
|
|
53
53
|
```ts
|
|
54
54
|
import { codexAgent } from "niceeval/adapter";
|
|
55
55
|
import { e2bSandbox } from "niceeval/sandbox";
|
|
56
|
+
import { NICEEVAL_CODEX_E2B_TEMPLATE } from "niceeval/sandbox/e2b-template";
|
|
56
57
|
|
|
57
58
|
export default defineExperiment({
|
|
58
59
|
agent: codexAgent(),
|
|
59
60
|
model: "gpt-5.4",
|
|
60
|
-
sandbox: e2bSandbox({
|
|
61
|
-
template: "correctroads-default-team/niceeval-codex:v0.6.1",
|
|
62
|
-
}),
|
|
61
|
+
sandbox: e2bSandbox({ template: NICEEVAL_CODEX_E2B_TEMPLATE }),
|
|
63
62
|
});
|
|
64
63
|
```
|
|
65
64
|
|
|
66
|
-
Claude Code
|
|
65
|
+
Claude Code 用 `NICEEVAL_CLAUDE_CODE_E2B_TEMPLATE`,Bub 用 `NICEEVAL_BUB_E2B_TEMPLATE`。常量的值是完整、版本钉死的引用,版本跟着模板里那个 Agent 的版本走,你不用跟踪它。添加系统包、二进制或模型缓存的步骤见 [Sandbox Provider · 从官方基线继续构建以提速](/zh/tutorials/sandbox-providers#从官方基线继续构建以提速)。
|
|
67
66
|
|
|
68
67
|
内置 agent 从 `niceeval/adapter` 导出的是工厂函数。需要配置鉴权、代理、MCP 或 GitHub skill 时,把这些写进工厂参数;模型仍然写在 experiment 的 `model` 字段,sandbox provider 仍然写在 `sandbox` 字段:
|
|
69
68
|
|
|
@@ -154,10 +154,10 @@ Adapter 与 Sandbox 不互相猜配置:Adapter 负责检查所需 CLI,Sandbo
|
|
|
154
154
|
|
|
155
155
|
#### E2B:从官方与公共模板派生
|
|
156
156
|
|
|
157
|
-
E2B 已提供 Claude Code 的 `claude` template 和 Codex 的 `codex` template。NiceEval 提供一个 E2B 专属的薄封装,让你从这两个官方起点继续链原生 E2B API。E2B 暂无 Bub 官方 template,因此 Bub 分支使用 NiceEval
|
|
157
|
+
E2B 已提供 Claude Code 的 `claude` template 和 Codex 的 `codex` template。NiceEval 提供一个 E2B 专属的薄封装,让你从这两个官方起点继续链原生 E2B API。E2B 暂无 Bub 官方 template,因此 Bub 分支使用 NiceEval 钉死版本的安装配方(Bub 版本 + 同代 OTel 插件):
|
|
158
158
|
|
|
159
|
-
NiceEval 同时发布了三份任何 E2B Team 都能引用的公共模板。完整 namespace
|
|
160
|
-
|
|
159
|
+
NiceEval 同时发布了三份任何 E2B Team 都能引用的公共模板。完整 namespace 与版本由 NiceEval 自己维护,
|
|
160
|
+
下游直接取完整引用:
|
|
161
161
|
|
|
162
162
|
```ts
|
|
163
163
|
import {
|
|
@@ -171,9 +171,9 @@ e2bSandbox({ template: NICEEVAL_CODEX_E2B_TEMPLATE })
|
|
|
171
171
|
e2bSandbox({ template: NICEEVAL_BUB_E2B_TEMPLATE })
|
|
172
172
|
```
|
|
173
173
|
|
|
174
|
-
|
|
174
|
+
这些基线都实际启动验证过:Claude Code `2.1.207`、Codex `0.144.1`、Bub `0.4.0`(安装指纹随版本与 OTel 插件 pin 一起变)。模板的版本号跟着它装的那个 Agent 走(形如 `0.144.1-r1`:Agent 版本 + NiceEval 配方修订号),三个 Agent 各自独立发版,NiceEval 库自己的版本不参与命名。所以业务仓库直接用常量,不复制这些字符串、也不跟踪版本;派生模板需要记录 base 身份时,同样用常量的值。
|
|
175
175
|
|
|
176
|
-
|
|
176
|
+
当前这批公共模板中,Claude Code 模板沿用 E2B 官方 `/usr` npm prefix,普通用户不能直接写全局模块。需要在运行时追加 pnpm、yarn 等全局 Node 工具时,显式把它们装到两个模板都已加入 PATH 的 `/usr/local`:
|
|
177
177
|
|
|
178
178
|
```ts
|
|
179
179
|
await t.sandbox.runCommand("npm", [
|
|
@@ -194,7 +194,7 @@ const template = e2bCodingAgentTemplate("codex")
|
|
|
194
194
|
.runCmd("corepack enable && pnpm --version")
|
|
195
195
|
.copy("fixtures/toolchain.lock", "/opt/evals/toolchain.lock");
|
|
196
196
|
|
|
197
|
-
await Template.build(template, "acme-codex-evals:
|
|
197
|
+
await Template.build(template, "acme-codex-evals:0.144.1-r1", {
|
|
198
198
|
cpuCount: 2,
|
|
199
199
|
memoryMB: 4096,
|
|
200
200
|
});
|
|
@@ -210,7 +210,7 @@ pnpm tsx scripts/build-e2b-template.ts
|
|
|
210
210
|
```ts
|
|
211
211
|
import { e2bSandbox } from "niceeval/sandbox";
|
|
212
212
|
|
|
213
|
-
sandbox: e2bSandbox({ template: "acme-codex-evals:
|
|
213
|
+
sandbox: e2bSandbox({ template: "acme-codex-evals:0.144.1-r1" })
|
|
214
214
|
```
|
|
215
215
|
|
|
216
216
|
也可以直接从 NiceEval 公共模板继续派生,只支付项目依赖的构建成本:
|
|
@@ -240,17 +240,17 @@ e2bCodingAgentTemplate("bub", {
|
|
|
240
240
|
[`niceeval/claude-code`](https://hub.docker.com/r/niceeval/claude-code)、
|
|
241
241
|
[`niceeval/codex`](https://hub.docker.com/r/niceeval/codex) 或
|
|
242
242
|
[`niceeval/bub`](https://hub.docker.com/r/niceeval/bub)。每个镜像只包含自己的 Agent CLI,并为
|
|
243
|
-
`linux/amd64` 和 `linux/arm64` 发布 manifest
|
|
244
|
-
|
|
243
|
+
`linux/amd64` 和 `linux/arm64` 发布 manifest。tag 与对应的 E2B 公共模板同号——版本位是镜像里那个
|
|
244
|
+
Agent 的版本。稳定 CI 用具名常量或 digest,不要依赖会移动的 `latest`:
|
|
245
245
|
|
|
246
246
|
```ts
|
|
247
|
-
import { dockerSandbox } from "niceeval/sandbox";
|
|
247
|
+
import { NICEEVAL_CODEX_DOCKER_IMAGE, dockerSandbox } from "niceeval/sandbox";
|
|
248
248
|
|
|
249
|
-
sandbox: dockerSandbox({ image:
|
|
249
|
+
sandbox: dockerSandbox({ image: NICEEVAL_CODEX_DOCKER_IMAGE })
|
|
250
250
|
```
|
|
251
251
|
|
|
252
|
-
这个镜像是 NiceEval 维护的公开镜像,不是 Docker 的 `library/*` Official Image
|
|
253
|
-
|
|
252
|
+
这个镜像是 NiceEval 维护的公开镜像,不是 Docker 的 `library/*` Official Image。它在 Agent 版本或
|
|
253
|
+
构建配方变化时发布新 tag,跟 NiceEval 库的发版节奏无关。
|
|
254
254
|
|
|
255
255
|
如果只需要一个 Agent,或还要加入项目专有依赖,写 Dockerfile 从 Docker 的官方基线
|
|
256
256
|
`node:24-slim` 派生。省略 `image` 时,NiceEval 也会按 runtime 使用该默认镜像:
|
|
@@ -266,7 +266,7 @@ RUN npm install -g @openai/codex@0.144.1
|
|
|
266
266
|
```
|
|
267
267
|
|
|
268
268
|
```bash
|
|
269
|
-
docker build -t acme-codex-evals:
|
|
269
|
+
docker build -t acme-codex-evals:0.144.1-r1 .
|
|
270
270
|
```
|
|
271
271
|
|
|
272
272
|
然后在 Experiment 里只引用构建结果:
|
|
@@ -274,7 +274,7 @@ docker build -t acme-codex-evals:2026-07-13 .
|
|
|
274
274
|
```ts
|
|
275
275
|
import { dockerSandbox } from "niceeval/sandbox";
|
|
276
276
|
|
|
277
|
-
sandbox: dockerSandbox({ image: "acme-codex-evals:
|
|
277
|
+
sandbox: dockerSandbox({ image: "acme-codex-evals:0.144.1-r1" })
|
|
278
278
|
```
|
|
279
279
|
|
|
280
280
|
Docker Sandbox 默认以非 root 的 `node` 用户(UID 1000)跑命令,并把 `/usr/local/bin` 放进 PATH,所以 `npm install -g` 装的全局二进制天然可见;要装到别处的 Agent(如落在 `~/.local/bin`)记得让它进 PATH。本地快速迭代可以直接省略 `image` 用默认 `node:*-slim`,稳定 CI 引用不可变 tag。
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "给报告换一套主题"
|
|
3
|
+
sidebarTitle: "报告主题"
|
|
4
|
+
description: "用 --theme 换报告外观,用 defineTheme 写一份自己的主题,并把它设成项目默认或发成 npm 包。"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
报告说这份结果给谁看什么,主题说它长什么样。两者是分开的:一份主题可以套在任何报告上,一份报告也可以换任何主题。所以给结果站换品牌色不必复制报告文件,团队的配色也只写一次。
|
|
8
|
+
|
|
9
|
+
主题只作用于网页:`niceeval view` 的页面外壳和页内组件读同一份主题,本地查看和 `--out` 导出的静态站是同一套。`niceeval show` 是终端输出,没有主题这一层。
|
|
10
|
+
|
|
11
|
+
## 换一次外观
|
|
12
|
+
|
|
13
|
+
不改任何文件,对着同一份报告试装:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
niceeval view --theme ./themes/acme.ts
|
|
17
|
+
niceeval view --report reports/site.tsx --theme ./themes/acme.ts
|
|
18
|
+
niceeval view --theme basalt # 内置主题名,回到官方外观
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`--theme` 的裸词查内置主题名(当前只有 `basalt`),带路径的值按文件装载。
|
|
22
|
+
|
|
23
|
+
每次运行只有一份主题生效,四档,前面没有才用后面:
|
|
24
|
+
|
|
25
|
+
| 档 | 取值 |
|
|
26
|
+
|---|---|
|
|
27
|
+
| 1 | `--theme <名字\|文件>` |
|
|
28
|
+
| 2 | 报告自己的 `theme` 字段 |
|
|
29
|
+
| 3 | `niceeval.config.ts` 的 `theme` 字段 |
|
|
30
|
+
| 4 | 内置 `basalt` |
|
|
31
|
+
|
|
32
|
+
生效的那一份里没写的东西取官方值,不会跟下一档拼起来。
|
|
33
|
+
|
|
34
|
+
## 写一份自己的主题
|
|
35
|
+
|
|
36
|
+
只换品牌色时,一个字段就是一份完整主题:
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
// themes/acme.ts
|
|
40
|
+
import { defineTheme } from "niceeval/report";
|
|
41
|
+
|
|
42
|
+
export default defineTheme({ accent: { light: "#6D28D9", dark: "#C4B5FD" } });
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
写一个颜色表示浅色深色都用它。品牌色只在某种背景下好看时,两支分别给出——NiceEval 不替你猜另一支长什么样。
|
|
46
|
+
|
|
47
|
+
做一份认得出来的主题,动的是色板、底色、字体和圆角:
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
// themes/acme.ts
|
|
51
|
+
import { defineTheme } from "niceeval/report";
|
|
52
|
+
|
|
53
|
+
export default defineTheme({
|
|
54
|
+
appearance: "system",
|
|
55
|
+
accent: { light: "#6D28D9", dark: "#C4B5FD" },
|
|
56
|
+
positive: { light: "#047857", dark: "#6EE7B7" },
|
|
57
|
+
negative: { light: "#BE123C", dark: "#FDA4AF" },
|
|
58
|
+
warning: { light: "#A16207", dark: "#FDE047" },
|
|
59
|
+
series: [
|
|
60
|
+
{ light: "#2563EB", dark: "#60A5FA" },
|
|
61
|
+
{ light: "#059669", dark: "#34D399" },
|
|
62
|
+
{ light: "#D97706", dark: "#FBBF24" },
|
|
63
|
+
{ light: "#7C3AED", dark: "#C4B5FD" },
|
|
64
|
+
{ light: "#DB2777", dark: "#F472B6" },
|
|
65
|
+
{ light: "#0891B2", dark: "#22D3EE" },
|
|
66
|
+
],
|
|
67
|
+
page: { light: "#FFFBF5", dark: "#120F0C" },
|
|
68
|
+
surface: { light: "#FFFFFF", dark: "#1C1713" },
|
|
69
|
+
font: { sans: '"IBM Plex Sans", ui-sans-serif, sans-serif' },
|
|
70
|
+
radius: "2px",
|
|
71
|
+
styles: [{ src: "./acme.css" }],
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`./acme.css` 按主题文件的位置解析,不是报告文件——把主题连同它的 CSS 搬到别的项目,路径照旧成立。
|
|
76
|
+
|
|
77
|
+
每个字段说的是含义,不是某个颜色:
|
|
78
|
+
|
|
79
|
+
| 字段 | 用来表达 |
|
|
80
|
+
|---|---|
|
|
81
|
+
| `accent` | 站点身份、当前导航、链接与 Attempt 定位符 |
|
|
82
|
+
| `positive` / `negative` / `warning` | passed / failed / errored 与截断、覆盖不全 |
|
|
83
|
+
| `series` | 实验、Agent、标签这些分类身份,六个槽 |
|
|
84
|
+
| `page` / `surface` / `border` / `text` 等 | 底色、卡片、分隔线与三级文字 |
|
|
85
|
+
| `font` / `fontSize` / `radius` | 字体栈、正文基准字号、圆角 |
|
|
86
|
+
|
|
87
|
+
颜色只收 `#RRGGBB`。字体、字号、圆角的值本来就是 CSS,原样写进去。令牌表达不了的效果写在 `styles` 里,那是完整的 CSS 出口。
|
|
88
|
+
|
|
89
|
+
## 浅色与深色
|
|
90
|
+
|
|
91
|
+
`appearance` 一个字段同时决定默认哪一支、读者能不能换:
|
|
92
|
+
|
|
93
|
+
- `system`(默认):初始跟随浏览器和系统,页头带一个浅 / 深切换,读者选过之后这个站记住。
|
|
94
|
+
- `light` / `dark`:锁定整站,不出现切换控件。主题的 CSS 只为一种背景写过时用这一档。
|
|
95
|
+
|
|
96
|
+
关掉 JavaScript 打开静态站,看到的就是声明的那一支,所有数值照常可读。
|
|
97
|
+
|
|
98
|
+
## 设成项目默认
|
|
99
|
+
|
|
100
|
+
团队里没人应该每次都敲 `--theme`:
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
// niceeval.config.ts
|
|
104
|
+
import { defineConfig } from "niceeval";
|
|
105
|
+
import acme from "./themes/acme";
|
|
106
|
+
import site from "./reports/site";
|
|
107
|
+
|
|
108
|
+
export default defineConfig({
|
|
109
|
+
report: site,
|
|
110
|
+
theme: acme,
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
之后裸跑 `niceeval view` 就是自己的报告加自己的外观。要给某一份报告钉死外观、不受项目配置影响(例如对外发布的那份必须是品牌色),写进那份报告:
|
|
115
|
+
|
|
116
|
+
```tsx
|
|
117
|
+
export default defineReport({
|
|
118
|
+
extends: standard,
|
|
119
|
+
title: "Acme Evals",
|
|
120
|
+
theme: acme,
|
|
121
|
+
});
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## 发成包,或在别人的主题上改
|
|
125
|
+
|
|
126
|
+
主题是普通值,发布不需要额外机制:一个 npm 包,默认导出 `defineTheme` 的产物,CSS 和字体随包走。
|
|
127
|
+
|
|
128
|
+
```tsx
|
|
129
|
+
import acme from "@acme/niceeval-theme";
|
|
130
|
+
|
|
131
|
+
export default defineConfig({ theme: acme });
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
在一份主题上改两项,用普通的对象展开;`styles` 是数组,拼接就是数组拼接:
|
|
135
|
+
|
|
136
|
+
```tsx
|
|
137
|
+
export default defineTheme({
|
|
138
|
+
...acme,
|
|
139
|
+
accent: { light: "#0F766E", dark: "#5EEAD4" },
|
|
140
|
+
styles: [...(acme.styles ?? []), { inline: ".nre .nre-hero { padding-block: 48px; }" }],
|
|
141
|
+
});
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## 发布与嵌入
|
|
145
|
+
|
|
146
|
+
导出的静态站带着同一份主题,本地看到什么发出去就是什么:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
niceeval view --report reports/site.tsx --theme ./themes/acme.ts --out site
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
把报告组件嵌进自己的 React 页面时,同一份主题也能直接用:
|
|
153
|
+
|
|
154
|
+
```tsx
|
|
155
|
+
import { themeStylesheet } from "niceeval/report";
|
|
156
|
+
import acme from "@acme/niceeval-theme";
|
|
157
|
+
|
|
158
|
+
<style dangerouslySetInnerHTML={{ __html: themeStylesheet(acme) }} />;
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
反过来也行:不注入主题,在包住 `.nre` 的容器上声明自己产品的 `--nre-*` 令牌,报告组件就长成产品的样子。
|
|
162
|
+
|
|
163
|
+
## 边界
|
|
164
|
+
|
|
165
|
+
- 主题只影响网页。`niceeval show --theme …` 会报错并告诉你改用 `view`。
|
|
166
|
+
- 换主题不改数字:主题不参与取数和计算,改颜色不需要重跑评估。
|
|
167
|
+
- 哪个实验恒用哪个颜色槽写在报告的 `seriesPins` 里,不写在主题里——换配色不该让基线和候选方案对调身份(见[自定义报告](/zh/tutorials/custom-reports#钉住某个值的颜色))。
|
|
168
|
+
- 自己写的组件要按取色纪律写才跟得上主题,写死的颜色值在深色分支会失灵(见[让自己的组件跟随任何主题](/zh/tutorials/custom-reports#让自己的组件跟随任何主题))。
|
|
169
|
+
- 页头的 NiceEval 标识和品牌行不从 `accent` 取色,主题不是隐藏它们的开关。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "niceeval",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.3",
|
|
4
4
|
"description": "Agent-native eval tool — eval agents, services, functions, and coding-agent fixtures",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -175,6 +175,7 @@
|
|
|
175
175
|
"e2e": "tsx e2e/scripts/run.ts",
|
|
176
176
|
"gen:diff-code": "tsx scripts/gen-diff-code.ts",
|
|
177
177
|
"docs:reference": "tsx scripts/generate-reference.ts",
|
|
178
|
+
"docs:lint": "tsx scripts/docs-writing-lint.ts",
|
|
178
179
|
"tiers:sync": "node scripts/sync-tiers.mjs sync",
|
|
179
180
|
"tiers:check": "node scripts/sync-tiers.mjs check",
|
|
180
181
|
"view:build": "vite build --config src/view/app/vite.config.ts",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// cases: docs/engineering/testing/unit/
|
|
1
|
+
// cases: docs/engineering/testing/unit/record.md
|
|
2
2
|
// 「Usage、facts 与失败命令证据落盘」桶恒互斥归一:AI SDK 的 inputTokens 是含缓存明细的
|
|
3
3
|
// 输入总量,落桶前扣掉在场的 cacheRead / cacheWrite 明细。
|
|
4
4
|
// bug: memory/estimatecost-openai-inclusive-cache-double-billed.md
|
|
@@ -1,32 +1,50 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
+
import { DEFAULT_BUB_VERSION } from "./coding-cli-versions.ts";
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Bub 的 OTel tape store 插件——时间轨的来源。插件不发 PyPI,git 依赖是唯一安装方式。
|
|
6
|
+
*
|
|
7
|
+
* 插件与 Bub 的 tape 协议同代:这个 pin(bub-contrib#53 之后)从 `bub.tape` 取类型,
|
|
8
|
+
* 要求 Bub ≥ 0.3.10;更早的 pin 按 republic 的类型校验,配 Bub ≤ 0.3.9。配错代不会安装失败,
|
|
9
|
+
* 而是 span 全被拒、时间轨静默为空(台账见 memory/bub-tapestore-otel-tapeentry-drift.md)。
|
|
10
|
+
*/
|
|
6
11
|
export const DEFAULT_BUB_OTEL_PLUGIN =
|
|
7
|
-
"git+https://github.com/bubbuild/bub-contrib.git@
|
|
12
|
+
"git+https://github.com/bubbuild/bub-contrib.git@34715077877041b21472dcca39d91529296a1a9e#subdirectory=packages/bub-tapestore-otel";
|
|
13
|
+
|
|
14
|
+
/** 装哪一版 Bub:`uv tool install` 的 requirement,版本单源在 `coding-cli-versions.ts`。 */
|
|
15
|
+
export const DEFAULT_BUB_REQUIREMENT = bubRequirement(DEFAULT_BUB_VERSION);
|
|
8
16
|
|
|
9
17
|
export const BUB_CHECKPOINT_SUBDIRS = [".local"] as const;
|
|
10
18
|
export const BUB_INSTALL_MARKER = ".local/share/niceeval/bub-install-hash";
|
|
11
19
|
|
|
20
|
+
/**
|
|
21
|
+
* PyPI requirement for a pinned Bub version.
|
|
22
|
+
*
|
|
23
|
+
* 这行同时是安装用的 uv override:OTel 插件所在 workspace 把 `bub` 声明成 git 依赖,
|
|
24
|
+
* 不覆盖的话每次安装都会去拉 Bub 主干,版本失控。
|
|
25
|
+
*/
|
|
26
|
+
export function bubRequirement(version: string): string {
|
|
27
|
+
return `bub==${version}`;
|
|
28
|
+
}
|
|
29
|
+
|
|
12
30
|
export function normalizeBubPackages(packages: readonly string[]): string[] {
|
|
13
31
|
return [...new Set(packages.map((value) => value.trim()).filter(Boolean))].sort();
|
|
14
32
|
}
|
|
15
33
|
|
|
16
34
|
export function bubInstallSpec(
|
|
17
35
|
packages: readonly string[],
|
|
18
|
-
|
|
36
|
+
requirement = DEFAULT_BUB_REQUIREMENT,
|
|
19
37
|
otelPlugin = DEFAULT_BUB_OTEL_PLUGIN,
|
|
20
38
|
): string {
|
|
21
39
|
const normalized = normalizeBubPackages(packages);
|
|
22
40
|
const plugins = normalized.length ? ` --with ${normalized.join(" --with ")}` : "";
|
|
23
|
-
return
|
|
41
|
+
return `${requirement} --with ${otelPlugin}${plugins} --checkpoint(${BUB_CHECKPOINT_SUBDIRS.join(",")})`;
|
|
24
42
|
}
|
|
25
43
|
|
|
26
44
|
export function bubInstallHash(
|
|
27
45
|
packages: readonly string[],
|
|
28
|
-
|
|
46
|
+
requirement = DEFAULT_BUB_REQUIREMENT,
|
|
29
47
|
otelPlugin = DEFAULT_BUB_OTEL_PLUGIN,
|
|
30
48
|
): string {
|
|
31
|
-
return createHash("md5").update(bubInstallSpec(packages,
|
|
49
|
+
return createHash("md5").update(bubInstallSpec(packages, requirement, otelPlugin)).digest("hex").slice(0, 12);
|
|
32
50
|
}
|