@webywz/eval-smoke 0.3.1 → 0.5.0
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 +232 -127
- package/dist/baseline.d.ts +25 -0
- package/dist/baseline.d.ts.map +1 -0
- package/dist/baseline.js +86 -0
- package/dist/baseline.js.map +1 -0
- package/dist/github-summary.d.ts +4 -0
- package/dist/github-summary.d.ts.map +1 -0
- package/dist/github-summary.js +39 -0
- package/dist/github-summary.js.map +1 -0
- package/dist/html-report.d.ts +8 -0
- package/dist/html-report.d.ts.map +1 -0
- package/dist/html-report.js +196 -0
- package/dist/html-report.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/run-cli.d.ts +7 -1
- package/dist/run-cli.d.ts.map +1 -1
- package/dist/run-cli.js +87 -3
- package/dist/run-cli.js.map +1 -1
- package/dist/summary-md.d.ts +4 -0
- package/dist/summary-md.d.ts.map +1 -0
- package/dist/summary-md.js +22 -0
- package/dist/summary-md.js.map +1 -0
- package/examples/github-action.yml +41 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,8 +1,22 @@
|
|
|
1
1
|
# eval-smoke
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@webywz/eval-smoke)
|
|
4
|
+
[](https://www.npmjs.com/package/@webywz/eval-smoke)
|
|
5
|
+
[](./LICENSE)
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
> **One-minute smoke evals for prompts and models.**
|
|
8
|
+
>
|
|
9
|
+
> **一分钟烟雾测试:在发版前拦住 prompt / 模型回归。**
|
|
10
|
+
|
|
11
|
+
YAML or JSON in. Pass/fail out. Exit `1` on regression. Optional JUnit for CI.
|
|
12
|
+
|
|
13
|
+
YAML 或 JSON 进,通过/失败出。回归时退出码 `1`。可选 JUnit 对接 CI。
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## English
|
|
18
|
+
|
|
19
|
+
### Demo
|
|
6
20
|
|
|
7
21
|
```bash
|
|
8
22
|
npm i -g @webywz/eval-smoke
|
|
@@ -10,8 +24,8 @@ eval-smoke init
|
|
|
10
24
|
eval-smoke suite.yaml --mock
|
|
11
25
|
```
|
|
12
26
|
|
|
13
|
-
```
|
|
14
|
-
eval-smoke 0.
|
|
27
|
+
```text
|
|
28
|
+
eval-smoke 0.5.0 · mock · gpt-4o-mini · smoke · 4 cases
|
|
15
29
|
|
|
16
30
|
pass greeting 0ms
|
|
17
31
|
pass capital-france 0ms
|
|
@@ -21,48 +35,60 @@ eval-smoke 0.3.0 · mock · gpt-4o-mini · smoke · 4 cases
|
|
|
21
35
|
4 passed 0 failed 1ms
|
|
22
36
|
```
|
|
23
37
|
|
|
24
|
-
`--mock` is offline and deterministic.
|
|
38
|
+
`--mock` is offline and deterministic. CI and contributors do not need API keys.
|
|
39
|
+
|
|
40
|
+
### Features
|
|
25
41
|
|
|
26
|
-
|
|
42
|
+
| | |
|
|
43
|
+
| --- | --- |
|
|
44
|
+
| **Tiny suite** | A handful of cases — not a full harness |
|
|
45
|
+
| **Matchers** | `equals`, `contains`, `not_contains`, `regex`, `json`, `min_tokens` / `max_tokens` |
|
|
46
|
+
| **init** | `eval-smoke init` scaffolds a mock-friendly `suite.yaml` |
|
|
47
|
+
| **Filter** | `--only` by id substring or `/regex/` |
|
|
48
|
+
| **CI** | `--junit`, `--html`, `--github-summary`, exit codes, `--fail-fast`, `--concurrency` |
|
|
49
|
+
| **Baseline** | `--baseline` / `--update-baseline` catch pass→fail regressions |
|
|
50
|
+
| **Providers** | Mock offline; live OpenAI / Anthropic when keyed |
|
|
51
|
+
|
|
52
|
+
### Install
|
|
27
53
|
|
|
28
54
|
```bash
|
|
29
55
|
npm i -g @webywz/eval-smoke
|
|
30
|
-
# or
|
|
56
|
+
# or one-off:
|
|
31
57
|
npx @webywz/eval-smoke suite.yaml --mock
|
|
32
58
|
```
|
|
33
59
|
|
|
34
|
-
|
|
60
|
+
Node 18+.
|
|
35
61
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
1. Scaffold a suite with `eval-smoke init`, or copy the example.
|
|
39
|
-
2. Run mock mode until the runner and matchers look right.
|
|
40
|
-
3. Point the same file at a live model when you are ready.
|
|
62
|
+
### Quick start
|
|
41
63
|
|
|
42
64
|
```bash
|
|
43
65
|
eval-smoke init
|
|
44
66
|
eval-smoke suite.yaml --mock
|
|
45
67
|
eval-smoke suite.yaml --mock --only greeting
|
|
46
68
|
eval-smoke suite.yaml --mock --junit junit.xml
|
|
69
|
+
eval-smoke suite.yaml --mock --html report.html
|
|
47
70
|
eval-smoke examples/suite.yaml --mock --json
|
|
48
71
|
eval-smoke examples/suite.yaml --mock --concurrency 4
|
|
72
|
+
eval-smoke examples/suite.yaml --mock --update-baseline baseline.json
|
|
73
|
+
eval-smoke examples/suite.yaml --mock --baseline baseline.json
|
|
74
|
+
eval-smoke examples/suite.yaml --mock --github-summary
|
|
49
75
|
OPENAI_API_KEY=sk-... eval-smoke examples/suite.yaml
|
|
50
76
|
ANTHROPIC_API_KEY=sk-ant-... eval-smoke examples/suite.yaml --model claude-3-5-haiku-20241022
|
|
51
77
|
```
|
|
52
78
|
|
|
53
|
-
|
|
79
|
+
### `eval-smoke init`
|
|
54
80
|
|
|
55
81
|
```bash
|
|
56
82
|
eval-smoke init # writes ./suite.yaml
|
|
57
83
|
eval-smoke init ./prompts # writes ./prompts/suite.yaml
|
|
58
|
-
eval-smoke init --force # overwrite
|
|
84
|
+
eval-smoke init --force # overwrite
|
|
59
85
|
```
|
|
60
86
|
|
|
61
|
-
Creates a small mock-friendly suite (greeting, capital, arithmetic, refusal). Refuses to overwrite unless
|
|
87
|
+
Creates a small mock-friendly suite (greeting, capital, arithmetic, refusal). Refuses to overwrite unless `--force`.
|
|
62
88
|
|
|
63
|
-
|
|
89
|
+
### Suite format
|
|
64
90
|
|
|
65
|
-
|
|
91
|
+
YAML or JSON — a list of cases, or an object with `cases`:
|
|
66
92
|
|
|
67
93
|
```yaml
|
|
68
94
|
name: demo
|
|
@@ -87,132 +113,222 @@ cases:
|
|
|
87
113
|
city: Paris
|
|
88
114
|
```
|
|
89
115
|
|
|
90
|
-
|
|
116
|
+
| Field | Notes |
|
|
117
|
+
| --- | --- |
|
|
118
|
+
| `id` / `prompt` | Required |
|
|
119
|
+
| `expect.contains` | Substring, case-insensitive; string or list |
|
|
120
|
+
| `expect.not_contains` | Must be absent |
|
|
121
|
+
| `expect.regex` | `/pattern/i`, `(?i)pattern`, or raw |
|
|
122
|
+
| `expect.equals` | Exact after NFC + LF normalize |
|
|
123
|
+
| `expect.json` | `true` = parseable; object/array = deep-equal |
|
|
124
|
+
| `expect.min_tokens` / `max_tokens` | Approx via `ceil(chars / 4)` |
|
|
125
|
+
| `system` / `model` | Suite or case |
|
|
126
|
+
| `provider` | `openai`, `anthropic`, or `mock` |
|
|
127
|
+
|
|
128
|
+
At least one matcher per case. Failures print expected/actual (and a first-diff for `equals`).
|
|
129
|
+
|
|
130
|
+
### CLI
|
|
91
131
|
|
|
92
|
-
```yaml
|
|
93
|
-
- id: capital-france
|
|
94
|
-
prompt: What is the capital of France? Answer with the city name only.
|
|
95
|
-
expect:
|
|
96
|
-
contains: Paris
|
|
97
132
|
```
|
|
133
|
+
eval-smoke <suite.yaml|suite.json> [options]
|
|
134
|
+
eval-smoke init [dir] [--force]
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
| Flag | Meaning |
|
|
138
|
+
| --- | --- |
|
|
139
|
+
| `--mock` | Offline deterministic runner |
|
|
140
|
+
| `--json` | Machine-readable JSON report |
|
|
141
|
+
| `--junit <path>` | Write JUnit XML (still prints human report unless `--json`) |
|
|
142
|
+
| `--html <path>` | Write self-contained HTML report (offline, no CDN) |
|
|
143
|
+
| `--baseline <path>` | Exit `1` if a baseline-passing case now fails or is missing |
|
|
144
|
+
| `--update-baseline <path>` | Write current report JSON (pretty) to path |
|
|
145
|
+
| `--github-summary [path]` | Append pass/fail Markdown to `$GITHUB_STEP_SUMMARY` or path |
|
|
146
|
+
| `--only <pattern>` | Cases whose id contains pattern, or matches `/regex/` |
|
|
147
|
+
| `--fail-fast` | Stop after first failure |
|
|
148
|
+
| `--concurrency <n>` | Parallel cases (default 1; ignored with `--fail-fast`) |
|
|
149
|
+
| `--timeout <ms>` | Per-case timeout (default 30000) |
|
|
150
|
+
| `--model <name>` | Override suite model |
|
|
151
|
+
| `--provider <name>` | `openai` \| `anthropic` \| `mock` |
|
|
152
|
+
| `--force` | Overwrite for `init` |
|
|
153
|
+
| `--no-color` | Disable ANSI (`NO_COLOR` respected) |
|
|
154
|
+
|
|
155
|
+
| Exit | Meaning |
|
|
156
|
+
| --- | --- |
|
|
157
|
+
| `0` | All passed |
|
|
158
|
+
| `1` | One or more failed |
|
|
159
|
+
| `2` | Usage / config error (missing file, bad YAML, no key, `--only` matched nothing) |
|
|
160
|
+
|
|
161
|
+
### Runners
|
|
98
162
|
|
|
99
|
-
|
|
100
|
-
| --- | --- | --- |
|
|
101
|
-
| `id` | case | Required, unique. |
|
|
102
|
-
| `prompt` | case | Required. |
|
|
103
|
-
| `expect.contains` | case | Substring, case-insensitive. String or list; all must match. |
|
|
104
|
-
| `expect.not_contains` | case | Substring must be absent. String or list. |
|
|
105
|
-
| `expect.regex` | case | JavaScript regex. `/pattern/i`, `(?i)pattern`, or a raw pattern. |
|
|
106
|
-
| `expect.equals` | case | Exact match after NFC + LF normalize; one trailing newline ignored. |
|
|
107
|
-
| `expect.json` | case | `true` = must parse as JSON; object/array = deep-equal after parse. |
|
|
108
|
-
| `expect.min_tokens` | case | Approximate floor using `ceil(chars / 4)`. |
|
|
109
|
-
| `expect.max_tokens` | case | Approximate cap using `ceil(chars / 4)`. |
|
|
110
|
-
| `system` | suite or case | Optional system prompt for live providers. |
|
|
111
|
-
| `model` | suite or case | Live default: `gpt-4o-mini` or `claude-3-5-haiku-20241022`. |
|
|
112
|
-
| `provider` | suite | `openai`, `anthropic`, or `mock`. |
|
|
163
|
+
**Mock** never calls the network. Canned replies keep the example suite green in CI:
|
|
113
164
|
|
|
114
|
-
|
|
165
|
+
| Prompt contains | Mock reply |
|
|
166
|
+
| --- | --- |
|
|
167
|
+
| `hello` | `Hello` |
|
|
168
|
+
| `capital of France` | `Paris` |
|
|
169
|
+
| `2 + 2` | `4` |
|
|
170
|
+
| `ignore previous` / `PWNED` | a refusal without `PWNED` |
|
|
115
171
|
|
|
116
|
-
|
|
172
|
+
**Live OpenAI** with `OPENAI_API_KEY` (optional `OPENAI_BASE_URL`). **Live Anthropic** with `ANTHROPIC_API_KEY`, or when the model name starts with `claude`.
|
|
117
173
|
|
|
174
|
+
### Library
|
|
175
|
+
|
|
176
|
+
```ts
|
|
177
|
+
import { loadSuite, runSuite, formatJUnit, initSuite } from "@webywz/eval-smoke";
|
|
178
|
+
|
|
179
|
+
initSuite(".", { force: false });
|
|
180
|
+
const suite = loadSuite("suite.yaml");
|
|
181
|
+
const report = await runSuite(suite, { mock: true, concurrency: 4 });
|
|
182
|
+
console.log(formatJUnit(report));
|
|
183
|
+
if (!report.ok) process.exit(1);
|
|
118
184
|
```
|
|
119
|
-
eval-smoke <suite.yaml|suite.json> [options]
|
|
120
|
-
eval-smoke init [dir] [--force]
|
|
121
185
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
--force Overwrite existing suite.yaml for init
|
|
132
|
-
--no-color Disable ANSI colors (also respects NO_COLOR)
|
|
133
|
-
-h, --help
|
|
134
|
-
-v, --version
|
|
186
|
+
### CI snippet
|
|
187
|
+
|
|
188
|
+
```yaml
|
|
189
|
+
- run: npx @webywz/eval-smoke@0.5.0 prompts/smoke.yaml --mock --junit junit.xml
|
|
190
|
+
- uses: actions/upload-artifact@v4
|
|
191
|
+
if: always()
|
|
192
|
+
with:
|
|
193
|
+
name: junit
|
|
194
|
+
path: junit.xml
|
|
135
195
|
```
|
|
136
196
|
|
|
137
|
-
|
|
197
|
+
### Links
|
|
198
|
+
|
|
199
|
+
| | |
|
|
138
200
|
| --- | --- |
|
|
139
|
-
|
|
|
140
|
-
|
|
|
141
|
-
|
|
|
201
|
+
| npm | [npmjs.com/package/@webywz/eval-smoke](https://www.npmjs.com/package/@webywz/eval-smoke) |
|
|
202
|
+
| Issues | [github.com/webywz/eval-smoke/issues](https://github.com/webywz/eval-smoke/issues) |
|
|
203
|
+
| License | [MIT](./LICENSE) © webywz |
|
|
204
|
+
|
|
205
|
+
---
|
|
142
206
|
|
|
143
|
-
|
|
207
|
+
## 中文
|
|
144
208
|
|
|
145
|
-
|
|
209
|
+
### 演示
|
|
146
210
|
|
|
147
211
|
```bash
|
|
148
|
-
eval-smoke
|
|
149
|
-
eval-smoke
|
|
150
|
-
eval-smoke suite.yaml --mock
|
|
212
|
+
npm i -g @webywz/eval-smoke
|
|
213
|
+
eval-smoke init
|
|
214
|
+
eval-smoke suite.yaml --mock
|
|
151
215
|
```
|
|
152
216
|
|
|
153
|
-
|
|
217
|
+
`--mock` 离线、确定。CI 与贡献者不需要 API key。
|
|
154
218
|
|
|
155
|
-
###
|
|
219
|
+
### 功能
|
|
156
220
|
|
|
157
|
-
|
|
221
|
+
| | |
|
|
222
|
+
| --- | --- |
|
|
223
|
+
| **轻量套件** | 少量用例,不是完整评测框架 |
|
|
224
|
+
| **匹配器** | `equals`、`contains`、`not_contains`、`regex`、`json`、token 上下限 |
|
|
225
|
+
| **init** | `eval-smoke init` 生成可 mock 的 `suite.yaml` |
|
|
226
|
+
| **过滤** | `--only`:id 子串或 `/regex/` |
|
|
227
|
+
| **CI** | `--junit`、`--html`、`--github-summary`、退出码、`--fail-fast`、`--concurrency` |
|
|
228
|
+
| **基线** | `--baseline` / `--update-baseline` 拦住 pass→fail 回归 |
|
|
229
|
+
| **提供商** | Mock 离线;有 key 时走 OpenAI / Anthropic |
|
|
230
|
+
|
|
231
|
+
### 安装
|
|
158
232
|
|
|
159
233
|
```bash
|
|
160
|
-
|
|
234
|
+
npm i -g @webywz/eval-smoke
|
|
235
|
+
# 或一次性:
|
|
236
|
+
npx @webywz/eval-smoke suite.yaml --mock
|
|
161
237
|
```
|
|
162
238
|
|
|
163
|
-
|
|
239
|
+
需要 Node 18+。
|
|
240
|
+
|
|
241
|
+
### 快速开始
|
|
164
242
|
|
|
165
|
-
|
|
243
|
+
```bash
|
|
244
|
+
eval-smoke init
|
|
245
|
+
eval-smoke suite.yaml --mock
|
|
246
|
+
eval-smoke suite.yaml --mock --only greeting
|
|
247
|
+
eval-smoke suite.yaml --mock --junit junit.xml
|
|
248
|
+
eval-smoke suite.yaml --mock --html report.html
|
|
249
|
+
eval-smoke examples/suite.yaml --mock --json
|
|
250
|
+
eval-smoke examples/suite.yaml --mock --concurrency 4
|
|
251
|
+
eval-smoke examples/suite.yaml --mock --update-baseline baseline.json
|
|
252
|
+
eval-smoke examples/suite.yaml --mock --baseline baseline.json
|
|
253
|
+
eval-smoke examples/suite.yaml --mock --github-summary
|
|
254
|
+
OPENAI_API_KEY=sk-... eval-smoke examples/suite.yaml
|
|
255
|
+
ANTHROPIC_API_KEY=sk-ant-... eval-smoke examples/suite.yaml --model claude-3-5-haiku-20241022
|
|
256
|
+
```
|
|
166
257
|
|
|
167
|
-
|
|
258
|
+
### `eval-smoke init`
|
|
168
259
|
|
|
169
|
-
|
|
260
|
+
```bash
|
|
261
|
+
eval-smoke init # 写入 ./suite.yaml
|
|
262
|
+
eval-smoke init ./prompts # 写入 ./prompts/suite.yaml
|
|
263
|
+
eval-smoke init --force # 覆盖已有文件
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
生成小型 mock 友好套件(问候、首都、算术、拒答)。不加 `--force` 不会覆盖已有文件。
|
|
267
|
+
|
|
268
|
+
### 套件格式
|
|
269
|
+
|
|
270
|
+
YAML 或 JSON —— 用例列表,或带 `cases` 的对象:
|
|
271
|
+
|
|
272
|
+
```yaml
|
|
273
|
+
name: demo
|
|
274
|
+
model: gpt-4o-mini
|
|
275
|
+
cases:
|
|
276
|
+
- id: greeting
|
|
277
|
+
prompt: Reply with exactly the word Hello.
|
|
278
|
+
expect:
|
|
279
|
+
equals: Hello
|
|
280
|
+
contains: Hello
|
|
281
|
+
not_contains: Goodbye
|
|
282
|
+
regex: "(?i)^hello\\b"
|
|
283
|
+
min_tokens: 1
|
|
284
|
+
max_tokens: 32
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
| 字段 | 说明 |
|
|
170
288
|
| --- | --- |
|
|
171
|
-
| `
|
|
172
|
-
| `
|
|
173
|
-
| `
|
|
174
|
-
| `
|
|
289
|
+
| `id` / `prompt` | 必填 |
|
|
290
|
+
| `expect.contains` | 子串,忽略大小写;字符串或列表 |
|
|
291
|
+
| `expect.not_contains` | 不得出现 |
|
|
292
|
+
| `expect.regex` | `/pattern/i`、`(?i)pattern` 或裸模式 |
|
|
293
|
+
| `expect.equals` | NFC + LF 规范化后精确匹配 |
|
|
294
|
+
| `expect.json` | `true` = 可解析;对象/数组 = 深相等 |
|
|
295
|
+
| `expect.min_tokens` / `max_tokens` | 约 `ceil(chars / 4)` |
|
|
296
|
+
| `system` / `model` | 套件级或用例级 |
|
|
297
|
+
| `provider` | `openai` / `anthropic` / `mock` |
|
|
175
298
|
|
|
176
|
-
|
|
299
|
+
每个用例至少要有一个匹配器。失败时打印期望/实际(`equals` 还有首差位置)。
|
|
177
300
|
|
|
178
|
-
|
|
301
|
+
### 命令行要点
|
|
179
302
|
|
|
180
|
-
|
|
303
|
+
| 参数 | 含义 |
|
|
304
|
+
| --- | --- |
|
|
305
|
+
| `--mock` | 离线确定性运行 |
|
|
306
|
+
| `--json` | JSON 报告 |
|
|
307
|
+
| `--junit <path>` | 写出 JUnit XML(仍打印人类可读报告,除非同时 `--json`) |
|
|
308
|
+
| `--html <path>` | 写入自包含 HTML 报告(离线、无 CDN) |
|
|
309
|
+
| `--baseline <path>` | 基线里曾通过的用例若失败或缺失则退出 `1` |
|
|
310
|
+
| `--update-baseline <path>` | 把当前报告 JSON(pretty)写入路径 |
|
|
311
|
+
| `--github-summary [path]` | 追加通过/失败 Markdown 到 `$GITHUB_STEP_SUMMARY` 或指定路径 |
|
|
312
|
+
| `--only <pattern>` | 按 id 子串或 `/regex/` 过滤 |
|
|
313
|
+
| `--fail-fast` | 首个失败即停 |
|
|
314
|
+
| `--concurrency <n>` | 并行用例数(默认 1) |
|
|
315
|
+
| `--timeout <ms>` | 单用例超时(默认 30000) |
|
|
316
|
+
| `--model` / `--provider` | 覆盖模型 / 提供商 |
|
|
317
|
+
| `--force` | `init` 时覆盖 |
|
|
318
|
+
|
|
319
|
+
| 退出码 | 含义 |
|
|
320
|
+
| --- | --- |
|
|
321
|
+
| `0` | 全部通过 |
|
|
322
|
+
| `1` | 有失败 |
|
|
323
|
+
| `2` | 用法/配置错误(缺文件、坏 YAML、无 key、`--only` 无匹配) |
|
|
181
324
|
|
|
182
|
-
|
|
325
|
+
### 运行器
|
|
183
326
|
|
|
184
|
-
|
|
327
|
+
**Mock** 永不联网。内置 canned 回复保证示例套件在 CI 里绿。
|
|
185
328
|
|
|
186
|
-
|
|
187
|
-
eval-smoke examples/suite.yaml --mock --json
|
|
188
|
-
```
|
|
329
|
+
**Live OpenAI**:`OPENAI_API_KEY`(可选 `OPENAI_BASE_URL`)。**Live Anthropic**:`ANTHROPIC_API_KEY`,或模型名以 `claude` 开头。
|
|
189
330
|
|
|
190
|
-
|
|
191
|
-
{
|
|
192
|
-
"ok": true,
|
|
193
|
-
"version": "0.3.0",
|
|
194
|
-
"runner": "mock",
|
|
195
|
-
"model": "gpt-4o-mini",
|
|
196
|
-
"suite": "demo",
|
|
197
|
-
"passed": 5,
|
|
198
|
-
"failed": 0,
|
|
199
|
-
"skipped": 0,
|
|
200
|
-
"duration_ms": 1,
|
|
201
|
-
"cases": [
|
|
202
|
-
{
|
|
203
|
-
"id": "greeting",
|
|
204
|
-
"ok": true,
|
|
205
|
-
"duration_ms": 1,
|
|
206
|
-
"output": "Hello",
|
|
207
|
-
"checks": [{ "name": "contains", "ok": true, "expected": "Hello" }]
|
|
208
|
-
}
|
|
209
|
-
]
|
|
210
|
-
}
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
Failed checks may include `actual` plus a multi-line `detail` (first-diff for `equals`).
|
|
214
|
-
|
|
215
|
-
## Programmatic API
|
|
331
|
+
### 库用法
|
|
216
332
|
|
|
217
333
|
```ts
|
|
218
334
|
import { loadSuite, runSuite, formatJUnit, initSuite } from "@webywz/eval-smoke";
|
|
@@ -224,21 +340,10 @@ console.log(formatJUnit(report));
|
|
|
224
340
|
if (!report.ok) process.exit(1);
|
|
225
341
|
```
|
|
226
342
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
## CI
|
|
343
|
+
### 链接
|
|
230
344
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
if: always()
|
|
237
|
-
with:
|
|
238
|
-
name: junit
|
|
239
|
-
path: junit.xml
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
## License
|
|
243
|
-
|
|
244
|
-
MIT
|
|
345
|
+
| | |
|
|
346
|
+
| --- | --- |
|
|
347
|
+
| npm | [npmjs.com/package/@webywz/eval-smoke](https://www.npmjs.com/package/@webywz/eval-smoke) |
|
|
348
|
+
| Issues | [github.com/webywz/eval-smoke/issues](https://github.com/webywz/eval-smoke/issues) |
|
|
349
|
+
| License | [MIT](./LICENSE) © webywz |
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Report } from "./types.js";
|
|
2
|
+
export interface BaselineCase {
|
|
3
|
+
id: string;
|
|
4
|
+
ok: boolean;
|
|
5
|
+
}
|
|
6
|
+
export interface BaselineFile {
|
|
7
|
+
cases: BaselineCase[];
|
|
8
|
+
}
|
|
9
|
+
/** Minimal shape: case id → ok. Accepts a full Report or a slim baseline. */
|
|
10
|
+
export declare function loadBaseline(path: string): Map<string, boolean>;
|
|
11
|
+
export interface BaselineCheck {
|
|
12
|
+
ok: boolean;
|
|
13
|
+
regressions: string[];
|
|
14
|
+
/** Human-readable lines */
|
|
15
|
+
report: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Fail if any case that passed in baseline now fails, or a baseline case is missing
|
|
19
|
+
* from the current run. New cases are OK. Skipped current cases that passed in
|
|
20
|
+
* baseline count as missing/regression.
|
|
21
|
+
*/
|
|
22
|
+
export declare function checkBaseline(report: Report, baseline: Map<string, boolean>): BaselineCheck;
|
|
23
|
+
/** Pretty-print current report JSON for --update-baseline. */
|
|
24
|
+
export declare function writeBaseline(path: string, report: Report): void;
|
|
25
|
+
//# sourceMappingURL=baseline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"baseline.d.ts","sourceRoot":"","sources":["../src/baseline.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAGzC,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,OAAO,CAAC;CACb;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB;AAED,6EAA6E;AAC7E,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAmC/D;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,OAAO,CAAC;IACZ,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,2BAA2B;IAC3B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,aAAa,CAgC3F;AAED,8DAA8D;AAC9D,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAEhE"}
|
package/dist/baseline.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { UsageError } from "./types.js";
|
|
3
|
+
/** Minimal shape: case id → ok. Accepts a full Report or a slim baseline. */
|
|
4
|
+
export function loadBaseline(path) {
|
|
5
|
+
let raw;
|
|
6
|
+
try {
|
|
7
|
+
raw = readFileSync(path, "utf8");
|
|
8
|
+
}
|
|
9
|
+
catch (error) {
|
|
10
|
+
const code = error && typeof error === "object" && "code" in error
|
|
11
|
+
? String(error.code)
|
|
12
|
+
: "";
|
|
13
|
+
if (code === "ENOENT")
|
|
14
|
+
throw new UsageError(`baseline not found: ${path}`);
|
|
15
|
+
throw error;
|
|
16
|
+
}
|
|
17
|
+
let parsed;
|
|
18
|
+
try {
|
|
19
|
+
parsed = JSON.parse(raw);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
throw new UsageError(`baseline is not valid JSON: ${path}`);
|
|
23
|
+
}
|
|
24
|
+
if (!parsed || typeof parsed !== "object") {
|
|
25
|
+
throw new UsageError(`baseline must be a JSON object: ${path}`);
|
|
26
|
+
}
|
|
27
|
+
const cases = parsed.cases;
|
|
28
|
+
if (!Array.isArray(cases)) {
|
|
29
|
+
throw new UsageError(`baseline missing cases[]: ${path}`);
|
|
30
|
+
}
|
|
31
|
+
const map = new Map();
|
|
32
|
+
for (const entry of cases) {
|
|
33
|
+
if (!entry || typeof entry !== "object")
|
|
34
|
+
continue;
|
|
35
|
+
const id = entry.id;
|
|
36
|
+
const ok = entry.ok;
|
|
37
|
+
if (typeof id !== "string" || !id)
|
|
38
|
+
continue;
|
|
39
|
+
if (typeof ok !== "boolean")
|
|
40
|
+
continue;
|
|
41
|
+
map.set(id, ok);
|
|
42
|
+
}
|
|
43
|
+
return map;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Fail if any case that passed in baseline now fails, or a baseline case is missing
|
|
47
|
+
* from the current run. New cases are OK. Skipped current cases that passed in
|
|
48
|
+
* baseline count as missing/regression.
|
|
49
|
+
*/
|
|
50
|
+
export function checkBaseline(report, baseline) {
|
|
51
|
+
const byId = new Map(report.cases.map((c) => [c.id, c]));
|
|
52
|
+
const regressions = [];
|
|
53
|
+
for (const [id, wasOk] of baseline) {
|
|
54
|
+
if (!wasOk)
|
|
55
|
+
continue; // only guard formerly-passing cases
|
|
56
|
+
const current = byId.get(id);
|
|
57
|
+
if (!current) {
|
|
58
|
+
regressions.push(`${id}: missing from current run (passed in baseline)`);
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (current.skipped) {
|
|
62
|
+
regressions.push(`${id}: skipped now (passed in baseline)`);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (!current.ok) {
|
|
66
|
+
regressions.push(`${id}: was pass, now fail`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const lines = ["baseline check:"];
|
|
70
|
+
if (regressions.length === 0) {
|
|
71
|
+
lines.push(" ok — no regressions vs baseline");
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
lines.push(` ${regressions.length} regression(s):`);
|
|
75
|
+
for (const line of regressions) {
|
|
76
|
+
lines.push(` - ${line}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
lines.push("");
|
|
80
|
+
return { ok: regressions.length === 0, regressions, report: lines.join("\n") };
|
|
81
|
+
}
|
|
82
|
+
/** Pretty-print current report JSON for --update-baseline. */
|
|
83
|
+
export function writeBaseline(path, report) {
|
|
84
|
+
writeFileSync(path, `${JSON.stringify(report, null, 2)}\n`, "utf8");
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=baseline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"baseline.js","sourceRoot":"","sources":["../src/baseline.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const DEFAULT_GITHUB_SUMMARY = "github-step-summary.md";
|
|
2
|
+
export declare function resolveGithubSummaryPath(flagPath: string | true | undefined, env?: NodeJS.ProcessEnv, cwd?: string): string | undefined;
|
|
3
|
+
export declare function appendGithubSummary(path: string, markdown: string): void;
|
|
4
|
+
//# sourceMappingURL=github-summary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-summary.d.ts","sourceRoot":"","sources":["../src/github-summary.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,sBAAsB,2BAA2B,CAAC;AAE/D,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACnC,GAAG,GAAE,MAAM,CAAC,UAAwB,EACpC,GAAG,GAAE,MAAsB,GAC1B,MAAM,GAAG,SAAS,CAWpB;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAoBxE"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { appendFileSync, mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, resolve } from "node:path";
|
|
3
|
+
export const DEFAULT_GITHUB_SUMMARY = "github-step-summary.md";
|
|
4
|
+
export function resolveGithubSummaryPath(flagPath, env = process.env, cwd = process.cwd()) {
|
|
5
|
+
if (flagPath === undefined) {
|
|
6
|
+
const fromEnv = env.GITHUB_STEP_SUMMARY;
|
|
7
|
+
return fromEnv && fromEnv.trim() ? fromEnv : undefined;
|
|
8
|
+
}
|
|
9
|
+
if (flagPath === true || flagPath === "") {
|
|
10
|
+
const fromEnv = env.GITHUB_STEP_SUMMARY;
|
|
11
|
+
if (fromEnv && fromEnv.trim())
|
|
12
|
+
return fromEnv;
|
|
13
|
+
return resolve(cwd, DEFAULT_GITHUB_SUMMARY);
|
|
14
|
+
}
|
|
15
|
+
return resolve(cwd, flagPath);
|
|
16
|
+
}
|
|
17
|
+
export function appendGithubSummary(path, markdown) {
|
|
18
|
+
const body = markdown.endsWith("\n") ? markdown : `${markdown}\n`;
|
|
19
|
+
try {
|
|
20
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
// ignore
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
appendFileSync(path, body, "utf8");
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
const code = error && typeof error === "object" && "code" in error
|
|
30
|
+
? String(error.code)
|
|
31
|
+
: "";
|
|
32
|
+
if (code === "ENOENT") {
|
|
33
|
+
writeFileSync(path, body, "utf8");
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=github-summary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-summary.js","sourceRoot":"","sources":["../src/github-summary.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACnE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE7C,MAAM,CAAC,MAAM,sBAAsB,GAAG,wBAAwB,CAAC;AAE/D,MAAM,UAAU,wBAAwB,CACtC,QAAmC,EACnC,MAAyB,OAAO,CAAC,GAAG,EACpC,MAAc,OAAO,CAAC,GAAG,EAAE;IAE3B,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,GAAG,CAAC,mBAAmB,CAAC;QACxC,OAAO,OAAO,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;IACzD,CAAC;IACD,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,KAAK,EAAE,EAAE,CAAC;QACzC,MAAM,OAAO,GAAG,GAAG,CAAC,mBAAmB,CAAC;QACxC,IAAI,OAAO,IAAI,OAAO,CAAC,IAAI,EAAE;YAAE,OAAO,OAAO,CAAC;QAC9C,OAAO,OAAO,CAAC,GAAG,EAAE,sBAAsB,CAAC,CAAC;IAC9C,CAAC;IACD,OAAO,OAAO,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,IAAY,EAAE,QAAgB;IAChE,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,IAAI,CAAC;IAClE,IAAI,CAAC;QACH,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,SAAS;IACX,CAAC;IACD,IAAI,CAAC;QACH,cAAc,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IACrC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,GACR,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,IAAI,KAAK;YACnD,CAAC,CAAC,MAAM,CAAE,KAA2B,CAAC,IAAI,CAAC;YAC3C,CAAC,CAAC,EAAE,CAAC;QACT,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;YACtB,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;YAClC,OAAO;QACT,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Report } from "./types.js";
|
|
2
|
+
/** Escape text for safe insertion into HTML text/attr contexts. */
|
|
3
|
+
export declare function escapeHtml(text: string): string;
|
|
4
|
+
/**
|
|
5
|
+
* Self-contained dark HTML report for an eval-smoke run (inline CSS+JS, no CDN).
|
|
6
|
+
*/
|
|
7
|
+
export declare function formatHtmlReport(report: Report): string;
|
|
8
|
+
//# sourceMappingURL=html-report.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html-report.d.ts","sourceRoot":"","sources":["../src/html-report.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA2B,MAAM,EAAE,MAAM,YAAY,CAAC;AAElE,mEAAmE;AACnE,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAO/C;AA8CD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAgJvD"}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/** Escape text for safe insertion into HTML text/attr contexts. */
|
|
2
|
+
export function escapeHtml(text) {
|
|
3
|
+
return text
|
|
4
|
+
.replace(/&/g, "&")
|
|
5
|
+
.replace(/</g, "<")
|
|
6
|
+
.replace(/>/g, ">")
|
|
7
|
+
.replace(/"/g, """)
|
|
8
|
+
.replace(/'/g, "'");
|
|
9
|
+
}
|
|
10
|
+
function truncate(text, max = 400) {
|
|
11
|
+
const compact = text.replace(/\s+/g, " ").trim();
|
|
12
|
+
if (compact.length <= max)
|
|
13
|
+
return compact;
|
|
14
|
+
return `${compact.slice(0, max - 1)}…`;
|
|
15
|
+
}
|
|
16
|
+
function failedChecks(result) {
|
|
17
|
+
return (result.checks ?? []).filter((check) => !check.ok);
|
|
18
|
+
}
|
|
19
|
+
function statusLabel(result) {
|
|
20
|
+
if (result.skipped)
|
|
21
|
+
return "skip";
|
|
22
|
+
return result.ok ? "pass" : "fail";
|
|
23
|
+
}
|
|
24
|
+
function statusClass(result) {
|
|
25
|
+
if (result.skipped)
|
|
26
|
+
return "skip";
|
|
27
|
+
return result.ok ? "pass" : "fail";
|
|
28
|
+
}
|
|
29
|
+
function failureDetails(result) {
|
|
30
|
+
const parts = [];
|
|
31
|
+
if (result.error)
|
|
32
|
+
parts.push(result.error);
|
|
33
|
+
for (const check of failedChecks(result)) {
|
|
34
|
+
const detail = check.detail ?? "failed";
|
|
35
|
+
parts.push(`${check.name}: ${detail}`);
|
|
36
|
+
if (check.expected != null && !detail.includes("expected:")) {
|
|
37
|
+
parts.push(`expected: ${truncate(check.expected, 200)}`);
|
|
38
|
+
}
|
|
39
|
+
if (check.actual != null && !detail.includes("actual:")) {
|
|
40
|
+
parts.push(`actual: ${truncate(check.actual, 200)}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (result.output &&
|
|
44
|
+
!result.ok &&
|
|
45
|
+
failedChecks(result).every((c) => c.name !== "equals")) {
|
|
46
|
+
parts.push(`output: ${truncate(result.output)}`);
|
|
47
|
+
}
|
|
48
|
+
if (!parts.length)
|
|
49
|
+
return "";
|
|
50
|
+
return `<details><summary>details</summary><pre>${escapeHtml(parts.join("\n"))}</pre></details>`;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Self-contained dark HTML report for an eval-smoke run (inline CSS+JS, no CDN).
|
|
54
|
+
*/
|
|
55
|
+
export function formatHtmlReport(report) {
|
|
56
|
+
const suite = escapeHtml(report.suite ?? "suite");
|
|
57
|
+
const version = escapeHtml(report.version);
|
|
58
|
+
const runner = escapeHtml(report.runner);
|
|
59
|
+
const model = escapeHtml(report.model);
|
|
60
|
+
const okClass = report.ok ? "ok" : "bad";
|
|
61
|
+
const rows = report.cases
|
|
62
|
+
.map((result) => {
|
|
63
|
+
const st = statusClass(result);
|
|
64
|
+
const details = failureDetails(result);
|
|
65
|
+
return `<tr class="${st}">
|
|
66
|
+
<td><span class="badge ${st}">${statusLabel(result)}</span></td>
|
|
67
|
+
<td><code>${escapeHtml(result.id)}</code></td>
|
|
68
|
+
<td class="num">${result.duration_ms}ms</td>
|
|
69
|
+
<td class="detail">${details}</td>
|
|
70
|
+
</tr>`;
|
|
71
|
+
})
|
|
72
|
+
.join("\n");
|
|
73
|
+
return `<!DOCTYPE html>
|
|
74
|
+
<html lang="en">
|
|
75
|
+
<head>
|
|
76
|
+
<meta charset="utf-8"/>
|
|
77
|
+
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
|
78
|
+
<title>eval-smoke · ${suite}</title>
|
|
79
|
+
<style>
|
|
80
|
+
:root {
|
|
81
|
+
--bg: #0a0a0a;
|
|
82
|
+
--panel: #111111;
|
|
83
|
+
--border: #222222;
|
|
84
|
+
--text: #eeeeee;
|
|
85
|
+
--muted: #888888;
|
|
86
|
+
--pass: #7ee787;
|
|
87
|
+
--fail: #ff7b72;
|
|
88
|
+
--skip: #d2a8ff;
|
|
89
|
+
--font: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
90
|
+
}
|
|
91
|
+
* { box-sizing: border-box; }
|
|
92
|
+
html, body {
|
|
93
|
+
margin: 0; padding: 0;
|
|
94
|
+
background: var(--bg); color: var(--text);
|
|
95
|
+
font: 13px/1.45 var(--font);
|
|
96
|
+
}
|
|
97
|
+
body { padding: 24px 20px 48px; max-width: 960px; margin: 0 auto; }
|
|
98
|
+
header.top {
|
|
99
|
+
border: 1px solid var(--border); background: var(--panel);
|
|
100
|
+
padding: 16px 18px; margin-bottom: 16px;
|
|
101
|
+
}
|
|
102
|
+
header.top h1 { margin: 0 0 6px; font-size: 15px; font-weight: 600; }
|
|
103
|
+
header.top .meta { color: var(--muted); font-size: 12px; }
|
|
104
|
+
header.top .summary { margin-top: 10px; font-size: 13px; }
|
|
105
|
+
header.top.ok .summary { color: var(--pass); }
|
|
106
|
+
header.top.bad .summary { color: var(--fail); }
|
|
107
|
+
.stats {
|
|
108
|
+
display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
109
|
+
gap: 8px; margin: 0 0 18px;
|
|
110
|
+
}
|
|
111
|
+
.stat {
|
|
112
|
+
border: 1px solid var(--border); background: var(--panel);
|
|
113
|
+
padding: 10px 12px;
|
|
114
|
+
}
|
|
115
|
+
.stat .label { color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em; }
|
|
116
|
+
.stat .value { margin-top: 4px; font-size: 16px; }
|
|
117
|
+
table {
|
|
118
|
+
width: 100%; border-collapse: collapse;
|
|
119
|
+
border: 1px solid var(--border); background: var(--panel);
|
|
120
|
+
}
|
|
121
|
+
th, td {
|
|
122
|
+
text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--border);
|
|
123
|
+
vertical-align: top;
|
|
124
|
+
}
|
|
125
|
+
th { color: var(--muted); font-weight: 500; font-size: 11px; text-transform: uppercase; }
|
|
126
|
+
td.num { text-align: right; color: var(--muted); white-space: nowrap; }
|
|
127
|
+
.badge {
|
|
128
|
+
display: inline-block; min-width: 3.2em; text-align: center;
|
|
129
|
+
padding: 1px 6px; border: 1px solid var(--border);
|
|
130
|
+
}
|
|
131
|
+
.badge.pass { color: var(--pass); border-color: #1e3d28; }
|
|
132
|
+
.badge.fail { color: var(--fail); border-color: #5a1d1d; }
|
|
133
|
+
.badge.skip { color: var(--skip); border-color: #3a2a55; }
|
|
134
|
+
tr.fail { background: #140a0a; }
|
|
135
|
+
details { margin-top: 4px; }
|
|
136
|
+
summary { cursor: pointer; color: var(--muted); user-select: none; }
|
|
137
|
+
pre {
|
|
138
|
+
margin: 6px 0 0; padding: 10px; overflow: auto;
|
|
139
|
+
background: #050505; border: 1px solid var(--border);
|
|
140
|
+
white-space: pre-wrap; word-break: break-word; color: #d8d8d8;
|
|
141
|
+
}
|
|
142
|
+
.toolbar { margin: 0 0 12px; display: flex; gap: 8px; }
|
|
143
|
+
.toolbar button {
|
|
144
|
+
background: var(--panel); color: var(--text); border: 1px solid var(--border);
|
|
145
|
+
padding: 6px 10px; font: inherit; cursor: pointer;
|
|
146
|
+
}
|
|
147
|
+
.toolbar button:hover { border-color: #444; }
|
|
148
|
+
footer {
|
|
149
|
+
margin-top: 28px; color: var(--muted); font-size: 11px;
|
|
150
|
+
border-top: 1px solid var(--border); padding-top: 12px;
|
|
151
|
+
}
|
|
152
|
+
code { font: inherit; }
|
|
153
|
+
@media (max-width: 640px) {
|
|
154
|
+
.stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
155
|
+
}
|
|
156
|
+
</style>
|
|
157
|
+
</head>
|
|
158
|
+
<body>
|
|
159
|
+
<header class="top ${okClass}">
|
|
160
|
+
<h1>eval-smoke ${version}</h1>
|
|
161
|
+
<div class="meta">${runner} · ${model} · ${suite} · ${report.cases.length} cases</div>
|
|
162
|
+
<div class="summary">${report.passed} passed · ${report.failed} failed${report.skipped ? ` · ${report.skipped} skipped` : ""} · ${report.duration_ms}ms</div>
|
|
163
|
+
</header>
|
|
164
|
+
<section class="stats" aria-label="stats">
|
|
165
|
+
<div class="stat"><div class="label">passed</div><div class="value">${report.passed}</div></div>
|
|
166
|
+
<div class="stat"><div class="label">failed</div><div class="value">${report.failed}</div></div>
|
|
167
|
+
<div class="stat"><div class="label">skipped</div><div class="value">${report.skipped}</div></div>
|
|
168
|
+
<div class="stat"><div class="label">duration</div><div class="value">${report.duration_ms}ms</div></div>
|
|
169
|
+
</section>
|
|
170
|
+
<div class="toolbar">
|
|
171
|
+
<button type="button" id="expand-all">Expand failures</button>
|
|
172
|
+
<button type="button" id="collapse-all">Collapse</button>
|
|
173
|
+
</div>
|
|
174
|
+
<table>
|
|
175
|
+
<thead><tr><th>Status</th><th>Case</th><th>Duration</th><th>Details</th></tr></thead>
|
|
176
|
+
<tbody>
|
|
177
|
+
${rows || `<tr><td colspan="4" style="color:var(--muted)">no cases</td></tr>`}
|
|
178
|
+
</tbody>
|
|
179
|
+
</table>
|
|
180
|
+
<footer>Generated by eval-smoke · self-contained · works offline (file://)</footer>
|
|
181
|
+
<script>
|
|
182
|
+
(function () {
|
|
183
|
+
var expand = document.getElementById("expand-all");
|
|
184
|
+
var collapse = document.getElementById("collapse-all");
|
|
185
|
+
function setAll(open) {
|
|
186
|
+
document.querySelectorAll("details").forEach(function (d) { d.open = open; });
|
|
187
|
+
}
|
|
188
|
+
if (expand) expand.addEventListener("click", function () { setAll(true); });
|
|
189
|
+
if (collapse) collapse.addEventListener("click", function () { setAll(false); });
|
|
190
|
+
})();
|
|
191
|
+
</script>
|
|
192
|
+
</body>
|
|
193
|
+
</html>
|
|
194
|
+
`;
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=html-report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html-report.js","sourceRoot":"","sources":["../src/html-report.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,9 +4,14 @@ export { formatJUnit } from "./junit.js";
|
|
|
4
4
|
export { evaluateCase, evaluateExpect, equalsDiff, normalizeEquals } from "./matchers.js";
|
|
5
5
|
export { createComplete, mockComplete, resolveProvider } from "./providers.js";
|
|
6
6
|
export { formatReport } from "./report.js";
|
|
7
|
+
export { formatHtmlReport, escapeHtml } from "./html-report.js";
|
|
7
8
|
export { runSuite } from "./run.js";
|
|
8
9
|
export { estimateTokens } from "./tokens.js";
|
|
9
10
|
export { VERSION } from "./version.js";
|
|
10
11
|
export type { Case, CaseResult, CheckName, CheckResult, CompleteFn, CompleteRequest, Expect, JsonExpect, ProviderName, Report, RunOptions, Suite, } from "./types.js";
|
|
11
12
|
export { UsageError } from "./types.js";
|
|
13
|
+
export { checkBaseline, loadBaseline, writeBaseline } from "./baseline.js";
|
|
14
|
+
export type { BaselineCheck, BaselineCase, BaselineFile } from "./baseline.js";
|
|
15
|
+
export { appendGithubSummary, resolveGithubSummaryPath, DEFAULT_GITHUB_SUMMARY } from "./github-summary.js";
|
|
16
|
+
export { formatGithubSummary } from "./summary-md.js";
|
|
12
17
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5E,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC1F,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC/E,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,YAAY,EACV,IAAI,EACJ,UAAU,EACV,SAAS,EACT,WAAW,EACX,UAAU,EACV,eAAe,EACf,MAAM,EACN,UAAU,EACV,YAAY,EACZ,MAAM,EACN,UAAU,EACV,KAAK,GACN,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5E,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC1F,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC/E,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,YAAY,EACV,IAAI,EACJ,UAAU,EACV,SAAS,EACT,WAAW,EACX,UAAU,EACV,eAAe,EACf,MAAM,EACN,UAAU,EACV,YAAY,EACZ,MAAM,EACN,UAAU,EACV,KAAK,GACN,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC3E,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC/E,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC5G,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -4,8 +4,12 @@ export { formatJUnit } from "./junit.js";
|
|
|
4
4
|
export { evaluateCase, evaluateExpect, equalsDiff, normalizeEquals } from "./matchers.js";
|
|
5
5
|
export { createComplete, mockComplete, resolveProvider } from "./providers.js";
|
|
6
6
|
export { formatReport } from "./report.js";
|
|
7
|
+
export { formatHtmlReport, escapeHtml } from "./html-report.js";
|
|
7
8
|
export { runSuite } from "./run.js";
|
|
8
9
|
export { estimateTokens } from "./tokens.js";
|
|
9
10
|
export { VERSION } from "./version.js";
|
|
10
11
|
export { UsageError } from "./types.js";
|
|
12
|
+
export { checkBaseline, loadBaseline, writeBaseline } from "./baseline.js";
|
|
13
|
+
export { appendGithubSummary, resolveGithubSummaryPath, DEFAULT_GITHUB_SUMMARY } from "./github-summary.js";
|
|
14
|
+
export { formatGithubSummary } from "./summary-md.js";
|
|
11
15
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5E,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC1F,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC/E,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAevC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5E,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC1F,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC/E,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAevC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE3E,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC5G,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/run-cli.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Suite } from "./types.js";
|
|
2
|
-
export declare const HELP = "eval-smoke \u2014 one-minute smoke evals for prompts and models\n\nUsage:\n eval-smoke <suite.yaml|suite.json> [options]\n eval-smoke init [dir] [--force]\n\nOptions:\n --mock Offline runner with deterministic fake answers\n --json Machine-readable JSON report\n --junit <path> Write JUnit XML report to path (still prints human report unless --json)\n --only <pattern> Run cases whose id contains pattern, or matches /regex/\n --fail-fast Stop after the first failing case\n --concurrency <n> Run up to N cases in parallel (default: 1; ignored with --fail-fast)\n --timeout <ms> Per-case timeout (default: 30000)\n --model <name> Override suite model\n --provider <name> openai | anthropic | mock\n --force Overwrite existing suite.yaml when using init\n --no-color Disable ANSI colors (also respects NO_COLOR)\n -h, --help Show this help\n -v, --version Show version\n\nExit codes:\n 0 all cases passed\n 1 one or more cases failed\n 2 usage or configuration error\n\nExamples:\n eval-smoke init\n eval-smoke init ./prompts --force\n eval-smoke examples/suite.yaml --mock\n eval-smoke examples/suite.yaml --mock --only greeting\n eval-smoke examples/suite.yaml --mock --only '/^capital/'\n eval-smoke examples/suite.yaml --mock --junit junit.xml\n eval-smoke examples/suite.yaml --json --fail-fast\n eval-smoke examples/suite.yaml --mock --concurrency 4\n OPENAI_API_KEY=sk-... eval-smoke examples/suite.yaml\n";
|
|
2
|
+
export declare const HELP = "eval-smoke \u2014 one-minute smoke evals for prompts and models\n\nUsage:\n eval-smoke <suite.yaml|suite.json> [options]\n eval-smoke init [dir] [--force]\n\nOptions:\n --mock Offline runner with deterministic fake answers\n --json Machine-readable JSON report\n --junit <path> Write JUnit XML report to path (still prints human report unless --json)\n --html <path> Write self-contained HTML report to path (offline, no CDN)\n --only <pattern> Run cases whose id contains pattern, or matches /regex/\n --fail-fast Stop after the first failing case\n --concurrency <n> Run up to N cases in parallel (default: 1; ignored with --fail-fast)\n --timeout <ms> Per-case timeout (default: 30000)\n --model <name> Override suite model\n --provider <name> openai | anthropic | mock\n --baseline <path> Fail if any baseline-passing case now fails or is missing\n --update-baseline <path> Write current report JSON to path (pretty)\n --github-summary [path] Append Markdown to GITHUB_STEP_SUMMARY or path\n --force Overwrite existing suite.yaml when using init\n --no-color Disable ANSI colors (also respects NO_COLOR)\n -h, --help Show this help\n -v, --version Show version\n\nExit codes:\n 0 all cases passed (and baseline clean when --baseline is set)\n 1 one or more cases failed, or baseline regression\n 2 usage or configuration error\n\nExamples:\n eval-smoke init\n eval-smoke init ./prompts --force\n eval-smoke examples/suite.yaml --mock\n eval-smoke examples/suite.yaml --mock --only greeting\n eval-smoke examples/suite.yaml --mock --only '/^capital/'\n eval-smoke examples/suite.yaml --mock --junit junit.xml\n eval-smoke examples/suite.yaml --mock --html report.html\n eval-smoke examples/suite.yaml --json --fail-fast\n eval-smoke examples/suite.yaml --mock --concurrency 4\n eval-smoke examples/suite.yaml --mock --update-baseline baseline.json\n eval-smoke examples/suite.yaml --mock --baseline baseline.json\n eval-smoke examples/suite.yaml --mock --github-summary\n OPENAI_API_KEY=sk-... eval-smoke examples/suite.yaml\n";
|
|
3
3
|
export interface CliIo {
|
|
4
4
|
stdout: {
|
|
5
5
|
write(text: string): void;
|
|
@@ -14,5 +14,11 @@ export interface CliIo {
|
|
|
14
14
|
/** Match case ids by substring, or by /regex/ flags (same style as matchers). */
|
|
15
15
|
export declare function matchOnly(id: string, pattern: string): boolean;
|
|
16
16
|
export declare function filterSuiteByOnly(suite: Suite, pattern: string): Suite;
|
|
17
|
+
/**
|
|
18
|
+
* Rewrite bare `--github-summary` into `--github-summary=__auto__` so node:util
|
|
19
|
+
* parseArgs (string option) accepts it. `--github-summary=path` and
|
|
20
|
+
* `--github-summary path` stay as-is.
|
|
21
|
+
*/
|
|
22
|
+
export declare function normalizeGithubSummaryArgv(argv: string[]): string[];
|
|
17
23
|
export declare function runCli(argv: string[], io?: CliIo): Promise<number>;
|
|
18
24
|
//# sourceMappingURL=run-cli.d.ts.map
|
package/dist/run-cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-cli.d.ts","sourceRoot":"","sources":["../src/run-cli.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"run-cli.d.ts","sourceRoot":"","sources":["../src/run-cli.ts"],"names":[],"mappings":"AAaA,OAAO,EAAiC,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC;AAMvE,eAAO,MAAM,IAAI,+nEA4ChB,CAAC;AAEF,MAAM,WAAW,KAAK;IACpB,MAAM,EAAE;QAAE,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACtC,MAAM,EAAE;QAAE,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACtC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,OAAO,CAAC;CAChB;AAkCD,iFAAiF;AACjF,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAQ9D;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,GAAG,KAAK,CAMtE;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAiBnE;AAQD,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,GAAE,KAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAsIrF"}
|
package/dist/run-cli.js
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import { writeFileSync } from "node:fs";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { parseArgs } from "node:util";
|
|
4
|
+
import { checkBaseline, loadBaseline, writeBaseline } from "./baseline.js";
|
|
5
|
+
import { appendGithubSummary, resolveGithubSummaryPath } from "./github-summary.js";
|
|
4
6
|
import { initSuite } from "./init.js";
|
|
5
7
|
import { formatJUnit } from "./junit.js";
|
|
6
8
|
import { loadSuite } from "./load-suite.js";
|
|
7
9
|
import { compileRegex } from "./matchers.js";
|
|
10
|
+
import { formatHtmlReport } from "./html-report.js";
|
|
8
11
|
import { formatReport } from "./report.js";
|
|
9
12
|
import { DEFAULT_TIMEOUT_MS, runSuite } from "./run.js";
|
|
13
|
+
import { formatGithubSummary } from "./summary-md.js";
|
|
10
14
|
import { UsageError } from "./types.js";
|
|
11
15
|
import { VERSION } from "./version.js";
|
|
16
|
+
/** Sentinel so node:util accepts a bare --github-summary flag. */
|
|
17
|
+
const GITHUB_SUMMARY_AUTO = "__auto__";
|
|
12
18
|
export const HELP = `eval-smoke — one-minute smoke evals for prompts and models
|
|
13
19
|
|
|
14
20
|
Usage:
|
|
@@ -19,20 +25,24 @@ Options:
|
|
|
19
25
|
--mock Offline runner with deterministic fake answers
|
|
20
26
|
--json Machine-readable JSON report
|
|
21
27
|
--junit <path> Write JUnit XML report to path (still prints human report unless --json)
|
|
28
|
+
--html <path> Write self-contained HTML report to path (offline, no CDN)
|
|
22
29
|
--only <pattern> Run cases whose id contains pattern, or matches /regex/
|
|
23
30
|
--fail-fast Stop after the first failing case
|
|
24
31
|
--concurrency <n> Run up to N cases in parallel (default: 1; ignored with --fail-fast)
|
|
25
32
|
--timeout <ms> Per-case timeout (default: ${DEFAULT_TIMEOUT_MS})
|
|
26
33
|
--model <name> Override suite model
|
|
27
34
|
--provider <name> openai | anthropic | mock
|
|
35
|
+
--baseline <path> Fail if any baseline-passing case now fails or is missing
|
|
36
|
+
--update-baseline <path> Write current report JSON to path (pretty)
|
|
37
|
+
--github-summary [path] Append Markdown to GITHUB_STEP_SUMMARY or path
|
|
28
38
|
--force Overwrite existing suite.yaml when using init
|
|
29
39
|
--no-color Disable ANSI colors (also respects NO_COLOR)
|
|
30
40
|
-h, --help Show this help
|
|
31
41
|
-v, --version Show version
|
|
32
42
|
|
|
33
43
|
Exit codes:
|
|
34
|
-
0 all cases passed
|
|
35
|
-
1 one or more cases failed
|
|
44
|
+
0 all cases passed (and baseline clean when --baseline is set)
|
|
45
|
+
1 one or more cases failed, or baseline regression
|
|
36
46
|
2 usage or configuration error
|
|
37
47
|
|
|
38
48
|
Examples:
|
|
@@ -42,8 +52,12 @@ Examples:
|
|
|
42
52
|
eval-smoke examples/suite.yaml --mock --only greeting
|
|
43
53
|
eval-smoke examples/suite.yaml --mock --only '/^capital/'
|
|
44
54
|
eval-smoke examples/suite.yaml --mock --junit junit.xml
|
|
55
|
+
eval-smoke examples/suite.yaml --mock --html report.html
|
|
45
56
|
eval-smoke examples/suite.yaml --json --fail-fast
|
|
46
57
|
eval-smoke examples/suite.yaml --mock --concurrency 4
|
|
58
|
+
eval-smoke examples/suite.yaml --mock --update-baseline baseline.json
|
|
59
|
+
eval-smoke examples/suite.yaml --mock --baseline baseline.json
|
|
60
|
+
eval-smoke examples/suite.yaml --mock --github-summary
|
|
47
61
|
OPENAI_API_KEY=sk-... eval-smoke examples/suite.yaml
|
|
48
62
|
`;
|
|
49
63
|
const defaultIo = () => ({
|
|
@@ -95,23 +109,58 @@ export function filterSuiteByOnly(suite, pattern) {
|
|
|
95
109
|
}
|
|
96
110
|
return { ...suite, cases };
|
|
97
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* Rewrite bare `--github-summary` into `--github-summary=__auto__` so node:util
|
|
114
|
+
* parseArgs (string option) accepts it. `--github-summary=path` and
|
|
115
|
+
* `--github-summary path` stay as-is.
|
|
116
|
+
*/
|
|
117
|
+
export function normalizeGithubSummaryArgv(argv) {
|
|
118
|
+
const out = [];
|
|
119
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
120
|
+
const token = argv[i];
|
|
121
|
+
if (token === "--github-summary") {
|
|
122
|
+
const next = argv[i + 1];
|
|
123
|
+
if (!next || next.startsWith("-")) {
|
|
124
|
+
out.push(`--github-summary=${GITHUB_SUMMARY_AUTO}`);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
out.push("--github-summary", next);
|
|
128
|
+
i += 1;
|
|
129
|
+
}
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
out.push(token);
|
|
133
|
+
}
|
|
134
|
+
return out;
|
|
135
|
+
}
|
|
136
|
+
function githubSummaryFlag(raw) {
|
|
137
|
+
if (raw == null)
|
|
138
|
+
return undefined;
|
|
139
|
+
if (raw === GITHUB_SUMMARY_AUTO)
|
|
140
|
+
return true;
|
|
141
|
+
return raw;
|
|
142
|
+
}
|
|
98
143
|
export async function runCli(argv, io = defaultIo()) {
|
|
99
144
|
let parsed;
|
|
100
145
|
try {
|
|
101
146
|
parsed = parseArgs({
|
|
102
|
-
args: argv,
|
|
147
|
+
args: normalizeGithubSummaryArgv(argv),
|
|
103
148
|
allowPositionals: true,
|
|
104
149
|
strict: true,
|
|
105
150
|
options: {
|
|
106
151
|
mock: { type: "boolean", default: false },
|
|
107
152
|
json: { type: "boolean", default: false },
|
|
108
153
|
junit: { type: "string" },
|
|
154
|
+
html: { type: "string" },
|
|
109
155
|
only: { type: "string" },
|
|
110
156
|
"fail-fast": { type: "boolean", default: false },
|
|
111
157
|
concurrency: { type: "string" },
|
|
112
158
|
timeout: { type: "string" },
|
|
113
159
|
model: { type: "string" },
|
|
114
160
|
provider: { type: "string" },
|
|
161
|
+
baseline: { type: "string" },
|
|
162
|
+
"update-baseline": { type: "string" },
|
|
163
|
+
"github-summary": { type: "string" },
|
|
115
164
|
force: { type: "boolean", default: false },
|
|
116
165
|
"no-color": { type: "boolean", default: false },
|
|
117
166
|
help: { type: "boolean", short: "h", default: false },
|
|
@@ -158,6 +207,10 @@ export async function runCli(argv, io = defaultIo()) {
|
|
|
158
207
|
const json = asBool(values.json);
|
|
159
208
|
const only = asString(values.only);
|
|
160
209
|
const junitPath = asString(values.junit);
|
|
210
|
+
const htmlPath = asString(values.html);
|
|
211
|
+
const baselinePath = asString(values.baseline);
|
|
212
|
+
const updateBaselinePath = asString(values["update-baseline"]);
|
|
213
|
+
const githubSummary = githubSummaryFlag(asString(values["github-summary"]));
|
|
161
214
|
let suite = loadSuite(suitePath, io.cwd);
|
|
162
215
|
if (only != null) {
|
|
163
216
|
suite = filterSuiteByOnly(suite, only);
|
|
@@ -175,9 +228,40 @@ export async function runCli(argv, io = defaultIo()) {
|
|
|
175
228
|
const out = resolve(io.cwd, junitPath);
|
|
176
229
|
writeFileSync(out, formatJUnit(report), "utf8");
|
|
177
230
|
}
|
|
231
|
+
if (htmlPath != null) {
|
|
232
|
+
const out = resolve(io.cwd, htmlPath);
|
|
233
|
+
writeFileSync(out, formatHtmlReport(report), "utf8");
|
|
234
|
+
}
|
|
235
|
+
if (updateBaselinePath != null) {
|
|
236
|
+
const out = resolve(io.cwd, updateBaselinePath);
|
|
237
|
+
writeBaseline(out, report);
|
|
238
|
+
io.stderr.write(`Wrote baseline ${out}\n`);
|
|
239
|
+
}
|
|
240
|
+
let baselineFailed = false;
|
|
241
|
+
if (baselinePath != null) {
|
|
242
|
+
const baseline = loadBaseline(resolve(io.cwd, baselinePath));
|
|
243
|
+
const check = checkBaseline(report, baseline);
|
|
244
|
+
io.stderr.write(check.report);
|
|
245
|
+
if (!check.ok)
|
|
246
|
+
baselineFailed = true;
|
|
247
|
+
}
|
|
248
|
+
const outSummary = githubSummary === undefined
|
|
249
|
+
? resolveGithubSummaryPath(undefined, io.env, io.cwd)
|
|
250
|
+
: resolveGithubSummaryPath(githubSummary, io.env, io.cwd);
|
|
251
|
+
if (outSummary) {
|
|
252
|
+
try {
|
|
253
|
+
appendGithubSummary(outSummary, formatGithubSummary(report));
|
|
254
|
+
}
|
|
255
|
+
catch (error) {
|
|
256
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
257
|
+
io.stderr.write(`could not write github summary: ${message}\n`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
178
260
|
const noColor = asBool(values["no-color"]) || Boolean(io.env.NO_COLOR);
|
|
179
261
|
const color = io.isTTY && !json && !noColor;
|
|
180
262
|
io.stdout.write(formatReport(report, { json, color }));
|
|
263
|
+
if (baselineFailed)
|
|
264
|
+
return 1;
|
|
181
265
|
return report.ok ? 0 : 1;
|
|
182
266
|
}
|
|
183
267
|
catch (error) {
|
package/dist/run-cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-cli.js","sourceRoot":"","sources":["../src/run-cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,kBAAkB,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACxD,OAAO,EAAE,UAAU,EAAiC,MAAM,YAAY,CAAC;AACvE,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,MAAM,CAAC,MAAM,IAAI,GAAG
|
|
1
|
+
{"version":3,"file":"run-cli.js","sourceRoot":"","sources":["../src/run-cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summary-md.d.ts","sourceRoot":"","sources":["../src/summary-md.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEzC,wEAAwE;AACxE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAoB1D"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** Concise pass/fail Markdown table for GitHub Actions step summary. */
|
|
2
|
+
export function formatGithubSummary(report) {
|
|
3
|
+
const lines = [];
|
|
4
|
+
lines.push(`### eval-smoke · ${escape(report.suite ?? "suite")}`);
|
|
5
|
+
lines.push("");
|
|
6
|
+
lines.push(`**${report.passed} passed · ${report.failed} failed${report.skipped ? ` · ${report.skipped} skipped` : ""} · ${report.duration_ms}ms** · \`${report.runner}\` / \`${escape(report.model)}\``);
|
|
7
|
+
lines.push("");
|
|
8
|
+
lines.push("| Case | Result | ms |");
|
|
9
|
+
lines.push("| --- | --- | ---: |");
|
|
10
|
+
for (const c of report.cases) {
|
|
11
|
+
const status = c.skipped ? "skip" : c.ok ? "✅ pass" : "❌ fail";
|
|
12
|
+
lines.push(`| \`${escape(c.id)}\` | ${status} | ${c.duration_ms} |`);
|
|
13
|
+
}
|
|
14
|
+
lines.push("");
|
|
15
|
+
lines.push(report.ok ? "**Suite: PASS**" : "**Suite: FAIL**");
|
|
16
|
+
lines.push("");
|
|
17
|
+
return lines.join("\n");
|
|
18
|
+
}
|
|
19
|
+
function escape(text) {
|
|
20
|
+
return text.replace(/\|/g, "\\|").replace(/\r?\n/g, " ");
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=summary-md.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summary-md.js","sourceRoot":"","sources":["../src/summary-md.ts"],"names":[],"mappings":"AAEA,wEAAwE;AACxE,MAAM,UAAU,mBAAmB,CAAC,MAAc;IAChD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,CAAC,IAAI,CAAC,oBAAoB,MAAM,CAAC,MAAM,CAAC,KAAK,IAAI,OAAO,CAAC,EAAE,CAAC,CAAC;IAClE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CACR,KAAK,MAAM,CAAC,MAAM,aAAa,MAAM,CAAC,MAAM,UAC1C,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC,CAAC,EACpD,MAAM,MAAM,CAAC,WAAW,YAAY,MAAM,CAAC,MAAM,UAAU,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CACpF,CAAC;IACF,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;IACrC,KAAK,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IACnC,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC;QAC/D,KAAK,CAAC,IAAI,CAAC,OAAO,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,MAAM,MAAM,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC;IACvE,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC;IAC9D,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,SAAS,MAAM,CAAC,IAAY;IAC1B,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;AAC3D,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Copy-paste: run eval-smoke in GitHub Actions (mock offline + JUnit + step summary).
|
|
2
|
+
# No clone of this repo required — npx pulls @webywz/eval-smoke from npm.
|
|
3
|
+
#
|
|
4
|
+
# Human report → stdout (job log). JUnit → file. Markdown → $GITHUB_STEP_SUMMARY.
|
|
5
|
+
|
|
6
|
+
name: Smoke eval with eval-smoke
|
|
7
|
+
|
|
8
|
+
on:
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
# Or: push / pull_request when you have a suite.yaml in the repo.
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
eval:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- uses: actions/setup-node@v4
|
|
19
|
+
with:
|
|
20
|
+
node-version: 22
|
|
21
|
+
|
|
22
|
+
# Offline smoke against the published example suite (no API key):
|
|
23
|
+
- name: Mock smoke + JUnit + step summary
|
|
24
|
+
run: |
|
|
25
|
+
npx --yes @webywz/eval-smoke --help >/dev/null
|
|
26
|
+
# Prefer a suite checked into YOUR repo; this uses the package example via init:
|
|
27
|
+
npx --yes @webywz/eval-smoke init ./eval-smoke-ci --force
|
|
28
|
+
npx --yes @webywz/eval-smoke ./eval-smoke-ci/suite.yaml \
|
|
29
|
+
--mock \
|
|
30
|
+
--junit junit-eval-smoke.xml \
|
|
31
|
+
--github-summary \
|
|
32
|
+
--update-baseline eval-smoke-baseline.json
|
|
33
|
+
|
|
34
|
+
- name: Upload JUnit
|
|
35
|
+
if: always()
|
|
36
|
+
uses: actions/upload-artifact@v4
|
|
37
|
+
with:
|
|
38
|
+
name: eval-smoke-junit
|
|
39
|
+
path: |
|
|
40
|
+
junit-eval-smoke.xml
|
|
41
|
+
eval-smoke-baseline.json
|