niceeval 0.11.1-canary.9 → 0.11.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/INDEX.md +1 -0
- package/dist/i18n/en.d.ts +0 -1
- package/dist/i18n/en.js +5 -5
- package/dist/i18n/index.d.ts +5 -0
- package/dist/i18n/index.js +14 -3
- package/dist/i18n/zh-CN.d.ts +1 -2
- package/dist/i18n/zh-CN.js +4 -5
- package/dist/runner/types.d.ts +7 -2
- package/docs-site/zh/examples/integrations/langgraph.mdx +3 -3
- package/docs-site/zh/explanation/judge.mdx +7 -5
- package/docs-site/zh/reference/cli.mdx +11 -17
- package/docs-site/zh/reference/define-config.mdx +14 -1
- package/docs-site/zh/tutorials/agent-onboarding.mdx +8 -22
- package/docs-site/zh/tutorials/ci-integration.mdx +2 -3
- package/docs-site/zh/tutorials/configuration.mdx +165 -0
- package/docs-site/zh/tutorials/custom-reports.mdx +30 -3
- package/package.json +1 -1
- package/src/agents/bub.ts +5 -8
- package/src/cli.ts +22 -17
- package/src/i18n/en.ts +5 -5
- package/src/i18n/index.ts +16 -3
- package/src/i18n/locale.test.ts +41 -0
- package/src/i18n/zh-CN.ts +4 -5
- package/src/report/assets/styles.css +16 -14
- package/src/runner/feedback/io.ts +1 -1
- package/src/runner/types.ts +7 -2
- package/src/scoring/judge.test.ts +47 -6
- package/src/scoring/judge.ts +15 -22
- package/src/show/index.ts +1 -1
- package/src/show/json.test.ts +3 -5
- package/src/show/show.test.ts +3 -5
- 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/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
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "把配置和密钥各放到该放的地方"
|
|
3
|
+
sidebarTitle: "配置与环境变量"
|
|
4
|
+
description: "跑几次、超时、并发、judge、语言这些值写进代码的哪一层,API key 和 provider token 用哪些环境变量。"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
[NiceEval](https://niceeval.com/) 里的值只有两个家:
|
|
8
|
+
|
|
9
|
+
- **配置写进代码**——CLI flag、`experiments/` 下的 experiment 文件、根目录的 `niceeval.config.ts`。跑几次、超时多久、并发多少、judge 用哪个模型、默认看哪份报告、界面什么语言,全都在这里,没有对应的环境变量。
|
|
10
|
+
- **密钥放环境变量**——API key、provider token。再加上 `NO_COLOR`、系统语言这类描述"输出到哪个终端"的事实。
|
|
11
|
+
|
|
12
|
+
所以同一个值只有一条来路。`niceeval exp --dry` 打印出来的就是真正生效的值,不用担心某个环境变量在背后改了它。
|
|
13
|
+
|
|
14
|
+
## 配置:想让这个值活多久,就写在哪一层
|
|
15
|
+
|
|
16
|
+
同一个值出现在多层时,按 **CLI flag → experiment → config → 内置默认** 取,前面有值就不看后面。
|
|
17
|
+
|
|
18
|
+
**只想这一次不一样**——写在命令上:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npx niceeval exp ci --runs 5 --timeout 600000
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**这个实验一直这样**——写进 experiment 文件:
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
// experiments/ci.ts
|
|
28
|
+
import { defineExperiment } from "niceeval";
|
|
29
|
+
import { codexAgent } from "niceeval/agents";
|
|
30
|
+
|
|
31
|
+
export default defineExperiment({
|
|
32
|
+
agent: codexAgent(),
|
|
33
|
+
model: "gpt-5.4",
|
|
34
|
+
runs: 3,
|
|
35
|
+
timeoutMs: 600_000,
|
|
36
|
+
budget: 5,
|
|
37
|
+
});
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`agent`、`model`、`flags` 只能写在这里,没有对应的 flag——换 agent 或换模型是复制一个 experiment 文件的事,这样每次运行对着谁跑都记在快照里,事后能复现。
|
|
41
|
+
|
|
42
|
+
**整个项目共享**——写进 `niceeval.config.ts`:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import { defineConfig } from "niceeval";
|
|
46
|
+
|
|
47
|
+
export default defineConfig({
|
|
48
|
+
locale: "en",
|
|
49
|
+
judge: { model: "gpt-5.4-mini" },
|
|
50
|
+
maxConcurrency: 4,
|
|
51
|
+
timeoutMs: 300_000,
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### `niceeval.config.ts` 能放什么
|
|
56
|
+
|
|
57
|
+
| 字段 | 放什么 |
|
|
58
|
+
| --- | --- |
|
|
59
|
+
| `name` | 项目名,显示在 `niceeval view` 顶部;可按语言给多份 |
|
|
60
|
+
| `locale` | CLI 与运行时文案的语言(`"en"` / `"zh-CN"`)。CI 里想让日志恒定英文,写这个 |
|
|
61
|
+
| `report` | 默认报告定义,裸 `show` / `view` 装载它(见[自定义报告](/zh/tutorials/custom-reports#设成项目默认)) |
|
|
62
|
+
| `judge` | 默认裁判配置:`model`、`baseUrl`、`apiKeyEnv` |
|
|
63
|
+
| `sandbox` | 默认 Sandbox provider,填 `dockerSandbox()` / `e2bSandbox()` / `vercelSandbox()` / `localSandbox()` 的产物 |
|
|
64
|
+
| `workspace` | 上传进 Sandbox 的工作区根目录 |
|
|
65
|
+
| `reporters` | 默认 reporter 列表(落盘、上报实验平台) |
|
|
66
|
+
| `maxConcurrency` | 默认并发上限 |
|
|
67
|
+
| `timeoutMs` | 默认单次 attempt 超时 |
|
|
68
|
+
| `telemetry` | OTLP 接收配置(`host` / `port`),OTel 接入只从这里配 |
|
|
69
|
+
| `pricing` | 价格表覆盖,按 model 名或 `provider/*` 通配 |
|
|
70
|
+
|
|
71
|
+
每个字段的类型和完整说明在 [defineConfig 参考](/zh/reference/define-config);flag 全表在 [CLI 参考](/zh/reference/cli)。
|
|
72
|
+
|
|
73
|
+
## 环境变量:只有密钥和终端两类
|
|
74
|
+
|
|
75
|
+
下面就是 NiceEval 会读的全部环境变量。每个 agent、sandbox 和 judge 只认自己那一个名字,不会在环境里翻找别的 key。
|
|
76
|
+
|
|
77
|
+
| 变量 | 谁在用 | 备注 |
|
|
78
|
+
| --- | --- | --- |
|
|
79
|
+
| `ANTHROPIC_API_KEY` | `claudeCodeAgent()`、`openClawAgent()` | 工厂参数 `apiKey` 可覆盖 |
|
|
80
|
+
| `ANTHROPIC_BASE_URL` | `claudeCodeAgent()` | 网关地址,工厂参数 `baseUrl` 可覆盖 |
|
|
81
|
+
| `CODEX_API_KEY` | `codexAgent()` | 不是 `OPENAI_API_KEY`;工厂参数 `apiKey` 可覆盖 |
|
|
82
|
+
| `CODEX_BASE_URL` | `codexAgent()` | OpenAI 兼容代理地址,工厂参数 `baseUrl` 可覆盖 |
|
|
83
|
+
| `BUB_API_KEY` | `bubAgent()` | 工厂参数 `apiKey` 可覆盖 |
|
|
84
|
+
| `BUB_API_BASE` | `bubAgent()` | 工厂参数 `apiBase` 可覆盖 |
|
|
85
|
+
| `NICEEVAL_JUDGE_KEY` | judge | judge 的默认 key 变量 |
|
|
86
|
+
| `E2B_API_KEY` | `e2bSandbox()` | |
|
|
87
|
+
| `VERCEL_API_TOKEN` | `vercelSandbox()` | |
|
|
88
|
+
| `VERCEL_TEAM_ID` | `vercelSandbox()` | |
|
|
89
|
+
| `VERCEL_PROJECT_ID` | `vercelSandbox()` | |
|
|
90
|
+
| `NO_COLOR` | CLI 输出 | 设了就不画颜色和框线 |
|
|
91
|
+
| `LC_ALL` / `LC_MESSAGES` / `LANG` | CLI 文案 | 系统语言,决定默认界面语言;`config.locale` 优先 |
|
|
92
|
+
|
|
93
|
+
judge 想用别的变量名装 key,在配置里指过去:
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
export default defineConfig({
|
|
97
|
+
judge: {
|
|
98
|
+
model: "gpt-5.4-mini",
|
|
99
|
+
baseUrl: "https://gateway.example.com/v1", // 网关地址是配置,写在这里
|
|
100
|
+
apiKeyEnv: "MY_GATEWAY_KEY", // key 是密钥,读这个环境变量
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
网关地址本身也不方便签入仓库时,不需要 niceeval 提供什么环境变量——配置是代码,自己读就行(`.env` 在这之前已经加载完):
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
export default defineConfig({
|
|
109
|
+
judge: {
|
|
110
|
+
model: "gpt-5.4-mini",
|
|
111
|
+
baseUrl: process.env.MY_GATEWAY_URL, // 变量名自己起,自己读
|
|
112
|
+
apiKeyEnv: "MY_GATEWAY_KEY",
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
区别只在于:变量名是你的项目定的,不是 NiceEval 内置一个名字然后到环境里猜。
|
|
118
|
+
|
|
119
|
+
本地把密钥放进 cwd 下的 `.env`,CLI 启动时自动加载(不覆盖已经存在的环境变量),不用每次 `export`:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
# .env
|
|
123
|
+
ANTHROPIC_API_KEY=sk-ant-...
|
|
124
|
+
NICEEVAL_JUDGE_KEY=sk-...
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
`.env` 是投递密钥的地方,不是第二个配置文件——往里写 `NICEEVAL_TIMEOUT` 这种东西不会有任何效果。CI 里同样只传密钥:
|
|
128
|
+
|
|
129
|
+
```yaml
|
|
130
|
+
- run: npx niceeval exp ci --strict --junit .niceeval/junit.xml
|
|
131
|
+
env:
|
|
132
|
+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## 在旧脚本里见到这些变量,改成配置
|
|
136
|
+
|
|
137
|
+
| 旧写法 | 改成 |
|
|
138
|
+
| --- | --- |
|
|
139
|
+
| `NICEEVAL_RUNS` | `--runs`,或 experiment 的 `runs` |
|
|
140
|
+
| `NICEEVAL_TIMEOUT` | `--timeout`,或 experiment / config 的 `timeoutMs` |
|
|
141
|
+
| `NICEEVAL_BUDGET` | `--budget`,或 experiment 的 `budget` |
|
|
142
|
+
| `NICEEVAL_MAX_CONCURRENCY` | `--max-concurrency`,或 config 的 `maxConcurrency` |
|
|
143
|
+
| `NICEEVAL_LANG` / `NICEEVAL_LOCALE` | config 的 `locale` |
|
|
144
|
+
| `NICEEVAL_JUDGE_MODEL` | config 或 eval 的 `judge.model` |
|
|
145
|
+
| `NICEEVAL_JUDGE_BASE` | config 或 eval 的 `judge.baseUrl` |
|
|
146
|
+
| `NICEEVAL_OTLP_HOST` / `NICEEVAL_OTLP_PORT` | config 的 `telemetry: { host, port }` |
|
|
147
|
+
|
|
148
|
+
judge 也不再从 `CODEX_API_KEY` / `OPENAI_API_KEY` 借 key、从 `OPENAI_BASE_URL` 猜端点。被测应用把标准的 `OPENAI_*` 挪作它用时,judge 不会再跟着串味。
|
|
149
|
+
|
|
150
|
+
## 接着看
|
|
151
|
+
|
|
152
|
+
<CardGroup cols={2}>
|
|
153
|
+
<Card title="defineConfig 参考" icon="gear" href="/zh/reference/define-config">
|
|
154
|
+
每个配置字段的类型与完整说明。
|
|
155
|
+
</Card>
|
|
156
|
+
<Card title="CLI 参考" icon="terminal" href="/zh/reference/cli">
|
|
157
|
+
命令、flag 全表与退出码。
|
|
158
|
+
</Card>
|
|
159
|
+
<Card title="写 experiment" icon="flask" href="/zh/tutorials/write-experiment">
|
|
160
|
+
哪些值属于一次具体运行。
|
|
161
|
+
</Card>
|
|
162
|
+
<Card title="CI 集成" icon="circle-play" href="/zh/tutorials/ci-integration">
|
|
163
|
+
在 CI 里传 secrets、钉住日志语言。
|
|
164
|
+
</Card>
|
|
165
|
+
</CardGroup>
|
|
@@ -6,11 +6,11 @@ description: "用 defineReport、内置报告组件和双面组件编写一份
|
|
|
6
6
|
|
|
7
7
|
[查看结果](/zh/tutorials/viewing-results)介绍 `niceeval show` 的终端输出和 `niceeval view` 的网页输出。本教程用相同的结果数据编写考试成绩单、代码行数表和质量 × 成本图等自定义报告。
|
|
8
8
|
|
|
9
|
-
一份报告对应一个报告文件:一棵由内置组件搭起来的树。`niceeval show` 和 `niceeval view` 打开结果、选出当前范围(Scope),交给这棵树渲染。把文件路径传给 `--report
|
|
9
|
+
一份报告对应一个报告文件:一棵由内置组件搭起来的树。`niceeval show` 和 `niceeval view` 打开结果、选出当前范围(Scope),交给这棵树渲染。把文件路径传给 `--report`,同一份定义在终端和网页都能渲染;写顺手了再把它填进 `niceeval.config.ts` 的 `report` 字段,裸跑 `show` / `view` 就默认是自己的报告(见[设成项目默认](#设成项目默认))。自定义报告同样支持 Attempt 证据链接、`--results` 指定结果根和静态导出。
|
|
10
10
|
|
|
11
11
|
## show / view 的默认报告也是一份报告文件
|
|
12
12
|
|
|
13
|
-
`niceeval show` / `view`
|
|
13
|
+
`niceeval show` / `view` 在没有 `--report`、配置里也没写 `report` 时渲染的默认报告不是私有实现,而是包里自带的一个普通报告文件:报告、Attempts、追踪三个导航页,加一个不进导航、按 Attempt 定位符打开的详情页,每页都由 `niceeval/report` 公开导出的组件搭成。
|
|
14
14
|
|
|
15
15
|
这份默认报告本身以 `standard` 为名从 `niceeval/report/built-in` 导出。只想在默认报告上加站点标题、GitHub 链接或统计脚本时,用 `extends` 在它上面叠自己的外壳,页面内容一行不用写;NiceEval 升级带来的页面改进也会自动跟过来:
|
|
16
16
|
|
|
@@ -105,6 +105,33 @@ const ProdSummary = defineComponent((_props: {}, ctx) => (
|
|
|
105
105
|
|
|
106
106
|
默认报告没有特权:它的四个页面全部由公开组件搭成。需要同样的当前 Scope 比较就写 `<ExperimentComparison />`;要子集就在报告里先 `filter`,或从 CLI 用 `--exp` 收窄。
|
|
107
107
|
|
|
108
|
+
## 设成项目默认
|
|
109
|
+
|
|
110
|
+
报告定型以后,不用再要求团队每次都敲 `--report`。把 `defineReport` 的产物 import 进配置,填给 `report` 字段:
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
// niceeval.config.ts
|
|
114
|
+
import { defineConfig } from "niceeval";
|
|
115
|
+
import site from "./reports/site";
|
|
116
|
+
|
|
117
|
+
export default defineConfig({
|
|
118
|
+
report: site,
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
字段收报告定义本身,不是路径字符串——配置是 TS 文件,import 就好,写错路径在类型检查时就报出来。之后:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
niceeval show # 自己的报告,不用再写 --report
|
|
126
|
+
niceeval view --page exam # --page、位置参数收窄、@locator 下钻全部照旧
|
|
127
|
+
niceeval show --report standard # 本次回到内建默认报告,不改配置
|
|
128
|
+
niceeval view --report ./reports/exam.tsx # 本次换成另一份文件
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
`--report` 的值有两种形态:写成带路径的样子(`./reports/exam.tsx`、`reports/exam.tsx`)按文件装载,裸词当内置报告名查(今天只有 `standard`)。裸词查不到就报错并列出可用的名字,不会去猜同名文件。
|
|
132
|
+
|
|
133
|
+
配置里的 `report` 只影响看结果,不影响跑:`niceeval exp` 不读它,报告也不写进结果快照,所以改报告不用重跑。
|
|
134
|
+
|
|
108
135
|
## 排版:内置排版原语
|
|
109
136
|
|
|
110
137
|
排版原语也是双面组件,同一份布局会分别渲染到网页和终端:
|
|
@@ -452,4 +479,4 @@ niceeval view --report reports/exam.tsx --out site
|
|
|
452
479
|
|
|
453
480
|
## 自定义报告的边界
|
|
454
481
|
|
|
455
|
-
|
|
482
|
+
一次只渲染一份报告,来源只有两处:`--report` 的显式取值,和配置里的 `report` 字段——没有 `reports/` 目录自动发现,也没有插件注册表。自定义指标和自定义组件都住在你的报告文件里,随文件一起递入,宿主不为它们长任何注册面。两处都没有时渲染的就是内置默认报告,你的报告和它是同级实现。
|
package/package.json
CHANGED
package/src/agents/bub.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { completeCoverage } from "../scoring/coverage.ts";
|
|
2
2
|
import { defineSandboxAgent } from "../define.ts";
|
|
3
|
-
import { requireEnv
|
|
3
|
+
import { requireEnv } from "../util.ts";
|
|
4
4
|
import { shared } from "./shared.ts";
|
|
5
5
|
import {
|
|
6
6
|
appendProjectInstruction,
|
|
@@ -85,16 +85,13 @@ const SKILL_DIR = ".agents/skills";
|
|
|
85
85
|
|
|
86
86
|
// TODO(upstream): BUB_OVERRIDE 钉在个人 fork 的修复分支上(tool-call 分支丢助手文本的修复
|
|
87
87
|
// 尚未进上游,见 memory/bub-tapestore-otel…drift.md 台账),等上游包含后改回发布版。
|
|
88
|
-
//
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
DEFAULT_BUB_OVERRIDE;
|
|
88
|
+
// 要试别的 pin 就改这两个常量——不设环境变量后门:装哪个包是配置,配置的家是代码
|
|
89
|
+
// (边界见 docs/architecture.md「配置从代码来,凭据从环境来」)。
|
|
90
|
+
const BUB_OVERRIDE = DEFAULT_BUB_OVERRIDE;
|
|
92
91
|
const BUB_OVERRIDE_FILE = "/tmp/bub-override.txt";
|
|
93
92
|
// otel 插件跟上游 main 走(bub-contrib#50 起从 bub.tape 导入,要求 bub ≥ 0.3.10dev,
|
|
94
93
|
// 与上面的 override 分支兼容)。插件不发 PyPI,git 依赖是唯一安装方式。
|
|
95
|
-
const OTEL_PLUGIN =
|
|
96
|
-
getEnv("NICEEVAL_BUB_OTEL_PLUGIN") ??
|
|
97
|
-
DEFAULT_BUB_OTEL_PLUGIN;
|
|
94
|
+
const OTEL_PLUGIN = DEFAULT_BUB_OTEL_PLUGIN;
|
|
98
95
|
|
|
99
96
|
// NiceEval 的预制配方与运行时安装都写到 $HOME/.local;显式使用该路径,避免 PATH 上
|
|
100
97
|
// 另一个未知版本的 bub 抢先命中。
|
package/src/cli.ts
CHANGED
|
@@ -54,7 +54,7 @@ import {
|
|
|
54
54
|
// 是两份不同源码位置,即使运行时同名同形,TS 类型与 instanceof 都不认。
|
|
55
55
|
import { ReportLoadError } from "../dist/report/runtime/load.js";
|
|
56
56
|
import { runShow } from "./show/index.ts";
|
|
57
|
-
import { t } from "./i18n/index.ts";
|
|
57
|
+
import { setConfiguredLocale, t } from "./i18n/index.ts";
|
|
58
58
|
import { formatThrown, upsertManagedBlock } from "./util.ts";
|
|
59
59
|
import type {
|
|
60
60
|
CompletionStatus,
|
|
@@ -382,18 +382,6 @@ function firstViewerOnlyFlag(flags: Flags): { flag: string; command: string } |
|
|
|
382
382
|
return undefined;
|
|
383
383
|
}
|
|
384
384
|
|
|
385
|
-
/** 调度项的环境变量层(标志 > 环境变量 > config > 默认,见 docs/cli.md「配置优先级」)。 */
|
|
386
|
-
function envNumber(name: string): number | undefined {
|
|
387
|
-
const raw = process.env[name]?.trim();
|
|
388
|
-
if (!raw) return undefined;
|
|
389
|
-
const n = Number(raw);
|
|
390
|
-
if (!Number.isFinite(n)) {
|
|
391
|
-
process.stderr.write(t("cli.envInvalidNumber", { name, value: raw }));
|
|
392
|
-
process.exit(1);
|
|
393
|
-
}
|
|
394
|
-
return n;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
385
|
/** 加载 cwd/.env(不覆盖已有环境变量)。 */
|
|
398
386
|
async function loadDotenv(cwd: string): Promise<void> {
|
|
399
387
|
const path = join(cwd, ".env");
|
|
@@ -413,6 +401,23 @@ async function loadDotenv(cwd: string): Promise<void> {
|
|
|
413
401
|
}
|
|
414
402
|
}
|
|
415
403
|
|
|
404
|
+
/**
|
|
405
|
+
* 界面语言只从 `defineConfig({ locale })` 来,所以每条命令(含不依赖 config 的 show / view)
|
|
406
|
+
* 都要在派发前看一眼配置。这里刻意宽容:没有配置文件、或配置本身有错时静默回到系统 locale
|
|
407
|
+
* 判定——语言是装饰性设置,不该让 `niceeval show` 因为一个坏 config 打不开结果;真正需要
|
|
408
|
+
* config 的命令随后走 loadConfig,由它报出完整错误(模块缓存让这次装载不重复付出代价)。
|
|
409
|
+
*/
|
|
410
|
+
async function applyConfiguredLocale(cwd: string): Promise<void> {
|
|
411
|
+
const path = join(cwd, "niceeval.config.ts");
|
|
412
|
+
if (!existsSync(path)) return;
|
|
413
|
+
try {
|
|
414
|
+
const mod = (await import(pathToFileURL(path).href)) as { default?: Config };
|
|
415
|
+
setConfiguredLocale(mod.default?.locale);
|
|
416
|
+
} catch {
|
|
417
|
+
// 交给后续 loadConfig 报错;这里不抢在语言还没定下来时打印任何东西。
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
416
421
|
async function loadConfig(cwd: string): Promise<Config> {
|
|
417
422
|
const path = join(cwd, "niceeval.config.ts");
|
|
418
423
|
if (!existsSync(path)) {
|
|
@@ -606,6 +611,7 @@ async function packageVersion(): Promise<string> {
|
|
|
606
611
|
async function main(): Promise<void> {
|
|
607
612
|
const cwd = process.cwd();
|
|
608
613
|
await loadDotenv(cwd);
|
|
614
|
+
await applyConfiguredLocale(cwd);
|
|
609
615
|
const { command, positionals, flags } = parseArgs(process.argv.slice(2));
|
|
610
616
|
|
|
611
617
|
// --help / --version 不需要 config,先于一切命令处理。
|
|
@@ -875,11 +881,11 @@ async function main(): Promise<void> {
|
|
|
875
881
|
reasoningEffort: exp.reasoningEffort,
|
|
876
882
|
flags: exp.flags ?? {},
|
|
877
883
|
...(exp.provenanceFlags !== undefined ? { provenanceFlags: exp.provenanceFlags } : {}),
|
|
878
|
-
runs: flags.runs ??
|
|
884
|
+
runs: flags.runs ?? exp.runs ?? 1,
|
|
879
885
|
earlyExit: flags.earlyExit ?? exp.earlyExit ?? false,
|
|
880
886
|
sandbox: exp.sandbox ?? config.sandbox,
|
|
881
|
-
timeoutMs: flags.timeout ??
|
|
882
|
-
budget: flags.budget ??
|
|
887
|
+
timeoutMs: flags.timeout ?? exp.timeoutMs ?? config.timeoutMs,
|
|
888
|
+
budget: flags.budget ?? exp.budget,
|
|
883
889
|
selectedEvalIds,
|
|
884
890
|
experimentId: exp.id,
|
|
885
891
|
description: exp.description,
|
|
@@ -1011,7 +1017,6 @@ async function main(): Promise<void> {
|
|
|
1011
1017
|
const sandboxDefaultConcurrency = resolvedSandboxRecommendedConcurrency(evals, agentRuns, config.sandbox);
|
|
1012
1018
|
const maxConcurrency =
|
|
1013
1019
|
flags.maxConcurrency ??
|
|
1014
|
-
envNumber("NICEEVAL_MAX_CONCURRENCY") ??
|
|
1015
1020
|
config.maxConcurrency ??
|
|
1016
1021
|
sandboxDefaultConcurrency;
|
|
1017
1022
|
|
package/src/i18n/en.ts
CHANGED
|
@@ -83,12 +83,11 @@ export const en = {
|
|
|
83
83
|
"runner.dispatchHaltedExperiment": "experiment halted (dispatch-halted): {{message}}\n",
|
|
84
84
|
"runner.dispatchHaltedEval": "eval halted: {{message}}\n",
|
|
85
85
|
"judge.modelMissing":
|
|
86
|
-
"No judge model configured. Set it in defineConfig({ judge: { model: \"...\" } })
|
|
86
|
+
"No judge model configured. Set it in defineConfig({ judge: { model: \"...\" } }) or the eval's judge config (there is no built-in default model, and no environment variable for it).\n" +
|
|
87
87
|
" Docs: node_modules/niceeval/docs-site/zh/tutorials/scoring-guide.mdx",
|
|
88
88
|
"loaders.yamlMissing":
|
|
89
89
|
"loadYaml(\"{{path}}\") needs a YAML parser: run `pnpm add yaml` first (or switch to loadJson with a JSON dataset).",
|
|
90
90
|
"cli.flag.parseError": "{{message}}\nRun `niceeval --help` for usage.\n",
|
|
91
|
-
"cli.envInvalidNumber": "Environment variable {{name}} is not a number: \"{{value}}\".\n",
|
|
92
91
|
"cli.help":
|
|
93
92
|
"niceeval — agent-native evals\n\n" +
|
|
94
93
|
"Usage:\n" +
|
|
@@ -142,8 +141,9 @@ export const en = {
|
|
|
142
141
|
" --json (machine feed: NDJSON on stdout; default is human text)\n" +
|
|
143
142
|
" --junit path --out dir --port n --open / --no-open -h, --help -v, --version\n\n" +
|
|
144
143
|
"Positional args only select which evals to run (id prefixes); which agent and\n" +
|
|
145
|
-
"how to run come from experiments/ + flags.
|
|
146
|
-
"
|
|
144
|
+
"how to run come from experiments/ + flags. Resolution: flag > experiment >\n" +
|
|
145
|
+
"niceeval.config.ts > built-in default. Configuration has no environment layer;\n" +
|
|
146
|
+
"environment variables hold credentials such as API keys.\n",
|
|
147
147
|
"cli.show.noResults": "No results found under {{root}}. Run `niceeval exp` first, then `niceeval show`.\n",
|
|
148
148
|
"cli.show.runDirMissing": "Results directory not found: {{dir}}\n",
|
|
149
149
|
"cli.show.noEvalMatch": "No results matched: {{patterns}}. Evals with results: {{evals}}\n",
|
|
@@ -317,7 +317,7 @@ export const en = {
|
|
|
317
317
|
"hitl.respondAllEmpty": "There is no pending input.requested request; respond() / respondAll() cannot work. Confirm the turn parked with t.parked(), then answer via t.requireInputRequest() or t.respond().",
|
|
318
318
|
"hitl.respondEmpty": "t.respond(...) requires at least one answer.",
|
|
319
319
|
"hitl.stringAmbiguous": "There are {{count}} pending input requests; a plain-string answer cannot be matched to one. Use the { request, optionId } or { request, text } object form to name it explicitly.",
|
|
320
|
-
"judge.apiKeyMissing": "judge is missing an API key
|
|
320
|
+
"judge.apiKeyMissing": "judge is missing an API key: set NICEEVAL_JUDGE_KEY, or point judge.apiKeyEnv at another environment variable.",
|
|
321
321
|
"judge.httpError": "judge HTTP {{status}}: {{body}}",
|
|
322
322
|
"judge.probeFailed": "judge precheck failed ({{model}}): {{error}}",
|
|
323
323
|
"judge.probeTimeout": "judge precheck timed out after {{seconds}}s ({{model}}): the endpoint accepted the connection but never responded — check the judge baseUrl / gateway, or point NICEEVAL_JUDGE_BASE at a responsive one",
|
package/src/i18n/index.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
// CLI 侧 i18n:内核(插值/归一)在 core.ts
|
|
1
|
+
// CLI 侧 i18n:内核(插值/归一)在 core.ts;这里只注入来源(config.locale + 系统 locale)与
|
|
2
|
+
// zh-CN 默认值。界面语言是配置,家在 `defineConfig({ locale })`;系统 locale 是「输出到哪个
|
|
3
|
+
// 终端」的环境事实,只作没配时的判定依据。niceeval 自己不发明 NICEEVAL_LANG 这类配置变量
|
|
4
|
+
// (边界见 docs/architecture.md「配置从代码来,凭据从环境来」)。
|
|
2
5
|
|
|
3
6
|
import { en } from "./en.ts";
|
|
4
7
|
import { zhCN, type MessageKey, type Messages } from "./zh-CN.ts";
|
|
@@ -11,10 +14,20 @@ const dictionaries: Record<Locale, Messages> = {
|
|
|
11
14
|
en,
|
|
12
15
|
};
|
|
13
16
|
|
|
17
|
+
/** `defineConfig({ locale })` 的归一结果;CLI 装载配置后调 setConfiguredLocale 注入一次。 */
|
|
18
|
+
let configuredLocale: Locale | undefined;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 注入项目配置声明的界面语言。传 undefined(没配 / 没有配置文件)时清空,回到系统 locale 判定。
|
|
22
|
+
* 无法归一的值(如 "C")同样按未声明处理——不为一个装饰性设置让命令失败。
|
|
23
|
+
*/
|
|
24
|
+
export function setConfiguredLocale(raw: string | undefined): void {
|
|
25
|
+
configuredLocale = normalizeLocale(raw);
|
|
26
|
+
}
|
|
27
|
+
|
|
14
28
|
export function detectLocale(env: NodeJS.ProcessEnv = process.env): Locale {
|
|
15
29
|
return (
|
|
16
|
-
|
|
17
|
-
normalizeLocale(env.NICEEVAL_LOCALE) ??
|
|
30
|
+
configuredLocale ??
|
|
18
31
|
normalizeLocale(env.LC_ALL) ??
|
|
19
32
|
normalizeLocale(env.LC_MESSAGES) ??
|
|
20
33
|
normalizeLocale(env.LANG) ??
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// cases: docs/engineering/testing/unit/experiments-runner.md
|
|
2
|
+
// 界面语言的取值链(「界面语言的取值链」类别):config.locale → 系统 locale
|
|
3
|
+
// (LC_ALL → LC_MESSAGES → LANG)→ zh-CN。语言是配置,家在 defineConfig({ locale });
|
|
4
|
+
// 系统 locale 只是「输出到哪个终端」的环境事实,niceeval 不再自备 NICEEVAL_LANG /
|
|
5
|
+
// NICEEVAL_LOCALE 这类配置变量(边界见 docs/architecture.md「配置从代码来,凭据从环境来」)。
|
|
6
|
+
|
|
7
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
8
|
+
import { detectLocale, setConfiguredLocale } from "./index.ts";
|
|
9
|
+
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
setConfiguredLocale(undefined);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
describe("界面语言的取值链", () => {
|
|
15
|
+
// 系统环境全是中文,config 说 en —— 取 en。反过来的错误实现(环境优先)会给出 zh-CN。
|
|
16
|
+
it("config.locale 压过系统 locale", () => {
|
|
17
|
+
setConfiguredLocale("en");
|
|
18
|
+
expect(detectLocale({ LC_ALL: "zh_CN.UTF-8", LANG: "zh_CN.UTF-8" })).toBe("en");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("config.locale 未声明时按 LC_ALL → LC_MESSAGES → LANG 逐级回落", () => {
|
|
22
|
+
expect(detectLocale({ LC_ALL: "en_US.UTF-8", LC_MESSAGES: "zh_CN.UTF-8", LANG: "zh_CN.UTF-8" })).toBe("en");
|
|
23
|
+
expect(detectLocale({ LC_MESSAGES: "en_US.UTF-8", LANG: "zh_CN.UTF-8" })).toBe("en");
|
|
24
|
+
expect(detectLocale({ LANG: "en_US.UTF-8" })).toBe("en");
|
|
25
|
+
expect(detectLocale({})).toBe("zh-CN");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// C / POSIX 是「未指定语言」,不是一种语言:该跳过它去看下一个候选,而不是当成非中文落 en。
|
|
29
|
+
it("无法归一的值按未声明处理,继续往下找", () => {
|
|
30
|
+
expect(detectLocale({ LC_ALL: "C", LANG: "zh_CN.UTF-8" })).toBe("zh-CN");
|
|
31
|
+
setConfiguredLocale("POSIX");
|
|
32
|
+
expect(detectLocale({ LANG: "zh_CN.UTF-8" })).toBe("zh-CN");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// 旧的 niceeval 专有变量已经不是取值链的一环:摆在环境里也不该改变结果。
|
|
36
|
+
it("NICEEVAL_LANG / NICEEVAL_LOCALE 不参与判定", () => {
|
|
37
|
+
expect(detectLocale({ NICEEVAL_LANG: "en", NICEEVAL_LOCALE: "en", LANG: "zh_CN.UTF-8" })).toBe("zh-CN");
|
|
38
|
+
setConfiguredLocale("en");
|
|
39
|
+
expect(detectLocale({ NICEEVAL_LANG: "zh-CN" })).toBe("en");
|
|
40
|
+
});
|
|
41
|
+
});
|
package/src/i18n/zh-CN.ts
CHANGED
|
@@ -80,12 +80,11 @@ export const zhCN = {
|
|
|
80
80
|
"runner.dispatchHaltedExperiment": "实验已止损(dispatch-halted):{{message}}\n",
|
|
81
81
|
"runner.dispatchHaltedEval": "eval 已止损:{{message}}\n",
|
|
82
82
|
"judge.modelMissing":
|
|
83
|
-
"judge 未配置模型:在 defineConfig({ judge: { model: \"...\" } })
|
|
83
|
+
"judge 未配置模型:在 defineConfig({ judge: { model: \"...\" } }) 或 eval 的 judge 配置里指定裁判模型(没有内置默认模型,也没有对应的环境变量)。\n" +
|
|
84
84
|
" 文档:node_modules/niceeval/docs-site/zh/tutorials/scoring-guide.mdx",
|
|
85
85
|
"loaders.yamlMissing":
|
|
86
86
|
"loadYaml(\"{{path}}\") 需要 YAML 解析器:请先 `pnpm add yaml`(或改用 loadJson + JSON 数据集)。",
|
|
87
87
|
"cli.flag.parseError": "{{message}}\n运行 `niceeval --help` 查看用法。\n",
|
|
88
|
-
"cli.envInvalidNumber": "环境变量 {{name}} 不是数字:\"{{value}}\"。\n",
|
|
89
88
|
"cli.help":
|
|
90
89
|
"niceeval — agent-native evals\n\n" +
|
|
91
90
|
"用法:\n" +
|
|
@@ -134,8 +133,8 @@ export const zhCN = {
|
|
|
134
133
|
" --json (机器面:stdout 上的 NDJSON 事件流;默认是人读文本)\n" +
|
|
135
134
|
" --junit path --out dir --port n --open / --no-open -h, --help -v, --version\n\n" +
|
|
136
135
|
"位置参数只选「跑哪些 eval」(id 前缀);对着哪个 agent、怎么跑来自 experiments/ 与\n" +
|
|
137
|
-
"
|
|
138
|
-
"
|
|
136
|
+
"标志。取值优先级:标志 > experiment > niceeval.config.ts > 内置默认;配置项没有\n" +
|
|
137
|
+
"环境变量层,环境变量只放 API key 这类凭据。\n",
|
|
139
138
|
// show 的错误文案保持英文(错误文案英文的仓库约定);noResults 是提示,翻译。
|
|
140
139
|
"cli.show.noResults": "{{root}} 下没有结果。先 `niceeval exp` 跑一轮,再 `niceeval show`。\n",
|
|
141
140
|
"cli.show.runDirMissing": "Results directory not found: {{dir}}\n",
|
|
@@ -310,7 +309,7 @@ export const zhCN = {
|
|
|
310
309
|
"hitl.respondAllEmpty": "没有待回答的 input.requested 请求,respond() / respondAll() 无法工作;先用 t.parked() 确认停轮,再用 t.requireInputRequest() 或 t.respond() 回答。",
|
|
311
310
|
"hitl.respondEmpty": "t.respond(...) 至少需要一个回答。",
|
|
312
311
|
"hitl.stringAmbiguous": "有 {{count}} 条待回答请求,字符串回答无法对位,请用 { request, optionId } 或 { request, text } 对象形式显式指名。",
|
|
313
|
-
"judge.apiKeyMissing": "judge 缺少 API key
|
|
312
|
+
"judge.apiKeyMissing": "judge 缺少 API key:设置 NICEEVAL_JUDGE_KEY,或用 judge.apiKeyEnv 指向别的环境变量。",
|
|
314
313
|
"judge.httpError": "judge HTTP {{status}}: {{body}}",
|
|
315
314
|
"judge.probeFailed": "judge 预检失败({{model}}): {{error}}",
|
|
316
315
|
"judge.probeTimeout": "judge 预检 {{seconds}}s 超时({{model}}):端点接受了连接但一直不回 —— 检查 judge 的 baseUrl / 网关,或把 NICEEVAL_JUDGE_BASE 指向一个有响应的网关",
|
|
@@ -56,9 +56,10 @@
|
|
|
56
56
|
|
|
57
57
|
/* ---- AttemptList:实体列表的叶子层,一条卡片一个 Attempt ---- */
|
|
58
58
|
.nre-attempt-list .nre-attempts { list-style:none; margin:0; padding:0; }
|
|
59
|
-
.nre-attempt-list .nre-attempt { border:1px solid var(--line); border-
|
|
59
|
+
.nre-attempt-list .nre-attempt { border:1px solid var(--line); border-radius:8px; padding:10px 14px; margin-bottom:8px; background:var(--panel); }
|
|
60
|
+
/* 只有 failed/errored 留左边语义色条;其余靠 hairline,避免每行都像 callout */
|
|
60
61
|
.nre-attempt-list .nre-attempt-failed,
|
|
61
|
-
.nre-attempt-list .nre-attempt-errored { border-left
|
|
62
|
+
.nre-attempt-list .nre-attempt-errored { border-left: 3px solid var(--bad); }
|
|
62
63
|
.nre-attempt-list .nre-attempt-head { display:flex; flex-wrap:wrap; gap:.75em; align-items:baseline; }
|
|
63
64
|
.nre-attempt-list .nre-attempt-eval { font-weight:620; }
|
|
64
65
|
.nre-attempt-list .nre-attempt-experiment,
|
|
@@ -336,9 +337,8 @@ table.nre,
|
|
|
336
337
|
.nre-scope-summary .nre-scope-kpi:last-child { border-right: 0; }
|
|
337
338
|
.nre-scope-summary .nre-scope-kpi dt {
|
|
338
339
|
color: var(--soft);
|
|
339
|
-
font-size:
|
|
340
|
-
|
|
341
|
-
letter-spacing: 0.08em;
|
|
340
|
+
font-size: 12px;
|
|
341
|
+
font-weight: 550;
|
|
342
342
|
}
|
|
343
343
|
.nre-scope-summary .nre-scope-kpi dd {
|
|
344
344
|
margin: 6px 0 0;
|
|
@@ -949,13 +949,13 @@ table.nre,
|
|
|
949
949
|
letter-spacing: 0.04em;
|
|
950
950
|
}
|
|
951
951
|
|
|
952
|
-
/* Error
|
|
952
|
+
/* Error 与断言:中性底 + 失败左边条;可展开明细。 */
|
|
953
953
|
.nre-attempt-error {
|
|
954
954
|
overflow: hidden;
|
|
955
|
-
border: 1px solid
|
|
955
|
+
border: 1px solid var(--line);
|
|
956
956
|
border-left: 3px solid var(--bad);
|
|
957
957
|
border-radius: 9px;
|
|
958
|
-
background:
|
|
958
|
+
background: var(--panel);
|
|
959
959
|
}
|
|
960
960
|
.nre-attempt-error .nre-attempt-error-fields {
|
|
961
961
|
display: grid;
|
|
@@ -980,8 +980,8 @@ table.nre,
|
|
|
980
980
|
overflow: auto;
|
|
981
981
|
margin: 0;
|
|
982
982
|
padding: 12px 15px;
|
|
983
|
-
border-top: 1px solid
|
|
984
|
-
background:
|
|
983
|
+
border-top: 1px solid var(--line);
|
|
984
|
+
background: var(--panel-2);
|
|
985
985
|
color: var(--muted);
|
|
986
986
|
font: 12px/1.55 ui-monospace, "SFMono-Regular", Consolas, "Liberation Mono", monospace;
|
|
987
987
|
white-space: pre-wrap;
|
|
@@ -996,10 +996,14 @@ table.nre,
|
|
|
996
996
|
.nre-attempt-source .nre-source-line-detail .nre-assertion-row {
|
|
997
997
|
overflow: hidden;
|
|
998
998
|
border: 1px solid var(--line);
|
|
999
|
-
border-left: 3px solid var(--line-strong);
|
|
1000
999
|
border-radius: 8px;
|
|
1001
1000
|
background: var(--panel);
|
|
1002
1001
|
}
|
|
1002
|
+
/* 只有 gate failed(tone-bad)留左边色条;soft/passed/na 用 hairline */
|
|
1003
|
+
.nre-attempt-assertions .nre-assertion-row:has(.nre-tone-bad),
|
|
1004
|
+
.nre-attempt-source .nre-source-line-detail .nre-assertion-row.nre-tone-bad {
|
|
1005
|
+
border-left: 3px solid var(--bad);
|
|
1006
|
+
}
|
|
1003
1007
|
.nre-attempt-assertions .nre-assertion-row > summary {
|
|
1004
1008
|
display: flex;
|
|
1005
1009
|
flex-wrap: wrap;
|
|
@@ -1358,12 +1362,10 @@ table.nre,
|
|
|
1358
1362
|
.nre-attempt-source .nre-attempt-source-unlocated > .nre-source-turn {
|
|
1359
1363
|
overflow: hidden;
|
|
1360
1364
|
border: 1px solid var(--line);
|
|
1361
|
-
border-left: 3px solid var(--blue, var(--nre-c0));
|
|
1362
1365
|
border-radius: 8px;
|
|
1363
1366
|
background: var(--panel-2);
|
|
1364
1367
|
}
|
|
1365
|
-
.nre-attempt-source .nre-attempt-source-unlocated > .nre-source-turn-failed { border-left
|
|
1366
|
-
.nre-attempt-source .nre-attempt-source-unlocated > .nre-source-turn-waiting { border-left-color: var(--warn); }
|
|
1368
|
+
.nre-attempt-source .nre-attempt-source-unlocated > .nre-source-turn-failed { border-left: 3px solid var(--bad); }
|
|
1367
1369
|
|
|
1368
1370
|
/* Other conversation(unlocated turns):TurnDetail 在这里复用 AttemptConversation 同一套回复
|
|
1369
1371
|
renderer(showMeta/showSent 都为真,没有可依附的 send 行),但 .nre-conv-* 的规则此前只覆盖了
|
|
@@ -33,7 +33,7 @@ export interface FeedbackClock {
|
|
|
33
33
|
export interface FeedbackIO {
|
|
34
34
|
readonly stdout: FeedbackStream;
|
|
35
35
|
readonly stderr: FeedbackStream;
|
|
36
|
-
/** 只读环境变量面(profile 自动检测的 CI 标记、
|
|
36
|
+
/** 只读环境变量面(profile 自动检测的 CI 标记、NO_COLOR 等都从这里读,不直接碰
|
|
37
37
|
* process.env),同样是为了让 profile.ts 的纯函数可测。 */
|
|
38
38
|
readonly env: Readonly<Record<string, string | undefined>>;
|
|
39
39
|
readonly clock: FeedbackClock;
|
package/src/runner/types.ts
CHANGED
|
@@ -323,7 +323,7 @@ export interface InvocationShape {
|
|
|
323
323
|
configs: number;
|
|
324
324
|
/** 总 attempt 数(evals × configs × runs);逐行输出与汇总计数都按它。 */
|
|
325
325
|
totalAttempts: number;
|
|
326
|
-
/** 本次运行实际生效的全局并发数(flag/
|
|
326
|
+
/** 本次运行实际生效的全局并发数(flag/config/sandbox 默认值解析后的结果);
|
|
327
327
|
* 实验级 maxConcurrency 只在该实验内部限流,不改这个全局值。 */
|
|
328
328
|
maxConcurrency: number;
|
|
329
329
|
/**
|
|
@@ -643,6 +643,11 @@ export interface Config {
|
|
|
643
643
|
* 可传字符串,或按 locale 提供多语言(如 `{ en: "...", "zh-CN": "..." }`),随 view 语言切换。
|
|
644
644
|
*/
|
|
645
645
|
name?: LocalizedText;
|
|
646
|
+
/**
|
|
647
|
+
* CLI 与运行时文案的界面语言(BCP 47,如 `"en"` / `"zh-CN"`);CI 里想让日志恒定一种语言就写这个。
|
|
648
|
+
* 省略则按系统 locale(`LC_ALL` / `LC_MESSAGES` / `LANG`)判定,都没有时用 `zh-CN`。
|
|
649
|
+
*/
|
|
650
|
+
locale?: string;
|
|
646
651
|
/** 项目级默认 Sandbox provider(docker / vercel / e2b / custom);experiment 可覆盖。 */
|
|
647
652
|
sandbox?: SandboxOption;
|
|
648
653
|
/** 上传进 Sandbox 的工作区根目录,省略则用项目根;评估用例的 sandbox 视图从这里起步。 */
|
|
@@ -651,7 +656,7 @@ export interface Config {
|
|
|
651
656
|
judge?: JudgeConfig;
|
|
652
657
|
/** 项目级默认 reporter 列表(如落盘 / 上传结果);EvalDef.reporters 会与它合并。 */
|
|
653
658
|
reporters?: Reporter[];
|
|
654
|
-
/** 项目级默认并发上限;CLI flag /
|
|
659
|
+
/** 项目级默认并发上限;CLI flag / experiment 的同名设置优先级更高(没有环境变量层)。 */
|
|
655
660
|
maxConcurrency?: number;
|
|
656
661
|
/** 项目级默认单次 attempt 超时(毫秒);CLI flag / experiment / EvalDef 的同名设置优先级更高。 */
|
|
657
662
|
timeoutMs?: number;
|