niceeval 0.5.4 → 0.6.1

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.
Files changed (337) hide show
  1. package/INDEX.md +55 -0
  2. package/dist/agents/types.d.ts +321 -0
  3. package/dist/context/types.d.ts +335 -0
  4. package/dist/i18n/core.d.ts +11 -0
  5. package/dist/i18n/en.d.ts +181 -0
  6. package/dist/i18n/index.d.ts +5 -0
  7. package/dist/i18n/zh-CN.d.ts +183 -0
  8. package/dist/o11y/otlp/parse.d.ts +3 -0
  9. package/dist/o11y/otlp/receiver.d.ts +21 -0
  10. package/dist/o11y/otlp/turn-otel.d.ts +43 -0
  11. package/dist/o11y/types.d.ts +190 -0
  12. package/dist/report/aggregate.d.ts +57 -0
  13. package/dist/report/aggregate.js +212 -0
  14. package/dist/report/built-ins/experiment-comparison.d.ts +1 -0
  15. package/dist/report/built-ins/experiment-comparison.js +13 -0
  16. package/dist/report/built-ins/index.d.ts +1 -0
  17. package/dist/report/built-ins/index.js +2 -0
  18. package/dist/report/components.d.ts +180 -0
  19. package/dist/report/components.js +132 -0
  20. package/dist/report/compute.d.ts +106 -0
  21. package/dist/report/compute.js +600 -0
  22. package/dist/report/flag.d.ts +6 -0
  23. package/dist/report/flag.js +8 -0
  24. package/dist/report/format.d.ts +26 -0
  25. package/dist/report/format.js +109 -0
  26. package/dist/report/index.d.ts +19 -0
  27. package/dist/report/index.js +30 -0
  28. package/dist/report/load.d.ts +12 -0
  29. package/dist/report/load.js +56 -0
  30. package/dist/report/locale.d.ts +94 -0
  31. package/dist/report/locale.js +183 -0
  32. package/dist/report/metrics.d.ts +21 -0
  33. package/dist/report/metrics.js +115 -0
  34. package/dist/report/primitives.d.ts +64 -0
  35. package/dist/report/primitives.js +113 -0
  36. package/dist/report/react/AttemptList.d.ts +23 -0
  37. package/dist/report/react/AttemptList.js +19 -0
  38. package/dist/report/react/DeltaTable.d.ts +8 -0
  39. package/dist/report/react/DeltaTable.js +24 -0
  40. package/dist/report/react/EvalList.d.ts +10 -0
  41. package/dist/report/react/EvalList.js +0 -0
  42. package/dist/report/react/ExperimentList.d.ts +11 -0
  43. package/dist/report/react/ExperimentList.js +67 -0
  44. package/dist/report/react/GroupSummary.d.ts +8 -0
  45. package/dist/report/react/GroupSummary.js +8 -0
  46. package/dist/report/react/MetricBars.d.ts +10 -0
  47. package/dist/report/react/MetricBars.js +48 -0
  48. package/dist/report/react/MetricLine.d.ts +9 -0
  49. package/dist/report/react/MetricLine.js +68 -0
  50. package/dist/report/react/MetricMatrix.d.ts +10 -0
  51. package/dist/report/react/MetricMatrix.js +23 -0
  52. package/dist/report/react/MetricScatter.d.ts +9 -0
  53. package/dist/report/react/MetricScatter.js +96 -0
  54. package/dist/report/react/MetricTable.d.ts +11 -0
  55. package/dist/report/react/MetricTable.js +26 -0
  56. package/dist/report/react/RunOverview.d.ts +8 -0
  57. package/dist/report/react/RunOverview.js +12 -0
  58. package/dist/report/react/Scoreboard.d.ts +8 -0
  59. package/dist/report/react/Scoreboard.js +26 -0
  60. package/dist/report/react/cell.d.ts +9 -0
  61. package/dist/report/react/cell.js +9 -0
  62. package/dist/report/react/chart-math.d.ts +14 -0
  63. package/dist/report/react/chart-math.js +68 -0
  64. package/dist/report/react/colors.d.ts +14 -0
  65. package/dist/report/react/colors.js +45 -0
  66. package/dist/report/react/fixtures.d.ts +19 -0
  67. package/dist/report/react/fixtures.js +398 -0
  68. package/dist/report/react/format.d.ts +3 -0
  69. package/dist/report/react/format.js +7 -0
  70. package/dist/report/react/index.d.ts +6 -0
  71. package/dist/report/react/index.js +14 -0
  72. package/dist/report/report.d.ts +24 -0
  73. package/dist/report/report.js +49 -0
  74. package/dist/report/text/faces.d.ts +29 -0
  75. package/dist/report/text/faces.js +541 -0
  76. package/dist/report/text/layout.d.ts +75 -0
  77. package/dist/report/text/layout.js +173 -0
  78. package/dist/report/text/plot.d.ts +37 -0
  79. package/dist/report/text/plot.js +81 -0
  80. package/dist/report/text/table.d.ts +8 -0
  81. package/dist/report/text/table.js +86 -0
  82. package/dist/report/tree.d.ts +99 -0
  83. package/dist/report/tree.js +228 -0
  84. package/dist/report/types.d.ts +408 -0
  85. package/dist/report/types.js +4 -0
  86. package/dist/report/web.d.ts +15 -0
  87. package/dist/report/web.js +37 -0
  88. package/dist/results/locator.d.ts +75 -0
  89. package/dist/results/locator.js +0 -0
  90. package/dist/results/select.d.ts +45 -0
  91. package/dist/results/select.js +230 -0
  92. package/dist/results/source-hash.d.ts +9 -0
  93. package/dist/results/types.d.ts +204 -0
  94. package/dist/runner/eval-source.d.ts +22 -0
  95. package/dist/runner/fingerprint.d.ts +23 -0
  96. package/dist/runner/types.d.ts +332 -0
  97. package/dist/sandbox/docker-stream.d.ts +24 -0
  98. package/dist/sandbox/docker.d.ts +84 -0
  99. package/dist/sandbox/e2b.d.ts +31 -0
  100. package/dist/sandbox/errors.d.ts +4 -0
  101. package/dist/sandbox/local-files.d.ts +6 -0
  102. package/dist/sandbox/paths.d.ts +4 -0
  103. package/dist/sandbox/registry.d.ts +13 -0
  104. package/dist/sandbox/resolve.d.ts +40 -0
  105. package/dist/sandbox/retry.d.ts +10 -0
  106. package/dist/sandbox/shell.d.ts +11 -0
  107. package/dist/sandbox/source-files.d.ts +13 -0
  108. package/dist/sandbox/types.d.ts +203 -0
  109. package/dist/sandbox/vercel.d.ts +37 -0
  110. package/dist/scoring/types.d.ts +70 -0
  111. package/dist/shared/aggregate.d.ts +29 -0
  112. package/dist/shared/aggregate.js +55 -0
  113. package/dist/shared/types.d.ts +27 -0
  114. package/dist/shared/verdict.d.ts +27 -0
  115. package/dist/shared/verdict.js +34 -0
  116. package/dist/tty-line.d.ts +6 -0
  117. package/dist/types.d.ts +7 -0
  118. package/dist/util.d.ts +22 -0
  119. package/docs-site/zh/concepts/adapter.mdx +7 -7
  120. package/docs-site/zh/concepts/assert.mdx +19 -18
  121. package/docs-site/zh/concepts/evals.mdx +12 -11
  122. package/docs-site/zh/concepts/experiment.mdx +2 -2
  123. package/docs-site/zh/concepts/judge.mdx +9 -9
  124. package/docs-site/zh/concepts/overview.mdx +3 -3
  125. package/docs-site/zh/concepts/tier.mdx +6 -6
  126. package/docs-site/zh/example/claude-code-codex-plugin.mdx +9 -5
  127. package/docs-site/zh/example/claude-code-codex-skill.mdx +12 -7
  128. package/docs-site/zh/guides/agent-feedback-loop.mdx +237 -78
  129. package/docs-site/zh/guides/connect-otel.mdx +3 -3
  130. package/docs-site/zh/guides/connect-your-agent.mdx +2 -2
  131. package/docs-site/zh/guides/custom-reports.mdx +196 -36
  132. package/docs-site/zh/guides/experiments.mdx +7 -1
  133. package/docs-site/zh/guides/fixtures.mdx +4 -2
  134. package/docs-site/zh/guides/official-adapters.mdx +31 -9
  135. package/docs-site/zh/guides/publish-report.mdx +4 -4
  136. package/docs-site/zh/guides/report-components.mdx +194 -55
  137. package/docs-site/zh/guides/reporters.mdx +2 -2
  138. package/docs-site/zh/guides/results-data.mdx +61 -34
  139. package/docs-site/zh/guides/runner.mdx +2 -2
  140. package/docs-site/zh/guides/sandbox-agent.mdx +3 -2
  141. package/docs-site/zh/guides/{sandbox-backends.mdx → sandbox-providers.mdx} +12 -12
  142. package/docs-site/zh/guides/viewing-results.mdx +200 -79
  143. package/docs-site/zh/guides/write-experiment.mdx +9 -1
  144. package/docs-site/zh/guides/write-send.mdx +4 -3
  145. package/docs-site/zh/index.mdx +4 -3
  146. package/docs-site/zh/introduction.mdx +2 -1
  147. package/docs-site/zh/quickstart.mdx +5 -3
  148. package/docs-site/zh/reference/builtin-agents.mdx +38 -13
  149. package/docs-site/zh/reference/capabilities.mdx +1 -1
  150. package/docs-site/zh/reference/cli.mdx +36 -13
  151. package/docs-site/zh/reference/define-agent.mdx +15 -13
  152. package/docs-site/zh/reference/define-config.mdx +1 -1
  153. package/docs-site/zh/reference/define-eval.mdx +3 -3
  154. package/docs-site/zh/reference/events.mdx +9 -3
  155. package/docs-site/zh/reference/expect.mdx +13 -8
  156. package/package.json +10 -6
  157. package/src/agents/bub.ts +129 -42
  158. package/src/agents/claude-code.test.ts +152 -0
  159. package/src/agents/claude-code.ts +127 -13
  160. package/src/agents/codex.test.ts +192 -0
  161. package/src/agents/codex.ts +151 -12
  162. package/src/agents/index.ts +9 -3
  163. package/src/agents/manifest.ts +41 -0
  164. package/src/agents/plugin-config.test.ts +94 -0
  165. package/src/agents/shared.ts +33 -2
  166. package/src/agents/skills.test.ts +214 -0
  167. package/src/agents/skills.ts +304 -0
  168. package/src/agents/streaming.ts +5 -5
  169. package/src/agents/types.ts +59 -3
  170. package/src/cli.ts +74 -47
  171. package/src/context/context.test.ts +20 -2
  172. package/src/context/control-flow.ts +1 -1
  173. package/src/context/session.test.ts +92 -1
  174. package/src/context/session.ts +23 -1
  175. package/src/context/types.ts +4 -4
  176. package/src/define.test.ts +8 -8
  177. package/src/define.ts +11 -11
  178. package/src/expect/index.ts +5 -5
  179. package/src/i18n/en.ts +33 -15
  180. package/src/i18n/zh-CN.ts +37 -21
  181. package/src/index.ts +1 -1
  182. package/src/o11y/execution-tree.test.ts +452 -0
  183. package/src/o11y/execution-tree.ts +367 -0
  184. package/src/o11y/otlp/receiver.ts +1 -1
  185. package/src/o11y/otlp/select.ts +7 -3
  186. package/src/o11y/parsers/claude-code.test.ts +175 -0
  187. package/src/o11y/parsers/claude-code.ts +32 -0
  188. package/src/o11y/types.ts +6 -1
  189. package/src/report/aggregate.ts +26 -20
  190. package/src/report/built-in-user-parity.test.tsx +640 -0
  191. package/src/report/built-ins/experiment-comparison.tsx +19 -0
  192. package/src/report/built-ins/index.ts +2 -0
  193. package/src/report/components.tsx +225 -50
  194. package/src/report/compute.ts +335 -54
  195. package/src/report/dual-render.test.tsx +1129 -0
  196. package/src/report/flag.ts +1 -1
  197. package/src/report/format.ts +35 -2
  198. package/src/report/index.ts +47 -14
  199. package/src/report/load.ts +1 -1
  200. package/src/report/locale.ts +229 -0
  201. package/src/report/metrics.ts +39 -15
  202. package/src/report/primitives.tsx +100 -2
  203. package/src/report/react/AttemptList.tsx +113 -0
  204. package/src/report/react/DeltaTable.tsx +9 -6
  205. package/src/report/react/EvalList.tsx +0 -0
  206. package/src/report/react/ExperimentList.tsx +220 -0
  207. package/src/report/react/GroupSummary.tsx +66 -0
  208. package/src/report/react/MetricBars.tsx +24 -13
  209. package/src/report/react/MetricLine.tsx +21 -21
  210. package/src/report/react/MetricMatrix.tsx +8 -4
  211. package/src/report/react/MetricScatter.tsx +126 -96
  212. package/src/report/react/MetricTable.tsx +115 -15
  213. package/src/report/react/RunOverview.tsx +48 -26
  214. package/src/report/react/Scoreboard.tsx +15 -7
  215. package/src/report/react/cell.tsx +18 -9
  216. package/src/{view/app/lib/chart.ts → report/react/chart-math.ts} +2 -6
  217. package/src/report/react/colors.ts +24 -15
  218. package/src/report/react/enhance.js +168 -0
  219. package/src/report/react/fixtures.ts +169 -34
  220. package/src/report/react/format.ts +1 -1
  221. package/src/report/react/index.tsx +21 -6
  222. package/src/report/react/render.test.tsx +180 -31
  223. package/src/report/react/styles.css +432 -213
  224. package/src/report/report.test.ts +398 -94
  225. package/src/report/report.ts +29 -11
  226. package/src/report/text/faces.ts +344 -96
  227. package/src/report/text/layout.ts +76 -11
  228. package/src/report/text/table.ts +98 -0
  229. package/src/report/tree.ts +103 -20
  230. package/src/report/types.ts +184 -25
  231. package/src/report/web.ts +46 -19
  232. package/src/results/annotated-source.test.ts +146 -0
  233. package/src/results/annotated-source.ts +128 -0
  234. package/src/results/attempt-evidence.test.ts +158 -0
  235. package/src/results/attempt-evidence.ts +158 -0
  236. package/src/results/attempt-source.ts +48 -0
  237. package/src/results/copy.ts +154 -139
  238. package/src/results/format.ts +54 -47
  239. package/src/results/host-equivalence.test.ts +527 -0
  240. package/src/results/index.ts +56 -14
  241. package/src/results/locator.test.ts +198 -0
  242. package/src/results/locator.ts +0 -0
  243. package/src/results/open.ts +392 -175
  244. package/src/results/results.test.ts +943 -494
  245. package/src/results/select.ts +129 -23
  246. package/src/results/skipped-notice.ts +0 -0
  247. package/src/results/source-hash.ts +28 -0
  248. package/src/results/types.ts +98 -62
  249. package/src/results/writer.ts +304 -176
  250. package/src/runner/attempt.test.ts +170 -0
  251. package/src/runner/attempt.ts +48 -13
  252. package/src/runner/discover.test.ts +65 -0
  253. package/src/runner/discover.ts +6 -2
  254. package/src/runner/eval-source.test.ts +121 -0
  255. package/src/runner/eval-source.ts +45 -0
  256. package/src/runner/fingerprint.ts +54 -1
  257. package/src/runner/remote-sandbox.ts +1 -1
  258. package/src/runner/report.test.ts +6 -8
  259. package/src/runner/report.ts +5 -4
  260. package/src/runner/reporters/artifacts.ts +27 -41
  261. package/src/runner/reporters/braintrust.test.ts +3 -3
  262. package/src/runner/reporters/braintrust.ts +1 -1
  263. package/src/runner/reporters/console.ts +6 -6
  264. package/src/runner/reporters/json.ts +4 -4
  265. package/src/runner/reporters/live.test.ts +56 -0
  266. package/src/runner/reporters/live.ts +36 -10
  267. package/src/runner/reporters/quiet.test.ts +66 -0
  268. package/src/runner/reporters/quiet.ts +49 -0
  269. package/src/runner/reporters/shared.ts +5 -5
  270. package/src/runner/reporters/table.ts +17 -17
  271. package/src/runner/run.ts +61 -102
  272. package/src/runner/types.ts +43 -32
  273. package/src/sandbox/checkpoint.ts +1 -1
  274. package/src/sandbox/docker.ts +15 -2
  275. package/src/sandbox/e2b.ts +11 -5
  276. package/src/sandbox/errors.ts +13 -0
  277. package/src/sandbox/index.ts +3 -3
  278. package/src/sandbox/local-files.ts +1 -1
  279. package/src/sandbox/registry.ts +6 -5
  280. package/src/sandbox/resolve.ts +38 -23
  281. package/src/sandbox/retry.test.ts +68 -0
  282. package/src/sandbox/retry.ts +44 -0
  283. package/src/sandbox/types.ts +24 -24
  284. package/src/sandbox/vercel.ts +17 -3
  285. package/src/scoring/collector.ts +3 -3
  286. package/src/scoring/judge.ts +2 -2
  287. package/src/scoring/scoped.ts +13 -1
  288. package/src/scoring/types.ts +7 -7
  289. package/src/scoring/verdict.ts +4 -4
  290. package/src/shared/aggregate.ts +23 -3
  291. package/src/shared/types.ts +2 -2
  292. package/src/shared/{outcome.ts → verdict.ts} +15 -15
  293. package/src/show/compose.ts +12 -132
  294. package/src/show/index.ts +119 -95
  295. package/src/show/render.ts +462 -159
  296. package/src/show/show.test.ts +471 -253
  297. package/src/source-loc.ts +1 -1
  298. package/src/tty-line.ts +32 -0
  299. package/src/types.ts +2 -2
  300. package/src/util.test.ts +21 -1
  301. package/src/util.ts +5 -1
  302. package/src/view/app/App.tsx +33 -198
  303. package/src/view/app/components/AttemptModal.tsx +6 -6
  304. package/src/view/app/components/CodeView.tsx +2 -2
  305. package/src/view/app/components/CopyControls.tsx +32 -36
  306. package/src/view/app/components/SkippedRunsBanner.tsx +140 -0
  307. package/src/view/app/components/ui/badge.tsx +1 -1
  308. package/src/view/app/components/ui/dialog.tsx +2 -2
  309. package/src/view/app/i18n.ts +40 -31
  310. package/src/view/app/lib/artifact-url.ts +1 -1
  311. package/src/view/app/lib/attempt-route.test.ts +37 -47
  312. package/src/view/app/lib/attempt-route.ts +26 -26
  313. package/src/view/app/lib/rows.ts +9 -112
  314. package/src/view/app/lib/verdict.ts +25 -0
  315. package/src/view/app/main.tsx +9 -12
  316. package/src/view/app/pages/RunsPage.tsx +4 -4
  317. package/src/view/app/pages/TracesPage.tsx +3 -3
  318. package/src/view/app/shared.ts +0 -1
  319. package/src/view/app/types.ts +6 -51
  320. package/src/view/artifact-serving.test.ts +136 -0
  321. package/src/view/client-dist/app.css +1 -1
  322. package/src/view/client-dist/app.js +21 -21
  323. package/src/view/data.test.ts +214 -143
  324. package/src/view/data.ts +166 -120
  325. package/src/view/index.ts +23 -11
  326. package/src/view/server.ts +52 -17
  327. package/src/view/shared/types.ts +22 -31
  328. package/src/view/styles.css +117 -327
  329. package/src/view/view-report.test.ts +146 -98
  330. package/src/report/default-report.tsx +0 -222
  331. package/src/report/dual-face.test.tsx +0 -527
  332. package/src/report/react/CaseList.tsx +0 -70
  333. package/src/view/app/components/CostScoreChart.tsx +0 -147
  334. package/src/view/app/components/ExperimentTable.tsx +0 -284
  335. package/src/view/app/components/GroupSelector.tsx +0 -61
  336. package/src/view/app/components/primitives.tsx +0 -43
  337. package/src/view/app/lib/outcome.ts +0 -82
@@ -191,7 +191,19 @@ export function maxToolCalls(max: number): Spec {
191
191
  }
192
192
 
193
193
  export function loadedSkill(skill: string): Spec {
194
- return calledTool("load_skill", { input: { skill } });
194
+ return {
195
+ name: `loadedSkill(${skill})`,
196
+ severity: "gate",
197
+ // 读 skill.loaded 一等事件,不按名字猜工具调用:各 agent 表达 Skill 加载的原生形态不同
198
+ // (Claude Code 是 Skill tool_use、eve 是 load-skill action kind),归一化的责任在 parser。
199
+ evaluate: (ctx) => {
200
+ const loaded = ctx.events.filter((e): e is Extract<StreamEvent, { type: "skill.loaded" }> => e.type === "skill.loaded");
201
+ const matched = loaded.filter((e) => e.skill === skill);
202
+ // 没命中时把实际加载过的 skill 列出来(常见失败是名字对不上,而不是一个都没加载)。
203
+ const shown = matched.length ? matched : loaded;
204
+ return { score: matched.length ? 1 : 0, evidence: shown.length ? shown.map((e) => e.skill).join(", ") : undefined };
205
+ },
206
+ };
195
207
  }
196
208
 
197
209
  export function noFailedActions(): Spec {
@@ -1,9 +1,9 @@
1
- // scoring 域类型:值级断言(expect 匹配器)、断言记录与结果、评分上下文、judge 配置。
1
+ // scoring 域类型:值断言(expect 匹配器)、断言记录与结果、评分上下文、judge 配置。
2
2
 
3
3
  import type { Severity, SourceLoc } from "../shared/types.ts";
4
4
  import type { DerivedFacts, StreamEvent, Usage } from "../o11y/types.ts";
5
5
 
6
- /** 值级断言(expect 匹配器)。纯函数 score + 可链式改严重级 / 阈值。 */
6
+ /** 值断言(expect 匹配器)。纯函数 score + 可链式改严重度 / 阈值。 */
7
7
  export interface ValueAssertion {
8
8
  readonly name: string;
9
9
  readonly severity: Severity;
@@ -12,8 +12,8 @@ export interface ValueAssertion {
12
12
  /** 转成硬门槛断言:未达阈值(省略 threshold 则按 score > 0 判定)整条 eval 判为 failed。返回新实例,不改原对象。 */
13
13
  gate(threshold?: number): ValueAssertion;
14
14
  /**
15
- * 转成软阈值断言:未达 threshold 时该条记为 failed,但默认不拖累整条 eval 的 outcome;
16
- * `--strict` 运行下,软阈值失败也会把整条 eval 的 outcome 计为 failed。返回新实例,不改原对象。
15
+ * 转成软阈值断言:未达 threshold 时该条记为 failed,但默认不拖累整条 eval 的 verdict;
16
+ * `--strict` 运行下,软阈值失败也会把整条 eval 的 verdict 计为 failed。返回新实例,不改原对象。
17
17
  */
18
18
  atLeast(threshold: number): ValueAssertion;
19
19
  }
@@ -27,7 +27,7 @@ export interface AssertionSpec {
27
27
  evaluate(ctx: ScoringContext): Promise<number> | number;
28
28
  }
29
29
 
30
- /** 断言评估完的结果(进判决 / 报告)。 */
30
+ /** 断言评估完的结果(进判定 / 报告)。 */
31
31
  export interface AssertionResult {
32
32
  name: string;
33
33
  severity: Severity;
@@ -39,7 +39,7 @@ export interface AssertionResult {
39
39
  evidence?: string;
40
40
  /** 所属分组(t.group 标题)。纯报告用,不影响 passed/score。 */
41
41
  group?: string;
42
- /** 断言在 eval 源码里的调用点(栈回溯抠出);view 把判决叠回这一行。 */
42
+ /** 断言在 eval 源码里的调用点(栈回溯抠出);view 把判定叠回这一行。 */
43
43
  loc?: SourceLoc;
44
44
  }
45
45
 
@@ -71,7 +71,7 @@ export interface DiffData {
71
71
  deletedFiles: string[];
72
72
  }
73
73
 
74
- export type ResultOutcome = "passed" | "failed" | "errored" | "skipped";
74
+ export type Verdict = "passed" | "failed" | "errored" | "skipped";
75
75
 
76
76
  export interface JudgeConfig {
77
77
  model: string;
@@ -1,13 +1,13 @@
1
- // 判决:把执行结果 + 断言 + 跳过原因折叠成一个 Outcome(见 docs/scoring.md)。
1
+ // 判定:把执行结果 + 断言 + 跳过原因折叠成一个 Verdict(见 docs/feature/scoring/README.md)。
2
2
 
3
- import type { AssertionResult, ResultOutcome } from "../types.ts";
3
+ import type { AssertionResult, Verdict } from "../types.ts";
4
4
 
5
- export function computeOutcome(input: {
5
+ export function computeVerdict(input: {
6
6
  error?: string;
7
7
  assertions: readonly AssertionResult[];
8
8
  skipReason?: string;
9
9
  strict?: boolean;
10
- }): ResultOutcome {
10
+ }): Verdict {
11
11
  if (input.error !== undefined) return "errored";
12
12
 
13
13
  for (const a of input.assertions) {
@@ -1,13 +1,13 @@
1
1
  // CLI 表格(runner/reporters/table.ts)与 view 榜单(view/aggregate.ts)共用的聚合小工具。
2
- // 实验标签推导、token/成本求和、outcome 排序各只有一份 —— 否则同一个实验在终端和网页上
2
+ // 实验标签推导、token/成本求和、verdict 排序各只有一份 —— 否则同一个实验在终端和网页上
3
3
  // 会显示成两个名字 / 两组数。保持环境无关(纯函数,只 type import)。
4
4
 
5
5
  import type { Usage } from "../o11y/types.ts";
6
6
  import type { EvalResult, ExperimentRunInfo } from "../runner/types.ts";
7
- import type { ResultOutcome } from "../scoring/types.ts";
7
+ import type { Verdict } from "../scoring/types.ts";
8
8
 
9
9
  /** 明细行排序:失败最靠前(failed > errored > skipped > passed 的紧急程度)。 */
10
- export const OUTCOME_ORDER: Record<ResultOutcome, number> = {
10
+ export const VERDICT_ORDER: Record<Verdict, number> = {
11
11
  failed: 0,
12
12
  errored: 1,
13
13
  skipped: 2,
@@ -34,6 +34,26 @@ export function displayExperimentName(id: string | undefined): string | undefine
34
34
  return id.split("/").filter(Boolean).at(-1) ?? id;
35
35
  }
36
36
 
37
+ /**
38
+ * 实验 id 的组推导:去掉末段的目录前缀("compare/bub-low" → "compare");
39
+ * 无 "/" 的顶层实验不属于任何组,返回 undefined。view 榜单分组与自定义报告
40
+ * 的分组用同一份,两边的「组」永远指同一个东西。
41
+ */
42
+ export function experimentGroupOf(experimentId: string): string | undefined {
43
+ if (!experimentId.includes("/")) return undefined;
44
+ return experimentId.split("/").slice(0, -1).join("/");
45
+ }
46
+
47
+ /**
48
+ * eval id 前缀过滤,同 CLI 位置参数的分段语义(src/runner/discover.ts):
49
+ * "algebra" 匹配自身与 "algebra/..." 子级,不误配 "algebra2";允许 "algebra/" 尾斜杠写法,等价。
50
+ */
51
+ export function evalPrefixPredicate(evals?: string | string[]): (id: string) => boolean {
52
+ if (evals === undefined) return () => true;
53
+ const prefixes = (Array.isArray(evals) ? evals : [evals]).map((p) => p.replace(/\/+$/, ""));
54
+ return (id) => prefixes.some((prefix) => id === prefix || id.startsWith(prefix + "/"));
55
+ }
56
+
37
57
  /** 无 experimentId 时的兜底标签。 */
38
58
  export function fallbackExperimentLabel(result: {
39
59
  experiment?: ExperimentRunInfo;
@@ -1,4 +1,4 @@
1
- // 真正跨域的原子类型:序列化 / 严重级 / 源码位置 / 生命周期。
1
+ // 真正跨域的原子类型:序列化 / 严重度 / 源码位置 / 生命周期。
2
2
  // 各域的类型住在各自目录的 types.ts(o11y / sandbox / agents / scoring / context / runner),
3
3
  // src/types.ts 是聚合 facade —— 模块代码统一从那里 import,不必记住每个类型的家。
4
4
 
@@ -11,7 +11,7 @@ export type JsonValue =
11
11
  | JsonValue[]
12
12
  | { [key: string]: JsonValue };
13
13
 
14
- /** 断言的严重级:"gate" 失败必判整轮 failed;"soft" 默认只记录不拦截,仅在 `--strict` 模式或显式设阈值未达标时才计入失败。 */
14
+ /** 断言的严重度:"gate" 失败必判整轮 failed;"soft" 默认只记录不拦截,仅在 `--strict` 模式或显式设阈值未达标时才计入失败。 */
15
15
  export type Severity = "gate" | "soft";
16
16
 
17
17
  /**
@@ -1,23 +1,23 @@
1
- // server(aggregate.ts)与前端(app/lib/outcome.ts)共用的判决折叠口径。
1
+ // server(aggregate.ts)与前端(app/lib/verdict.ts)共用的判定折叠口径。
2
2
  // 必须保持环境无关且只依赖 type import,vite 前端会直接打包它。
3
3
  // 单独成模块的原因:折叠/计票口径两边必须逐字一致,否则折叠行状态会和 KPI / 成功率对不上。
4
4
 
5
- import type { ResultOutcome } from "../types.ts";
5
+ import type { Verdict } from "../types.ts";
6
6
 
7
- /** 折叠/计票只需要 outcome 字段;server 传 EvalResult,前端传 ViewResult 都满足。 */
8
- export interface OutcomeLike {
9
- outcome: ResultOutcome;
7
+ /** 折叠/计票只需要 verdict 字段;server 传 EvalResult,前端传 ViewResult 都满足。 */
8
+ export interface VerdictLike {
9
+ verdict: Verdict;
10
10
  }
11
11
 
12
12
  /**
13
- * 把同一个 eval 的多轮 attempt 折叠成单一判决:任一轮通过 → 该 eval 通过(对齐 earlyExit
13
+ * 把同一个 eval 的多轮 attempt 折叠成单一判定:任一轮通过 → 该 eval 通过(对齐 earlyExit
14
14
  * 「先过一次即停」语义),否则按 failed > errored > skipped 取最严重的一个。
15
15
  */
16
- export function foldEvalOutcome(attempts: OutcomeLike[]): ResultOutcome {
17
- const outcomes = attempts.map((a) => a.outcome);
18
- if (outcomes.some((o) => o === "passed")) return "passed";
19
- if (outcomes.some((o) => o === "failed")) return "failed";
20
- if (outcomes.some((o) => o === "errored")) return "errored";
16
+ export function foldEvalVerdict(attempts: VerdictLike[]): Verdict {
17
+ const verdicts = attempts.map((a) => a.verdict);
18
+ if (verdicts.some((o) => o === "passed")) return "passed";
19
+ if (verdicts.some((o) => o === "failed")) return "failed";
20
+ if (verdicts.some((o) => o === "errored")) return "errored";
21
21
  return "skipped";
22
22
  }
23
23
 
@@ -33,16 +33,16 @@ export interface EvalLevelStats {
33
33
 
34
34
  /**
35
35
  * 通过率与 passed/failed 一律按 eval 计票,不按 attempt:每个 eval 不管跑几轮都只占一票,先把它
36
- * 的多轮折叠成单一判决再计数。否则 runs>1 时同一 eval 的 N 次 attempt 各算一票 —— 尤其 earlyExit
36
+ * 的多轮折叠成单一判定再计数。否则 runs>1 时同一 eval 的 N 次 attempt 各算一票 —— 尤其 earlyExit
37
37
  * 开时通过的 eval 只留 1 次、失败的 eval 跑满 N 次,失败 eval 被重复计入分母,把通过率拉低
38
- * (见 docs/runner.md、docs/scoring.md)。keyOf 决定「一个 eval」的粒度:单实验按 eval id,
38
+ * (见 docs/runner.md、docs/feature/scoring/README.md)。keyOf 决定「一个 eval」的粒度:单实验按 eval id,
39
39
  * 跨实验组按 experimentId|eval id。
40
40
  */
41
- export function evalLevelStats<T extends OutcomeLike>(results: T[], keyOf: (r: T) => string): EvalLevelStats {
41
+ export function evalLevelStats<T extends VerdictLike>(results: T[], keyOf: (r: T) => string): EvalLevelStats {
42
42
  const byEval = new Map<string, T[]>();
43
43
  for (const r of results) byEval.set(keyOf(r), [...(byEval.get(keyOf(r)) ?? []), r]);
44
44
  const counts = { passed: 0, failed: 0, errored: 0, skipped: 0 };
45
- for (const group of byEval.values()) counts[foldEvalOutcome(group)] += 1;
45
+ for (const group of byEval.values()) counts[foldEvalVerdict(group)] += 1;
46
46
  const ran = counts.passed + counts.failed + counts.errored; // skipped 不进分母
47
47
  return { evals: byEval.size, ...counts, passRate: ran ? counts.passed / ran : 0 };
48
48
  }
@@ -1,140 +1,20 @@
1
- // show 宿主的选集合成与时间轴口径(docs-site/zh/guides/viewing-results.mdx 是行为规范)。
1
+ // show 专属的跨 run 时间轴口径(--history;docs-site/zh/guides/viewing-results.mdx 是行为规范)。
2
2
  //
3
- // 「现刻水位」= 每个 experiment × eval 取时间上最新的那份判决,跨 run 合成:
4
- // results.latest() 只挑「每实验最新快照」,带 eval 前缀的局部重跑会产出残缺快照;
5
- // 榜单承诺「不会因为一次局部重跑变残缺」,所以宿主在实验的全部历史快照上逐 eval
6
- // 向更早的 run 补齐,再把合成好的选集注入报告槽——内置默认报告与 --report 吃同一份,
7
- // 默认报告口径 = 宿主注入口径。本文件只消费 niceeval/results 的读取面。
3
+ // 现刻水位 Selection(两个宿主共用)住在 ../results/select.ts selectCurrentResults;
4
+ // 本文件只留 show 独有的时间轴计算:每个快照 / 每次真实执行一行,resume 携带的复印件不占行。
5
+ // 数据只消费 niceeval/results 的读取面。
8
6
 
9
- import { foldEvalOutcome } from "../shared/outcome.ts";
10
- import { evalPrefixPredicate } from "../report/aggregate.ts";
7
+ import { foldEvalVerdict } from "../shared/verdict.ts";
11
8
  import { attemptCostUSD } from "../report/metrics.ts";
12
- import { makeSelection } from "../results/select.ts";
13
- import type { ResultOutcome } from "../types.ts";
14
- import type {
15
- AttemptHandle,
16
- Eval,
17
- Experiment,
18
- Results,
19
- Selection,
20
- SelectionWarning,
21
- Snapshot,
22
- } from "../results/index.ts";
23
-
24
- export interface ComposeOptions {
25
- /** experiment id 前缀(--experiment),与 latest({ experiments }) 同一分段匹配语义。 */
26
- experiment?: string;
27
- /** eval id 前缀(位置参数),收窄选集覆盖的 eval;覆盖警告分母 = 已知并集 ∩ 范围。 */
28
- patterns?: string[];
29
- }
30
-
31
- /** --experiment 的实验过滤,同 results.latest({ experiments }) 的分段前缀语义。 */
32
- export function filterExperiments(experiments: Experiment[], prefix?: string): Experiment[] {
33
- if (prefix === undefined) return experiments;
34
- const p = prefix.replace(/\/+$/, "");
35
- return experiments.filter((exp) => exp.id === p || exp.id.startsWith(p + "/"));
36
- }
37
-
38
- /**
39
- * 合成「现刻水位」选集:每个实验一份合成快照,快照里每道题的判决取该题最后一次
40
- * 出现的快照(--resume 携带的复印件身份与原判决相同,取到哪份内容都一致)。
41
- * 警告随选集重算:partial-coverage 的分母 = 已知并集 ∩ 范围;stale / synthetic
42
- * 与 results.latest() 同口径。
43
- */
44
- export function composeShowSelection(results: Results, opts: ComposeOptions = {}): Selection {
45
- const match =
46
- opts.patterns && opts.patterns.length > 0 ? evalPrefixPredicate(opts.patterns) : () => true;
47
- const experiments = filterExperiments(results.experiments, opts.experiment);
48
-
49
- const snapshots: Snapshot[] = [];
50
- const warnings: SelectionWarning[] = [];
51
-
52
- for (const exp of experiments) {
53
- // 逐题取最新:快照按最新在前,首个出现即最新判决
54
- const taken = new Map<string, { ev: Eval; snapshot: Snapshot }>();
55
- for (const snapshot of exp.snapshots) {
56
- for (const ev of snapshot.evals) {
57
- if (!match(ev.id) || taken.has(ev.id)) continue;
58
- taken.set(ev.id, { ev, snapshot });
59
- }
60
- }
61
- if (taken.size === 0) continue;
62
-
63
- const picks = [...taken.values()].sort((a, b) => a.ev.id.localeCompare(b.ev.id));
64
- let startedAt = "";
65
- let newest: Snapshot = picks[0].snapshot;
66
- for (const pick of picks) {
67
- if (pick.snapshot.startedAt > startedAt) {
68
- startedAt = pick.snapshot.startedAt;
69
- newest = pick.snapshot;
70
- }
71
- }
72
- const evals = picks.map((p) => p.ev);
73
- const base = exp.latest;
74
- snapshots.push({
75
- experimentId: exp.id,
76
- startedAt,
77
- agent: base.agent,
78
- ...(base.model !== undefined ? { model: base.model } : {}),
79
- ...(base.producer ? { producer: base.producer } : {}),
80
- schemaVersion: base.schemaVersion,
81
- evals,
82
- attempts: evals.flatMap((ev) => ev.attempts),
83
- runDir: newest.runDir,
84
- ...(base.synthetic ? { synthetic: true } : {}),
85
- ...(base.knownEvalIds ? { knownEvalIds: [...base.knownEvalIds] } : {}),
86
- });
87
-
88
- // 残缺检测:跨 run 补齐后仍缺,只可能是历史上见过(或 knownEvalIds 声明过)
89
- // 却从未在可读落盘里出现的题 —— 分母收窄到范围内,不让范围外的缺口刷屏。
90
- const total = exp.evalIds.filter(match).length;
91
- if (evals.length < total) {
92
- const hint = base.synthetic
93
- ? "re-run the experiment for a full snapshot"
94
- : `re-run \`niceeval exp ${exp.id}\` for a full snapshot`;
95
- warnings.push({
96
- kind: "partial-coverage",
97
- experimentId: exp.id,
98
- covered: evals.length,
99
- total,
100
- message: `verdicts cover ${evals.length} of ${total} evals seen in history; ${hint}`,
101
- });
102
- }
103
- }
104
-
105
- let latestStartedAt = "";
106
- for (const snapshot of snapshots) {
107
- if (snapshot.startedAt > latestStartedAt) latestStartedAt = snapshot.startedAt;
108
- }
109
- for (const snapshot of snapshots) {
110
- if (snapshot.startedAt < latestStartedAt) {
111
- warnings.push({
112
- kind: "stale-snapshot",
113
- experimentId: snapshot.experimentId,
114
- startedAt: snapshot.startedAt,
115
- latestStartedAt,
116
- message: `verdicts for "${snapshot.experimentId}" were produced at ${snapshot.startedAt}, before the latest run in this selection (${latestStartedAt})`,
117
- });
118
- }
119
- if (snapshot.synthetic) {
120
- warnings.push({
121
- kind: "synthetic-experiment-id",
122
- experimentId: snapshot.experimentId,
123
- runDir: snapshot.runDir.dir,
124
- message: `run "${snapshot.runDir.dir}" has results without experimentId; grouped as "${snapshot.experimentId}" by agent/model`,
125
- });
126
- }
127
- }
128
-
129
- return makeSelection(snapshots, warnings);
130
- }
9
+ import type { Verdict } from "../types.ts";
10
+ import type { AttemptHandle, Experiment } from "../results/index.ts";
131
11
 
132
12
  // ───────────────────────── 时间轴(--history)─────────────────────────
133
13
 
134
14
  export interface EvalHistoryRow {
135
15
  /** 该次真实执行所在 run(快照)的时刻。 */
136
16
  startedAt: string;
137
- outcome: ResultOutcome;
17
+ verdict: Verdict;
138
18
  attempts: number;
139
19
  costUSD: number | null;
140
20
  /** 最新一次 attempt 的第一条失败断言("gate calledTool(...)")。 */
@@ -151,7 +31,7 @@ function attemptKey(attempt: AttemptHandle): string | undefined {
151
31
 
152
32
  /**
153
33
  * 单 eval 的跨 run 时间轴:每次真实执行一行,新→旧。--resume 携带的复印件
154
- * (身份键与原判决相同的条目)不占行 —— 否则趋势会被复印件灌满假数据。
34
+ * (身份键与原判定相同的条目)不占行 —— 否则趋势会被复印件灌满假数据。
155
35
  */
156
36
  export function evalHistory(exp: Experiment, evalId: string): EvalHistoryRow[] {
157
37
  const rows: EvalHistoryRow[] = [];
@@ -171,7 +51,7 @@ export function evalHistory(exp: Experiment, evalId: string): EvalHistoryRow[] {
171
51
  seen.add(key);
172
52
  fresh.push(attempt);
173
53
  }
174
- if (fresh.length === 0) continue; // 纯复印件:判决在更早的行里已经出现过
54
+ if (fresh.length === 0) continue; // 纯复印件:判定在更早的行里已经出现过
175
55
  let cost: number | null = null;
176
56
  for (const attempt of fresh) {
177
57
  const c = attemptCostUSD(attempt.result);
@@ -181,7 +61,7 @@ export function evalHistory(exp: Experiment, evalId: string): EvalHistoryRow[] {
181
61
  const failed = latest.result.assertions.find((a) => !a.passed);
182
62
  rows.push({
183
63
  startedAt: snapshot.startedAt,
184
- outcome: foldEvalOutcome(fresh.map((a) => a.result)),
64
+ verdict: foldEvalVerdict(fresh.map((a) => a.result)),
185
65
  attempts: fresh.length,
186
66
  costUSD: cost,
187
67
  ...(failed ? { failedAssertion: `${failed.severity} ${failed.name}` } : {}),
@@ -203,7 +83,7 @@ export function experimentHistory(exp: Experiment): ExperimentHistoryRow[] {
203
83
  return exp.snapshots.map((snapshot) => {
204
84
  let passed = 0;
205
85
  for (const ev of snapshot.evals) {
206
- if (foldEvalOutcome(ev.attempts.map((a) => a.result)) === "passed") passed += 1;
86
+ if (foldEvalVerdict(ev.attempts.map((a) => a.result)) === "passed") passed += 1;
207
87
  }
208
88
  let cost: number | null = null;
209
89
  for (const attempt of snapshot.attempts) {