niceeval 0.1.0 → 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 +9 -9
- package/README.zh.md +14 -14
- 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 +46 -17
- 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
package/src/types.ts
CHANGED
|
@@ -1,913 +1,22 @@
|
|
|
1
|
-
// niceeval
|
|
1
|
+
// niceeval 的核心类型契约 —— 聚合 facade。
|
|
2
|
+
//
|
|
3
|
+
// 类型按域住在各自目录(改哪个域的类型去哪个文件),这里只做 re-export,
|
|
4
|
+
// 模块代码统一 `import type { … } from "../types.ts"`,不必记住每个类型的家:
|
|
5
|
+
// · shared/types.ts 跨域原子(JsonValue / Severity / SourceLoc / Cleanup / LocalizedText)
|
|
6
|
+
// · o11y/types.ts 标准事件流 / DerivedFacts / TraceSpan / Usage / O11ySummary
|
|
7
|
+
// · sandbox/types.ts Sandbox 接口 / 后端 spec / 命令与文件 IO
|
|
8
|
+
// · agents/types.ts Agent / Adapter 契约 / 会话 / tracing 导出
|
|
9
|
+
// · scoring/types.ts 断言(值级 / 记录 / 结果)/ ScoringContext / JudgeConfig
|
|
10
|
+
// · context/types.ts TestContext(t)与子句柄(turn / session / sandbox 视图)
|
|
11
|
+
// · runner/types.ts EvalResult / RunSummary / Reporter / eval・experiment・config 定义 / 调度编排
|
|
12
|
+
//
|
|
13
|
+
// 架构规矩不变:所有模块对着这里编程;agents/ 与 sandbox/ 之外不出现
|
|
2
14
|
// agent 名 / sandbox 名的行为分支(见 docs/architecture.md)。
|
|
3
15
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
| JsonValue[]
|
|
12
|
-
| { [key: string]: JsonValue };
|
|
13
|
-
|
|
14
|
-
export type Severity = "gate" | "soft";
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* eval 源码里一次调用的位置(`t.send` / 各断言),运行期从栈回溯抠出来(见 src/source-loc.ts)。
|
|
18
|
-
* view 据此把运行结果叠回真实源码行(github-diff 式代码视图)。`file` 为相对项目根的路径。
|
|
19
|
-
*/
|
|
20
|
-
export interface SourceLoc {
|
|
21
|
-
file: string;
|
|
22
|
-
line: number;
|
|
23
|
-
column?: number;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/** 随结果回传的一份 eval 源码(相对项目根的路径 + 文本),供 view 渲染代码视图。 */
|
|
27
|
-
export interface SourceArtifact {
|
|
28
|
-
path: string;
|
|
29
|
-
content: string;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/** 一次运行的 token 用量(沙箱型从 transcript 抠,remote 由 send 返回)。 */
|
|
33
|
-
export interface Usage {
|
|
34
|
-
inputTokens: number;
|
|
35
|
-
outputTokens: number;
|
|
36
|
-
cacheReadTokens?: number;
|
|
37
|
-
cacheWriteTokens?: number;
|
|
38
|
-
requests?: number;
|
|
39
|
-
/** 网关实测成本(若 agent 带回)——优先于价格表估算。 */
|
|
40
|
-
costUSD?: number;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// ───────────────────────── 标准事件流 ─────────────────────────
|
|
44
|
-
|
|
45
|
-
/** 跨 agent 归一化后的规范工具名。 */
|
|
46
|
-
export type ToolName =
|
|
47
|
-
| "file_read"
|
|
48
|
-
| "file_write"
|
|
49
|
-
| "file_edit"
|
|
50
|
-
| "shell"
|
|
51
|
-
| "web_fetch"
|
|
52
|
-
| "web_search"
|
|
53
|
-
| "glob"
|
|
54
|
-
| "grep"
|
|
55
|
-
| "list_dir"
|
|
56
|
-
| "agent_task"
|
|
57
|
-
| "unknown";
|
|
58
|
-
|
|
59
|
-
export interface InputRequest {
|
|
60
|
-
readonly id?: string;
|
|
61
|
-
readonly prompt?: string;
|
|
62
|
-
readonly display?: string;
|
|
63
|
-
readonly action?: string;
|
|
64
|
-
readonly input?: JsonValue;
|
|
65
|
-
readonly options?: readonly { id: string; label?: string }[];
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* 标准事件流的词汇(对标 docs/agents-and-adapters.md)。adapter 唯一的硬活就是把
|
|
70
|
-
* 各 agent 五花八门的原始 transcript 映射成 StreamEvent[];映射完,整套断言免费。
|
|
71
|
-
*/
|
|
72
|
-
export type StreamEvent =
|
|
73
|
-
| { type: "message"; role: "assistant" | "user"; text: string; loc?: SourceLoc }
|
|
74
|
-
| { type: "action.called"; callId: string; name: string; input: JsonValue; tool?: ToolName }
|
|
75
|
-
| {
|
|
76
|
-
type: "action.result";
|
|
77
|
-
callId: string;
|
|
78
|
-
output?: JsonValue;
|
|
79
|
-
status: "completed" | "failed" | "rejected";
|
|
80
|
-
}
|
|
81
|
-
| { type: "subagent.called"; callId: string; name: string; remoteUrl?: string }
|
|
82
|
-
| { type: "subagent.completed"; callId: string; output?: JsonValue; status: "completed" | "failed" }
|
|
83
|
-
| { type: "input.requested"; request: InputRequest }
|
|
84
|
-
| { type: "thinking"; text: string }
|
|
85
|
-
| { type: "compaction"; reason?: string }
|
|
86
|
-
| { type: "error"; message: string };
|
|
87
|
-
|
|
88
|
-
/** core 从事件流折叠出的结构化事实(deriveRunFacts)。 */
|
|
89
|
-
export interface ToolCall {
|
|
90
|
-
callId: string;
|
|
91
|
-
name: ToolName;
|
|
92
|
-
originalName?: string;
|
|
93
|
-
input: JsonValue;
|
|
94
|
-
output?: JsonValue;
|
|
95
|
-
status: "completed" | "failed" | "rejected";
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export interface SubagentCall {
|
|
99
|
-
callId: string;
|
|
100
|
-
name: string;
|
|
101
|
-
remoteUrl?: string;
|
|
102
|
-
output?: JsonValue;
|
|
103
|
-
status: "completed" | "failed";
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export interface DerivedFacts {
|
|
107
|
-
readonly toolCalls: readonly ToolCall[];
|
|
108
|
-
readonly subagentCalls: readonly SubagentCall[];
|
|
109
|
-
readonly inputRequests: readonly InputRequest[];
|
|
110
|
-
readonly parked: boolean;
|
|
111
|
-
readonly messageCount: number;
|
|
112
|
-
readonly compactions: number;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* span 的【语义角色】,从 OTel GenAI 语义约定的 gen_ai.operation.name 归一而来
|
|
117
|
-
* (见 o11y/otlp/canonical.ts)。view 据此着色 / 分组 / 跨 agent 对比,**只认这个,
|
|
118
|
-
* 不读原生 span 名**。未识别的 span 落 "other",view 折叠。
|
|
119
|
-
*/
|
|
120
|
-
export type SpanKind = "turn" | "model" | "tool" | "agent" | "other";
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* 一条分布式追踪的 span(从 agent 经 OpenTelemetry 导出的 OTLP traces 归一而来)。
|
|
124
|
-
* 与 StreamEvent 不同:它带【时间】(起止 epoch 毫秒)与【父子】(parentSpanId),
|
|
125
|
-
* 所以 view 能画成瀑布图。事件流回答「做了什么」,trace 回答「各花了多久、谁套谁」。
|
|
126
|
-
*
|
|
127
|
-
* 两层归一:线格式层(OTLP/JSON|protobuf → 本结构,见 otlp/parse.ts,通用);
|
|
128
|
-
* 语义层(原生 span 名/属性 → canonical GenAI semconv,见 otlp/mappers/<agent>.ts,每 agent 一个薄 mapper)。
|
|
129
|
-
*/
|
|
130
|
-
export interface TraceSpan {
|
|
131
|
-
traceId: string;
|
|
132
|
-
spanId: string;
|
|
133
|
-
parentSpanId?: string;
|
|
134
|
-
name: string;
|
|
135
|
-
/** span 起点 / 终点(epoch 毫秒)。 */
|
|
136
|
-
startMs: number;
|
|
137
|
-
endMs: number;
|
|
138
|
-
status?: "ok" | "error" | "unset";
|
|
139
|
-
/**
|
|
140
|
-
* 归一后的语义角色(每-agent mapper 据 canonical GenAI semconv 定;view/select 只认它)。
|
|
141
|
-
* 未经 mapper 或未识别时为 undefined / "other"。
|
|
142
|
-
*/
|
|
143
|
-
kind?: SpanKind;
|
|
144
|
-
/** OTLP span 属性(gen_ai.* / tool 名 / token 等),按 key 摊平。raw 属性始终保留供下钻。 */
|
|
145
|
-
attributes?: Record<string, JsonValue>;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/** 给人 / 给 EVAL.ts 看的 o11y 摘要(注入沙箱 __niceeval__/results.json)。 */
|
|
149
|
-
export interface O11ySummary {
|
|
150
|
-
totalTurns: number;
|
|
151
|
-
toolCalls: Record<string, number>;
|
|
152
|
-
totalToolCalls: number;
|
|
153
|
-
filesRead: string[];
|
|
154
|
-
filesModified: string[];
|
|
155
|
-
shellCommands: { command: string; exitCode?: number; success?: boolean }[];
|
|
156
|
-
webFetches: { url: string; status?: number; success?: boolean }[];
|
|
157
|
-
errors: string[];
|
|
158
|
-
thinkingBlocks: number;
|
|
159
|
-
compactions: number;
|
|
160
|
-
durationMs: number;
|
|
161
|
-
usage: Usage;
|
|
162
|
-
estimatedCostUSD?: number;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// ───────────────────────── Plugins / Skills ─────────────────────────
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* MCP server 描述符 —— Claude Code 与 Codex 共用的扩展插件单元。
|
|
169
|
-
* 在 agent factory config 里声明,setup 阶段写进各自的配置文件。
|
|
170
|
-
*/
|
|
171
|
-
export interface McpServer {
|
|
172
|
-
/** 服务器唯一名(config key)。 */
|
|
173
|
-
name: string;
|
|
174
|
-
/** 启动命令(如 "npx"、"node"、"uvx")。 */
|
|
175
|
-
command: string;
|
|
176
|
-
/** 传给命令的参数。 */
|
|
177
|
-
args?: string[];
|
|
178
|
-
/** 注入服务器进程的环境变量。 */
|
|
179
|
-
env?: Record<string, string>;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// ───────────────────────── Agent 契约 ─────────────────────────
|
|
183
|
-
|
|
184
|
-
/** 随一轮消息附带的文件(图片等多模态输入)。 */
|
|
185
|
-
export interface InputFile {
|
|
186
|
-
/** 文件名(可选,供 adapter / 模型参考)。 */
|
|
187
|
-
readonly filename?: string;
|
|
188
|
-
/** MIME 类型,如 `image/png`、`image/jpeg`。 */
|
|
189
|
-
readonly mimeType: string;
|
|
190
|
-
/** base64 编码的文件内容(JSON 友好,remote adapter 可直接放进请求体)。 */
|
|
191
|
-
readonly dataBase64: string;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
export interface TurnInput {
|
|
195
|
-
readonly text: string;
|
|
196
|
-
/** 本轮附带的文件(图片等)。adapter 自行决定怎么投递;不支持多模态的 adapter 忽略它。 */
|
|
197
|
-
readonly files?: readonly InputFile[];
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/** adapter 的 send 返回值(事件流为核心)。 */
|
|
201
|
-
export interface Turn {
|
|
202
|
-
readonly events: StreamEvent[];
|
|
203
|
-
readonly data?: unknown;
|
|
204
|
-
readonly status: "completed" | "failed" | "waiting";
|
|
205
|
-
readonly usage?: Usage;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
export interface AgentCapabilities {
|
|
209
|
-
conversation?: boolean;
|
|
210
|
-
toolObservability?: boolean;
|
|
211
|
-
workspace?: boolean;
|
|
212
|
-
sandbox?: boolean;
|
|
213
|
-
compactionObservability?: boolean;
|
|
214
|
-
/**
|
|
215
|
-
* agent 能经 OpenTelemetry 导出 OTLP traces。声明它,运行器就在每个沙箱起一个
|
|
216
|
-
* 本机 OTLP 接收器,把端点经 ctx.telemetry 交给 agent;跑完把收到的 span 归一到
|
|
217
|
-
* canonical GenAI semconv、挂到 EvalResult.trace,view 画成瀑布图。
|
|
218
|
-
* **怎么把端点交给 CLI**(env / config 文件)由 agent 的 `tracing` 块声明(见 AgentTracing),
|
|
219
|
-
* 与「装 CLI / 写主配置」的 setup 分开。
|
|
220
|
-
*/
|
|
221
|
-
tracing?: boolean;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* 本次运行的 OTLP traces 接收信息(仅当 agent 声明 capabilities.tracing 时有)。
|
|
226
|
-
* 经 ctx.telemetry 交给 agent。
|
|
227
|
-
*/
|
|
228
|
-
export interface Telemetry {
|
|
229
|
-
/** 接收端点(完整路径,形如 http://host.docker.internal:PORT/v1/traces)。 */
|
|
230
|
-
readonly endpoint: string;
|
|
231
|
-
/**
|
|
232
|
-
* env-based 导出的 env(= AgentTracing.env(endpoint) 的结果),ready-to-spread。
|
|
233
|
-
* adapter 的 send 直接 `{ ...ctx.telemetry?.env }` 注入,不必手搓 OTEL_* 拼装。
|
|
234
|
-
*/
|
|
235
|
-
readonly env?: Readonly<Record<string, string>>;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* agent 的 OTLP 导出配置 —— 「沙箱里怎么让这个 CLI 把 trace 发到 endpoint」。
|
|
240
|
-
* 刻意从 setup 里拆出来:setup 管装 CLI / 写主配置,这里只管 otel 导出。两种投递方式
|
|
241
|
-
* (互不排斥,按 CLI 而定):
|
|
242
|
-
* · env-based(标准 OTEL_* 环境变量,如 bub/Python OTel SDK)—— 用 `env`;
|
|
243
|
-
* · file-based(CLI 自有配置文件,如 codex 的 config.toml [otel] 块)—— 用 `configure`。
|
|
244
|
-
*/
|
|
245
|
-
export interface AgentTracing {
|
|
246
|
-
/**
|
|
247
|
-
* 线协议(codex 发 OTLP/JSON、bub 发 OTLP/protobuf)。接收器按 content-type 自动解码,
|
|
248
|
-
* 此字段仅作声明/日志用,也为将来按协议分流留口。
|
|
249
|
-
*/
|
|
250
|
-
protocol?: "http/json" | "http/protobuf";
|
|
251
|
-
/**
|
|
252
|
-
* env-based 导出:给 endpoint → 返回要注入每轮 send 的 env(纯函数)。运行器把结果
|
|
253
|
-
* 放进 ctx.telemetry.env,send 直接 spread。
|
|
254
|
-
*/
|
|
255
|
-
env?(endpoint: string): Record<string, string>;
|
|
256
|
-
/**
|
|
257
|
-
* file-based 导出:给 sandbox + ctx(ctx.telemetry.endpoint 必有),自己写 / 追加配置文件。
|
|
258
|
-
* 运行器在 agent.setup 之后、首次 send 之前调一次(仅当 tracing 开 + 有 endpoint)。
|
|
259
|
-
* 注:codex 的 [otel.trace_exporter.otlp-http] 是子表,configure 在 setup 写完主配置后
|
|
260
|
-
* 追加到 config.toml 末尾,天然满足「子表在所有上层表之后」。
|
|
261
|
-
*/
|
|
262
|
-
configure?(sandbox: Sandbox, ctx: AgentContext): Promise<void> | void;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
/** 多轮 resume / newSession 用。id 可写(adapter 回传供下轮续接)。 */
|
|
266
|
-
export interface AgentSession {
|
|
267
|
-
id?: string;
|
|
268
|
-
readonly isNew: boolean;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
export interface AgentContext {
|
|
272
|
-
readonly signal: AbortSignal;
|
|
273
|
-
readonly model?: string;
|
|
274
|
-
readonly flags: Readonly<Record<string, unknown>>;
|
|
275
|
-
/** 仅沙箱型 agent 有(运行器按 --sandbox 备好)。 */
|
|
276
|
-
readonly sandbox: Sandbox;
|
|
277
|
-
readonly session: AgentSession;
|
|
278
|
-
/**
|
|
279
|
-
* 仅当 agent 声明 capabilities.tracing 时有:本次运行的 OTLP traces 接收信息
|
|
280
|
-
*(endpoint + env-based 导出 env)。怎么把它交给 CLI 由 agent 的 `tracing` 块声明:
|
|
281
|
-
* env-based 的把 ctx.telemetry.env spread 进 send;file-based 的在 tracing.configure 里写配置。
|
|
282
|
-
*/
|
|
283
|
-
readonly telemetry?: Telemetry;
|
|
284
|
-
log(msg: string): void;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
/**
|
|
288
|
-
* agent 自己的沙箱生命周期(每个沙箱一次,与「每轮 send」分开):
|
|
289
|
-
* `setup` 装 CLI、写配置(model/base/auth 等本轮内不变的东西),`send` 只管把一轮 prompt
|
|
290
|
-
* 跑起来(第一次 fresh / 后续 resume)+ 解析 transcript,`teardown` 清理。
|
|
291
|
-
* 运行器在备好沙箱(上传 / 基线 / eval.setup)后、第一次 send 前调一次 `setup`;
|
|
292
|
-
* `setup` 可返回 cleanup 闭包,与 `teardown` 都在 finally 跑。
|
|
293
|
-
*/
|
|
294
|
-
export type AgentSetup = (sandbox: Sandbox, ctx: AgentContext) => Promise<void | Cleanup> | void | Cleanup;
|
|
295
|
-
export type AgentTeardown = (sandbox: Sandbox, ctx: AgentContext) => Promise<void> | void;
|
|
296
|
-
|
|
297
|
-
/** 注册表里的 agent(defineAgent / defineSandboxAgent 产出)。 */
|
|
298
|
-
export interface Agent {
|
|
299
|
-
readonly name: string;
|
|
300
|
-
readonly capabilities: AgentCapabilities;
|
|
301
|
-
setup?: AgentSetup;
|
|
302
|
-
/** OTLP 导出配置(仅 capabilities.tracing 时有意义);与 setup 分开,见 AgentTracing。 */
|
|
303
|
-
tracing?: AgentTracing;
|
|
304
|
-
send(input: TurnInput, ctx: AgentContext): Promise<Turn>;
|
|
305
|
-
teardown?: AgentTeardown;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
export interface SandboxAgentDef {
|
|
309
|
-
name: string;
|
|
310
|
-
capabilities?: AgentCapabilities;
|
|
311
|
-
/** 每个沙箱一次:装 CLI、写 config.toml / 鉴权配置。 */
|
|
312
|
-
setup?: AgentSetup;
|
|
313
|
-
/** OTLP 导出配置:沙箱里怎么让 CLI 把 trace 发到 endpoint(env / 配置文件),从 setup 拆出。 */
|
|
314
|
-
tracing?: AgentTracing;
|
|
315
|
-
/** 每轮一次:跑 prompt(fresh / resume)+ 解析成 events。 */
|
|
316
|
-
send(input: TurnInput, ctx: AgentContext): Promise<Turn>;
|
|
317
|
-
teardown?: AgentTeardown;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
export interface RemoteAgentDef {
|
|
321
|
-
name: string;
|
|
322
|
-
capabilities?: AgentCapabilities;
|
|
323
|
-
setup?: AgentSetup;
|
|
324
|
-
tracing?: AgentTracing;
|
|
325
|
-
send(input: TurnInput, ctx: AgentContext): Promise<Turn>;
|
|
326
|
-
teardown?: AgentTeardown;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// ───────────────────────── Sandbox ─────────────────────────
|
|
330
|
-
|
|
331
|
-
export interface CommandResult {
|
|
332
|
-
stdout: string;
|
|
333
|
-
stderr: string;
|
|
334
|
-
exitCode: number;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
export interface SandboxFile {
|
|
338
|
-
path: string;
|
|
339
|
-
content: string | Buffer;
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
/** 一个源码文件:相对工作区根的路径 + 文本内容。readSourceFiles 的返回元素。 */
|
|
343
|
-
export interface SourceFile {
|
|
344
|
-
path: string;
|
|
345
|
-
content: string;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
/**
|
|
349
|
-
* readSourceFiles 的返回值:仍是一个 SourceFile 数组(.filter/.some/.map 照用),
|
|
350
|
-
* 额外挂上整体匹配 / 按文件匹配的便利方法,省掉 eval 目录里手写的 source-helpers。
|
|
351
|
-
*/
|
|
352
|
-
export interface SourceFiles extends ReadonlyArray<SourceFile> {
|
|
353
|
-
/** 全部文件内容拼接(每段前带 `// path` 注释),用于整体 regex。 */
|
|
354
|
-
text(): string;
|
|
355
|
-
/** 同 text() 但先剥注释,只看真实代码。 */
|
|
356
|
-
code(): string;
|
|
357
|
-
/** 第一个内容命中 pattern 的文件。 */
|
|
358
|
-
fileMatching(pattern: RegExp): SourceFile | undefined;
|
|
359
|
-
/** 第一个内容命中全部 patterns 的文件(同文件共现,per-file 而非拼接源码)。 */
|
|
360
|
-
fileMatchingAll(patterns: RegExp[]): SourceFile | undefined;
|
|
361
|
-
/** 是否存在路径命中 pattern 的文件。 */
|
|
362
|
-
hasPath(pattern: RegExp): boolean;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
/** readSourceFiles 的可选项;不传则用一套合理默认。 */
|
|
366
|
-
export interface ReadSourceFilesOptions {
|
|
367
|
-
/** 文件扩展名(不带点)。默认 ts/tsx/js/jsx。 */
|
|
368
|
-
extensions?: string[];
|
|
369
|
-
/** 按目录名(任意深度)剪枝。默认 .git/.next/node_modules/dist/build/coverage。 */
|
|
370
|
-
ignoreDirs?: string[];
|
|
371
|
-
/** 按文件 basename 忽略。默认 EVAL.ts/PROMPT.md。 */
|
|
372
|
-
ignoreFiles?: string[];
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
export type SandboxBackend = "docker" | "vercel" | "e2b" | "auto" | string;
|
|
376
|
-
|
|
377
|
-
/** 镜像/模板里的 Node 运行时版本。 */
|
|
378
|
-
export type SandboxRuntime = "node20" | "node24";
|
|
379
|
-
|
|
380
|
-
/**
|
|
381
|
-
* Sandbox 的「数据结构」定义 —— 与 agent 一样可带参数(见 docs/sandbox.md)。
|
|
382
|
-
* 用工厂函数构造(`dockerSandbox()` / `vercelSandbox()` / `e2bSandbox()`),
|
|
383
|
-
* 放进 config / experiment 的 `sandbox` 字段;字符串后端名(`"docker"` 等)仍兼容。
|
|
384
|
-
* 各后端的参数互不相同 —— 这是个按 `backend` 区分的可辨识联合(discriminated union)。
|
|
385
|
-
*/
|
|
386
|
-
export interface DockerSandboxSpec {
|
|
387
|
-
readonly backend: "docker";
|
|
388
|
-
/** 覆盖默认镜像;默认按 runtime 选 `node:*-slim`。预制模板:传烘焙好 agent CLI 的镜像名。 */
|
|
389
|
-
readonly image?: string;
|
|
390
|
-
readonly runtime?: SandboxRuntime;
|
|
391
|
-
}
|
|
392
|
-
export interface VercelSandboxSpec {
|
|
393
|
-
readonly backend: "vercel";
|
|
394
|
-
/** 从已有快照起 microVM。预制模板:烘焙好 agent CLI 的 snapshotId。 */
|
|
395
|
-
readonly snapshotId?: string;
|
|
396
|
-
readonly runtime?: SandboxRuntime;
|
|
397
|
-
}
|
|
398
|
-
export interface E2BSandboxSpec {
|
|
399
|
-
readonly backend: "e2b";
|
|
400
|
-
/** e2b 模板名/ID。预制模板:烘焙好 agent CLI 的模板(如 `"niceeval-agents"`)。省略用 e2b 默认 `"base"`。 */
|
|
401
|
-
readonly template?: string;
|
|
402
|
-
/** 仅作记录;e2b 的 node 版本由模板决定,不在创建时选。 */
|
|
403
|
-
readonly runtime?: SandboxRuntime;
|
|
404
|
-
}
|
|
405
|
-
/**
|
|
406
|
-
* 用户自定义后端:`create` 直接产出一个 `Sandbox` 实例,不经 resolve.ts 的内置 backend switch。
|
|
407
|
-
* 用 `defineSandbox()` 构造(见 src/define.ts)。`backend` 只用于展示 / 日志,不参与分发。
|
|
408
|
-
*/
|
|
409
|
-
export interface CustomSandboxSpec {
|
|
410
|
-
readonly backend: string;
|
|
411
|
-
readonly runtime?: SandboxRuntime;
|
|
412
|
-
readonly recommendedConcurrency?: number;
|
|
413
|
-
readonly create: (opts: { timeout?: number; runtime?: SandboxRuntime }) => Promise<Sandbox>;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
export type SandboxSpec = DockerSandboxSpec | VercelSandboxSpec | E2BSandboxSpec | CustomSandboxSpec;
|
|
417
|
-
|
|
418
|
-
/** config / experiment 的 `sandbox` 字段:后端名(字符串)或带参数的 spec 数据结构。 */
|
|
419
|
-
export type SandboxOption = SandboxBackend | SandboxSpec;
|
|
420
|
-
|
|
421
|
-
export interface CommandOptions {
|
|
422
|
-
env?: Record<string, string>;
|
|
423
|
-
cwd?: string;
|
|
424
|
-
/**
|
|
425
|
-
* 把本命令的输出也送进沙箱的「原生日志流」(于是 `docker logs` / Docker UI 的 Logs
|
|
426
|
-
* 标签页能实时看到它)。给 agent 命令(codex exec / bub run / claude)开它,就能在容器
|
|
427
|
-
* 日志里看到 agent 的【原始输出】。后端各自实现(docker:tee 到 PID1 tail 的文件;
|
|
428
|
-
* 不支持的后端忽略)—— 日志怎么浮现是 backend 的事,adapter 只声明意图。
|
|
429
|
-
*/
|
|
430
|
-
stream?: boolean;
|
|
431
|
-
/**
|
|
432
|
-
* 以 root 跑本命令。默认 `false` —— 命令以沙箱的标准**非 root** 用户跑(agent 的自然环境)。
|
|
433
|
-
* 给 setup 阶段装系统依赖用(`apt-get install …`、`pip install --break-system-packages …`)。
|
|
434
|
-
*
|
|
435
|
-
* 语义跨后端一致:"本命令以 root 跑,否则以标准非 root 用户跑"。各后端映射到自己的原生机制
|
|
436
|
-
* (docker:`exec --user root`;E2B:`{ user: "root" }`;Vercel:`{ sudo: true }`;Daytona:`{ user }`)。
|
|
437
|
-
* 本就全程 root 的后端(如 Modal)视作 no-op;完全无法提权的后端可不支持(抛错)—— 但**默认值与
|
|
438
|
-
* 语义保持一致**,不因后端而变。
|
|
439
|
-
*/
|
|
440
|
-
root?: boolean;
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
export interface Sandbox {
|
|
444
|
-
runCommand(cmd: string, args?: string[], opts?: CommandOptions): Promise<CommandResult>;
|
|
445
|
-
runShell(script: string, opts?: CommandOptions): Promise<CommandResult>;
|
|
446
|
-
readFile(path: string): Promise<string>;
|
|
447
|
-
fileExists(path: string): Promise<boolean>;
|
|
448
|
-
/**
|
|
449
|
-
* 一次 shell 往返读全部源码文件(按扩展名收、按目录/文件名忽略)。
|
|
450
|
-
* 取代每个 eval 目录里手写的 find + 逐文件 readFile。
|
|
451
|
-
*/
|
|
452
|
-
readSourceFiles(opts?: ReadSourceFilesOptions): Promise<SourceFiles>;
|
|
453
|
-
writeFiles(files: Record<string, string>, targetDir?: string): Promise<void>;
|
|
454
|
-
uploadFiles(files: SandboxFile[], targetDir?: string): Promise<void>;
|
|
455
|
-
uploadDirectory(localDir: string, targetDir: string, opts?: { ignore?: string[] }): Promise<void>;
|
|
456
|
-
stop(): Promise<void>;
|
|
457
|
-
readonly sandboxId: string;
|
|
458
|
-
/**
|
|
459
|
-
* 本地 OTLP 接收器的目标 host。
|
|
460
|
-
* - `string`:沙箱内可通过该 hostname 回连宿主 OTLP 端口(如 docker 的 `host.docker.internal`)。
|
|
461
|
-
* - `null`:沙箱运行在远程云端(如 e2b/vercel),无法访问宿主本地端口 → 跳过 tracing。
|
|
462
|
-
* 可通过环境变量 `NICEEVAL_OTLP_HOST` 强制覆盖(如配置 tunnel 时)。
|
|
463
|
-
*/
|
|
464
|
-
readonly otlpHost: string | null;
|
|
465
|
-
|
|
466
|
-
/**
|
|
467
|
-
* 可选:把一行写进容器的「主日志」(PID1 在 tail 它)——于是 `docker logs` /
|
|
468
|
-
* Docker UI 的 Logs 标签页能实时看到 agent 逐轮活动。docker 后端实现,其它可省略。
|
|
469
|
-
*/
|
|
470
|
-
appendLog?(line: string): Promise<void>;
|
|
471
|
-
|
|
472
|
-
/**
|
|
473
|
-
* 从沙箱内任意路径读取文件,返回二进制 Buffer。
|
|
474
|
-
* 对应各 backend:Docker getArchive / Vercel readFileToBuffer / e2b files.read(bytes) / …
|
|
475
|
-
*/
|
|
476
|
-
downloadFile(path: string): Promise<Buffer>;
|
|
477
|
-
|
|
478
|
-
/**
|
|
479
|
-
* 向沙箱内任意路径写入文件(二进制)。
|
|
480
|
-
* 对应各 backend:Docker putArchive / Vercel fs.writeFile(Buffer) / e2b files.write / …
|
|
481
|
-
*/
|
|
482
|
-
uploadFile(path: string, content: Buffer): Promise<void>;
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
/** eval 作者可见的受限沙箱视图:能执行命令 / 文件 IO / 读最终 diff,但不能 stop。 */
|
|
486
|
-
export interface SandboxHandle {
|
|
487
|
-
runCommand(cmd: string, args?: string[], opts?: CommandOptions): Promise<CommandResult>;
|
|
488
|
-
runShell(script: string, opts?: CommandOptions): Promise<CommandResult>;
|
|
489
|
-
readFile(path: string): Promise<string>;
|
|
490
|
-
fileExists(path: string): Promise<boolean>;
|
|
491
|
-
readSourceFiles(opts?: ReadSourceFilesOptions): Promise<SourceFiles>;
|
|
492
|
-
writeFiles(files: Record<string, string>, targetDir?: string): Promise<void>;
|
|
493
|
-
uploadFiles(files: SandboxFile[], targetDir?: string): Promise<void>;
|
|
494
|
-
uploadDirectory(localDir: string, targetDir: string, opts?: { ignore?: string[] }): Promise<void>;
|
|
495
|
-
downloadFile(path: string): Promise<Buffer>;
|
|
496
|
-
uploadFile(path: string, content: Buffer): Promise<void>;
|
|
497
|
-
readonly sandboxId: string;
|
|
498
|
-
readonly diff: DiffView;
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
// ───────────────────────── 评分 / 断言 ─────────────────────────
|
|
502
|
-
|
|
503
|
-
/** 值级断言(expect 匹配器)。纯函数 score + 可链式改严重级 / 阈值。 */
|
|
504
|
-
export interface ValueAssertion {
|
|
505
|
-
readonly name: string;
|
|
506
|
-
readonly severity: Severity;
|
|
507
|
-
readonly threshold?: number;
|
|
508
|
-
score(value: unknown): number | Promise<number>;
|
|
509
|
-
gate(threshold?: number): ValueAssertion;
|
|
510
|
-
atLeast(threshold: number): ValueAssertion;
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
/** 收集到 collector 里的一条断言记录(评估前)。 */
|
|
514
|
-
export interface AssertionSpec {
|
|
515
|
-
name: string;
|
|
516
|
-
severity: Severity;
|
|
517
|
-
threshold?: number;
|
|
518
|
-
/** 延迟评估:final 时拿到完整运行结果再算分。 */
|
|
519
|
-
evaluate(ctx: ScoringContext): Promise<number> | number;
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
/** 断言评估完的结果(进判决 / 报告)。 */
|
|
523
|
-
export interface AssertionResult {
|
|
524
|
-
name: string;
|
|
525
|
-
severity: Severity;
|
|
526
|
-
threshold?: number;
|
|
527
|
-
score: number;
|
|
528
|
-
passed: boolean;
|
|
529
|
-
detail?: string;
|
|
530
|
-
/** 这条分数是看着什么材料算出来的(judge 收到的输入)。view 展开排查「为什么是这个分」,默认不展示。 */
|
|
531
|
-
evidence?: string;
|
|
532
|
-
/** 所属分组(t.group 标题)。纯报告用,不影响 passed/score。 */
|
|
533
|
-
group?: string;
|
|
534
|
-
/** 断言在 eval 源码里的调用点(栈回溯抠出);view 把判决叠回这一行。 */
|
|
535
|
-
loc?: SourceLoc;
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
/** eval 作者拿到的可链式句柄(t.judge.autoevals.closedQA(...).atLeast(0.7))。 */
|
|
539
|
-
export interface AssertionHandle {
|
|
540
|
-
atLeast(threshold: number): AssertionHandle;
|
|
541
|
-
gate(threshold?: number): AssertionHandle;
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
/** scoped / judge 断言在 final 评估时拿到的运行结果。 */
|
|
545
|
-
export interface ScoringContext {
|
|
546
|
-
readonly events: readonly StreamEvent[];
|
|
547
|
-
readonly facts: DerivedFacts;
|
|
548
|
-
readonly diff: DiffData;
|
|
549
|
-
readonly scripts: Record<string, ScriptResult>;
|
|
550
|
-
readonly usage: Usage;
|
|
551
|
-
readonly status: "completed" | "failed" | "waiting";
|
|
552
|
-
/** 读沙箱里某文件的最终内容(judge / file 断言用)。 */
|
|
553
|
-
readFile(path: string): Promise<string | undefined>;
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
export interface ScriptResult {
|
|
557
|
-
success: boolean;
|
|
558
|
-
output: string;
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
export interface DiffData {
|
|
562
|
-
generatedFiles: Record<string, string>;
|
|
563
|
-
deletedFiles: string[];
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
export type ResultOutcome = "passed" | "failed" | "errored" | "skipped";
|
|
567
|
-
|
|
568
|
-
// ───────────────────────── Judge ─────────────────────────
|
|
569
|
-
|
|
570
|
-
export interface JudgeConfig {
|
|
571
|
-
model: string;
|
|
572
|
-
/** OpenAI 兼容 base url + key 来源;省略则从 env 探测(见 scoring/judge.ts)。 */
|
|
573
|
-
baseUrl?: string;
|
|
574
|
-
apiKeyEnv?: string;
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
// ───────────────────────── 价格 / 报告 ─────────────────────────
|
|
578
|
-
|
|
579
|
-
export interface ExperimentRunInfo {
|
|
580
|
-
id?: string;
|
|
581
|
-
flags?: Record<string, unknown>;
|
|
582
|
-
runs?: number;
|
|
583
|
-
earlyExit?: boolean;
|
|
584
|
-
sandbox?: string;
|
|
585
|
-
timeoutMs?: number;
|
|
586
|
-
budget?: number;
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
export interface EvalResult {
|
|
590
|
-
id: string;
|
|
591
|
-
description?: string;
|
|
592
|
-
experimentId?: string;
|
|
593
|
-
experiment?: ExperimentRunInfo;
|
|
594
|
-
agent: string;
|
|
595
|
-
model?: string;
|
|
596
|
-
outcome: ResultOutcome;
|
|
597
|
-
fingerprint?: string;
|
|
598
|
-
attempt: number;
|
|
599
|
-
/** 本 attempt 开始的墙钟时刻(ISO);view 按 eval 粒度展示「何时跑的」。 */
|
|
600
|
-
startedAt?: string;
|
|
601
|
-
durationMs: number;
|
|
602
|
-
assertions: AssertionResult[];
|
|
603
|
-
usage?: Usage;
|
|
604
|
-
estimatedCostUSD?: number;
|
|
605
|
-
error?: string;
|
|
606
|
-
skipReason?: string;
|
|
607
|
-
events?: StreamEvent[];
|
|
608
|
-
/** test 引用到的 eval 源码(按 loc 收集),供 view 渲染 github-diff 式代码视图。 */
|
|
609
|
-
sources?: SourceArtifact[];
|
|
610
|
-
o11y?: O11ySummary;
|
|
611
|
-
/** agent 经 OpenTelemetry 导出的运行追踪(有 tracing 能力且收到 span 时)。 */
|
|
612
|
-
trace?: TraceSpan[];
|
|
613
|
-
diff?: DiffData;
|
|
614
|
-
rawTranscript?: string;
|
|
615
|
-
// ── 拆分工件的引用(Artifacts 报告器写 summary.json 时填;view 按需懒加载)──
|
|
616
|
-
/** 本 attempt 工件目录(相对 run 根),下有 events/trace/o11y/diff.json。 */
|
|
617
|
-
artifactsDir?: string;
|
|
618
|
-
/** view 拼好的工件目录(相对 view 输入根,供前端 fetch);loadSummaries 注入。 */
|
|
619
|
-
artifactBase?: string;
|
|
620
|
-
/** 工件目录的绝对路径;loadSummaries 注入,供复制/展示用。 */
|
|
621
|
-
artifactAbsBase?: string;
|
|
622
|
-
hasTrace?: boolean;
|
|
623
|
-
hasEvents?: boolean;
|
|
624
|
-
hasSources?: boolean;
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
/**
|
|
628
|
-
* 可本地化文案:纯字符串,或按 locale 代码(如 "en"、"zh-CN")映射多语言。
|
|
629
|
-
* view 按当前界面语言挑一条,挑不到回退到 en / 第一条。
|
|
630
|
-
*/
|
|
631
|
-
export type LocalizedText = string | Record<string, string>;
|
|
632
|
-
|
|
633
|
-
export interface RunSummary {
|
|
634
|
-
/** 项目名(来自 config.name),透传给 `niceeval view` 顶部 hero 显示。 */
|
|
635
|
-
name?: LocalizedText;
|
|
636
|
-
agent: string;
|
|
637
|
-
model?: string;
|
|
638
|
-
startedAt: string;
|
|
639
|
-
completedAt: string;
|
|
640
|
-
passed: number;
|
|
641
|
-
/** 断言不通过的数量;不包含 errored。 */
|
|
642
|
-
failed: number;
|
|
643
|
-
skipped: number;
|
|
644
|
-
/** 环境、超时、adapter、agent runtime 等执行错误数量;与 failed 互斥。 */
|
|
645
|
-
errored: number;
|
|
646
|
-
durationMs: number;
|
|
647
|
-
usage?: Usage;
|
|
648
|
-
estimatedCostUSD?: number;
|
|
649
|
-
results: EvalResult[];
|
|
650
|
-
outputDir?: string;
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
/** onRunStart 的运行规模:去重后 eval 数 × 配置(agent×model×flags)数 → 总运行(attempt)数。 */
|
|
654
|
-
export interface RunShape {
|
|
655
|
-
/** 去重后实际要跑的 eval 数(= evals.length)。 */
|
|
656
|
-
evals: number;
|
|
657
|
-
/** (agent, model, flags) 配置组合数;compare 多 agent 时 > 1。 */
|
|
658
|
-
configs: number;
|
|
659
|
-
/** 总 attempt 数(evals × configs × runs);逐行输出与汇总计数都按它。 */
|
|
660
|
-
totalRuns: number;
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
export interface Reporter {
|
|
664
|
-
onEvent?(event: ReporterEvent): void | Promise<void>;
|
|
665
|
-
onRunStart?(evals: { id: string }[], agent: Agent, shape?: RunShape): void | Promise<void>;
|
|
666
|
-
onEvalComplete?(result: EvalResult): void | Promise<void>;
|
|
667
|
-
onRunComplete?(summary: RunSummary): void | Promise<void>;
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
export type ReporterEvent =
|
|
671
|
-
| { type: "run:start"; evals: { id: string }[]; agent: Agent; shape: RunShape }
|
|
672
|
-
| { type: "eval:start"; eval: { id: string }; agent: Agent; attempt: number; experimentId?: string }
|
|
673
|
-
| { type: "eval:complete"; result: EvalResult }
|
|
674
|
-
| { type: "run:earlyExit"; evalId: string; experimentId?: string }
|
|
675
|
-
| { type: "run:budgetExceeded"; budget: number; spent: number }
|
|
676
|
-
| { type: "run:saved"; summary: RunSummary }
|
|
677
|
-
| { type: "run:summary"; summary: RunSummary };
|
|
678
|
-
|
|
679
|
-
// ───────────────────────── 生命周期 ─────────────────────────
|
|
680
|
-
|
|
681
|
-
export type Cleanup = () => Promise<void> | void;
|
|
682
|
-
|
|
683
|
-
// ───────────────────────── eval / experiment / config 定义 ─────────────────────────
|
|
684
|
-
|
|
685
|
-
export interface EvalDef {
|
|
686
|
-
/** 路径推导,定义里禁止手写。 */
|
|
687
|
-
id?: string;
|
|
688
|
-
description?: string;
|
|
689
|
-
agent?: string;
|
|
690
|
-
tags?: string[];
|
|
691
|
-
judge?: JudgeConfig;
|
|
692
|
-
reporters?: Reporter[];
|
|
693
|
-
timeoutMs?: number;
|
|
694
|
-
metadata?: Record<string, unknown>;
|
|
695
|
-
/**
|
|
696
|
-
* eval 级预置:拿到沙箱(已上传 workspace + git 基线 + 装好依赖前)。
|
|
697
|
-
* 默认命令以非 root 跑(agent 的自然环境);装系统依赖时给 `runCommand` 传 `{ root: true }`
|
|
698
|
-
* (如 `runCommand("apt-get", ["install", …], { root: true })`),跨后端语义一致。
|
|
699
|
-
*/
|
|
700
|
-
setup?: (sandbox: Sandbox) => Promise<void | Cleanup> | void | Cleanup;
|
|
701
|
-
test(t: TestContext): Promise<void> | void;
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
/** 内部:发现后带上 id 的 eval。 */
|
|
705
|
-
export interface DiscoveredEval extends EvalDef {
|
|
706
|
-
id: string;
|
|
707
|
-
/** 定义文件所在目录(解析相对 workspace 用)。 */
|
|
708
|
-
baseDir: string;
|
|
709
|
-
/** 定义文件绝对路径,用于内容指纹缓存。 */
|
|
710
|
-
sourcePath: string;
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
export interface ExperimentDef {
|
|
714
|
-
id?: string;
|
|
715
|
-
description?: string;
|
|
716
|
-
agent: Agent;
|
|
717
|
-
/** 单个模型(agent 留空时实验决定);省略=用 agent 原生默认。跨模型对比写多个实验文件,别用数组。 */
|
|
718
|
-
model?: string;
|
|
719
|
-
flags?: Record<string, unknown>;
|
|
720
|
-
runs?: number;
|
|
721
|
-
earlyExit?: boolean;
|
|
722
|
-
evals?: "*" | string[] | ((id: string) => boolean);
|
|
723
|
-
timeoutMs?: number;
|
|
724
|
-
sandbox?: SandboxOption;
|
|
725
|
-
budget?: number;
|
|
726
|
-
maxConcurrency?: number;
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
export interface DiscoveredExperiment extends ExperimentDef {
|
|
730
|
-
id: string;
|
|
731
|
-
group: string;
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
export interface Config {
|
|
735
|
-
/**
|
|
736
|
-
* 项目名,显示在 `niceeval view` 顶部 hero(`<h1>`),省略则回退到通用标题。
|
|
737
|
-
* 可传字符串,或按 locale 提供多语言(如 `{ en: "...", "zh-CN": "..." }`),随 view 语言切换。
|
|
738
|
-
*/
|
|
739
|
-
name?: LocalizedText;
|
|
740
|
-
sandbox?: SandboxOption;
|
|
741
|
-
workspace?: string;
|
|
742
|
-
judge?: JudgeConfig;
|
|
743
|
-
reporters?: Reporter[];
|
|
744
|
-
maxConcurrency?: number;
|
|
745
|
-
timeoutMs?: number;
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
// ───────────────────────── TestContext(t)与子句柄 ─────────────────────────
|
|
749
|
-
|
|
750
|
-
/** t.send() 返回的句柄:从事件流派生便利字段 + expectOk。 */
|
|
751
|
-
export interface TurnHandle {
|
|
752
|
-
readonly events: StreamEvent[];
|
|
753
|
-
readonly toolCalls: readonly ToolCall[];
|
|
754
|
-
readonly status: "completed" | "failed" | "waiting";
|
|
755
|
-
readonly message: string;
|
|
756
|
-
readonly data?: unknown;
|
|
757
|
-
readonly usage?: Usage;
|
|
758
|
-
/** 上一轮若 failed 则抛(中止后续)。 */
|
|
759
|
-
expectOk(): TurnHandle;
|
|
760
|
-
outputEquals(value: unknown): AssertionHandle;
|
|
761
|
-
outputMatches(schema: unknown): AssertionHandle;
|
|
762
|
-
/** 断言本轮助手回复包含 token(仅限本轮事件流,不跨轮)。 */
|
|
763
|
-
messageIncludes(token: string | RegExp): AssertionHandle;
|
|
764
|
-
succeeded(): AssertionHandle;
|
|
765
|
-
calledTool(name: string, match?: ToolMatch): AssertionHandle;
|
|
766
|
-
notCalledTool(name: string, match?: ToolMatch): AssertionHandle;
|
|
767
|
-
toolOrder(names: string[]): AssertionHandle;
|
|
768
|
-
usedNoTools(): AssertionHandle;
|
|
769
|
-
maxToolCalls(max: number): AssertionHandle;
|
|
770
|
-
event(type: StreamEvent["type"], opts?: { count?: number }): AssertionHandle;
|
|
771
|
-
notEvent(type: StreamEvent["type"]): AssertionHandle;
|
|
772
|
-
calledSubagent(name: string, match?: SubagentMatch): AssertionHandle;
|
|
773
|
-
eventOrder(types: StreamEvent["type"][]): AssertionHandle;
|
|
774
|
-
eventsSatisfy(predicate: (events: readonly StreamEvent[]) => boolean, label?: string): AssertionHandle;
|
|
775
|
-
readonly judge: JudgeNamespace;
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
/** autoevals 子命名空间:结构化的参考材料对照评估(closedQA / factuality / summarizes)。 */
|
|
779
|
-
export interface AutoevalsNamespace {
|
|
780
|
-
closedQA(question: string, opts?: { on?: string; model?: string }): AssertionHandle;
|
|
781
|
-
factuality(expected: string, opts?: { on?: string; model?: string }): AssertionHandle;
|
|
782
|
-
summarizes(source: string, opts?: { on?: string; model?: string }): AssertionHandle;
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
export interface JudgeNamespace {
|
|
786
|
-
/** 结构化对照评估的子命名空间(t.judge.autoevals.closedQA / .factuality / .summarizes)。 */
|
|
787
|
-
autoevals: AutoevalsNamespace;
|
|
788
|
-
}
|
|
789
|
-
|
|
790
|
-
export interface DiffView {
|
|
791
|
-
get(path: string): string | undefined;
|
|
792
|
-
isEmpty(): boolean;
|
|
793
|
-
matches(re: RegExp): boolean;
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
/** 工具匹配小语言。 */
|
|
797
|
-
export interface ToolMatch {
|
|
798
|
-
input?: Record<string, unknown>;
|
|
799
|
-
count?: number;
|
|
800
|
-
status?: "completed" | "failed" | "rejected";
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
export interface SubagentMatch {
|
|
804
|
-
count?: number;
|
|
805
|
-
status?: "completed" | "failed";
|
|
806
|
-
remoteUrl?: string | RegExp;
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
export interface InputRequestFilter {
|
|
810
|
-
id?: string | RegExp;
|
|
811
|
-
prompt?: string | RegExp;
|
|
812
|
-
display?: string | RegExp;
|
|
813
|
-
action?: string | RegExp;
|
|
814
|
-
input?: Record<string, unknown>;
|
|
815
|
-
optionIds?: readonly string[];
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
export interface SessionHandle {
|
|
819
|
-
send(text: string): Promise<TurnHandle>;
|
|
820
|
-
sendFile(path: string, text?: string): Promise<TurnHandle>;
|
|
821
|
-
requireInputRequest(filter?: InputRequestFilter): InputRequest;
|
|
822
|
-
respond(...responses: string[]): Promise<TurnHandle>;
|
|
823
|
-
respondAll(optionId: string): Promise<TurnHandle>;
|
|
824
|
-
readonly reply: string;
|
|
825
|
-
readonly sessionId: string | undefined;
|
|
826
|
-
readonly events: readonly StreamEvent[];
|
|
827
|
-
succeeded(): AssertionHandle;
|
|
828
|
-
parked(): AssertionHandle;
|
|
829
|
-
messageIncludes(token: string | RegExp): AssertionHandle;
|
|
830
|
-
calledTool(name: string, match?: ToolMatch): AssertionHandle;
|
|
831
|
-
notCalledTool(name: string, match?: ToolMatch): AssertionHandle;
|
|
832
|
-
toolOrder(names: string[]): AssertionHandle;
|
|
833
|
-
usedNoTools(): AssertionHandle;
|
|
834
|
-
maxToolCalls(max: number): AssertionHandle;
|
|
835
|
-
loadedSkill(skill: string): AssertionHandle;
|
|
836
|
-
noFailedActions(): AssertionHandle;
|
|
837
|
-
event(type: StreamEvent["type"], opts?: { count?: number }): AssertionHandle;
|
|
838
|
-
notEvent(type: StreamEvent["type"]): AssertionHandle;
|
|
839
|
-
calledSubagent(name: string, match?: SubagentMatch): AssertionHandle;
|
|
840
|
-
eventOrder(types: StreamEvent["type"][]): AssertionHandle;
|
|
841
|
-
eventsSatisfy(predicate: (events: readonly StreamEvent[]) => boolean, label?: string): AssertionHandle;
|
|
842
|
-
maxTokens(max: number): AssertionHandle;
|
|
843
|
-
maxCost(usd: number): AssertionHandle;
|
|
844
|
-
readonly usage: Usage;
|
|
845
|
-
readonly judge: JudgeNamespace;
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
/**
|
|
849
|
-
* eval 作者拿到的高层上下文。运行器按 agent 能力组装;tsx 不做类型检查,所以这里
|
|
850
|
-
* 用一个宽接口承载全部动作(运行时按 capability 守卫)。
|
|
851
|
-
*/
|
|
852
|
-
export interface TestContext {
|
|
853
|
-
// 会话
|
|
854
|
-
send(text: string): Promise<TurnHandle>;
|
|
855
|
-
/** 发一条带文件(图片等多模态输入)的消息。`path` 相对项目根;读出后 base64 随 TurnInput.files 交给 adapter。 */
|
|
856
|
-
sendFile(path: string, text?: string): Promise<TurnHandle>;
|
|
857
|
-
requireInputRequest(filter?: InputRequestFilter): InputRequest;
|
|
858
|
-
respond(...responses: string[]): Promise<TurnHandle>;
|
|
859
|
-
respondAll(optionId: string): Promise<TurnHandle>;
|
|
860
|
-
readonly reply: string;
|
|
861
|
-
readonly sessionId: string | undefined;
|
|
862
|
-
readonly events: readonly StreamEvent[];
|
|
863
|
-
newSession(): SessionHandle;
|
|
864
|
-
|
|
865
|
-
// 运行上下文
|
|
866
|
-
readonly signal: AbortSignal;
|
|
867
|
-
readonly model?: string;
|
|
868
|
-
readonly flags: Readonly<Record<string, unknown>>;
|
|
869
|
-
log(msg: string): void;
|
|
870
|
-
skip(reason: string): never;
|
|
871
|
-
|
|
872
|
-
// 值级断言
|
|
873
|
-
check(value: unknown, assertion: ValueAssertion): AssertionHandle;
|
|
874
|
-
require(value: unknown, assertion: ValueAssertion): Promise<unknown>;
|
|
875
|
-
/**
|
|
876
|
-
* 把一组断言归到一个有标题的分组下(对照 vitest 的 test('title', ...))。纯组织/报告用,
|
|
877
|
-
* 不改打分:组里每条断言仍独立计分。可嵌套(标题用 › 连接)。
|
|
878
|
-
*/
|
|
879
|
-
group<T>(title: string, fn: () => Promise<T> | T): Promise<T>;
|
|
880
|
-
|
|
881
|
-
// 作用域断言(工具 / 会话)
|
|
882
|
-
succeeded(): AssertionHandle;
|
|
883
|
-
parked(): AssertionHandle;
|
|
884
|
-
messageIncludes(token: string | RegExp): AssertionHandle;
|
|
885
|
-
calledTool(name: string, match?: ToolMatch): AssertionHandle;
|
|
886
|
-
notCalledTool(name: string, match?: ToolMatch): AssertionHandle;
|
|
887
|
-
toolOrder(names: string[]): AssertionHandle;
|
|
888
|
-
usedNoTools(): AssertionHandle;
|
|
889
|
-
maxToolCalls(max: number): AssertionHandle;
|
|
890
|
-
loadedSkill(skill: string): AssertionHandle;
|
|
891
|
-
noFailedActions(): AssertionHandle;
|
|
892
|
-
event(type: StreamEvent["type"], opts?: { count?: number }): AssertionHandle;
|
|
893
|
-
notEvent(type: StreamEvent["type"]): AssertionHandle;
|
|
894
|
-
calledSubagent(name: string, match?: SubagentMatch): AssertionHandle;
|
|
895
|
-
eventOrder(types: StreamEvent["type"][]): AssertionHandle;
|
|
896
|
-
eventsSatisfy(predicate: (events: readonly StreamEvent[]) => boolean, label?: string): AssertionHandle;
|
|
897
|
-
|
|
898
|
-
// 工作区 / 沙箱
|
|
899
|
-
readonly sandbox: SandboxHandle;
|
|
900
|
-
file(path: string): string;
|
|
901
|
-
fileChanged(path: string): AssertionHandle;
|
|
902
|
-
fileDeleted(path: string): AssertionHandle;
|
|
903
|
-
notInDiff(re: RegExp): AssertionHandle;
|
|
904
|
-
noFailedShellCommands(): AssertionHandle;
|
|
905
|
-
|
|
906
|
-
// 效率 / 成本
|
|
907
|
-
readonly usage: Usage;
|
|
908
|
-
maxTokens(max: number): AssertionHandle;
|
|
909
|
-
maxCost(usd: number): AssertionHandle;
|
|
910
|
-
|
|
911
|
-
// judge
|
|
912
|
-
readonly judge: JudgeNamespace;
|
|
913
|
-
}
|
|
16
|
+
export * from "./shared/types.ts";
|
|
17
|
+
export * from "./o11y/types.ts";
|
|
18
|
+
export * from "./sandbox/types.ts";
|
|
19
|
+
export * from "./agents/types.ts";
|
|
20
|
+
export * from "./scoring/types.ts";
|
|
21
|
+
export * from "./context/types.ts";
|
|
22
|
+
export * from "./runner/types.ts";
|