niceeval 0.1.1 → 0.1.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/README.md +8 -8
- package/README.zh.md +13 -13
- package/docs/README.md +119 -0
- package/docs/adapters/README.md +60 -0
- package/docs/adapters/authoring.md +179 -0
- package/docs/adapters/coding-agent-skills-plugins.md +324 -0
- package/docs/adapters/collection.md +128 -0
- package/docs/adapters/contract.md +263 -0
- package/docs/adapters/reference/agent-eval.md +215 -0
- package/docs/adapters/reference/agent-loop-apis.md +69 -0
- package/docs/adapters/reference/claude-code-otel-telemetry.md +100 -0
- package/docs/adapters/reference/eve-protocol.md +127 -0
- package/docs/adapters/reference/otel-genai.md +107 -0
- package/docs/adapters/reference/otel-instrumentation.md +65 -0
- package/docs/adapters/targets.md +99 -0
- package/docs/architecture.md +140 -0
- package/docs/assertions.md +388 -0
- package/docs/capabilities-by-construction.md +48 -0
- package/docs/cli.md +171 -0
- package/docs/concepts.md +106 -0
- package/docs/e2e-ci.md +295 -0
- package/docs/eval-authoring.md +319 -0
- package/docs/experiments.md +154 -0
- package/docs/getting-started.md +224 -0
- package/docs/multi-agent.md +145 -0
- package/docs/observability.md +333 -0
- package/docs/origin-integration.md +195 -0
- package/docs/references.md +35 -0
- package/docs/results-format.md +228 -0
- package/docs/runner.md +104 -0
- package/docs/sandbox.md +276 -0
- package/docs/scoring.md +119 -0
- package/docs/source-map.md +106 -0
- package/docs/tier-sync.md +177 -0
- package/docs/view.md +157 -0
- package/docs/vision.md +88 -0
- package/package.json +35 -5
- package/src/agents/ai-sdk-otel.ts +0 -0
- package/src/agents/ai-sdk.test.ts +377 -0
- package/src/agents/ai-sdk.ts +577 -0
- package/src/agents/bub.ts +30 -37
- package/src/agents/claude-code.ts +7 -4
- package/src/agents/codex.ts +15 -10
- package/src/agents/index.ts +43 -1
- package/src/agents/sdk-streams.test.ts +145 -0
- package/src/agents/sdk-streams.ts +457 -0
- package/src/agents/shared.ts +77 -39
- package/src/agents/streaming.test.ts +152 -0
- package/src/agents/streaming.ts +195 -0
- package/src/agents/types.ts +218 -0
- package/src/agents/ui-message-stream.test.ts +249 -0
- package/src/agents/ui-message-stream.ts +372 -0
- package/src/cli.ts +124 -77
- package/src/context/context.test.ts +203 -7
- package/src/context/context.ts +158 -49
- package/src/context/session.test.ts +96 -0
- package/src/context/session.ts +119 -9
- package/src/context/types.ts +205 -0
- package/src/define.ts +4 -14
- package/src/expect/index.ts +8 -71
- package/src/i18n/core.ts +28 -0
- package/src/i18n/en.ts +47 -5
- package/src/i18n/index.ts +5 -17
- package/src/i18n/zh-CN.ts +47 -5
- package/src/index.ts +12 -0
- package/src/loaders/index.ts +8 -39
- package/src/o11y/otlp/canonical.ts +7 -6
- package/src/o11y/otlp/mappers/codex.ts +10 -8
- package/src/o11y/otlp/mappers/index.ts +9 -21
- package/src/o11y/otlp/receiver.ts +25 -7
- package/src/o11y/otlp/sandbox-receiver.ts +57 -21
- package/src/o11y/otlp/turn-otel.test.ts +110 -0
- package/src/o11y/otlp/turn-otel.ts +125 -0
- package/src/o11y/parsers/bub.ts +13 -11
- package/src/o11y/parsers/claude-code.ts +27 -51
- package/src/o11y/parsers/codex.ts +29 -46
- package/src/o11y/parsers/index.ts +5 -14
- package/src/o11y/tool-names.test.ts +61 -0
- package/src/o11y/tool-names.ts +74 -0
- package/src/o11y/types.ts +135 -0
- package/src/runner/attempt.ts +456 -0
- package/src/runner/fingerprint.ts +59 -0
- package/src/runner/remote-sandbox.ts +53 -0
- package/src/runner/report.ts +53 -0
- package/src/runner/reporters/artifacts.ts +25 -4
- package/src/runner/reporters/console.ts +2 -8
- package/src/runner/reporters/live.ts +2 -8
- package/src/runner/reporters/shared.ts +15 -0
- package/src/runner/reporters/table.ts +10 -62
- package/src/runner/run.ts +114 -619
- package/src/runner/types.ts +237 -0
- package/src/sandbox/checkpoint.ts +15 -13
- package/src/sandbox/docker-stream.ts +87 -0
- package/src/sandbox/docker.ts +42 -120
- package/src/sandbox/e2b.ts +24 -75
- package/src/sandbox/local-files.ts +33 -0
- package/src/sandbox/paths.test.ts +85 -0
- package/src/sandbox/paths.ts +45 -0
- package/src/sandbox/resolve.ts +10 -34
- package/src/sandbox/shell.ts +17 -0
- package/src/sandbox/source-files.ts +42 -2
- package/src/sandbox/types.ts +158 -0
- package/src/sandbox/vercel.ts +55 -71
- package/src/scoring/collector.ts +3 -2
- package/src/scoring/judge.ts +72 -146
- package/src/scoring/match.ts +79 -0
- package/src/scoring/types.ts +76 -0
- package/src/shared/aggregate.ts +48 -0
- package/src/shared/format.ts +20 -0
- package/src/shared/outcome.ts +48 -0
- package/src/shared/types.ts +37 -0
- package/src/types.ts +20 -911
- package/src/view/aggregate.ts +103 -0
- package/src/view/app/App.tsx +26 -5
- package/src/view/app/components/AttemptModal.tsx +12 -3
- package/src/view/app/components/CodeView.tsx +9 -5
- package/src/view/app/components/CopyControls.tsx +4 -4
- package/src/view/app/components/ExperimentTable.tsx +5 -5
- package/src/view/app/i18n.ts +31 -7
- package/src/view/app/lib/format.ts +17 -20
- package/src/view/app/lib/outcome.ts +4 -16
- package/src/view/app/main.tsx +3 -5
- package/src/view/app/pages/RunsPage.tsx +2 -2
- package/src/view/app/pages/TracesPage.tsx +2 -2
- package/src/view/app/shared.ts +2 -1
- package/src/view/app/types.ts +6 -43
- package/src/view/client-dist/app.css +1 -1
- package/src/view/client-dist/app.js +18 -18
- package/src/view/index.ts +24 -401
- package/src/view/loader.ts +234 -0
- package/src/view/server.ts +136 -0
- package/src/view/shared/types.ts +64 -0
- package/src/view/styles.css +60 -9
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
// context 域类型:eval 作者拿到的 `t`(TestContext)及其子句柄(turn / session / sandbox 视图)。
|
|
2
|
+
// `t` 的形状按 Agent 能力组装(见 docs/architecture.md「能力决定形状」)。
|
|
3
|
+
|
|
4
|
+
import type { InputRequest, StreamEvent, ToolCall, Usage } from "../o11y/types.ts";
|
|
5
|
+
import type { AssertionHandle, ValueAssertion } from "../scoring/types.ts";
|
|
6
|
+
import type {
|
|
7
|
+
CommandOptions,
|
|
8
|
+
CommandResult,
|
|
9
|
+
ReadSourceFilesOptions,
|
|
10
|
+
SandboxFile,
|
|
11
|
+
SourceFiles,
|
|
12
|
+
} from "../sandbox/types.ts";
|
|
13
|
+
|
|
14
|
+
/** t.send() 返回的句柄:从事件流派生便利字段 + expectOk。 */
|
|
15
|
+
export interface TurnHandle {
|
|
16
|
+
readonly events: StreamEvent[];
|
|
17
|
+
readonly toolCalls: readonly ToolCall[];
|
|
18
|
+
readonly status: "completed" | "failed" | "waiting";
|
|
19
|
+
readonly message: string;
|
|
20
|
+
readonly data?: unknown;
|
|
21
|
+
readonly usage?: Usage;
|
|
22
|
+
/** 上一轮若 failed 则抛(中止后续)。 */
|
|
23
|
+
expectOk(): TurnHandle;
|
|
24
|
+
outputEquals(value: unknown): AssertionHandle;
|
|
25
|
+
outputMatches(schema: unknown): AssertionHandle;
|
|
26
|
+
/** 断言本轮助手回复包含 token(仅限本轮事件流,不跨轮)。 */
|
|
27
|
+
messageIncludes(token: string | RegExp): AssertionHandle;
|
|
28
|
+
succeeded(): AssertionHandle;
|
|
29
|
+
calledTool(name: string, match?: ToolMatch): AssertionHandle;
|
|
30
|
+
notCalledTool(name: string, match?: ToolMatch): AssertionHandle;
|
|
31
|
+
toolOrder(names: string[]): AssertionHandle;
|
|
32
|
+
usedNoTools(): AssertionHandle;
|
|
33
|
+
maxToolCalls(max: number): AssertionHandle;
|
|
34
|
+
event(type: StreamEvent["type"], opts?: { count?: number }): AssertionHandle;
|
|
35
|
+
notEvent(type: StreamEvent["type"]): AssertionHandle;
|
|
36
|
+
calledSubagent(name: string, match?: SubagentMatch): AssertionHandle;
|
|
37
|
+
eventOrder(types: StreamEvent["type"][]): AssertionHandle;
|
|
38
|
+
eventsSatisfy(predicate: (events: readonly StreamEvent[]) => boolean, label?: string): AssertionHandle;
|
|
39
|
+
readonly judge: JudgeNamespace;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** autoevals 子命名空间:结构化的参考材料对照评估(closedQA / factuality / summarizes)。 */
|
|
43
|
+
export interface AutoevalsNamespace {
|
|
44
|
+
closedQA(question: string, opts?: { on?: string; model?: string }): AssertionHandle;
|
|
45
|
+
factuality(expected: string, opts?: { on?: string; model?: string }): AssertionHandle;
|
|
46
|
+
summarizes(source: string, opts?: { on?: string; model?: string }): AssertionHandle;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface JudgeNamespace {
|
|
50
|
+
/** 结构化对照评估的子命名空间(t.judge.autoevals.closedQA / .factuality / .summarizes)。 */
|
|
51
|
+
autoevals: AutoevalsNamespace;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface DiffView {
|
|
55
|
+
get(path: string): string | undefined;
|
|
56
|
+
isEmpty(): boolean;
|
|
57
|
+
matches(re: RegExp): boolean;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** 工具匹配小语言。 */
|
|
61
|
+
export interface ToolMatch {
|
|
62
|
+
input?: Record<string, unknown>;
|
|
63
|
+
count?: number;
|
|
64
|
+
status?: "completed" | "failed" | "rejected";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface SubagentMatch {
|
|
68
|
+
count?: number;
|
|
69
|
+
status?: "completed" | "failed";
|
|
70
|
+
remoteUrl?: string | RegExp;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface InputRequestFilter {
|
|
74
|
+
id?: string | RegExp;
|
|
75
|
+
prompt?: string | RegExp;
|
|
76
|
+
display?: string | RegExp;
|
|
77
|
+
action?: string | RegExp;
|
|
78
|
+
input?: Record<string, unknown>;
|
|
79
|
+
optionIds?: readonly string[];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* `t.respond(...)` 的对象形式:显式指名回答的是哪条请求(多个请求并停时用它消歧,
|
|
84
|
+
* 字符串形式做不到)。`optionId` 与 `text` 二选一——`optionId` 必须存在于
|
|
85
|
+
* `request.options` 里,写错直接抛;`text` 是自由文本,不做校验。
|
|
86
|
+
*/
|
|
87
|
+
export interface RespondAnswer {
|
|
88
|
+
readonly request: InputRequest;
|
|
89
|
+
readonly optionId?: string;
|
|
90
|
+
readonly text?: string;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** eval 作者可见的受限沙箱视图:能执行命令 / 文件 IO / 读最终 diff,但不能 stop。 */
|
|
94
|
+
export interface SandboxHandle {
|
|
95
|
+
readonly workdir: string;
|
|
96
|
+
runCommand(cmd: string, args?: string[], opts?: CommandOptions): Promise<CommandResult>;
|
|
97
|
+
runShell(script: string, opts?: CommandOptions): Promise<CommandResult>;
|
|
98
|
+
readFile(path: string): Promise<string>;
|
|
99
|
+
fileExists(path: string): Promise<boolean>;
|
|
100
|
+
readSourceFiles(opts?: ReadSourceFilesOptions): Promise<SourceFiles>;
|
|
101
|
+
writeFiles(files: Record<string, string>, targetDir?: string): Promise<void>;
|
|
102
|
+
uploadFiles(files: SandboxFile[], targetDir?: string): Promise<void>;
|
|
103
|
+
uploadDirectory(localDir: string, targetDir?: string, opts?: { ignore?: string[] }): Promise<void>;
|
|
104
|
+
downloadFile(path: string): Promise<Buffer>;
|
|
105
|
+
uploadFile(path: string, content: Buffer): Promise<void>;
|
|
106
|
+
readonly sandboxId: string;
|
|
107
|
+
readonly diff: DiffView;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export interface SessionHandle {
|
|
111
|
+
send(text: string): Promise<TurnHandle>;
|
|
112
|
+
sendFile(path: string, text?: string): Promise<TurnHandle>;
|
|
113
|
+
requireInputRequest(filter?: InputRequestFilter): InputRequest;
|
|
114
|
+
respond(...responses: (string | RespondAnswer)[]): Promise<TurnHandle>;
|
|
115
|
+
respondAll(optionId: string): Promise<TurnHandle>;
|
|
116
|
+
readonly reply: string;
|
|
117
|
+
readonly sessionId: string | undefined;
|
|
118
|
+
readonly events: readonly StreamEvent[];
|
|
119
|
+
succeeded(): AssertionHandle;
|
|
120
|
+
parked(): AssertionHandle;
|
|
121
|
+
messageIncludes(token: string | RegExp): AssertionHandle;
|
|
122
|
+
calledTool(name: string, match?: ToolMatch): AssertionHandle;
|
|
123
|
+
notCalledTool(name: string, match?: ToolMatch): AssertionHandle;
|
|
124
|
+
toolOrder(names: string[]): AssertionHandle;
|
|
125
|
+
usedNoTools(): AssertionHandle;
|
|
126
|
+
maxToolCalls(max: number): AssertionHandle;
|
|
127
|
+
loadedSkill(skill: string): AssertionHandle;
|
|
128
|
+
noFailedActions(): AssertionHandle;
|
|
129
|
+
event(type: StreamEvent["type"], opts?: { count?: number }): AssertionHandle;
|
|
130
|
+
notEvent(type: StreamEvent["type"]): AssertionHandle;
|
|
131
|
+
calledSubagent(name: string, match?: SubagentMatch): AssertionHandle;
|
|
132
|
+
eventOrder(types: StreamEvent["type"][]): AssertionHandle;
|
|
133
|
+
eventsSatisfy(predicate: (events: readonly StreamEvent[]) => boolean, label?: string): AssertionHandle;
|
|
134
|
+
maxTokens(max: number): AssertionHandle;
|
|
135
|
+
maxCost(usd: number): AssertionHandle;
|
|
136
|
+
readonly usage: Usage;
|
|
137
|
+
readonly judge: JudgeNamespace;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* eval 作者拿到的高层上下文。运行器按 agent 能力组装;tsx 不做类型检查,所以这里
|
|
142
|
+
* 用一个宽接口承载全部动作(运行时按 capability 守卫)。
|
|
143
|
+
*/
|
|
144
|
+
export interface TestContext {
|
|
145
|
+
// 会话
|
|
146
|
+
send(text: string): Promise<TurnHandle>;
|
|
147
|
+
/** 发一条带文件(图片等多模态输入)的消息。`path` 相对项目根;读出后 base64 随 TurnInput.files 交给 adapter。 */
|
|
148
|
+
sendFile(path: string, text?: string): Promise<TurnHandle>;
|
|
149
|
+
requireInputRequest(filter?: InputRequestFilter): InputRequest;
|
|
150
|
+
respond(...responses: (string | RespondAnswer)[]): Promise<TurnHandle>;
|
|
151
|
+
respondAll(optionId: string): Promise<TurnHandle>;
|
|
152
|
+
readonly reply: string;
|
|
153
|
+
readonly sessionId: string | undefined;
|
|
154
|
+
readonly events: readonly StreamEvent[];
|
|
155
|
+
newSession(): SessionHandle;
|
|
156
|
+
|
|
157
|
+
// 运行上下文
|
|
158
|
+
readonly signal: AbortSignal;
|
|
159
|
+
readonly model?: string;
|
|
160
|
+
readonly flags: Readonly<Record<string, unknown>>;
|
|
161
|
+
log(msg: string): void;
|
|
162
|
+
skip(reason: string): never;
|
|
163
|
+
|
|
164
|
+
// 值级断言
|
|
165
|
+
check(value: unknown, assertion: ValueAssertion): AssertionHandle;
|
|
166
|
+
require(value: unknown, assertion: ValueAssertion): Promise<unknown>;
|
|
167
|
+
/**
|
|
168
|
+
* 把一组断言归到一个有标题的分组下(对照 vitest 的 test('title', ...))。纯组织/报告用,
|
|
169
|
+
* 不改打分:组里每条断言仍独立计分。可嵌套(标题用 › 连接)。
|
|
170
|
+
*/
|
|
171
|
+
group<T>(title: string, fn: () => Promise<T> | T): Promise<T>;
|
|
172
|
+
|
|
173
|
+
// 作用域断言(工具 / 会话)
|
|
174
|
+
succeeded(): AssertionHandle;
|
|
175
|
+
parked(): AssertionHandle;
|
|
176
|
+
messageIncludes(token: string | RegExp): AssertionHandle;
|
|
177
|
+
calledTool(name: string, match?: ToolMatch): AssertionHandle;
|
|
178
|
+
notCalledTool(name: string, match?: ToolMatch): AssertionHandle;
|
|
179
|
+
toolOrder(names: string[]): AssertionHandle;
|
|
180
|
+
usedNoTools(): AssertionHandle;
|
|
181
|
+
maxToolCalls(max: number): AssertionHandle;
|
|
182
|
+
loadedSkill(skill: string): AssertionHandle;
|
|
183
|
+
noFailedActions(): AssertionHandle;
|
|
184
|
+
event(type: StreamEvent["type"], opts?: { count?: number }): AssertionHandle;
|
|
185
|
+
notEvent(type: StreamEvent["type"]): AssertionHandle;
|
|
186
|
+
calledSubagent(name: string, match?: SubagentMatch): AssertionHandle;
|
|
187
|
+
eventOrder(types: StreamEvent["type"][]): AssertionHandle;
|
|
188
|
+
eventsSatisfy(predicate: (events: readonly StreamEvent[]) => boolean, label?: string): AssertionHandle;
|
|
189
|
+
|
|
190
|
+
// 工作区 / 沙箱
|
|
191
|
+
readonly sandbox: SandboxHandle;
|
|
192
|
+
file(path: string): string;
|
|
193
|
+
fileChanged(path: string): AssertionHandle;
|
|
194
|
+
fileDeleted(path: string): AssertionHandle;
|
|
195
|
+
notInDiff(re: RegExp): AssertionHandle;
|
|
196
|
+
noFailedShellCommands(): AssertionHandle;
|
|
197
|
+
|
|
198
|
+
// 效率 / 成本
|
|
199
|
+
readonly usage: Usage;
|
|
200
|
+
maxTokens(max: number): AssertionHandle;
|
|
201
|
+
maxCost(usd: number): AssertionHandle;
|
|
202
|
+
|
|
203
|
+
// judge
|
|
204
|
+
readonly judge: JudgeNamespace;
|
|
205
|
+
}
|
package/src/define.ts
CHANGED
|
@@ -15,26 +15,15 @@ import type {
|
|
|
15
15
|
} from "./types.ts";
|
|
16
16
|
import { t } from "./i18n/index.ts";
|
|
17
17
|
|
|
18
|
-
const SANDBOX_DEFAULT_CAPS = {
|
|
19
|
-
conversation: true,
|
|
20
|
-
toolObservability: true,
|
|
21
|
-
workspace: true,
|
|
22
|
-
sandbox: true,
|
|
23
|
-
} as const;
|
|
24
|
-
|
|
25
|
-
const REMOTE_DEFAULT_CAPS = {
|
|
26
|
-
conversation: true,
|
|
27
|
-
toolObservability: true,
|
|
28
|
-
} as const;
|
|
29
|
-
|
|
30
18
|
/** 沙箱型 agent:在沙箱里 spawn 一个 coding agent 的 CLI,跑完读回 transcript。 */
|
|
31
19
|
export function defineSandboxAgent(def: SandboxAgentDef): Agent {
|
|
32
20
|
if (!def.name) throw new Error(t("define.sandboxAgentNameRequired"));
|
|
33
21
|
return {
|
|
34
22
|
name: def.name,
|
|
35
|
-
|
|
23
|
+
kind: "sandbox",
|
|
36
24
|
setup: def.setup,
|
|
37
25
|
tracing: def.tracing,
|
|
26
|
+
spanMapper: def.spanMapper,
|
|
38
27
|
send: def.send,
|
|
39
28
|
teardown: def.teardown,
|
|
40
29
|
};
|
|
@@ -45,9 +34,10 @@ export function defineAgent(def: RemoteAgentDef): Agent {
|
|
|
45
34
|
if (!def.name) throw new Error(t("define.agentNameRequired"));
|
|
46
35
|
return {
|
|
47
36
|
name: def.name,
|
|
48
|
-
|
|
37
|
+
kind: "remote",
|
|
49
38
|
setup: def.setup,
|
|
50
39
|
tracing: def.tracing,
|
|
40
|
+
spanMapper: def.spanMapper,
|
|
51
41
|
send: def.send,
|
|
52
42
|
teardown: def.teardown,
|
|
53
43
|
};
|
package/src/expect/index.ts
CHANGED
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
|
|
4
4
|
import type { Severity, ValueAssertion } from "../types.ts";
|
|
5
5
|
import { stripComments } from "../util.ts";
|
|
6
|
+
import { deepEqual, validateSchema } from "../scoring/match.ts";
|
|
7
|
+
|
|
8
|
+
// 自定义匹配器作者用的公共类型(docs/scoring.md 的 `Assertion` 即它)。
|
|
9
|
+
export type { Severity, ValueAssertion } from "../types.ts";
|
|
10
|
+
export type { ValueAssertion as Assertion } from "../types.ts";
|
|
6
11
|
|
|
7
12
|
/** includes / excludes 的可选项。stripComments:先剥注释再匹配(只对真实代码生效)。 */
|
|
8
13
|
export interface MatchOptions {
|
|
@@ -45,48 +50,6 @@ function safeLabel(v: unknown): string {
|
|
|
45
50
|
}
|
|
46
51
|
}
|
|
47
52
|
|
|
48
|
-
/** 小而全的深比较:处理基本值、NaN、数组、Date、纯对象。 */
|
|
49
|
-
function deepEqual(a: unknown, b: unknown): boolean {
|
|
50
|
-
if (a === b) return true;
|
|
51
|
-
|
|
52
|
-
// NaN === NaN
|
|
53
|
-
if (typeof a === "number" && typeof b === "number") {
|
|
54
|
-
return Number.isNaN(a) && Number.isNaN(b);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (a === null || b === null || typeof a !== "object" || typeof b !== "object") {
|
|
58
|
-
return false;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Date 按时间戳比
|
|
62
|
-
if (a instanceof Date || b instanceof Date) {
|
|
63
|
-
return a instanceof Date && b instanceof Date && a.getTime() === b.getTime();
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const aIsArr = Array.isArray(a);
|
|
67
|
-
const bIsArr = Array.isArray(b);
|
|
68
|
-
if (aIsArr !== bIsArr) return false;
|
|
69
|
-
|
|
70
|
-
if (aIsArr && bIsArr) {
|
|
71
|
-
if (a.length !== b.length) return false;
|
|
72
|
-
for (let i = 0; i < a.length; i++) {
|
|
73
|
-
if (!deepEqual(a[i], b[i])) return false;
|
|
74
|
-
}
|
|
75
|
-
return true;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const ao = a as Record<string, unknown>;
|
|
79
|
-
const bo = b as Record<string, unknown>;
|
|
80
|
-
const ak = Object.keys(ao);
|
|
81
|
-
const bk = Object.keys(bo);
|
|
82
|
-
if (ak.length !== bk.length) return false;
|
|
83
|
-
for (const k of ak) {
|
|
84
|
-
if (!Object.prototype.hasOwnProperty.call(bo, k)) return false;
|
|
85
|
-
if (!deepEqual(ao[k], bo[k])) return false;
|
|
86
|
-
}
|
|
87
|
-
return true;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
53
|
/** 经典 DP 编辑距离(滚动两行)。 */
|
|
91
54
|
function levenshtein(a: string, b: string): number {
|
|
92
55
|
const m = a.length;
|
|
@@ -153,35 +116,9 @@ export function equals(expected: unknown): ValueAssertion {
|
|
|
153
116
|
* 否则退化到 zod 风格的 .safeParse / .parse。校验通过 1,否则 0;任何异常 → 0。
|
|
154
117
|
*/
|
|
155
118
|
export function matches(schema: unknown): ValueAssertion {
|
|
156
|
-
return createAssertion("matches(schema)", "gate", async (value) =>
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
"~standard"
|
|
160
|
-
];
|
|
161
|
-
if (std && typeof std.validate === "function") {
|
|
162
|
-
// validate 可能同步也可能返回 Promise;成功结果不带 issues。
|
|
163
|
-
const result = (await std.validate(value)) as { issues?: unknown } | null | undefined;
|
|
164
|
-
return result != null && result.issues == null ? 1 : 0;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const zodish = schema as {
|
|
168
|
-
safeParse?: (v: unknown) => { success?: boolean };
|
|
169
|
-
parse?: (v: unknown) => unknown;
|
|
170
|
-
} | null;
|
|
171
|
-
|
|
172
|
-
if (zodish && typeof zodish.safeParse === "function") {
|
|
173
|
-
const result = zodish.safeParse(value);
|
|
174
|
-
return result && result.success ? 1 : 0;
|
|
175
|
-
}
|
|
176
|
-
if (zodish && typeof zodish.parse === "function") {
|
|
177
|
-
zodish.parse(value);
|
|
178
|
-
return 1;
|
|
179
|
-
}
|
|
180
|
-
return 0;
|
|
181
|
-
} catch {
|
|
182
|
-
return 0;
|
|
183
|
-
}
|
|
184
|
-
});
|
|
119
|
+
return createAssertion("matches(schema)", "gate", async (value) =>
|
|
120
|
+
(await validateSchema(value, schema)) ? 1 : 0,
|
|
121
|
+
);
|
|
185
122
|
}
|
|
186
123
|
|
|
187
124
|
/** 归一化 Levenshtein 相似度 [0,1]。默认软分,阈值 0.6。 */
|
package/src/i18n/core.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// i18n 内核:CLI(src/i18n/index.ts)与 view 前端(src/view/app/i18n.ts)共用。
|
|
2
|
+
// 必须保持环境无关(不 import node/browser API),vite 前端和 node CLI 都直接打包它。
|
|
3
|
+
// 两侧只在这里之外各自注入 locale 来源(env vs navigator+localStorage)与默认值(zh-CN vs en)。
|
|
4
|
+
|
|
5
|
+
export type Locale = "zh-CN" | "en";
|
|
6
|
+
|
|
7
|
+
export type Vars = Record<string, string | number | boolean | undefined>;
|
|
8
|
+
|
|
9
|
+
/** 一份字典:消息 key → 文案(可含 {{var}} 占位)。 */
|
|
10
|
+
export type Dictionary<K extends string = string> = Record<K, string>;
|
|
11
|
+
|
|
12
|
+
/** 把 {{var}} 占位替换成 vars 里的值;缺失的变量替换成空串而不是保留占位。 */
|
|
13
|
+
export function interpolate(message: string, vars: Vars = {}): string {
|
|
14
|
+
return message.replace(/\{\{\s*([a-zA-Z0-9_]+)\s*\}\}/g, (_match, name: string) =>
|
|
15
|
+
vars[name] === undefined ? "" : String(vars[name]),
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 把 "zh_CN"、"en-US"、navigator.language 这类原始值归一成受支持的 Locale。
|
|
21
|
+
* C / POSIX 视为「未指定」返回 undefined(交给下一个候选);非中文一律落 en。
|
|
22
|
+
*/
|
|
23
|
+
export function normalizeLocale(raw: string | undefined): Locale | undefined {
|
|
24
|
+
if (!raw) return undefined;
|
|
25
|
+
const value = raw.trim().toLowerCase().replace("_", "-");
|
|
26
|
+
if (!value || value === "c" || value === "posix") return undefined;
|
|
27
|
+
return value.startsWith("zh") ? "zh-CN" : "en";
|
|
28
|
+
}
|
package/src/i18n/en.ts
CHANGED
|
@@ -3,12 +3,14 @@ import type { Messages } from "./zh-CN.ts";
|
|
|
3
3
|
export const en = {
|
|
4
4
|
"agent.installFailed": "Install failed: {{key}}\n{{tail}}",
|
|
5
5
|
"agent.unknown": "Unknown agent \"{{name}}\". Registered agents: {{known}}.",
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
6
|
+
"agent.diagnose.exitCode": "agent run exited with code {{code}}",
|
|
7
|
+
"agent.diagnose.lastError": "last error: {{message}}",
|
|
8
|
+
"agent.diagnose.noTranscript": "transcript was not generated",
|
|
9
|
+
"agent.diagnose.outputTail": "output tail: {{tail}}",
|
|
10
|
+
"agent.diagnose.zeroEvents": "transcript exists but contains 0 events",
|
|
11
|
+
"bub.homeDetectFailed": "Failed to detect sandbox $HOME (empty output from `printf $HOME`). Refusing to fall back to a backend-specific path; check the sandbox backend.",
|
|
11
12
|
"bub.installFailed": "bub install failed after {{attempts}} attempts:\n{{tail}}",
|
|
13
|
+
"bub.setupNotRun": "bub adapter setup() has not run in this sandbox (missing home/workspace info). The runner must call setup before send.",
|
|
12
14
|
"checkpoint.emptyTar": "checkpoint: tar is empty (paths: {{paths}})",
|
|
13
15
|
"cli.all": "(all)",
|
|
14
16
|
"cli.browserOpenFailed": "Could not open the browser automatically. Open manually: {{url}}\n",
|
|
@@ -19,6 +21,30 @@ export const en = {
|
|
|
19
21
|
"cli.dry.noMatches": "(no matches)",
|
|
20
22
|
"cli.dry.row": " {{who}}{{experiment}}: {{evals}} ×{{runs}}\n",
|
|
21
23
|
"cli.error": "niceeval error: {{error}}\n",
|
|
24
|
+
"cli.flag.invalidNumber": "Flag --{{flag}} expects a number, got \"{{value}}\".\n",
|
|
25
|
+
"runner.budgetUnenforceable":
|
|
26
|
+
"budget for {{budgetKey}}: several attempts completed without any cost data (agent reports no usage and the model is not in the price table) — the budget cannot be enforced for this agent; continuing without the guard.\n",
|
|
27
|
+
"judge.modelMissing":
|
|
28
|
+
"No judge model configured. Set it in defineConfig({ judge: { model: \"...\" } }), the eval's judge config, or the NICEEVAL_JUDGE_MODEL environment variable (there is no built-in default model).",
|
|
29
|
+
"loaders.yamlMissing":
|
|
30
|
+
"loadYaml(\"{{path}}\") needs a YAML parser: run `pnpm add yaml` first (or switch to loadJson with a JSON dataset).",
|
|
31
|
+
"cli.flag.parseError": "{{message}}\nRun `niceeval --help` for usage.\n",
|
|
32
|
+
"cli.envInvalidNumber": "Environment variable {{name}} is not a number: \"{{value}}\".\n",
|
|
33
|
+
"cli.help":
|
|
34
|
+
"niceeval — agent-native evals\n\n" +
|
|
35
|
+
"Usage:\n" +
|
|
36
|
+
" niceeval exp [group|experiment] [eval-id-prefix…] run experiments\n" +
|
|
37
|
+
" niceeval list list discovered evals\n" +
|
|
38
|
+
" niceeval view [summary.json|dir] [--out d] [--port n] [--no-open]\n" +
|
|
39
|
+
" niceeval clean delete .niceeval/ artifacts\n" +
|
|
40
|
+
" niceeval init scaffold config + evals/\n\n" +
|
|
41
|
+
"Flags:\n" +
|
|
42
|
+
" --runs n --max-concurrency n --timeout ms --budget usd --tag t\n" +
|
|
43
|
+
" --early-exit / --no-early-exit --strict --force --dry --quiet\n" +
|
|
44
|
+
" --junit path --out dir --port n --open / --no-open -h, --help\n\n" +
|
|
45
|
+
"Positional args only select which evals to run (id prefixes); which agent and\n" +
|
|
46
|
+
"how to run come from experiments/ + flags. Env overrides (flag > env > config):\n" +
|
|
47
|
+
" NICEEVAL_RUNS NICEEVAL_MAX_CONCURRENCY NICEEVAL_TIMEOUT NICEEVAL_BUDGET\n",
|
|
22
48
|
"cli.eval.noMatch": "No eval matched: {{patterns}}.\n",
|
|
23
49
|
"cli.eval.noMatchHintExperiment": "Hint: \"{{pattern}}\" is an experiment{{kind}}; you probably meant: niceeval exp {{pattern}}\n",
|
|
24
50
|
"cli.eval.noMatchKnown": "Discovered {{count}} evals: {{evals}}\n",
|
|
@@ -36,9 +62,13 @@ export const en = {
|
|
|
36
62
|
"cli.run.experimentRequired": "Run evals through an experiment: use `niceeval exp [group|config] [eval id prefix]`.\n",
|
|
37
63
|
"cli.run.experimentRequiredHint": "Hint: \"{{pattern}}\" is an experiment{{kind}}; you probably meant: niceeval exp {{pattern}}\n",
|
|
38
64
|
"cli.run.experimentRequiredKnown": "Discovered experiments: {{experiments}}\n",
|
|
65
|
+
"cli.sandboxFlagRemoved": "`--sandbox` is not a CLI flag. Set `sandbox` in the experiment (or `niceeval.config.ts` as a project-wide fallback) to dockerSandbox() / vercelSandbox() / e2bSandbox() (import from \"niceeval/sandbox\").\n",
|
|
39
66
|
"cli.unimplemented": "Command \"{{command}}\" is not implemented yet (MVP).\n",
|
|
40
67
|
"cli.view.exported": "Exported eval report page: {{out}}\n",
|
|
68
|
+
"cli.view.incompatible": "{{dir}}: written by niceeval {{producer}} (schemaVersion {{schemaVersion}}); this CLI reads schemaVersion {{supported}}.\nRun `{{command}}` to view it.\n",
|
|
41
69
|
"cli.view.url": "niceeval view: {{url}}\n",
|
|
70
|
+
"context.capabilityMissing":
|
|
71
|
+
"Agent \"{{agent}}\" is not sandbox-backed (built with defineSandboxAgent), so t.{{method}} is unavailable. Use an agent built with defineSandboxAgent, or drop this assertion.",
|
|
42
72
|
"context.skipEmpty": "skip() requires a non-empty reason.",
|
|
43
73
|
"context.turnFailed": "This send returned failed (turn status = failed): {{message}}",
|
|
44
74
|
"context.turnFailedDefault": "This send returned failed (turn status = failed)",
|
|
@@ -56,6 +86,13 @@ export const en = {
|
|
|
56
86
|
"docker.imagePullStart": "Pulling Docker image: {{image}}...",
|
|
57
87
|
"docker.readFileFailed": "Failed to read file {{path}}: {{stderr}}",
|
|
58
88
|
"docker.unsupportedRuntime": "Unsupported runtime: {{runtime}}",
|
|
89
|
+
"hitl.answerNeedsOptionOrText": "The object form of t.respond needs either optionId or text (neither was given).",
|
|
90
|
+
"hitl.invalidOption": "Answer \"{{optionId}}\" is not an option of request {{requestId}} ({{options}}).",
|
|
91
|
+
"hitl.noOptions": "this request has no options",
|
|
92
|
+
"hitl.requestMissingId": "This input.requested request has no stable id, so a response cannot be built — the adapter must give every pending request a stable id.",
|
|
93
|
+
"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().",
|
|
94
|
+
"hitl.respondEmpty": "t.respond(...) requires at least one answer.",
|
|
95
|
+
"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.",
|
|
59
96
|
"judge.apiKeyMissing": "judge is missing an API key (CODEX_API_KEY / OPENAI_API_KEY).",
|
|
60
97
|
"judge.httpError": "judge HTTP {{status}}: {{body}}",
|
|
61
98
|
"judge.probeFailed": "judge precheck failed ({{model}}): {{error}}",
|
|
@@ -93,6 +130,9 @@ export const en = {
|
|
|
93
130
|
"report.table.status": "Status",
|
|
94
131
|
"report.table.successRate": "Success Rate",
|
|
95
132
|
"report.table.tokens": "Tokens",
|
|
133
|
+
"otel.noSpans": "otel: 0 spans this turn — endpoint not wired? (env not injected / service not restarted / no flush)",
|
|
134
|
+
"otel.portInUse": "OTLP receiver port {{port}} is already in use (another process is bound to it). Pick a free port in defineConfig({ telemetry: { port } }), or stop whatever is using {{port}} and retry.",
|
|
135
|
+
"otel.windowAttribution": "otel: spans missing our traceparent, attributing by time window (turns for this agent serialized; concurrency resumes once W3C propagation is confirmed)",
|
|
96
136
|
"runner.diffProgress": "captured diff: {{changed}} changed / {{deleted}} deleted",
|
|
97
137
|
"runner.driveAgent": "driving agent...",
|
|
98
138
|
"runner.evalSetup": "eval setup (installing dependencies)...",
|
|
@@ -101,6 +141,7 @@ export const en = {
|
|
|
101
141
|
"runner.otlpInSandbox": "OTLP in-sandbox collector -> {{endpoint}}{{proto}}",
|
|
102
142
|
"runner.otlpOverride": "OTLP receiver (host override) -> {{endpoint}}",
|
|
103
143
|
"runner.otlpReceiver": "OTLP receiver -> {{endpoint}}{{proto}}",
|
|
144
|
+
"runner.otlpShared": "OTLP shared receiver (run-scoped) -> {{endpoint}}",
|
|
104
145
|
"runner.remoteSandboxUnavailable": "remote agents do not have sandbox.{{method}}; use a sandbox agent or remove workspace assertions.",
|
|
105
146
|
"runner.reporterDiagnostic": " · [diagnostic] {{stage}} failed (ignored): {{message}}\n",
|
|
106
147
|
"runner.scoreJudge": "scoring / judge...",
|
|
@@ -113,6 +154,7 @@ export const en = {
|
|
|
113
154
|
"runner.resumeCarry": " · reusing {{carried}} passing results from last run, re-running {{retry}} evals\n",
|
|
114
155
|
"runner.useRemoteAgent": "using remote agent (no sandbox created)...",
|
|
115
156
|
"sandbox.backendNotImplemented": "{{backend}} sandbox backend is not implemented; use docker, vercel, or e2b",
|
|
157
|
+
"sandbox.missingSpec": "sandbox agent needs a sandbox, but none was given. niceeval no longer picks a default — set `sandbox` in defineExperiment()/defineConfig() to dockerSandbox() / vercelSandbox() / e2bSandbox() (import from \"niceeval/sandbox\").",
|
|
116
158
|
"sandbox.dependencyMissing.docker": "Docker sandbox requires 'dockerode'. Install it with: pnpm add dockerode @types/dockerode",
|
|
117
159
|
"sandbox.dependencyMissing.e2b": "E2B sandbox requires 'e2b'. Install it with: pnpm add e2b",
|
|
118
160
|
"sandbox.dependencyMissing.vercel": "Vercel sandbox requires '@vercel/sandbox'. Install it with: pnpm add @vercel/sandbox",
|
package/src/i18n/index.ts
CHANGED
|
@@ -1,25 +1,16 @@
|
|
|
1
|
+
// CLI 侧 i18n:内核(插值/归一)在 core.ts;这里只注入 env 来源与 zh-CN 默认值。
|
|
2
|
+
|
|
1
3
|
import { en } from "./en.ts";
|
|
2
4
|
import { zhCN, type MessageKey, type Messages } from "./zh-CN.ts";
|
|
5
|
+
import { interpolate, normalizeLocale, type Locale, type Vars } from "./core.ts";
|
|
3
6
|
|
|
4
|
-
export type Locale
|
|
5
|
-
|
|
6
|
-
type Vars = Record<string, string | number | boolean | undefined>;
|
|
7
|
+
export type { Locale, Vars } from "./core.ts";
|
|
7
8
|
|
|
8
9
|
const dictionaries: Record<Locale, Messages> = {
|
|
9
10
|
"zh-CN": zhCN,
|
|
10
11
|
en,
|
|
11
12
|
};
|
|
12
13
|
|
|
13
|
-
function normalizeLocale(raw: string | undefined): Locale | undefined {
|
|
14
|
-
if (!raw) return undefined;
|
|
15
|
-
const value = raw.trim().toLowerCase().replace("_", "-");
|
|
16
|
-
if (!value) return undefined;
|
|
17
|
-
if (value === "c" || value === "posix") return undefined;
|
|
18
|
-
if (value.startsWith("zh")) return "zh-CN";
|
|
19
|
-
if (value.startsWith("en")) return "en";
|
|
20
|
-
return "en";
|
|
21
|
-
}
|
|
22
|
-
|
|
23
14
|
export function detectLocale(env: NodeJS.ProcessEnv = process.env): Locale {
|
|
24
15
|
return (
|
|
25
16
|
normalizeLocale(env.NICEEVAL_LANG) ??
|
|
@@ -32,8 +23,5 @@ export function detectLocale(env: NodeJS.ProcessEnv = process.env): Locale {
|
|
|
32
23
|
}
|
|
33
24
|
|
|
34
25
|
export function t(key: MessageKey, vars: Vars = {}): string {
|
|
35
|
-
|
|
36
|
-
return message.replace(/\{\{\s*([a-zA-Z0-9_]+)\s*\}\}/g, (_match, name: string) =>
|
|
37
|
-
vars[name] === undefined ? "" : String(vars[name]),
|
|
38
|
-
);
|
|
26
|
+
return interpolate(dictionaries[detectLocale()][key], vars);
|
|
39
27
|
}
|
package/src/i18n/zh-CN.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
export const zhCN = {
|
|
2
2
|
"agent.installFailed": "安装失败:{{key}}\n{{tail}}",
|
|
3
3
|
"agent.unknown": "未知 agent \"{{name}}\"。已注册:{{known}}。",
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
4
|
+
"agent.diagnose.exitCode": "agent 运行退出码 {{code}}",
|
|
5
|
+
"agent.diagnose.lastError": "最后错误:{{message}}",
|
|
6
|
+
"agent.diagnose.noTranscript": "transcript 未生成",
|
|
7
|
+
"agent.diagnose.outputTail": "输出末尾:{{tail}}",
|
|
8
|
+
"agent.diagnose.zeroEvents": "transcript 存在但 0 事件",
|
|
9
|
+
"bub.homeDetectFailed": "无法探测沙箱 $HOME(printf $HOME 输出为空)。不兜底到后端专属固定路径,请检查沙箱后端。",
|
|
9
10
|
"bub.installFailed": "bub 安装失败(重试 {{attempts}} 次):\n{{tail}}",
|
|
11
|
+
"bub.setupNotRun": "bub adapter 的 setup() 尚未在该沙箱运行(缺 home/workspace 信息);运行器应先调 setup 再 send。",
|
|
10
12
|
"checkpoint.emptyTar": "checkpoint: tar 为空(paths: {{paths}})",
|
|
11
13
|
"cli.all": "(全部)",
|
|
12
14
|
"cli.browserOpenFailed": "无法自动打开浏览器,请手动访问:{{url}}\n",
|
|
@@ -17,6 +19,30 @@ export const zhCN = {
|
|
|
17
19
|
"cli.dry.noMatches": "(无匹配)",
|
|
18
20
|
"cli.dry.row": " {{who}}{{experiment}}: {{evals}} ×{{runs}}\n",
|
|
19
21
|
"cli.error": "niceeval 出错:{{error}}\n",
|
|
22
|
+
"cli.flag.invalidNumber": "标志 --{{flag}} 需要数字,收到 \"{{value}}\"。\n",
|
|
23
|
+
"runner.budgetUnenforceable":
|
|
24
|
+
"{{budgetKey}} 的 budget:连续多个 attempt 完成后都拿不到成本数据(agent 不上报用量且模型不在价格表)——该 agent 的 budget 无法执行,取消护栏继续跑。\n",
|
|
25
|
+
"judge.modelMissing":
|
|
26
|
+
"judge 未配置模型:在 defineConfig({ judge: { model: \"...\" } })、eval 的 judge 配置或环境变量 NICEEVAL_JUDGE_MODEL 里指定评判模型(没有内置默认模型)。",
|
|
27
|
+
"loaders.yamlMissing":
|
|
28
|
+
"loadYaml(\"{{path}}\") 需要 YAML 解析器:请先 `pnpm add yaml`(或改用 loadJson + JSON 数据集)。",
|
|
29
|
+
"cli.flag.parseError": "{{message}}\n运行 `niceeval --help` 查看用法。\n",
|
|
30
|
+
"cli.envInvalidNumber": "环境变量 {{name}} 不是数字:\"{{value}}\"。\n",
|
|
31
|
+
"cli.help":
|
|
32
|
+
"niceeval — agent-native evals\n\n" +
|
|
33
|
+
"用法:\n" +
|
|
34
|
+
" niceeval exp [组|实验] [eval-id 前缀…] 跑实验\n" +
|
|
35
|
+
" niceeval list 列出发现到的 eval\n" +
|
|
36
|
+
" niceeval view [summary.json|目录] [--out 目录] [--port n] [--no-open]\n" +
|
|
37
|
+
" niceeval clean 删除 .niceeval/ 历史工件\n" +
|
|
38
|
+
" niceeval init 脚手架 config + evals/\n\n" +
|
|
39
|
+
"标志:\n" +
|
|
40
|
+
" --runs n --max-concurrency n --timeout ms --budget usd --tag t\n" +
|
|
41
|
+
" --early-exit / --no-early-exit --strict --force --dry --quiet\n" +
|
|
42
|
+
" --junit path --out dir --port n --open / --no-open -h, --help\n\n" +
|
|
43
|
+
"位置参数只选「跑哪些 eval」(id 前缀);对着哪个 agent、怎么跑来自 experiments/ 与\n" +
|
|
44
|
+
"标志。环境变量覆盖(标志 > 环境变量 > config):\n" +
|
|
45
|
+
" NICEEVAL_RUNS NICEEVAL_MAX_CONCURRENCY NICEEVAL_TIMEOUT NICEEVAL_BUDGET\n",
|
|
20
46
|
"cli.eval.noMatch": "没有匹配的 eval:{{patterns}}。\n",
|
|
21
47
|
"cli.eval.noMatchHintExperiment": "提示:\"{{pattern}}\" 是实验{{kind}},你大概想跑:niceeval exp {{pattern}}\n",
|
|
22
48
|
"cli.eval.noMatchKnown": "已发现 {{count}} 个 eval:{{evals}}\n",
|
|
@@ -34,9 +60,13 @@ export const zhCN = {
|
|
|
34
60
|
"cli.run.experimentRequired": "运行 eval 必须通过 experiment:用 `niceeval exp [实验组|配置] [eval id 前缀]`。\n",
|
|
35
61
|
"cli.run.experimentRequiredHint": "提示:\"{{pattern}}\" 是实验{{kind}},你大概想跑:niceeval exp {{pattern}}\n",
|
|
36
62
|
"cli.run.experimentRequiredKnown": "已发现实验:{{experiments}}\n",
|
|
63
|
+
"cli.sandboxFlagRemoved": "`--sandbox` 不是 CLI flag。请在 experiment(或 niceeval.config.ts 做全项目兜底)里把 sandbox 设成 dockerSandbox() / vercelSandbox() / e2bSandbox()(从 \"niceeval/sandbox\" 导入)。\n",
|
|
37
64
|
"cli.unimplemented": "命令 \"{{command}}\" 暂未实现(MVP)。\n",
|
|
38
65
|
"cli.view.exported": "已导出实验查看页:{{out}}\n",
|
|
66
|
+
"cli.view.incompatible": "{{dir}}: 由 niceeval {{producer}} 写入(schemaVersion {{schemaVersion}}),当前 CLI 只读 schemaVersion {{supported}}。\n运行 `{{command}}` 查看这份报告。\n",
|
|
39
67
|
"cli.view.url": "niceeval view: {{url}}\n",
|
|
68
|
+
"context.capabilityMissing":
|
|
69
|
+
"agent \"{{agent}}\" 不是沙箱型(defineSandboxAgent 构造),t.{{method}} 这类断言只有沙箱型 agent 可用。换用 defineSandboxAgent 构造的 agent,或去掉这条断言。",
|
|
40
70
|
"context.skipEmpty": "skip() 需要一个非空理由。",
|
|
41
71
|
"context.turnFailed": "本轮 send 返回 failed(turn status = failed):{{message}}",
|
|
42
72
|
"context.turnFailedDefault": "本轮 send 返回 failed(turn status = failed)",
|
|
@@ -54,6 +84,13 @@ export const zhCN = {
|
|
|
54
84
|
"docker.imagePullStart": "Pulling Docker image: {{image}}...",
|
|
55
85
|
"docker.readFileFailed": "Failed to read file {{path}}: {{stderr}}",
|
|
56
86
|
"docker.unsupportedRuntime": "Unsupported runtime: {{runtime}}",
|
|
87
|
+
"hitl.answerNeedsOptionOrText": "t.respond 的对象形式需要 optionId 或 text 二选一(两者都没给)。",
|
|
88
|
+
"hitl.invalidOption": "回答 \"{{optionId}}\" 不是请求 {{requestId}} 的可选项({{options}})。",
|
|
89
|
+
"hitl.noOptions": "该请求没有可选项",
|
|
90
|
+
"hitl.requestMissingId": "该 input.requested 请求没有稳定的 id,无法生成 responses——adapter 侧要给每条待回答请求一个稳定 id。",
|
|
91
|
+
"hitl.respondAllEmpty": "没有待回答的 input.requested 请求,respond() / respondAll() 无法工作;先用 t.parked() 确认停轮,再用 t.requireInputRequest() 或 t.respond() 回答。",
|
|
92
|
+
"hitl.respondEmpty": "t.respond(...) 至少需要一个回答。",
|
|
93
|
+
"hitl.stringAmbiguous": "有 {{count}} 条待回答请求,字符串回答无法对位,请用 { request, optionId } 或 { request, text } 对象形式显式指名。",
|
|
57
94
|
"judge.apiKeyMissing": "judge 缺少 API key(CODEX_API_KEY / OPENAI_API_KEY)。",
|
|
58
95
|
"judge.httpError": "judge HTTP {{status}}: {{body}}",
|
|
59
96
|
"judge.probeFailed": "judge 预检失败({{model}}): {{error}}",
|
|
@@ -91,6 +128,9 @@ export const zhCN = {
|
|
|
91
128
|
"report.table.status": "状态",
|
|
92
129
|
"report.table.successRate": "成功率",
|
|
93
130
|
"report.table.tokens": "Tokens",
|
|
131
|
+
"otel.noSpans": "otel:本轮 0 span —— 端点没接上?(env 没注入 / 服务没重启 / 没 flush)",
|
|
132
|
+
"otel.portInUse": "OTLP 接收端口 {{port}} 已被占用(另一个进程占着这个端口)。在 defineConfig({ telemetry: { port } }) 里换一个空闲端口,或者停掉占用 {{port}} 的进程后重试。",
|
|
133
|
+
"otel.windowAttribution": "otel:span 未带本轮 traceparent,按时间窗口归属(该 agent 的轮次已串行;应用支持 W3C 传播后自动并发)",
|
|
94
134
|
"runner.diffProgress": "采 diff:{{changed}} 改 / {{deleted}} 删",
|
|
95
135
|
"runner.driveAgent": "驱动 agent…",
|
|
96
136
|
"runner.evalSetup": "eval setup(装依赖)…",
|
|
@@ -99,6 +139,7 @@ export const zhCN = {
|
|
|
99
139
|
"runner.otlpInSandbox": "OTLP in-sandbox collector → {{endpoint}}{{proto}}",
|
|
100
140
|
"runner.otlpOverride": "OTLP 接收器(覆盖 host) → {{endpoint}}",
|
|
101
141
|
"runner.otlpReceiver": "OTLP 接收器 → {{endpoint}}{{proto}}",
|
|
142
|
+
"runner.otlpShared": "OTLP 共享接收器(run 级) → {{endpoint}}",
|
|
102
143
|
"runner.remoteSandboxUnavailable": "remote agent 没有 sandbox.{{method}};请改用 sandbox agent 或移除 workspace 断言。",
|
|
103
144
|
"runner.reporterDiagnostic": " · [diagnostic] {{stage}} 失败(已忽略):{{message}}\n",
|
|
104
145
|
"runner.scoreJudge": "评分 / judge…",
|
|
@@ -111,6 +152,7 @@ export const zhCN = {
|
|
|
111
152
|
"runner.resumeCarry": " · 复用上次 {{carried}} 个通过的结果,重跑 {{retry}} 个 eval\n",
|
|
112
153
|
"runner.useRemoteAgent": "使用 remote agent(不创建沙箱)…",
|
|
113
154
|
"sandbox.backendNotImplemented": "{{backend}} sandbox backend not implemented; use docker, vercel, or e2b",
|
|
155
|
+
"sandbox.missingSpec": "沙箱型 agent 需要一个 sandbox,但没有提供。niceeval 不再自动选默认后端——请在 defineExperiment()/defineConfig() 里把 sandbox 设成 dockerSandbox() / vercelSandbox() / e2bSandbox()(从 \"niceeval/sandbox\" 导入)。",
|
|
114
156
|
"sandbox.dependencyMissing.docker": "Docker sandbox requires 'dockerode'. Install it with: pnpm add dockerode @types/dockerode",
|
|
115
157
|
"sandbox.dependencyMissing.e2b": "E2B sandbox requires 'e2b'. Install it with: pnpm add e2b",
|
|
116
158
|
"sandbox.dependencyMissing.vercel": "Vercel sandbox requires '@vercel/sandbox'. Install it with: pnpm add @vercel/sandbox",
|
package/src/index.ts
CHANGED
|
@@ -14,6 +14,8 @@ export type {
|
|
|
14
14
|
Turn,
|
|
15
15
|
TurnInput,
|
|
16
16
|
InputFile,
|
|
17
|
+
InputResponse,
|
|
18
|
+
RespondAnswer,
|
|
17
19
|
TurnHandle,
|
|
18
20
|
SessionHandle,
|
|
19
21
|
TestContext,
|
|
@@ -30,6 +32,16 @@ export type {
|
|
|
30
32
|
ReporterEvent,
|
|
31
33
|
EvalResult,
|
|
32
34
|
RunSummary,
|
|
35
|
+
RunShape,
|
|
36
|
+
AssertionResult,
|
|
37
|
+
ExperimentRunInfo,
|
|
38
|
+
DiffData,
|
|
39
|
+
DiffView,
|
|
40
|
+
ScriptResult,
|
|
41
|
+
SandboxHandle,
|
|
42
|
+
CommandResult,
|
|
43
|
+
InputRequest,
|
|
44
|
+
InputRequestFilter,
|
|
33
45
|
O11ySummary,
|
|
34
46
|
TraceSpan,
|
|
35
47
|
SpanKind,
|