niceeval 0.6.2 → 0.7.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/INDEX.md +23 -23
- package/README.zh.md +6 -6
- package/dist/agents/types.d.ts +2 -2
- package/dist/i18n/zh-CN.d.ts +3 -3
- package/dist/report/aggregate.d.ts +32 -26
- package/dist/report/aggregate.js +157 -76
- package/dist/report/built-in/index.d.ts +2 -0
- package/dist/report/built-in/index.js +8 -0
- package/dist/report/components.d.ts +91 -164
- package/dist/report/components.js +377 -114
- package/dist/report/compute.d.ts +86 -73
- package/dist/report/compute.js +592 -432
- package/dist/report/flag.d.ts +28 -17
- package/dist/report/flag.js +86 -16
- package/dist/report/format.d.ts +11 -11
- package/dist/report/format.js +17 -15
- package/dist/report/index.d.ts +16 -17
- package/dist/report/index.js +20 -22
- package/dist/report/load.js +3 -2
- package/dist/report/locale.d.ts +49 -34
- package/dist/report/locale.js +106 -58
- package/dist/report/metrics.d.ts +10 -3
- package/dist/report/metrics.js +46 -12
- package/dist/report/primitives.d.ts +42 -15
- package/dist/report/primitives.js +135 -26
- package/dist/report/react/AttemptList.d.ts +10 -8
- package/dist/report/react/AttemptList.js +18 -10
- package/dist/report/react/DeltaTable.js +19 -18
- package/dist/report/react/EvalList.d.ts +3 -3
- package/dist/report/react/EvalList.js +0 -0
- package/dist/report/react/ExperimentComparison.d.ts +4 -2
- package/dist/report/react/ExperimentComparison.js +5 -4
- package/dist/report/react/ExperimentList.d.ts +3 -3
- package/dist/report/react/ExperimentList.js +16 -15
- package/dist/report/react/MetricBars.js +5 -4
- package/dist/report/react/MetricLine.js +12 -5
- package/dist/report/react/MetricMatrix.js +1 -1
- package/dist/report/react/MetricScatter.js +54 -17
- package/dist/report/react/MetricTable.js +2 -12
- package/dist/report/react/ScopeSummary.d.ts +10 -0
- package/dist/report/react/ScopeSummary.js +17 -0
- package/dist/report/react/Scoreboard.js +6 -6
- package/dist/report/react/cell.js +2 -2
- package/dist/report/react/fixtures.d.ts +5 -9
- package/dist/report/react/fixtures.js +105 -149
- package/dist/report/react/index.d.ts +15 -5
- package/dist/report/react/index.js +18 -7
- package/dist/report/report.d.ts +137 -20
- package/dist/report/report.js +261 -34
- package/dist/report/text/faces.d.ts +17 -19
- package/dist/report/text/faces.js +225 -157
- package/dist/report/text/plot.js +1 -1
- package/dist/report/text/table.js +2 -2
- package/dist/report/tree.d.ts +90 -40
- package/dist/report/tree.js +252 -94
- package/dist/report/types.d.ts +245 -300
- package/dist/report/types.js +4 -3
- package/dist/report/web.d.ts +21 -5
- package/dist/report/web.js +42 -16
- package/dist/results/select.d.ts +38 -16
- package/dist/results/select.js +73 -25
- package/dist/results/types.d.ts +38 -14
- package/dist/shared/aggregate.d.ts +3 -2
- package/dist/shared/aggregate.js +5 -4
- package/docs-site/zh/README.md +44 -0
- package/docs-site/zh/examples/ai-agent-application.mdx +63 -0
- package/docs-site/zh/examples/coding-agent-extensions.mdx +57 -0
- package/docs-site/zh/examples/index.mdx +50 -0
- package/docs-site/zh/{concepts → explanation}/adapter.mdx +11 -11
- package/docs-site/zh/{concepts → explanation}/assert.mdx +7 -7
- package/docs-site/zh/{concepts → explanation}/drive.mdx +8 -8
- package/docs-site/zh/{concepts → explanation}/evals.mdx +4 -4
- package/docs-site/zh/{concepts → explanation}/experiment.mdx +8 -8
- package/docs-site/zh/{concepts → explanation}/hitl.mdx +8 -8
- package/docs-site/zh/{concepts → explanation}/judge.mdx +5 -5
- package/docs-site/zh/{concepts → explanation}/overview.mdx +5 -5
- package/docs-site/zh/{guides → explanation}/runner.mdx +1 -1
- package/docs-site/zh/{concepts → explanation}/tier.mdx +6 -6
- package/docs-site/zh/{guides → how-to}/agent-feedback-loop.mdx +7 -7
- package/docs-site/zh/{guides → how-to}/authoring.mdx +2 -2
- package/docs-site/zh/{guides → how-to}/connect-otel.mdx +6 -6
- package/docs-site/zh/{guides → how-to}/connect-your-agent.mdx +18 -18
- package/docs-site/zh/{guides → how-to}/custom-reports.mdx +6 -6
- package/docs-site/zh/{guides → how-to}/experiments.mdx +3 -3
- package/docs-site/zh/{guides → how-to}/publish-report.mdx +2 -2
- package/docs-site/zh/{guides → how-to}/sandbox-agent.mdx +2 -2
- package/docs-site/zh/{guides → how-to}/sandbox-providers.mdx +1 -1
- package/docs-site/zh/{guides → how-to}/viewing-results.mdx +6 -6
- package/docs-site/zh/{guides → how-to}/write-experiment.mdx +3 -3
- package/docs-site/zh/{guides → how-to}/write-send.mdx +13 -13
- package/docs-site/zh/index.mdx +23 -25
- package/docs-site/zh/introduction.mdx +8 -8
- package/docs-site/zh/reference/builtin-agents.mdx +5 -5
- package/docs-site/zh/reference/capabilities.mdx +6 -6
- package/docs-site/zh/reference/cli.mdx +9 -7
- package/docs-site/zh/reference/define-agent.mdx +1 -1
- package/docs-site/zh/reference/events.mdx +3 -3
- package/docs-site/zh/{guides → reference}/official-adapters.mdx +7 -7
- package/docs-site/zh/{guides → reference}/report-components.mdx +5 -5
- package/docs-site/zh/{guides → reference}/results-data.mdx +5 -5
- package/docs-site/zh/{guides → troubleshooting}/debug-sandbox.mdx +2 -2
- package/docs-site/zh/{guides → troubleshooting}/debugging.mdx +4 -2
- package/docs-site/zh/{quickstart.mdx → tutorials/quickstart.mdx} +5 -17
- package/package.json +6 -2
- package/src/agents/index.ts +2 -2
- package/src/agents/openai-compat.ts +1 -1
- package/src/agents/streaming.ts +2 -2
- package/src/agents/types.ts +3 -3
- package/src/cli.ts +42 -23
- package/src/context/context.ts +1 -1
- package/src/context/session.test.ts +1 -1
- package/src/context/session.ts +1 -1
- package/src/i18n/en.ts +18 -16
- package/src/i18n/zh-CN.ts +16 -15
- package/src/report/aggregate.ts +175 -87
- package/src/report/built-in/index.tsx +9 -0
- package/src/report/components.tsx +625 -285
- package/src/report/compute.ts +717 -515
- package/src/report/dual-render.test.tsx +738 -1148
- package/src/report/flag.ts +97 -33
- package/src/report/format.ts +18 -22
- package/src/report/index.ts +113 -58
- package/src/report/load.ts +3 -2
- package/src/report/locale.ts +120 -69
- package/src/report/metrics.ts +42 -12
- package/src/report/primitives.tsx +190 -45
- package/src/report/react/AttemptList.tsx +32 -20
- package/src/report/react/DeltaTable.tsx +63 -45
- package/src/report/react/EvalList.tsx +0 -0
- package/src/report/react/ExperimentComparison.tsx +12 -7
- package/src/report/react/ExperimentList.tsx +38 -26
- package/src/report/react/MetricBars.tsx +5 -4
- package/src/report/react/MetricLine.tsx +13 -8
- package/src/report/react/MetricMatrix.tsx +2 -2
- package/src/report/react/MetricScatter.tsx +74 -20
- package/src/report/react/MetricTable.tsx +4 -76
- package/src/report/react/ScopeSummary.tsx +86 -0
- package/src/report/react/Scoreboard.tsx +28 -10
- package/src/report/react/cell.tsx +2 -2
- package/src/report/react/enhance.js +57 -5
- package/src/report/react/fixtures.ts +109 -156
- package/src/report/react/index.tsx +24 -39
- package/src/report/react/render.test.tsx +139 -104
- package/src/report/react/styles.css +181 -91
- package/src/report/report.test.ts +761 -1031
- package/src/report/report.ts +425 -47
- package/src/report/text/faces.ts +257 -164
- package/src/report/text/plot.ts +1 -1
- package/src/report/text/table.ts +2 -2
- package/src/report/tree.ts +362 -104
- package/src/report/types.ts +257 -287
- package/src/report/web.ts +63 -20
- package/src/results/attempt-evidence.test.ts +4 -4
- package/src/results/attempt-evidence.ts +5 -5
- package/src/results/copy.ts +6 -6
- package/src/results/host-equivalence.test.ts +26 -14
- package/src/results/index.ts +10 -4
- package/src/results/open.ts +8 -4
- package/src/results/results.test.ts +4 -3
- package/src/results/select.ts +104 -34
- package/src/results/types.ts +36 -14
- package/src/runner/feedback/human.test.ts +1 -1
- package/src/runner/run.ts +1 -1
- package/src/sandbox/cli-commands.ts +2 -2
- package/src/scoring/judge.test.ts +1 -1
- package/src/shared/aggregate.ts +5 -4
- package/src/show/compose.ts +50 -67
- package/src/show/index.ts +107 -56
- package/src/show/render.ts +43 -27
- package/src/show/report-host.test.ts +188 -0
- package/src/show/report-host.ts +375 -0
- package/src/show/show.test.ts +86 -36
- package/src/view/app/App.test.tsx +69 -0
- package/src/view/app/App.tsx +144 -48
- package/src/view/app/components/AttemptModal.tsx +324 -63
- package/src/view/app/components/CodeView.tsx +10 -4
- package/src/view/app/i18n.ts +31 -17
- package/src/view/app/main.tsx +13 -8
- package/src/view/app/pages/{RunsPage.tsx → AttemptsPage.tsx} +6 -6
- package/src/view/app/types.ts +4 -1
- package/src/view/client-dist/app.css +1 -1
- package/src/view/client-dist/app.js +14 -14
- package/src/view/data.test.ts +9 -3
- package/src/view/data.ts +145 -49
- package/src/view/index.ts +48 -44
- package/src/view/server.ts +35 -15
- package/src/view/shared/types.ts +34 -5
- package/src/view/styles.css +224 -0
- package/src/view/view-report.test.ts +161 -57
- package/dist/report/built-ins/experiment-comparison.d.ts +0 -39
- package/dist/report/built-ins/experiment-comparison.js +0 -119
- package/dist/report/built-ins/index.d.ts +0 -2
- package/dist/report/built-ins/index.js +0 -2
- package/dist/report/react/GroupSummary.d.ts +0 -8
- package/dist/report/react/GroupSummary.js +0 -8
- package/dist/report/react/RunOverview.d.ts +0 -8
- package/dist/report/react/RunOverview.js +0 -12
- package/docs-site/zh/example/ai-agent-application.mdx +0 -152
- package/docs-site/zh/example/claude-code-codex-plugin.mdx +0 -167
- package/docs-site/zh/example/claude-code-codex-skill.mdx +0 -152
- package/docs-site/zh/example/showcase.mdx +0 -39
- package/src/report/built-in-user-parity.test.tsx +0 -597
- package/src/report/built-ins/experiment-comparison.tsx +0 -179
- package/src/report/built-ins/index.ts +0 -7
- package/src/report/react/GroupSummary.tsx +0 -66
- package/src/report/react/RunOverview.tsx +0 -109
- /package/docs-site/zh/{example/tier1-ai-sdk-v7.mdx → examples/integrations/ai-sdk-v7.mdx} +0 -0
- /package/docs-site/zh/{example/tier1-claude-sdk.mdx → examples/integrations/claude-sdk.mdx} +0 -0
- /package/docs-site/zh/{example/tier1-codex-sdk.mdx → examples/integrations/codex-sdk.mdx} +0 -0
- /package/docs-site/zh/{example/tier1-langgraph.mdx → examples/integrations/langgraph.mdx} +0 -0
- /package/docs-site/zh/{example/tier1-pi-sdk.mdx → examples/integrations/pi-sdk.mdx} +0 -0
- /package/docs-site/zh/{guides → how-to}/ci-integration.mdx +0 -0
- /package/docs-site/zh/{guides → how-to}/dataset-fanout.mdx +0 -0
- /package/docs-site/zh/{guides → how-to}/fixtures.mdx +0 -0
- /package/docs-site/zh/{guides → how-to}/reporters.mdx +0 -0
- /package/docs-site/zh/{guides → how-to}/scoring-guide.mdx +0 -0
package/INDEX.md
CHANGED
|
@@ -8,45 +8,45 @@
|
|
|
8
8
|
|
|
9
9
|
| 任务 | 读取 |
|
|
10
10
|
| --- | --- |
|
|
11
|
-
| 初始化并跑通第一条 Eval | `docs-site/zh/quickstart.mdx` |
|
|
12
|
-
| 理解 Adapter、Experiment、Eval 的分工 | `docs-site/zh/
|
|
13
|
-
| 接入自己的 Agent | `docs-site/zh/
|
|
14
|
-
| 选择官方 Adapter | `docs-site/zh/
|
|
15
|
-
| 接入 coding agent CLI | `docs-site/zh/
|
|
16
|
-
| 选择 Sandbox Provider | `docs-site/zh/
|
|
11
|
+
| 初始化并跑通第一条 Eval | `docs-site/zh/tutorials/quickstart.mdx` |
|
|
12
|
+
| 理解 Adapter、Experiment、Eval 的分工 | `docs-site/zh/explanation/overview.mdx` |
|
|
13
|
+
| 接入自己的 Agent | `docs-site/zh/how-to/connect-your-agent.mdx` |
|
|
14
|
+
| 选择官方 Adapter | `docs-site/zh/reference/official-adapters.mdx` |
|
|
15
|
+
| 接入 coding agent CLI | `docs-site/zh/how-to/sandbox-agent.mdx` |
|
|
16
|
+
| 选择 Sandbox Provider | `docs-site/zh/how-to/sandbox-providers.mdx` |
|
|
17
17
|
|
|
18
18
|
## 编写配置与 Eval
|
|
19
19
|
|
|
20
20
|
| 任务 | 读取 |
|
|
21
21
|
| --- | --- |
|
|
22
|
-
| 编写 Adapter | `docs-site/zh/
|
|
23
|
-
| 映射消息、工具调用和事件流 | `docs-site/zh/
|
|
24
|
-
| 编写 Experiment | `docs-site/zh/
|
|
25
|
-
| 编写 Eval | `docs-site/zh/
|
|
26
|
-
| 编写断言和评分 | `docs-site/zh/
|
|
27
|
-
| 配置 Judge | `docs-site/zh/
|
|
28
|
-
| 配置 OTel | `docs-site/zh/
|
|
22
|
+
| 编写 Adapter | `docs-site/zh/explanation/adapter.mdx`、`docs-site/zh/reference/define-agent.mdx` |
|
|
23
|
+
| 映射消息、工具调用和事件流 | `docs-site/zh/how-to/write-send.mdx`、`docs-site/zh/reference/events.mdx` |
|
|
24
|
+
| 编写 Experiment | `docs-site/zh/how-to/write-experiment.mdx` |
|
|
25
|
+
| 编写 Eval | `docs-site/zh/explanation/evals.mdx`、`docs-site/zh/reference/define-eval.mdx` |
|
|
26
|
+
| 编写断言和评分 | `docs-site/zh/how-to/authoring.mdx`、`docs-site/zh/how-to/scoring-guide.mdx`、`docs-site/zh/reference/expect.mdx` |
|
|
27
|
+
| 配置 Judge | `docs-site/zh/explanation/judge.mdx`、`docs-site/zh/reference/define-config.mdx` |
|
|
28
|
+
| 配置 OTel | `docs-site/zh/how-to/connect-otel.mdx` |
|
|
29
29
|
| 查 CLI 命令与 flag | `docs-site/zh/reference/cli.mdx` |
|
|
30
30
|
|
|
31
31
|
## 运行、调试与优化
|
|
32
32
|
|
|
33
33
|
| 任务 | 读取 |
|
|
34
34
|
| --- | --- |
|
|
35
|
-
| 让 AI 自主运行、观察、修改和重跑 | `docs-site/zh/
|
|
36
|
-
| 用 `show`、`view` 和 artifact 定位失败 | `docs-site/zh/
|
|
37
|
-
| 组织实验和对比 | `docs-site/zh/
|
|
38
|
-
| 编写自定义报告 | `docs-site/zh/
|
|
39
|
-
| 读取或转换结构化结果 | `docs-site/zh/
|
|
35
|
+
| 让 AI 自主运行、观察、修改和重跑 | `docs-site/zh/how-to/agent-feedback-loop.mdx` |
|
|
36
|
+
| 用 `show`、`view` 和 artifact 定位失败 | `docs-site/zh/how-to/viewing-results.mdx` |
|
|
37
|
+
| 组织实验和对比 | `docs-site/zh/how-to/experiments.mdx` |
|
|
38
|
+
| 编写自定义报告 | `docs-site/zh/how-to/custom-reports.mdx`、`docs-site/zh/reference/report-components.mdx` |
|
|
39
|
+
| 读取或转换结构化结果 | `docs-site/zh/reference/results-data.mdx` |
|
|
40
40
|
|
|
41
41
|
## 专项场景
|
|
42
42
|
|
|
43
43
|
| 任务 | 读取 |
|
|
44
44
|
| --- | --- |
|
|
45
|
-
| 评估 Claude Code / Codex Skill | `docs-site/zh/
|
|
46
|
-
| 评估 Plugin、Hook 或 MCP server | `docs-site/zh/
|
|
47
|
-
| 编写多轮会话 | `docs-site/zh/
|
|
48
|
-
| 编写人工审批流(HITL) | `docs-site/zh/
|
|
49
|
-
| 做 feature flag A/B 对比 | `docs-site/zh/
|
|
45
|
+
| 评估 Claude Code / Codex Skill | `docs-site/zh/examples/coding-agent-extensions.mdx` |
|
|
46
|
+
| 评估 Plugin、Hook 或 MCP server | `docs-site/zh/examples/coding-agent-extensions.mdx` |
|
|
47
|
+
| 编写多轮会话 | `docs-site/zh/explanation/adapter.mdx`、`docs-site/zh/how-to/write-send.mdx` |
|
|
48
|
+
| 编写人工审批流(HITL) | `docs-site/zh/explanation/hitl.mdx` |
|
|
49
|
+
| 做 feature flag A/B 对比 | `docs-site/zh/explanation/tier.mdx`、`docs-site/zh/explanation/experiment.mdx` |
|
|
50
50
|
|
|
51
51
|
## 版本规则
|
|
52
52
|
|
package/README.zh.md
CHANGED
|
@@ -80,7 +80,7 @@ NiceEval 支持两种接入方式,取决于被测 agent 是否需要隔离的
|
|
|
80
80
|
| Sandbox | 需要隔离工作区的 coding agent 才用得到;直连 Web Agent 不需要。 |
|
|
81
81
|
| Tier | 接入 Adapter 的三档投入:Tier 1 只接 send,Tier 2 加 OTel 换调用瀑布图,Tier 3 侵入改造做 feature A/B。 |
|
|
82
82
|
|
|
83
|
-
完整术语表见[架构概览](https://niceeval.com/docs/zh/
|
|
83
|
+
完整术语表见[架构概览](https://niceeval.com/docs/zh/explanation/overview)。
|
|
84
84
|
|
|
85
85
|
## 示例
|
|
86
86
|
|
|
@@ -134,9 +134,9 @@ READ https://niceeval.com/INIT.md and install niceeval for this repo.
|
|
|
134
134
|
|
|
135
135
|
从你的场景开始:
|
|
136
136
|
|
|
137
|
-
- [
|
|
138
|
-
- [
|
|
139
|
-
- [
|
|
137
|
+
- [浏览可运行示例](https://niceeval.com/docs/zh/examples)
|
|
138
|
+
- [评估 Coding Agent 扩展](https://niceeval.com/docs/zh/examples/coding-agent-extensions)
|
|
139
|
+
- [自写 Adapter 评估 AI Agent 应用](https://niceeval.com/docs/zh/examples/ai-agent-application)
|
|
140
140
|
|
|
141
141
|
|
|
142
142
|
## Roadmap
|
|
@@ -161,7 +161,7 @@ READ https://niceeval.com/INIT.md and install niceeval for this repo.
|
|
|
161
161
|
|
|
162
162
|
## 文档
|
|
163
163
|
|
|
164
|
-
- [快速开始](https://www.niceeval.com/docs/zh/quickstart)
|
|
164
|
+
- [快速开始](https://www.niceeval.com/docs/zh/tutorials/quickstart)
|
|
165
165
|
|
|
166
166
|
# 感谢
|
|
167
167
|
该项目受下面项目所启发或者是由AI从下面项目中学习代码所写
|
|
@@ -170,4 +170,4 @@ READ https://niceeval.com/INIT.md and install niceeval for this repo.
|
|
|
170
170
|
- [ponytail](https://github.com/DietrichGebert/ponytail)
|
|
171
171
|
|
|
172
172
|
感谢下列社区
|
|
173
|
-
- WIP
|
|
173
|
+
- WIP
|
package/dist/agents/types.d.ts
CHANGED
|
@@ -131,7 +131,7 @@ export interface InputFile {
|
|
|
131
131
|
}
|
|
132
132
|
/**
|
|
133
133
|
* HITL 回答轮里,人的裁决以结构化形式随 `input.responses` 到达——adapter 不需要解析
|
|
134
|
-
* `text` 去猜哪句回答对应哪个请求、算不算批准。见 docs-site/zh/
|
|
134
|
+
* `text` 去猜哪句回答对应哪个请求、算不算批准。见 docs-site/zh/explanation/adapter.mdx
|
|
135
135
|
* 「不同回答的入参」一节的四种典型形态。
|
|
136
136
|
*/
|
|
137
137
|
export interface InputResponse {
|
|
@@ -223,7 +223,7 @@ export interface AgentTracing {
|
|
|
223
223
|
* 新会话线(eval 第一轮 / t.newSession() 之后)拿到一个全新的。**
|
|
224
224
|
* 会话续接(`id`/`capture`、`history`)和 HITL 停轮现场(`hold`/`take`)的存取器都在它上面,
|
|
225
225
|
* "第一轮"是新会话线的自然形态,没有要判断的分支;`state` 是这些存取器之外的逃生舱,
|
|
226
|
-
* 框架从不往里写数据。见 docs-site/zh/
|
|
226
|
+
* 框架从不往里写数据。见 docs-site/zh/explanation/adapter.mdx「AgentContext」一节。
|
|
227
227
|
*/
|
|
228
228
|
export interface AgentSession {
|
|
229
229
|
/** 会话续接:服务端记历史。本线记过的会话 id;新会话线是 undefined。 */
|
package/dist/i18n/zh-CN.d.ts
CHANGED
|
@@ -54,8 +54,8 @@ export declare const zhCN: {
|
|
|
54
54
|
readonly "cli.show.runDirMissing": "Results directory not found: {{dir}}\n";
|
|
55
55
|
readonly "cli.show.noEvalMatch": "No results matched: {{patterns}}. Evals with results: {{evals}}\n";
|
|
56
56
|
readonly "cli.show.noExperimentMatch": "No experiment matched --experiment {{arg}}. Experiments with results: {{experiments}}\n";
|
|
57
|
-
readonly "cli.show.historyReportConflict": "`--history` and `--report` are mutually exclusive: --history is the
|
|
58
|
-
readonly "cli.show.evidenceNeedsEval": "--
|
|
57
|
+
readonly "cli.show.historyReportConflict": "`--history` and `--report` are mutually exclusive: both take over the main output. --history is the host's per-attempt execution timeline; for snapshot-level trends, compose exp.snapshots inside your report file instead.\n";
|
|
58
|
+
readonly "cli.show.evidenceNeedsEval": "--source / --execution / --diff show one attempt's evidence, but the selection matched {{matched}} evals. Pick an attempt locator from the index below:\n{{index}}\n";
|
|
59
59
|
readonly "cli.show.locatorMalformed": "{{message}}\n";
|
|
60
60
|
readonly "cli.show.locatorNotFound": "{{message}}\n";
|
|
61
61
|
readonly "cli.eval.noMatch": "没有匹配的 eval:{{patterns}}。\n";
|
|
@@ -109,7 +109,7 @@ export declare const zhCN: {
|
|
|
109
109
|
readonly "feedback.human.compare": "Compare: niceeval view {{group}}";
|
|
110
110
|
readonly "feedback.human.counts": "共 {{total}} · 复用 {{reused}} · 运行中 {{running}} · 排队 {{queued}} · 已完成 {{completed}}";
|
|
111
111
|
readonly "feedback.human.diffHint": "Diff: niceeval show {{locator}} --diff";
|
|
112
|
-
readonly "feedback.human.evalHint": "Eval: niceeval show {{locator}} --
|
|
112
|
+
readonly "feedback.human.evalHint": "Eval: niceeval show {{locator}} --source";
|
|
113
113
|
readonly "feedback.human.failuresHeader": "FAILURES";
|
|
114
114
|
readonly "feedback.human.heartbeat": "已运行 {{elapsed}} · {{counts}}";
|
|
115
115
|
readonly "feedback.human.inspect": "Inspect: niceeval show {{locator}}";
|
|
@@ -1,14 +1,11 @@
|
|
|
1
|
-
import type { AttemptHandle,
|
|
1
|
+
import type { AttemptHandle, ScopeWarning, Snapshot } from "../results/types.ts";
|
|
2
2
|
import { type AttemptLocator } from "../results/locator.ts";
|
|
3
|
-
import type { Aggregator,
|
|
3
|
+
import type { Aggregator, DimensionInput, Metric, MetricCell, MetricColumn, NumericAxis, ReportInput } from "./types.ts";
|
|
4
|
+
import { type LocalizedText } from "./locale.ts";
|
|
4
5
|
import { evalPrefixPredicate } from "../shared/aggregate.ts";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export type SnapshotsInput = Selection | Snapshot[];
|
|
9
|
-
export declare function resolveInput(input: SnapshotsInput): {
|
|
10
|
-
snapshots: Snapshot[];
|
|
11
|
-
warnings: SelectionWarning[];
|
|
6
|
+
export declare function resolveInput(input: ReportInput): {
|
|
7
|
+
snapshots: readonly Snapshot[];
|
|
8
|
+
warnings: readonly ScopeWarning[];
|
|
12
9
|
};
|
|
13
10
|
/** 展平后的一条样本:attempt + 它所属的快照(维度解析与题级折叠都需要快照身份)。 */
|
|
14
11
|
export interface Item {
|
|
@@ -17,13 +14,13 @@ export interface Item {
|
|
|
17
14
|
}
|
|
18
15
|
export declare function experimentIdOf(item: Item): string;
|
|
19
16
|
export declare function evalIdOf(item: Item): string;
|
|
20
|
-
/**
|
|
17
|
+
/** 快照键:"<experimentId> @ <startedAt>"("snapshot" 维度与手挑快照数组的对比用)。 */
|
|
21
18
|
export declare function snapshotKeyOf(snapshot: Snapshot): string;
|
|
19
|
+
/** 一组 Item 的 eval 全身份键:experimentId + eval id(聚合中的题级身份始终是这一对)。 */
|
|
20
|
+
export declare function fullEvalKey(item: Item): string;
|
|
22
21
|
/**
|
|
23
22
|
* 一条 Item 的 AttemptLocator:真实读取路径(openResults() 产出的 handle)恒有
|
|
24
|
-
* `attempt.locator`;手工构造的测试 fixture
|
|
25
|
-
* (与 `loadAttemptEvidence`、`open.ts` 的回填同一口径)——只服务这类场景,不改变真实
|
|
26
|
-
* 读取路径的行为。
|
|
23
|
+
* `attempt.locator`;手工构造的测试 fixture 若省略它,按当前身份元组兜底算一份。
|
|
27
24
|
*/
|
|
28
25
|
export declare function locatorOf(item: Item): AttemptLocator;
|
|
29
26
|
/**
|
|
@@ -31,29 +28,38 @@ export declare function locatorOf(item: Item): AttemptLocator;
|
|
|
31
28
|
* (experimentId, evalId, attempt, startedAt))。missing-startedAt 的警告不透出:
|
|
32
29
|
* 官方产出永不缺 startedAt,缺失只可能来自 legacy 落盘,「不去重、如实保留重复」即终稿。
|
|
33
30
|
*/
|
|
34
|
-
export declare function collectItems(snapshots: Snapshot[]): Item[];
|
|
31
|
+
export declare function collectItems(snapshots: readonly Snapshot[]): Item[];
|
|
35
32
|
export { evalPrefixPredicate };
|
|
36
|
-
export declare function filterItems(items: Item[], evals?: string | string[]): Item[];
|
|
33
|
+
export declare function filterItems(items: Item[], evals?: string | readonly string[]): Item[];
|
|
37
34
|
export declare function dimensionName(dimension: DimensionInput): string;
|
|
38
|
-
/** eval id
|
|
35
|
+
/** eval id 的完整父路径:"a/b/c" → "a/b";没有 "/" 时取完整 id(与可比组同一条派生规则)。 */
|
|
39
36
|
export declare function evalGroupOf(id: string): string;
|
|
40
|
-
/** flag
|
|
41
|
-
export declare
|
|
42
|
-
/**
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
37
|
+
/** flag / runConfig 未声明时的显示键:内置文案,en / zh-CN 同形。 */
|
|
38
|
+
export declare const MISSING_GROUP_KEY: string;
|
|
39
|
+
/**
|
|
40
|
+
* flag / runConfig 声明值 → 分组显示键(稳定 JSON 规则):字符串直接显示,其它值用对象键
|
|
41
|
+
* 递归排序后的 JSON,缺失值显示内置文案 `(missing)`。返回 [显示键, 冲突检测用的规范形]。
|
|
42
|
+
*/
|
|
43
|
+
export declare function refDisplayKey(value: unknown): [display: string, canonical: string];
|
|
46
44
|
export declare function dimensionKey(dimension: DimensionInput, item: Item): string;
|
|
47
|
-
/**
|
|
45
|
+
/**
|
|
46
|
+
* 按维度分组;维度 domain 按稳定 key 字典序(Unicode)排列,不让文件扫描顺序渗进报告。
|
|
47
|
+
* flag / runConfig 维度做显示键冲突检测:不同原始值生成同一显示键时报错并要求改用
|
|
48
|
+
* CustomDimension,绝不静默合组。
|
|
49
|
+
*/
|
|
48
50
|
export declare function groupItems(items: Item[], dimension: DimensionInput): Map<string, Item[]>;
|
|
49
|
-
export declare function applyAggregator(aggregator: Aggregator, values: number[]): number;
|
|
50
|
-
/** where 不满足 → null,语义等价于 value 开头 return null
|
|
51
|
+
export declare function applyAggregator(aggregator: Aggregator, values: readonly number[]): number;
|
|
52
|
+
/** where 不满足 → null,语义等价于 value 开头 return null;抛错与非有限数按完整用户反馈失败。 */
|
|
51
53
|
export declare function evaluateMetric(metric: Metric, attempt: AttemptHandle): Promise<number | null>;
|
|
52
|
-
|
|
54
|
+
/** 单值 → LocalizedText display:metric.display 覆盖内置 unit 格式化;null 的兜底归渲染面。 */
|
|
55
|
+
export declare function displayValue(metric: Metric, value: number | null): LocalizedText;
|
|
53
56
|
/**
|
|
54
57
|
* 一个格子:组内 attempt → 两级聚合 → 终值。
|
|
55
58
|
* null 值不进聚合但计入 total(覆盖率经 samples/total 如实暴露);全 null → value null。
|
|
59
|
+
* refs 跟随覆盖范围(含值为 null 的 attempt),去重后按 locator 字典序。
|
|
56
60
|
*/
|
|
57
61
|
export declare function computeCell(metric: Metric, items: Item[]): Promise<MetricCell>;
|
|
58
62
|
export declare function toColumn(metric: Metric): MetricColumn;
|
|
59
63
|
export declare function assertUniqueMetricNames(metrics: readonly Metric[], where: string): void;
|
|
64
|
+
/** NumericAxis 的取值,带完整反馈的非法值检查(NaN / Infinity 一律报错,不静默当缺失)。 */
|
|
65
|
+
export declare function axisValueOf(axis: NumericAxis, attempt: AttemptHandle): number | null;
|
package/dist/report/aggregate.js
CHANGED
|
@@ -1,20 +1,23 @@
|
|
|
1
|
-
// 两级聚合引擎:去重 → 按维度分组 → 组内按 (
|
|
1
|
+
// 两级聚合引擎:去重 → 按维度分组 → 组内按 (experiment × eval) 折叠(perEval)→ 跨题折叠
|
|
2
|
+
// (acrossEvals)→ MetricCell(docs/feature/reports/architecture.md「指标聚合不变量」、
|
|
3
|
+
// docs/feature/reports/library/metrics.md「公开计算模型」)。
|
|
2
4
|
//
|
|
3
5
|
// 为什么是两级:earlyExit 默认开,失败的题天然比通过的题样本多;平铺求均值会把分数
|
|
4
6
|
// 和重试策略纠缠在一起(eval A=[1]、eval B=[0,0,0] 平铺 = 0.25,两级宏平均 = 0.5)。
|
|
5
7
|
// 自定义维度把同一道题的 attempt 分进不同组时,第一级折叠发生在各组内部。
|
|
6
8
|
import { dedupeAttempts } from "../results/select.js";
|
|
7
9
|
import { encodeAttemptLocator } from "../results/locator.js";
|
|
8
|
-
import {
|
|
10
|
+
import { flagValueOf, runConfigValueOf } from "./flag.js";
|
|
11
|
+
import { formatMetricValue, localizedDisplay } from "./format.js";
|
|
12
|
+
import { localeText } from "./locale.js";
|
|
9
13
|
import { evalPrefixPredicate } from "../shared/aggregate.js";
|
|
10
14
|
// 复合键分隔符:NUL 不会出现在 eval id / experimentId / ISO 时间里,拼接键不会串味
|
|
11
15
|
const KEY_SEP = "\u0000";
|
|
12
|
-
/** flag 未声明时的组名:不猜,如实归一组。 */
|
|
13
|
-
export const FLAG_UNSET = "(unset)";
|
|
14
16
|
export function resolveInput(input) {
|
|
15
17
|
if (Array.isArray(input))
|
|
16
18
|
return { snapshots: input, warnings: [] };
|
|
17
|
-
|
|
19
|
+
const scope = input;
|
|
20
|
+
return { snapshots: scope.snapshots, warnings: scope.warnings };
|
|
18
21
|
}
|
|
19
22
|
export function experimentIdOf(item) {
|
|
20
23
|
return item.attempt.experimentId || item.snapshot.experimentId;
|
|
@@ -22,15 +25,17 @@ export function experimentIdOf(item) {
|
|
|
22
25
|
export function evalIdOf(item) {
|
|
23
26
|
return item.attempt.evalId || item.attempt.result.id;
|
|
24
27
|
}
|
|
25
|
-
/**
|
|
28
|
+
/** 快照键:"<experimentId> @ <startedAt>"("snapshot" 维度与手挑快照数组的对比用)。 */
|
|
26
29
|
export function snapshotKeyOf(snapshot) {
|
|
27
30
|
return `${snapshot.experimentId} @ ${snapshot.startedAt}`;
|
|
28
31
|
}
|
|
32
|
+
/** 一组 Item 的 eval 全身份键:experimentId + eval id(聚合中的题级身份始终是这一对)。 */
|
|
33
|
+
export function fullEvalKey(item) {
|
|
34
|
+
return `${experimentIdOf(item)}${KEY_SEP}${evalIdOf(item)}`;
|
|
35
|
+
}
|
|
29
36
|
/**
|
|
30
37
|
* 一条 Item 的 AttemptLocator:真实读取路径(openResults() 产出的 handle)恒有
|
|
31
|
-
* `attempt.locator`;手工构造的测试 fixture
|
|
32
|
-
* (与 `loadAttemptEvidence`、`open.ts` 的回填同一口径)——只服务这类场景,不改变真实
|
|
33
|
-
* 读取路径的行为。
|
|
38
|
+
* `attempt.locator`;手工构造的测试 fixture 若省略它,按当前身份元组兜底算一份。
|
|
34
39
|
*/
|
|
35
40
|
export function locatorOf(item) {
|
|
36
41
|
return (item.attempt.locator ??
|
|
@@ -64,71 +69,55 @@ export { evalPrefixPredicate };
|
|
|
64
69
|
export function filterItems(items, evals) {
|
|
65
70
|
if (evals === undefined)
|
|
66
71
|
return items;
|
|
67
|
-
const match = evalPrefixPredicate(evals);
|
|
72
|
+
const match = evalPrefixPredicate(Array.isArray(evals) ? [...evals] : evals);
|
|
68
73
|
return items.filter((item) => match(evalIdOf(item)));
|
|
69
74
|
}
|
|
70
75
|
// ───────────────────────── 维度 ─────────────────────────
|
|
71
76
|
export function dimensionName(dimension) {
|
|
72
77
|
return typeof dimension === "string" ? dimension : dimension.name;
|
|
73
78
|
}
|
|
74
|
-
/** eval id
|
|
79
|
+
/** eval id 的完整父路径:"a/b/c" → "a/b";没有 "/" 时取完整 id(与可比组同一条派生规则)。 */
|
|
75
80
|
export function evalGroupOf(id) {
|
|
76
|
-
const slash = id.
|
|
81
|
+
const slash = id.lastIndexOf("/");
|
|
77
82
|
return slash === -1 ? id : id.slice(0, slash);
|
|
78
83
|
}
|
|
79
|
-
function
|
|
80
|
-
return typeof dimension === "object" &&
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
return typeof dimension === "object" && "kind" in dimension && dimension.kind === "config";
|
|
84
|
+
function isDimensionRef(dimension) {
|
|
85
|
+
return (typeof dimension === "object" &&
|
|
86
|
+
"kind" in dimension &&
|
|
87
|
+
(dimension.kind === "flag" || dimension.kind === "runConfig"));
|
|
84
88
|
}
|
|
85
|
-
/**
|
|
86
|
-
|
|
87
|
-
|
|
89
|
+
/** flag / runConfig 未声明时的显示键:内置文案,en / zh-CN 同形。 */
|
|
90
|
+
export const MISSING_GROUP_KEY = localeText("en", "cell.missingValue");
|
|
91
|
+
/** 对象键递归排序后的稳定 JSON(分组显示键与冲突检测共用)。 */
|
|
92
|
+
function canonicalJson(value) {
|
|
93
|
+
if (Array.isArray(value))
|
|
94
|
+
return `[${value.map(canonicalJson).join(",")}]`;
|
|
95
|
+
if (typeof value === "object" && value !== null) {
|
|
96
|
+
const keys = Object.keys(value).sort();
|
|
97
|
+
return `{${keys.map((k) => `${JSON.stringify(k)}:${canonicalJson(value[k])}`).join(",")}}`;
|
|
98
|
+
}
|
|
99
|
+
return JSON.stringify(value);
|
|
88
100
|
}
|
|
89
101
|
/**
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
* (与 "model" / "agent" 内置维度同一读法,不另造第二套口径)。未投影 → undefined。
|
|
102
|
+
* flag / runConfig 声明值 → 分组显示键(稳定 JSON 规则):字符串直接显示,其它值用对象键
|
|
103
|
+
* 递归排序后的 JSON,缺失值显示内置文案 `(missing)`。返回 [显示键, 冲突检测用的规范形]。
|
|
93
104
|
*/
|
|
94
|
-
function
|
|
95
|
-
if (ref.name === "model")
|
|
96
|
-
return item.attempt.result.model ?? item.snapshot.model;
|
|
97
|
-
if (ref.name === "agent")
|
|
98
|
-
return item.attempt.result.agent || item.snapshot.agent;
|
|
99
|
-
const info = item.snapshot.experiment ?? item.attempt.result.experiment;
|
|
100
|
-
// ExperimentRunInfo 是穷尽可序列化投影;按字段名取值,新增投影字段无需改这里
|
|
101
|
-
return info?.[ref.name];
|
|
102
|
-
}
|
|
103
|
-
/** flag 声明值 → 组标签:label 函数优先,其余 String();未声明 → FLAG_UNSET。 */
|
|
104
|
-
export function flagGroupKey(ref, item) {
|
|
105
|
-
return refGroupKey(ref, flagsOf(item.attempt)?.[ref.name]);
|
|
106
|
-
}
|
|
107
|
-
/** config 投影值 → 组标签,规则与 flagGroupKey 一致;未投影 → FLAG_UNSET。 */
|
|
108
|
-
export function configGroupKey(ref, item) {
|
|
109
|
-
return refGroupKey(ref, configValueOf(ref, item));
|
|
110
|
-
}
|
|
111
|
-
function refGroupKey(ref, value) {
|
|
105
|
+
export function refDisplayKey(value) {
|
|
112
106
|
if (value === undefined)
|
|
113
|
-
return
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
return
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
export function axisValue(ref, item) {
|
|
123
|
-
const value = ref.kind === "flag" ? flagsOf(item.attempt)?.[ref.name] : configValueOf(ref, item);
|
|
124
|
-
return typeof value === "number" ? value : null;
|
|
107
|
+
return [MISSING_GROUP_KEY, "undefined"];
|
|
108
|
+
if (typeof value === "string")
|
|
109
|
+
return [value, `string:${value}`];
|
|
110
|
+
return [canonicalJson(value), `json:${canonicalJson(value)}`];
|
|
111
|
+
}
|
|
112
|
+
function refValueOf(ref, item) {
|
|
113
|
+
return ref.kind === "flag"
|
|
114
|
+
? flagValueOf(item.attempt, ref.name)
|
|
115
|
+
: runConfigValueOf(item.attempt, ref.name);
|
|
125
116
|
}
|
|
126
117
|
export function dimensionKey(dimension, item) {
|
|
127
118
|
if (typeof dimension !== "string") {
|
|
128
|
-
if (
|
|
129
|
-
return
|
|
130
|
-
if (isConfigRef(dimension))
|
|
131
|
-
return configGroupKey(dimension, item);
|
|
119
|
+
if (isDimensionRef(dimension))
|
|
120
|
+
return refDisplayKey(refValueOf(dimension, item))[0];
|
|
132
121
|
return dimension.of(item.attempt);
|
|
133
122
|
}
|
|
134
123
|
const result = item.attempt.result;
|
|
@@ -152,18 +141,39 @@ export function dimensionKey(dimension, item) {
|
|
|
152
141
|
}
|
|
153
142
|
}
|
|
154
143
|
}
|
|
155
|
-
/**
|
|
144
|
+
/**
|
|
145
|
+
* 按维度分组;维度 domain 按稳定 key 字典序(Unicode)排列,不让文件扫描顺序渗进报告。
|
|
146
|
+
* flag / runConfig 维度做显示键冲突检测:不同原始值生成同一显示键时报错并要求改用
|
|
147
|
+
* CustomDimension,绝不静默合组。
|
|
148
|
+
*/
|
|
156
149
|
export function groupItems(items, dimension) {
|
|
157
150
|
const groups = new Map();
|
|
151
|
+
const canonicalByDisplay = new Map();
|
|
152
|
+
const checkConflicts = typeof dimension !== "string" && isDimensionRef(dimension);
|
|
158
153
|
for (const item of items) {
|
|
159
|
-
|
|
154
|
+
let key;
|
|
155
|
+
if (checkConflicts) {
|
|
156
|
+
const [display, canonical] = refDisplayKey(refValueOf(dimension, item));
|
|
157
|
+
const existing = canonicalByDisplay.get(display);
|
|
158
|
+
if (existing === undefined)
|
|
159
|
+
canonicalByDisplay.set(display, canonical);
|
|
160
|
+
else if (existing !== canonical) {
|
|
161
|
+
throw new Error(`Dimension "${dimensionName(dimension)}" maps two different raw values to the same display key "${display}" ` +
|
|
162
|
+
`(e.g. the string "5" and the number 5). Grouping them silently would merge distinct configurations; ` +
|
|
163
|
+
`use a CustomDimension ({ name, of(attempt) }) to define an unambiguous key.`);
|
|
164
|
+
}
|
|
165
|
+
key = display;
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
key = dimensionKey(dimension, item);
|
|
169
|
+
}
|
|
160
170
|
const list = groups.get(key);
|
|
161
171
|
if (list)
|
|
162
172
|
list.push(item);
|
|
163
173
|
else
|
|
164
174
|
groups.set(key, [item]);
|
|
165
175
|
}
|
|
166
|
-
return groups;
|
|
176
|
+
return new Map([...groups.entries()].sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)));
|
|
167
177
|
}
|
|
168
178
|
// ───────────────────────── 聚合 ─────────────────────────
|
|
169
179
|
export function applyAggregator(aggregator, values) {
|
|
@@ -180,52 +190,109 @@ export function applyAggregator(aggregator, values) {
|
|
|
180
190
|
return Math.max(...values);
|
|
181
191
|
}
|
|
182
192
|
}
|
|
183
|
-
|
|
193
|
+
function metricError(metric, step, cause, locator) {
|
|
194
|
+
const at = locator === undefined ? "" : ` at attempt ${locator}`;
|
|
195
|
+
return new Error(`Metric "${metric.name}" ${step} failed${at}: ${cause instanceof Error ? cause.message : String(cause)}. ` +
|
|
196
|
+
"Computation errors are not disguised as missing data — fix the metric (return null for expected gaps), then re-run.", { cause });
|
|
197
|
+
}
|
|
198
|
+
/** where 不满足 → null,语义等价于 value 开头 return null;抛错与非有限数按完整用户反馈失败。 */
|
|
184
199
|
export async function evaluateMetric(metric, attempt) {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
200
|
+
const locator = attempt.locator;
|
|
201
|
+
if (metric.where) {
|
|
202
|
+
let pass;
|
|
203
|
+
try {
|
|
204
|
+
pass = metric.where(attempt);
|
|
205
|
+
}
|
|
206
|
+
catch (e) {
|
|
207
|
+
throw metricError(metric, "where()", e, locator);
|
|
208
|
+
}
|
|
209
|
+
if (!pass)
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
let value;
|
|
213
|
+
try {
|
|
214
|
+
value = await metric.value(attempt);
|
|
215
|
+
}
|
|
216
|
+
catch (e) {
|
|
217
|
+
throw metricError(metric, "value()", e, locator);
|
|
218
|
+
}
|
|
219
|
+
if (value !== null && !Number.isFinite(value)) {
|
|
220
|
+
throw metricError(metric, "value()", `returned a non-finite number (${String(value)}); return null for "not measurable"`, locator);
|
|
221
|
+
}
|
|
222
|
+
return value;
|
|
188
223
|
}
|
|
224
|
+
/** 单值 → LocalizedText display:metric.display 覆盖内置 unit 格式化;null 的兜底归渲染面。 */
|
|
189
225
|
export function displayValue(metric, value) {
|
|
190
226
|
// null 的纯文本兜底;组件把 null 渲染成「缺数据」,绝不画 0
|
|
191
227
|
if (value === null)
|
|
192
228
|
return "—";
|
|
193
|
-
|
|
229
|
+
if (metric.display) {
|
|
230
|
+
const display = metric.display;
|
|
231
|
+
return localizedDisplay((locale) => {
|
|
232
|
+
try {
|
|
233
|
+
return display(value, locale);
|
|
234
|
+
}
|
|
235
|
+
catch (e) {
|
|
236
|
+
throw metricError(metric, "display()", e);
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
return formatMetricValue(value, metric.unit);
|
|
241
|
+
}
|
|
242
|
+
function foldAggregator(metric, step, values) {
|
|
243
|
+
const aggregator = metric.aggregate?.[step] ?? "mean";
|
|
244
|
+
let folded;
|
|
245
|
+
try {
|
|
246
|
+
folded = applyAggregator(aggregator, values);
|
|
247
|
+
}
|
|
248
|
+
catch (e) {
|
|
249
|
+
throw metricError(metric, `${step} aggregator`, e);
|
|
250
|
+
}
|
|
251
|
+
if (!Number.isFinite(folded)) {
|
|
252
|
+
throw metricError(metric, `${step} aggregator`, `returned a non-finite number (${String(folded)})`);
|
|
253
|
+
}
|
|
254
|
+
return folded;
|
|
194
255
|
}
|
|
195
256
|
/**
|
|
196
257
|
* 一个格子:组内 attempt → 两级聚合 → 终值。
|
|
197
258
|
* null 值不进聚合但计入 total(覆盖率经 samples/total 如实暴露);全 null → value null。
|
|
259
|
+
* refs 跟随覆盖范围(含值为 null 的 attempt),去重后按 locator 字典序。
|
|
198
260
|
*/
|
|
199
261
|
export async function computeCell(metric, items) {
|
|
200
|
-
// 第一级桶:同一 (eval × 快照) 的 attempt 折成一个题级值
|
|
262
|
+
// 第一级桶:同一 (experiment × eval × 快照) 的 attempt 折成一个题级值
|
|
201
263
|
const buckets = new Map();
|
|
202
|
-
const refs =
|
|
264
|
+
const refs = new Set();
|
|
203
265
|
let samples = 0;
|
|
204
266
|
for (const item of items) {
|
|
267
|
+
refs.add(locatorOf(item));
|
|
205
268
|
const value = await evaluateMetric(metric, item.attempt);
|
|
206
269
|
if (value === null)
|
|
207
270
|
continue;
|
|
208
271
|
samples += 1;
|
|
209
|
-
|
|
210
|
-
const bucketKey = `${evalIdOf(item)}${KEY_SEP}${snapshotKeyOf(item.snapshot)}`;
|
|
272
|
+
const bucketKey = `${fullEvalKey(item)}${KEY_SEP}${snapshotKeyOf(item.snapshot)}`;
|
|
211
273
|
const bucket = buckets.get(bucketKey);
|
|
212
274
|
if (bucket)
|
|
213
275
|
bucket.push(value);
|
|
214
276
|
else
|
|
215
277
|
buckets.set(bucketKey, [value]);
|
|
216
278
|
}
|
|
217
|
-
const
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
279
|
+
const evalValues = [...buckets.values()].map((values) => foldAggregator(metric, "perEval", values));
|
|
280
|
+
const value = evalValues.length === 0 ? null : foldAggregator(metric, "acrossEvals", evalValues);
|
|
281
|
+
return {
|
|
282
|
+
value,
|
|
283
|
+
display: displayValue(metric, value),
|
|
284
|
+
samples,
|
|
285
|
+
total: items.length,
|
|
286
|
+
refs: [...refs].sort(),
|
|
287
|
+
};
|
|
222
288
|
}
|
|
223
289
|
export function toColumn(metric) {
|
|
224
290
|
return {
|
|
225
291
|
key: metric.name,
|
|
226
292
|
label: metric.label ?? metric.name,
|
|
227
|
-
|
|
228
|
-
|
|
293
|
+
...(metric.description !== undefined ? { description: metric.description } : {}),
|
|
294
|
+
...(metric.unit !== undefined ? { unit: metric.unit } : {}),
|
|
295
|
+
...(metric.better !== undefined ? { better: metric.better } : {}),
|
|
229
296
|
};
|
|
230
297
|
}
|
|
231
298
|
export function assertUniqueMetricNames(metrics, where) {
|
|
@@ -237,3 +304,17 @@ export function assertUniqueMetricNames(metrics, where) {
|
|
|
237
304
|
seen.add(metric.name);
|
|
238
305
|
}
|
|
239
306
|
}
|
|
307
|
+
/** NumericAxis 的取值,带完整反馈的非法值检查(NaN / Infinity 一律报错,不静默当缺失)。 */
|
|
308
|
+
export function axisValueOf(axis, attempt) {
|
|
309
|
+
let value;
|
|
310
|
+
try {
|
|
311
|
+
value = axis.of(attempt);
|
|
312
|
+
}
|
|
313
|
+
catch (e) {
|
|
314
|
+
throw new Error(`Numeric axis "${axis.name}" of() failed${attempt.locator ? ` at attempt ${attempt.locator}` : ""}: ${e instanceof Error ? e.message : String(e)}`, { cause: e });
|
|
315
|
+
}
|
|
316
|
+
if (value !== null && !Number.isFinite(value)) {
|
|
317
|
+
throw new Error(`Numeric axis "${axis.name}" of() returned a non-finite number (${String(value)}); return null for attempts without a plottable x value.`);
|
|
318
|
+
}
|
|
319
|
+
return value;
|
|
320
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// niceeval/report/built-in —— 包里自带的一个报告文件,没有任何私有钩子
|
|
3
|
+
// (docs/feature/reports/library/built-in.md)。裸 `niceeval show` 与 `niceeval view`
|
|
4
|
+
// 装载的就是这份默认导出:一份普通 defineReport,与用户的 --report 文件同层、
|
|
5
|
+
// 走同一条 装载 → resolve → validate → render 管线。「builtin」不是装载逻辑里的类别,
|
|
6
|
+
// 只是宿主默认拿哪个值的事实。
|
|
7
|
+
import { ExperimentComparison, defineReport } from "../index.js";
|
|
8
|
+
export default defineReport(_jsx(ExperimentComparison, {}));
|