niceeval 0.5.4 → 0.6.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 +55 -0
- package/dist/agents/types.d.ts +317 -0
- package/dist/context/types.d.ts +335 -0
- package/dist/i18n/core.d.ts +11 -0
- package/dist/i18n/en.d.ts +181 -0
- package/dist/i18n/index.d.ts +5 -0
- package/dist/i18n/zh-CN.d.ts +183 -0
- package/dist/o11y/execution-tree.d.ts +103 -0
- package/dist/o11y/otlp/parse.d.ts +3 -0
- package/dist/o11y/otlp/receiver.d.ts +21 -0
- package/dist/o11y/otlp/select.d.ts +22 -0
- package/dist/o11y/otlp/turn-otel.d.ts +43 -0
- package/dist/o11y/types.d.ts +190 -0
- package/dist/report/aggregate.d.ts +57 -0
- package/dist/report/aggregate.js +212 -0
- package/dist/report/built-ins/cost-pass-rate-comparison.d.ts +1 -0
- package/dist/report/built-ins/cost-pass-rate-comparison.js +17 -0
- package/dist/report/built-ins/index.d.ts +1 -0
- package/dist/report/built-ins/index.js +2 -0
- package/dist/report/components.d.ts +178 -0
- package/dist/report/components.js +132 -0
- package/dist/report/compute.d.ts +106 -0
- package/dist/report/compute.js +618 -0
- package/dist/report/flag.d.ts +6 -0
- package/dist/report/flag.js +8 -0
- package/dist/report/format.d.ts +32 -0
- package/dist/report/format.js +126 -0
- package/dist/report/index.d.ts +19 -0
- package/dist/report/index.js +30 -0
- package/dist/report/load.d.ts +12 -0
- package/dist/report/load.js +56 -0
- package/dist/report/locale.d.ts +83 -0
- package/dist/report/locale.js +160 -0
- package/dist/report/metrics.d.ts +21 -0
- package/dist/report/metrics.js +115 -0
- package/dist/report/primitives.d.ts +64 -0
- package/dist/report/primitives.js +113 -0
- package/dist/report/react/AttemptList.d.ts +23 -0
- package/dist/report/react/AttemptList.js +20 -0
- package/dist/report/react/DeltaTable.d.ts +8 -0
- package/dist/report/react/DeltaTable.js +24 -0
- package/dist/report/react/EvalList.d.ts +10 -0
- package/dist/report/react/EvalList.js +0 -0
- package/dist/report/react/ExperimentList.d.ts +10 -0
- package/dist/report/react/ExperimentList.js +15 -0
- package/dist/report/react/GroupSummary.d.ts +8 -0
- package/dist/report/react/GroupSummary.js +8 -0
- package/dist/report/react/MetricBars.d.ts +10 -0
- package/dist/report/react/MetricBars.js +48 -0
- package/dist/report/react/MetricLine.d.ts +9 -0
- package/dist/report/react/MetricLine.js +68 -0
- package/dist/report/react/MetricMatrix.d.ts +10 -0
- package/dist/report/react/MetricMatrix.js +23 -0
- package/dist/report/react/MetricScatter.d.ts +9 -0
- package/dist/report/react/MetricScatter.js +100 -0
- package/dist/report/react/MetricTable.d.ts +11 -0
- package/dist/report/react/MetricTable.js +26 -0
- package/dist/report/react/RunOverview.d.ts +8 -0
- package/dist/report/react/RunOverview.js +12 -0
- package/dist/report/react/Scoreboard.d.ts +8 -0
- package/dist/report/react/Scoreboard.js +26 -0
- package/dist/report/react/cell.d.ts +9 -0
- package/dist/report/react/cell.js +9 -0
- package/dist/report/react/chart-math.d.ts +14 -0
- package/dist/report/react/chart-math.js +68 -0
- package/dist/report/react/colors.d.ts +14 -0
- package/dist/report/react/colors.js +45 -0
- package/dist/report/react/fixtures.d.ts +19 -0
- package/dist/report/react/fixtures.js +393 -0
- package/dist/report/react/format.d.ts +3 -0
- package/dist/report/react/format.js +7 -0
- package/dist/report/react/index.d.ts +6 -0
- package/dist/report/react/index.js +14 -0
- package/dist/report/report.d.ts +24 -0
- package/dist/report/report.js +49 -0
- package/dist/report/text/faces.d.ts +29 -0
- package/dist/report/text/faces.js +503 -0
- package/dist/report/text/layout.d.ts +75 -0
- package/dist/report/text/layout.js +173 -0
- package/dist/report/text/plot.d.ts +37 -0
- package/dist/report/text/plot.js +81 -0
- package/dist/report/text/table.d.ts +8 -0
- package/dist/report/text/table.js +86 -0
- package/dist/report/tree.d.ts +99 -0
- package/dist/report/tree.js +228 -0
- package/dist/report/types.d.ts +420 -0
- package/dist/report/types.js +4 -0
- package/dist/report/web.d.ts +15 -0
- package/dist/report/web.js +37 -0
- package/dist/results/annotated-source.d.ts +61 -0
- package/dist/results/attempt-evidence.d.ts +69 -0
- package/dist/results/attempt-source.d.ts +15 -0
- package/dist/results/locator.d.ts +75 -0
- package/dist/results/locator.js +0 -0
- package/dist/results/select.d.ts +45 -0
- package/dist/results/select.js +230 -0
- package/dist/results/source-hash.d.ts +9 -0
- package/dist/results/types.d.ts +204 -0
- package/dist/runner/eval-source.d.ts +22 -0
- package/dist/runner/fingerprint.d.ts +23 -0
- package/dist/runner/types.d.ts +332 -0
- package/dist/sandbox/docker-stream.d.ts +24 -0
- package/dist/sandbox/docker.d.ts +84 -0
- package/dist/sandbox/e2b.d.ts +31 -0
- package/dist/sandbox/errors.d.ts +4 -0
- package/dist/sandbox/local-files.d.ts +6 -0
- package/dist/sandbox/paths.d.ts +4 -0
- package/dist/sandbox/registry.d.ts +13 -0
- package/dist/sandbox/resolve.d.ts +40 -0
- package/dist/sandbox/retry.d.ts +10 -0
- package/dist/sandbox/shell.d.ts +11 -0
- package/dist/sandbox/source-files.d.ts +13 -0
- package/dist/sandbox/types.d.ts +203 -0
- package/dist/sandbox/vercel.d.ts +37 -0
- package/dist/scoring/types.d.ts +70 -0
- package/dist/shared/aggregate.d.ts +29 -0
- package/dist/shared/aggregate.js +55 -0
- package/dist/shared/types.d.ts +27 -0
- package/dist/shared/verdict.d.ts +27 -0
- package/dist/shared/verdict.js +34 -0
- package/dist/tty-line.d.ts +6 -0
- package/dist/types.d.ts +7 -0
- package/dist/util.d.ts +22 -0
- package/docs-site/zh/concepts/adapter.mdx +5 -5
- package/docs-site/zh/concepts/assert.mdx +8 -8
- package/docs-site/zh/concepts/evals.mdx +5 -5
- package/docs-site/zh/concepts/experiment.mdx +2 -2
- package/docs-site/zh/concepts/judge.mdx +9 -9
- package/docs-site/zh/concepts/overview.mdx +2 -2
- package/docs-site/zh/concepts/tier.mdx +6 -6
- package/docs-site/zh/example/claude-code-codex-plugin.mdx +9 -5
- package/docs-site/zh/example/claude-code-codex-skill.mdx +12 -7
- package/docs-site/zh/guides/agent-feedback-loop.mdx +235 -78
- package/docs-site/zh/guides/connect-otel.mdx +3 -3
- package/docs-site/zh/guides/connect-your-agent.mdx +2 -2
- package/docs-site/zh/guides/custom-reports.mdx +196 -36
- package/docs-site/zh/guides/experiments.mdx +7 -1
- package/docs-site/zh/guides/fixtures.mdx +1 -1
- package/docs-site/zh/guides/official-adapters.mdx +31 -9
- package/docs-site/zh/guides/publish-report.mdx +3 -3
- package/docs-site/zh/guides/report-components.mdx +194 -55
- package/docs-site/zh/guides/reporters.mdx +2 -2
- package/docs-site/zh/guides/results-data.mdx +35 -34
- package/docs-site/zh/guides/runner.mdx +2 -2
- package/docs-site/zh/guides/sandbox-agent.mdx +2 -2
- package/docs-site/zh/guides/{sandbox-backends.mdx → sandbox-providers.mdx} +11 -11
- package/docs-site/zh/guides/viewing-results.mdx +199 -79
- package/docs-site/zh/guides/write-experiment.mdx +9 -1
- package/docs-site/zh/guides/write-send.mdx +2 -2
- package/docs-site/zh/index.mdx +4 -3
- package/docs-site/zh/introduction.mdx +2 -1
- package/docs-site/zh/quickstart.mdx +5 -3
- package/docs-site/zh/reference/builtin-agents.mdx +38 -13
- package/docs-site/zh/reference/capabilities.mdx +1 -1
- package/docs-site/zh/reference/cli.mdx +36 -13
- package/docs-site/zh/reference/define-agent.mdx +12 -12
- package/docs-site/zh/reference/define-config.mdx +1 -1
- package/docs-site/zh/reference/define-eval.mdx +3 -3
- package/docs-site/zh/reference/events.mdx +7 -1
- package/docs-site/zh/reference/expect.mdx +3 -3
- package/package.json +10 -6
- package/src/agents/bub.ts +129 -42
- package/src/agents/claude-code.test.ts +152 -0
- package/src/agents/claude-code.ts +127 -13
- package/src/agents/codex.test.ts +192 -0
- package/src/agents/codex.ts +151 -12
- package/src/agents/index.ts +9 -3
- package/src/agents/manifest.ts +41 -0
- package/src/agents/plugin-config.test.ts +94 -0
- package/src/agents/shared.ts +33 -2
- package/src/agents/skills.test.ts +214 -0
- package/src/agents/skills.ts +304 -0
- package/src/agents/streaming.ts +5 -5
- package/src/agents/types.ts +54 -2
- package/src/cli.ts +74 -47
- package/src/context/context.test.ts +20 -2
- package/src/context/control-flow.ts +1 -1
- package/src/context/session.test.ts +92 -1
- package/src/context/session.ts +23 -1
- package/src/context/types.ts +4 -4
- package/src/define.test.ts +8 -8
- package/src/define.ts +11 -11
- package/src/expect/index.ts +5 -5
- package/src/i18n/en.ts +34 -15
- package/src/i18n/zh-CN.ts +37 -21
- package/src/index.ts +1 -1
- package/src/o11y/execution-tree.test.ts +452 -0
- package/src/o11y/execution-tree.ts +367 -0
- package/src/o11y/otlp/receiver.ts +1 -1
- package/src/o11y/otlp/select.ts +7 -3
- package/src/o11y/parsers/claude-code.test.ts +175 -0
- package/src/o11y/parsers/claude-code.ts +32 -0
- package/src/o11y/types.ts +6 -1
- package/src/report/aggregate.ts +26 -20
- package/src/report/built-in-user-parity.test.tsx +643 -0
- package/src/report/built-ins/cost-pass-rate-comparison.tsx +23 -0
- package/src/report/built-ins/index.ts +2 -0
- package/src/report/components.tsx +223 -50
- package/src/report/compute.ts +358 -53
- package/src/report/dual-render.test.tsx +1125 -0
- package/src/report/flag.ts +1 -1
- package/src/report/format.ts +50 -2
- package/src/report/index.ts +48 -14
- package/src/report/load.ts +1 -1
- package/src/report/locale.ts +206 -0
- package/src/report/metrics.ts +39 -15
- package/src/report/primitives.tsx +100 -2
- package/src/report/react/AttemptList.tsx +115 -0
- package/src/report/react/DeltaTable.tsx +9 -6
- package/src/report/react/EvalList.tsx +0 -0
- package/src/report/react/ExperimentList.tsx +108 -0
- package/src/report/react/GroupSummary.tsx +66 -0
- package/src/report/react/MetricBars.tsx +24 -13
- package/src/report/react/MetricLine.tsx +21 -21
- package/src/report/react/MetricMatrix.tsx +8 -4
- package/src/report/react/MetricScatter.tsx +136 -96
- package/src/report/react/MetricTable.tsx +115 -15
- package/src/report/react/RunOverview.tsx +48 -26
- package/src/report/react/Scoreboard.tsx +15 -7
- package/src/report/react/cell.tsx +18 -9
- package/src/{view/app/lib/chart.ts → report/react/chart-math.ts} +2 -6
- package/src/report/react/colors.ts +24 -15
- package/src/report/react/enhance.js +129 -0
- package/src/report/react/fixtures.ts +163 -34
- package/src/report/react/format.ts +1 -1
- package/src/report/react/index.tsx +22 -6
- package/src/report/react/render.test.tsx +164 -31
- package/src/report/react/styles.css +405 -213
- package/src/report/report.test.ts +403 -94
- package/src/report/report.ts +29 -11
- package/src/report/text/faces.ts +303 -95
- package/src/report/text/layout.ts +76 -11
- package/src/report/text/table.ts +98 -0
- package/src/report/tree.ts +103 -20
- package/src/report/types.ts +196 -25
- package/src/report/web.ts +46 -19
- package/src/results/annotated-source.test.ts +146 -0
- package/src/results/annotated-source.ts +128 -0
- package/src/results/attempt-evidence.test.ts +158 -0
- package/src/results/attempt-evidence.ts +158 -0
- package/src/results/attempt-source.ts +48 -0
- package/src/results/copy.ts +154 -139
- package/src/results/format.ts +54 -47
- package/src/results/host-equivalence.test.ts +527 -0
- package/src/results/index.ts +56 -14
- package/src/results/locator.test.ts +198 -0
- package/src/results/locator.ts +0 -0
- package/src/results/open.ts +392 -175
- package/src/results/results.test.ts +943 -494
- package/src/results/select.ts +129 -23
- package/src/results/skipped-notice.ts +0 -0
- package/src/results/source-hash.ts +28 -0
- package/src/results/types.ts +98 -62
- package/src/results/writer.ts +304 -176
- package/src/runner/attempt.test.ts +170 -0
- package/src/runner/attempt.ts +48 -13
- package/src/runner/discover.test.ts +65 -0
- package/src/runner/discover.ts +6 -2
- package/src/runner/eval-source.test.ts +121 -0
- package/src/runner/eval-source.ts +45 -0
- package/src/runner/fingerprint.ts +54 -1
- package/src/runner/remote-sandbox.ts +1 -1
- package/src/runner/report.test.ts +6 -8
- package/src/runner/report.ts +5 -4
- package/src/runner/reporters/artifacts.ts +27 -41
- package/src/runner/reporters/braintrust.test.ts +3 -3
- package/src/runner/reporters/braintrust.ts +1 -1
- package/src/runner/reporters/console.ts +6 -6
- package/src/runner/reporters/json.ts +4 -4
- package/src/runner/reporters/live.test.ts +56 -0
- package/src/runner/reporters/live.ts +36 -10
- package/src/runner/reporters/quiet.test.ts +66 -0
- package/src/runner/reporters/quiet.ts +49 -0
- package/src/runner/reporters/shared.ts +5 -5
- package/src/runner/reporters/table.ts +17 -17
- package/src/runner/run.ts +61 -102
- package/src/runner/types.ts +43 -32
- package/src/sandbox/checkpoint.ts +1 -1
- package/src/sandbox/docker.ts +15 -2
- package/src/sandbox/e2b.ts +11 -5
- package/src/sandbox/errors.ts +13 -0
- package/src/sandbox/index.ts +3 -3
- package/src/sandbox/local-files.ts +1 -1
- package/src/sandbox/registry.ts +6 -5
- package/src/sandbox/resolve.ts +38 -23
- package/src/sandbox/retry.test.ts +68 -0
- package/src/sandbox/retry.ts +44 -0
- package/src/sandbox/types.ts +24 -24
- package/src/sandbox/vercel.ts +17 -3
- package/src/scoring/collector.ts +3 -3
- package/src/scoring/judge.ts +2 -2
- package/src/scoring/scoped.ts +13 -1
- package/src/scoring/types.ts +7 -7
- package/src/scoring/verdict.ts +4 -4
- package/src/shared/aggregate.ts +23 -3
- package/src/shared/types.ts +2 -2
- package/src/shared/{outcome.ts → verdict.ts} +15 -15
- package/src/show/compose.ts +12 -132
- package/src/show/index.ts +125 -94
- package/src/show/render.ts +660 -160
- package/src/show/show.test.ts +472 -253
- package/src/source-loc.ts +1 -1
- package/src/tty-line.ts +32 -0
- package/src/types.ts +2 -2
- package/src/util.test.ts +21 -1
- package/src/util.ts +5 -1
- package/src/view/app/App.tsx +33 -198
- package/src/view/app/components/AttemptModal.tsx +6 -6
- package/src/view/app/components/CodeView.tsx +2 -2
- package/src/view/app/components/CopyControls.tsx +32 -36
- package/src/view/app/components/SkippedRunsBanner.tsx +140 -0
- package/src/view/app/components/ui/badge.tsx +1 -1
- package/src/view/app/components/ui/dialog.tsx +2 -2
- package/src/view/app/i18n.ts +40 -31
- package/src/view/app/lib/artifact-url.ts +1 -1
- package/src/view/app/lib/attempt-route.test.ts +37 -47
- package/src/view/app/lib/attempt-route.ts +26 -26
- package/src/view/app/lib/rows.ts +9 -112
- package/src/view/app/lib/verdict.ts +25 -0
- package/src/view/app/main.tsx +9 -12
- package/src/view/app/pages/RunsPage.tsx +4 -4
- package/src/view/app/pages/TracesPage.tsx +3 -3
- package/src/view/app/shared.ts +0 -1
- package/src/view/app/types.ts +6 -51
- package/src/view/artifact-serving.test.ts +136 -0
- package/src/view/client-dist/app.css +1 -1
- package/src/view/client-dist/app.js +21 -21
- package/src/view/data.test.ts +214 -143
- package/src/view/data.ts +166 -120
- package/src/view/index.ts +23 -11
- package/src/view/server.ts +52 -17
- package/src/view/shared/types.ts +22 -31
- package/src/view/styles.css +117 -327
- package/src/view/view-report.test.ts +146 -98
- package/src/report/default-report.tsx +0 -222
- package/src/report/dual-face.test.tsx +0 -527
- package/src/report/react/CaseList.tsx +0 -70
- package/src/view/app/components/CostScoreChart.tsx +0 -147
- package/src/view/app/components/ExperimentTable.tsx +0 -284
- package/src/view/app/components/GroupSelector.tsx +0 -61
- package/src/view/app/components/primitives.tsx +0 -43
- package/src/view/app/lib/outcome.ts +0 -82
package/src/scoring/scoped.ts
CHANGED
|
@@ -191,7 +191,19 @@ export function maxToolCalls(max: number): Spec {
|
|
|
191
191
|
}
|
|
192
192
|
|
|
193
193
|
export function loadedSkill(skill: string): Spec {
|
|
194
|
-
return
|
|
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 {
|
package/src/scoring/types.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
// scoring
|
|
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
|
-
/**
|
|
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 的
|
|
16
|
-
* `--strict` 运行下,软阈值失败也会把整条 eval 的
|
|
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
|
|
74
|
+
export type Verdict = "passed" | "failed" | "errored" | "skipped";
|
|
75
75
|
|
|
76
76
|
export interface JudgeConfig {
|
|
77
77
|
model: string;
|
package/src/scoring/verdict.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
//
|
|
1
|
+
// 判定:把执行结果 + 断言 + 跳过原因折叠成一个 Verdict(见 docs/feature/scoring/README.md)。
|
|
2
2
|
|
|
3
|
-
import type { AssertionResult,
|
|
3
|
+
import type { AssertionResult, Verdict } from "../types.ts";
|
|
4
4
|
|
|
5
|
-
export function
|
|
5
|
+
export function computeVerdict(input: {
|
|
6
6
|
error?: string;
|
|
7
7
|
assertions: readonly AssertionResult[];
|
|
8
8
|
skipReason?: string;
|
|
9
9
|
strict?: boolean;
|
|
10
|
-
}):
|
|
10
|
+
}): Verdict {
|
|
11
11
|
if (input.error !== undefined) return "errored";
|
|
12
12
|
|
|
13
13
|
for (const a of input.assertions) {
|
package/src/shared/aggregate.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
// CLI 表格(runner/reporters/table.ts)与 view 榜单(view/aggregate.ts)共用的聚合小工具。
|
|
2
|
-
// 实验标签推导、token/成本求和、
|
|
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 {
|
|
7
|
+
import type { Verdict } from "../scoring/types.ts";
|
|
8
8
|
|
|
9
9
|
/** 明细行排序:失败最靠前(failed > errored > skipped > passed 的紧急程度)。 */
|
|
10
|
-
export const
|
|
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;
|
package/src/shared/types.ts
CHANGED
|
@@ -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
|
-
/**
|
|
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/
|
|
1
|
+
// server(aggregate.ts)与前端(app/lib/verdict.ts)共用的判定折叠口径。
|
|
2
2
|
// 必须保持环境无关且只依赖 type import,vite 前端会直接打包它。
|
|
3
3
|
// 单独成模块的原因:折叠/计票口径两边必须逐字一致,否则折叠行状态会和 KPI / 成功率对不上。
|
|
4
4
|
|
|
5
|
-
import type {
|
|
5
|
+
import type { Verdict } from "../types.ts";
|
|
6
6
|
|
|
7
|
-
/** 折叠/计票只需要
|
|
8
|
-
export interface
|
|
9
|
-
|
|
7
|
+
/** 折叠/计票只需要 verdict 字段;server 传 EvalResult,前端传 ViewResult 都满足。 */
|
|
8
|
+
export interface VerdictLike {
|
|
9
|
+
verdict: Verdict;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
* 把同一个 eval 的多轮 attempt
|
|
13
|
+
* 把同一个 eval 的多轮 attempt 折叠成单一判定:任一轮通过 → 该 eval 通过(对齐 earlyExit
|
|
14
14
|
* 「先过一次即停」语义),否则按 failed > errored > skipped 取最严重的一个。
|
|
15
15
|
*/
|
|
16
|
-
export function
|
|
17
|
-
const
|
|
18
|
-
if (
|
|
19
|
-
if (
|
|
20
|
-
if (
|
|
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
|
-
*
|
|
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
|
|
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[
|
|
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
|
}
|
package/src/show/compose.ts
CHANGED
|
@@ -1,140 +1,20 @@
|
|
|
1
|
-
// show
|
|
1
|
+
// show 专属的跨 run 时间轴口径(--history;docs-site/zh/guides/viewing-results.mdx 是行为规范)。
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// 向更早的 run 补齐,再把合成好的选集注入报告槽——内置默认报告与 --report 吃同一份,
|
|
7
|
-
// 默认报告口径 = 宿主注入口径。本文件只消费 niceeval/results 的读取面。
|
|
3
|
+
// 现刻水位 Selection(两个宿主共用)住在 ../results/select.ts 的 selectCurrentResults;
|
|
4
|
+
// 本文件只留 show 独有的时间轴计算:每个快照 / 每次真实执行一行,resume 携带的复印件不占行。
|
|
5
|
+
// 数据只消费 niceeval/results 的读取面。
|
|
8
6
|
|
|
9
|
-
import {
|
|
10
|
-
import { evalPrefixPredicate } from "../report/aggregate.ts";
|
|
7
|
+
import { foldEvalVerdict } from "../shared/verdict.ts";
|
|
11
8
|
import { attemptCostUSD } from "../report/metrics.ts";
|
|
12
|
-
import {
|
|
13
|
-
import type {
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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) {
|