oh-my-knowledge 0.23.0 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +146 -84
- package/README.zh.md +144 -83
- package/dist/src/analysis/report-diagnostics.d.ts +3 -2
- package/dist/src/analysis/report-diagnostics.d.ts.map +1 -1
- package/dist/src/analysis/report-diagnostics.js +107 -80
- package/dist/src/analysis/report-diagnostics.js.map +1 -1
- package/dist/src/analysis/sample-diagnostics.d.ts +4 -1
- package/dist/src/analysis/sample-diagnostics.d.ts.map +1 -1
- package/dist/src/analysis/sample-diagnostics.js +123 -29
- package/dist/src/analysis/sample-diagnostics.js.map +1 -1
- package/dist/src/analysis/saturation.d.ts +2 -2
- package/dist/src/analysis/saturation.js +2 -2
- package/dist/src/authoring/evolver.d.ts +12 -4
- package/dist/src/authoring/evolver.d.ts.map +1 -1
- package/dist/src/authoring/evolver.js +35 -10
- package/dist/src/authoring/evolver.js.map +1 -1
- package/dist/src/cli/i18n-dict.d.ts +1 -1
- package/dist/src/cli/i18n-dict.d.ts.map +1 -1
- package/dist/src/cli/i18n-dict.js +241 -64
- package/dist/src/cli/i18n-dict.js.map +1 -1
- package/dist/src/cli/index.js +267 -152
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/cli/parse-run-config.d.ts +32 -9
- package/dist/src/cli/parse-run-config.d.ts.map +1 -1
- package/dist/src/cli/parse-run-config.js +80 -25
- package/dist/src/cli/parse-run-config.js.map +1 -1
- package/dist/src/cli/parse-strict.d.ts +20 -0
- package/dist/src/cli/parse-strict.d.ts.map +1 -0
- package/dist/src/cli/parse-strict.js +25 -0
- package/dist/src/cli/parse-strict.js.map +1 -0
- package/dist/src/cli/progress.d.ts +1 -0
- package/dist/src/cli/progress.d.ts.map +1 -1
- package/dist/src/cli/progress.js +8 -1
- package/dist/src/cli/progress.js.map +1 -1
- package/dist/src/doctor/index.d.ts +19 -0
- package/dist/src/doctor/index.d.ts.map +1 -0
- package/dist/src/doctor/index.js +182 -0
- package/dist/src/doctor/index.js.map +1 -0
- package/dist/src/doctor/preflight.d.ts +32 -0
- package/dist/src/doctor/preflight.d.ts.map +1 -0
- package/dist/src/doctor/preflight.js +32 -0
- package/dist/src/doctor/preflight.js.map +1 -0
- package/dist/src/doctor/renderer.d.ts +13 -0
- package/dist/src/doctor/renderer.d.ts.map +1 -0
- package/dist/src/doctor/renderer.js +69 -0
- package/dist/src/doctor/renderer.js.map +1 -0
- package/dist/src/doctor/rules.d.ts +30 -0
- package/dist/src/doctor/rules.d.ts.map +1 -0
- package/dist/src/doctor/rules.js +216 -0
- package/dist/src/doctor/rules.js.map +1 -0
- package/dist/src/eval-core/bootstrap.d.ts +1 -1
- package/dist/src/eval-core/bootstrap.js +1 -1
- package/dist/src/eval-core/cache.d.ts +7 -5
- package/dist/src/eval-core/cache.d.ts.map +1 -1
- package/dist/src/eval-core/cache.js +11 -7
- package/dist/src/eval-core/cache.js.map +1 -1
- package/dist/src/eval-core/comparability.d.ts +11 -0
- package/dist/src/eval-core/comparability.d.ts.map +1 -0
- package/dist/src/eval-core/comparability.js +296 -0
- package/dist/src/eval-core/comparability.js.map +1 -0
- package/dist/src/eval-core/dependency-checker.js +1 -1
- package/dist/src/eval-core/dependency-checker.js.map +1 -1
- package/dist/src/eval-core/evaluation-execution.d.ts +23 -7
- package/dist/src/eval-core/evaluation-execution.d.ts.map +1 -1
- package/dist/src/eval-core/evaluation-execution.js +53 -21
- package/dist/src/eval-core/evaluation-execution.js.map +1 -1
- package/dist/src/eval-core/evaluation-job.d.ts +3 -5
- package/dist/src/eval-core/evaluation-job.d.ts.map +1 -1
- package/dist/src/eval-core/evaluation-job.js +2 -4
- package/dist/src/eval-core/evaluation-job.js.map +1 -1
- package/dist/src/eval-core/evaluation-reporting.d.ts +3 -1
- package/dist/src/eval-core/evaluation-reporting.d.ts.map +1 -1
- package/dist/src/eval-core/evaluation-reporting.js +67 -9
- package/dist/src/eval-core/evaluation-reporting.js.map +1 -1
- package/dist/src/eval-core/execution-strategy.js +2 -2
- package/dist/src/eval-core/execution-strategy.js.map +1 -1
- package/dist/src/eval-core/schema.d.ts.map +1 -1
- package/dist/src/eval-core/schema.js +20 -2
- package/dist/src/eval-core/schema.js.map +1 -1
- package/dist/src/eval-workflows/batch-evaluation-workflow.d.ts +113 -0
- package/dist/src/eval-workflows/batch-evaluation-workflow.d.ts.map +1 -0
- package/dist/src/eval-workflows/batch-evaluation-workflow.js +217 -0
- package/dist/src/eval-workflows/batch-evaluation-workflow.js.map +1 -0
- package/dist/src/eval-workflows/evaluation-pipeline.d.ts +6 -4
- package/dist/src/eval-workflows/evaluation-pipeline.d.ts.map +1 -1
- package/dist/src/eval-workflows/evaluation-pipeline.js +44 -38
- package/dist/src/eval-workflows/evaluation-pipeline.js.map +1 -1
- package/dist/src/eval-workflows/evaluation-preparation.d.ts +4 -17
- package/dist/src/eval-workflows/evaluation-preparation.d.ts.map +1 -1
- package/dist/src/eval-workflows/evaluation-preparation.js +3 -18
- package/dist/src/eval-workflows/evaluation-preparation.js.map +1 -1
- package/dist/src/eval-workflows/run-evaluation.d.ts +27 -23
- package/dist/src/eval-workflows/run-evaluation.d.ts.map +1 -1
- package/dist/src/eval-workflows/run-evaluation.js +137 -20
- package/dist/src/eval-workflows/run-evaluation.js.map +1 -1
- package/dist/src/executors/claude-cli.d.ts.map +1 -1
- package/dist/src/executors/claude-cli.js +11 -6
- package/dist/src/executors/claude-cli.js.map +1 -1
- package/dist/src/executors/codex-cli-trace.d.ts +10 -0
- package/dist/src/executors/codex-cli-trace.d.ts.map +1 -0
- package/dist/src/executors/codex-cli-trace.js +123 -0
- package/dist/src/executors/codex-cli-trace.js.map +1 -0
- package/dist/src/executors/codex-cli.d.ts +18 -0
- package/dist/src/executors/codex-cli.d.ts.map +1 -0
- package/dist/src/executors/codex-cli.js +254 -0
- package/dist/src/executors/codex-cli.js.map +1 -0
- package/dist/src/executors/codex-sdk.d.ts +18 -0
- package/dist/src/executors/codex-sdk.d.ts.map +1 -0
- package/dist/src/executors/codex-sdk.js +214 -0
- package/dist/src/executors/codex-sdk.js.map +1 -0
- package/dist/src/executors/gemini.d.ts.map +1 -1
- package/dist/src/executors/gemini.js +28 -24
- package/dist/src/executors/gemini.js.map +1 -1
- package/dist/src/executors/index.d.ts.map +1 -1
- package/dist/src/executors/index.js +7 -2
- package/dist/src/executors/index.js.map +1 -1
- package/dist/src/executors/runtime-fingerprint.d.ts +7 -0
- package/dist/src/executors/runtime-fingerprint.d.ts.map +1 -0
- package/dist/src/executors/runtime-fingerprint.js +277 -0
- package/dist/src/executors/runtime-fingerprint.js.map +1 -0
- package/dist/src/executors/script.d.ts.map +1 -1
- package/dist/src/executors/script.js +47 -55
- package/dist/src/executors/script.js.map +1 -1
- package/dist/src/executors/shared.d.ts +78 -1
- package/dist/src/executors/shared.d.ts.map +1 -1
- package/dist/src/executors/shared.js +203 -1
- package/dist/src/executors/shared.js.map +1 -1
- package/dist/src/grading/assertions.d.ts.map +1 -1
- package/dist/src/grading/assertions.js +22 -7
- package/dist/src/grading/assertions.js.map +1 -1
- package/dist/src/grading/gold-cli.js +4 -4
- package/dist/src/grading/gold-cli.js.map +1 -1
- package/dist/src/grading/human-gold.d.ts +1 -1
- package/dist/src/grading/human-gold.js +1 -1
- package/dist/src/grading/index.d.ts +20 -15
- package/dist/src/grading/index.d.ts.map +1 -1
- package/dist/src/grading/index.js +40 -16
- package/dist/src/grading/index.js.map +1 -1
- package/dist/src/grading/judge.d.ts +1 -1
- package/dist/src/grading/judge.d.ts.map +1 -1
- package/dist/src/grading/judge.js +76 -7
- package/dist/src/grading/judge.js.map +1 -1
- package/dist/src/inputs/eval-config.js +65 -7
- package/dist/src/inputs/eval-config.js.map +1 -1
- package/dist/src/inputs/skill-loader.d.ts +1 -1
- package/dist/src/inputs/skill-loader.d.ts.map +1 -1
- package/dist/src/inputs/skill-loader.js +1 -1
- package/dist/src/inputs/skill-loader.js.map +1 -1
- package/dist/src/renderer/html-renderer.d.ts +5 -4
- package/dist/src/renderer/html-renderer.d.ts.map +1 -1
- package/dist/src/renderer/html-renderer.js +240 -96
- package/dist/src/renderer/html-renderer.js.map +1 -1
- package/dist/src/renderer/layout.d.ts +2 -1
- package/dist/src/renderer/layout.d.ts.map +1 -1
- package/dist/src/renderer/layout.js +30 -42
- package/dist/src/renderer/layout.js.map +1 -1
- package/dist/src/renderer/summary.d.ts +3 -3
- package/dist/src/renderer/summary.d.ts.map +1 -1
- package/dist/src/renderer/summary.js +233 -43
- package/dist/src/renderer/summary.js.map +1 -1
- package/dist/src/renderer/trends.d.ts.map +1 -1
- package/dist/src/renderer/trends.js +5 -3
- package/dist/src/renderer/trends.js.map +1 -1
- package/dist/src/server/report-server.js +4 -4
- package/dist/src/server/report-server.js.map +1 -1
- package/dist/src/server/report-store.d.ts +7 -5
- package/dist/src/server/report-store.d.ts.map +1 -1
- package/dist/src/server/report-store.js +39 -11
- package/dist/src/server/report-store.js.map +1 -1
- package/dist/src/types/doctor.d.ts +95 -0
- package/dist/src/types/doctor.d.ts.map +1 -0
- package/dist/src/types/doctor.js +2 -0
- package/dist/src/types/doctor.js.map +1 -0
- package/dist/src/types/eval.d.ts +40 -19
- package/dist/src/types/eval.d.ts.map +1 -1
- package/dist/src/types/executor.d.ts +38 -0
- package/dist/src/types/executor.d.ts.map +1 -1
- package/dist/src/types/index.d.ts +1 -0
- package/dist/src/types/index.d.ts.map +1 -1
- package/dist/src/types/index.js +1 -0
- package/dist/src/types/index.js.map +1 -1
- package/dist/src/types/judge.d.ts +21 -0
- package/dist/src/types/judge.d.ts.map +1 -1
- package/dist/src/types/report.d.ts +100 -35
- package/dist/src/types/report.d.ts.map +1 -1
- package/dist/src/types/storage.d.ts +7 -7
- package/dist/src/types/storage.d.ts.map +1 -1
- package/package.json +6 -5
- package/dist/src/eval-workflows/each-evaluation-workflow.d.ts +0 -153
- package/dist/src/eval-workflows/each-evaluation-workflow.d.ts.map +0 -1
- package/dist/src/eval-workflows/each-evaluation-workflow.js +0 -178
- package/dist/src/eval-workflows/each-evaluation-workflow.js.map +0 -1
- package/dist/src/executors/openai-cli.d.ts +0 -3
- package/dist/src/executors/openai-cli.d.ts.map +0 -1
- package/dist/src/executors/openai-cli.js +0 -60
- package/dist/src/executors/openai-cli.js.map +0 -1
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* omk doctor 内置规则注册表。
|
|
3
|
+
*
|
|
4
|
+
* doctor 是**纯静态/低成本检查**, 不碰 LLM 连通性 — executor / judge 连通性由
|
|
5
|
+
* evaluation preflight 负责。边界清晰: doctor 静态(零 LLM 调用), eval 动态。
|
|
6
|
+
*
|
|
7
|
+
* 每条 rule 回答一个独立的「skill 能不能被有意义评测」子问题:
|
|
8
|
+
* - skill_readable: 文件能读、内容非空且有最小长度
|
|
9
|
+
* - skill_metadata: front-matter(若有) YAML 合法、directory-skill 有 SKILL.md
|
|
10
|
+
* - dependencies: 引用的 tool / file / env / preflight 完整(复用 preflightDependencies)
|
|
11
|
+
* - samples_contract: 仅在传 samples 时跑,warn 级,校验 samples 非空 + 含 prompt
|
|
12
|
+
*
|
|
13
|
+
* fatal-fail 时 rule 引擎不中断后续 rule 执行(让用户一次看到全貌),
|
|
14
|
+
* 但 DoctorReport.failed 会置 true。
|
|
15
|
+
*
|
|
16
|
+
* 扩展接口预留: registerRule() 允许业务方注入自定义 rule(v0.22 不暴露 CLI flag,
|
|
17
|
+
* 仅作为 library API 占位)。
|
|
18
|
+
*/
|
|
19
|
+
import { existsSync } from 'node:fs';
|
|
20
|
+
import { join } from 'node:path';
|
|
21
|
+
import yaml from 'js-yaml';
|
|
22
|
+
import { tCli } from '../cli/i18n.js';
|
|
23
|
+
import { preflightDependencies } from '../eval-core/dependency-checker.js';
|
|
24
|
+
/** 用 js-yaml 真正解析 front-matter。能抓 unterminated string、非法缩进、
|
|
25
|
+
* duplicate key 等手写 pattern check 漏掉的 case。 */
|
|
26
|
+
function checkFrontmatter(content) {
|
|
27
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
28
|
+
if (!match)
|
|
29
|
+
return { ok: true };
|
|
30
|
+
try {
|
|
31
|
+
yaml.load(match[1]);
|
|
32
|
+
return { ok: true };
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
const e = (typeof err === 'object' && err !== null ? err : {});
|
|
36
|
+
const lineSuffix = e.mark ? ` at line ${e.mark.line + 1}` : '';
|
|
37
|
+
return { ok: false, error: `${e.reason || e.message || 'YAML parse error'}${lineSuffix}` };
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function summarizeDependencyIssues(missing, lang) {
|
|
41
|
+
const counts = { tool: 0, file: 0, env: 0 };
|
|
42
|
+
for (const m of missing)
|
|
43
|
+
counts[m.category] += 1;
|
|
44
|
+
const parts = [];
|
|
45
|
+
const labels = lang === 'zh'
|
|
46
|
+
? { tool: '工具', file: '文件', env: '环境变量' }
|
|
47
|
+
: { tool: 'tool', file: 'file', env: 'env' };
|
|
48
|
+
if (counts.tool > 0)
|
|
49
|
+
parts.push(`${counts.tool} ${labels.tool}`);
|
|
50
|
+
if (counts.file > 0)
|
|
51
|
+
parts.push(`${counts.file} ${labels.file}`);
|
|
52
|
+
if (counts.env > 0)
|
|
53
|
+
parts.push(`${counts.env} ${labels.env}`);
|
|
54
|
+
return parts.join(', ') || (lang === 'zh' ? '未知' : 'unknown');
|
|
55
|
+
}
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
// Rules
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
const SKILL_MIN_LENGTH = 10;
|
|
60
|
+
export const skillReadableRule = {
|
|
61
|
+
id: 'skill_readable',
|
|
62
|
+
severity: 'fatal',
|
|
63
|
+
labelKey: 'cli.doctor.rule.skill_readable',
|
|
64
|
+
async check(ctx) {
|
|
65
|
+
const content = ctx.artifact.content;
|
|
66
|
+
if (content === null || content === undefined) {
|
|
67
|
+
return {
|
|
68
|
+
status: 'fail',
|
|
69
|
+
message: tCli('cli.doctor.skill_readable.fail.missing', ctx.lang),
|
|
70
|
+
hint: tCli('cli.doctor.skill_readable.hint.missing', ctx.lang),
|
|
71
|
+
detail: { length: 0 },
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
const trimmed = content.trim();
|
|
75
|
+
if (trimmed.length === 0) {
|
|
76
|
+
return {
|
|
77
|
+
status: 'fail',
|
|
78
|
+
message: tCli('cli.doctor.skill_readable.fail.empty', ctx.lang),
|
|
79
|
+
hint: tCli('cli.doctor.skill_readable.hint.missing', ctx.lang),
|
|
80
|
+
detail: { length: 0 },
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
if (trimmed.length < SKILL_MIN_LENGTH) {
|
|
84
|
+
return {
|
|
85
|
+
status: 'fail',
|
|
86
|
+
message: tCli('cli.doctor.skill_readable.fail.too_short', ctx.lang, { length: trimmed.length }),
|
|
87
|
+
hint: tCli('cli.doctor.skill_readable.hint.too_short', ctx.lang),
|
|
88
|
+
detail: { length: trimmed.length, minimum: SKILL_MIN_LENGTH },
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
status: 'pass',
|
|
93
|
+
message: tCli('cli.doctor.skill_readable.pass', ctx.lang, { length: trimmed.length }),
|
|
94
|
+
detail: { length: trimmed.length },
|
|
95
|
+
};
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
export const skillMetadataRule = {
|
|
99
|
+
id: 'skill_metadata',
|
|
100
|
+
severity: 'fatal',
|
|
101
|
+
labelKey: 'cli.doctor.rule.skill_metadata',
|
|
102
|
+
async check(ctx) {
|
|
103
|
+
// directory-skill 必须有 SKILL.md
|
|
104
|
+
if (ctx.artifact.skillRoot) {
|
|
105
|
+
const skillMdPath = join(ctx.artifact.skillRoot, 'SKILL.md');
|
|
106
|
+
if (!existsSync(skillMdPath)) {
|
|
107
|
+
return {
|
|
108
|
+
status: 'fail',
|
|
109
|
+
message: tCli('cli.doctor.skill_metadata.fail.missing_skillmd', ctx.lang),
|
|
110
|
+
hint: tCli('cli.doctor.skill_metadata.hint.missing_skillmd', ctx.lang),
|
|
111
|
+
detail: { skillRoot: ctx.artifact.skillRoot, expectedPath: skillMdPath },
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// front-matter 若存在则要合法(走 js-yaml 真解析)。不存在 = pure markdown skill,合法。
|
|
116
|
+
const content = ctx.artifact.content ?? '';
|
|
117
|
+
const fmCheck = checkFrontmatter(content);
|
|
118
|
+
if (!fmCheck.ok) {
|
|
119
|
+
return {
|
|
120
|
+
status: 'fail',
|
|
121
|
+
message: tCli('cli.doctor.skill_metadata.fail.frontmatter_invalid', ctx.lang, { error: fmCheck.error ?? '' }),
|
|
122
|
+
hint: tCli('cli.doctor.skill_metadata.hint.frontmatter', ctx.lang),
|
|
123
|
+
detail: { error: fmCheck.error },
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
status: 'pass',
|
|
128
|
+
message: tCli('cli.doctor.skill_metadata.pass', ctx.lang),
|
|
129
|
+
};
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
export const dependenciesPresentRule = {
|
|
133
|
+
id: 'dependencies_present',
|
|
134
|
+
severity: 'fatal',
|
|
135
|
+
labelKey: 'cli.doctor.rule.dependencies',
|
|
136
|
+
async check(ctx) {
|
|
137
|
+
const content = ctx.artifact.content ?? '';
|
|
138
|
+
const result = await preflightDependencies([content], ctx.samples ?? [], ctx.dependencyCwd ?? ctx.cwd, // 与 evaluation preflight 同规则; 见 DoctorContext.dependencyCwd
|
|
139
|
+
ctx.requires, // samples wrapper 显式 requires
|
|
140
|
+
[ctx.artifact]);
|
|
141
|
+
if (!result.ok) {
|
|
142
|
+
const summary = summarizeDependencyIssues(result.missing, ctx.lang);
|
|
143
|
+
return {
|
|
144
|
+
status: 'fail',
|
|
145
|
+
message: tCli('cli.doctor.dependencies.fail', ctx.lang, { summary }),
|
|
146
|
+
hint: tCli('cli.doctor.dependencies.hint', ctx.lang),
|
|
147
|
+
detail: { missing: result.missing },
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
status: 'pass',
|
|
152
|
+
message: tCli('cli.doctor.dependencies.pass', ctx.lang),
|
|
153
|
+
};
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
export const samplesContractAlignedRule = {
|
|
157
|
+
id: 'samples_contract_aligned',
|
|
158
|
+
severity: 'warn',
|
|
159
|
+
labelKey: 'cli.doctor.rule.samples_contract',
|
|
160
|
+
async check(ctx) {
|
|
161
|
+
if (!ctx.samples) {
|
|
162
|
+
return {
|
|
163
|
+
status: 'skipped',
|
|
164
|
+
message: tCli('cli.doctor.samples_contract.skipped', ctx.lang),
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
if (ctx.samples.length === 0) {
|
|
168
|
+
return {
|
|
169
|
+
status: 'warn',
|
|
170
|
+
message: tCli('cli.doctor.samples_contract.warn.empty', ctx.lang),
|
|
171
|
+
hint: tCli('cli.doctor.samples_contract.hint', ctx.lang),
|
|
172
|
+
detail: { count: 0 },
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
const missing = ctx.samples.filter((s) => !s.prompt || s.prompt.trim().length === 0);
|
|
176
|
+
if (missing.length > 0) {
|
|
177
|
+
return {
|
|
178
|
+
status: 'warn',
|
|
179
|
+
message: tCli('cli.doctor.samples_contract.warn.missing_prompt', ctx.lang, { count: missing.length }),
|
|
180
|
+
hint: tCli('cli.doctor.samples_contract.hint', ctx.lang),
|
|
181
|
+
detail: { missingCount: missing.length, totalCount: ctx.samples.length },
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
status: 'pass',
|
|
186
|
+
message: tCli('cli.doctor.samples_contract.pass', ctx.lang, { count: ctx.samples.length }),
|
|
187
|
+
detail: { count: ctx.samples.length },
|
|
188
|
+
};
|
|
189
|
+
},
|
|
190
|
+
};
|
|
191
|
+
// ---------------------------------------------------------------------------
|
|
192
|
+
// Registry
|
|
193
|
+
// ---------------------------------------------------------------------------
|
|
194
|
+
/** v0.22 内置规则注册表。执行顺序即此处定义顺序。
|
|
195
|
+
* doctor 不包含 executor 连通性检查 — 该职责属于 evaluation preflight。 */
|
|
196
|
+
export const BUILTIN_RULES = [
|
|
197
|
+
skillReadableRule,
|
|
198
|
+
skillMetadataRule,
|
|
199
|
+
dependenciesPresentRule,
|
|
200
|
+
samplesContractAlignedRule,
|
|
201
|
+
];
|
|
202
|
+
/** 扩展 hook(v0.22 不通过 CLI flag 暴露,仅 library API 占位)。 */
|
|
203
|
+
const customRules = [];
|
|
204
|
+
export function registerRule(rule) {
|
|
205
|
+
if (BUILTIN_RULES.some((r) => r.id === rule.id) || customRules.some((r) => r.id === rule.id)) {
|
|
206
|
+
throw new Error(`doctor rule id collision: ${rule.id}`);
|
|
207
|
+
}
|
|
208
|
+
customRules.push(rule);
|
|
209
|
+
}
|
|
210
|
+
export function getRegisteredRules() {
|
|
211
|
+
return [...BUILTIN_RULES, ...customRules];
|
|
212
|
+
}
|
|
213
|
+
export function __resetCustomRulesForTest() {
|
|
214
|
+
customRules.length = 0;
|
|
215
|
+
}
|
|
216
|
+
//# sourceMappingURL=rules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules.js","sourceRoot":"","sources":["../../../src/doctor/rules.ts"],"names":[],"mappings":"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"}
|
|
@@ -62,7 +62,7 @@ export declare function bootstrapMeanCI(scores: number[], alpha?: number, sample
|
|
|
62
62
|
export declare function bootstrapDiffCI(scoresA: number[], scoresB: number[], alpha?: number, samples?: number, seed?: number): BootstrapDiffCI;
|
|
63
63
|
/**
|
|
64
64
|
* Generic bootstrap CI for an arbitrary sample-level metric. Used by
|
|
65
|
-
* saturation analysis
|
|
65
|
+
* saturation analysis to get CI on metrics like stddev or
|
|
66
66
|
* agreement, not just mean.
|
|
67
67
|
*
|
|
68
68
|
* @param scores Original sample.
|
|
@@ -144,7 +144,7 @@ export function bootstrapDiffCI(scoresA, scoresB, alpha = 0.05, samples = 1000,
|
|
|
144
144
|
}
|
|
145
145
|
/**
|
|
146
146
|
* Generic bootstrap CI for an arbitrary sample-level metric. Used by
|
|
147
|
-
* saturation analysis
|
|
147
|
+
* saturation analysis to get CI on metrics like stddev or
|
|
148
148
|
* agreement, not just mean.
|
|
149
149
|
*
|
|
150
150
|
* @param scores Original sample.
|
|
@@ -2,14 +2,16 @@
|
|
|
2
2
|
* Executor result cache.
|
|
3
3
|
*
|
|
4
4
|
* Caches successful executor results to disk to avoid redundant API calls.
|
|
5
|
-
* Cache key
|
|
5
|
+
* Cache key v4 = sha256(model + system + prompt + cwd + allowedSkills + executor + runtime).
|
|
6
6
|
* Loaded into memory on init, flushed to disk on save().
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* Prefix bumps intentionally invalidate old entries when construct-validity
|
|
9
|
+
* dimensions enter the key:
|
|
10
|
+
* - v2: allowedSkills / strict isolation
|
|
11
|
+
* - v3: executor name
|
|
12
|
+
* - v4: executor runtime fingerprint
|
|
11
13
|
*/
|
|
12
14
|
import type { ExecutorCache } from '../types/index.js';
|
|
13
15
|
export declare function createCache(cacheDir: string): ExecutorCache;
|
|
14
|
-
export declare function cacheKey(model: string, system: string, prompt: string, cwd?: string | null, allowedSkills?: string[]): string;
|
|
16
|
+
export declare function cacheKey(model: string, system: string, prompt: string, cwd?: string | null, allowedSkills?: string[], executor?: string, runtimeFingerprint?: string): string;
|
|
15
17
|
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../src/eval-core/cache.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../src/eval-core/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,OAAO,KAAK,EAAc,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAInE,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,CAsC3D;AAED,wBAAgB,QAAQ,CACtB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,EACnB,aAAa,CAAC,EAAE,MAAM,EAAE,EACxB,QAAQ,CAAC,EAAE,MAAM,EACjB,kBAAkB,CAAC,EAAE,MAAM,GAC1B,MAAM,CAaR"}
|
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
* Executor result cache.
|
|
3
3
|
*
|
|
4
4
|
* Caches successful executor results to disk to avoid redundant API calls.
|
|
5
|
-
* Cache key
|
|
5
|
+
* Cache key v4 = sha256(model + system + prompt + cwd + allowedSkills + executor + runtime).
|
|
6
6
|
* Loaded into memory on init, flushed to disk on save().
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* Prefix bumps intentionally invalidate old entries when construct-validity
|
|
9
|
+
* dimensions enter the key:
|
|
10
|
+
* - v2: allowedSkills / strict isolation
|
|
11
|
+
* - v3: executor name
|
|
12
|
+
* - v4: executor runtime fingerprint
|
|
11
13
|
*/
|
|
12
14
|
import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs';
|
|
13
15
|
import { join } from 'node:path';
|
|
@@ -48,16 +50,18 @@ export function createCache(cacheDir) {
|
|
|
48
50
|
},
|
|
49
51
|
};
|
|
50
52
|
}
|
|
51
|
-
export function cacheKey(model, system, prompt, cwd, allowedSkills) {
|
|
53
|
+
export function cacheKey(model, system, prompt, cwd, allowedSkills, executor, runtimeFingerprint) {
|
|
52
54
|
// allowedSkills 序列化:undefined → "" / [] → "[]" / [...] → 排序后 JSON。
|
|
53
55
|
// 排序保证 ["a","b"] 和 ["b","a"] 命中同一缓存(语义等价)。
|
|
54
56
|
const isoStr = allowedSkills === undefined
|
|
55
57
|
? ''
|
|
56
58
|
: JSON.stringify([...allowedSkills].sort());
|
|
59
|
+
// executor + runtime 进 cache key:同 model 名走不同 executor 或同 executor
|
|
60
|
+
// 换 binary/SDK 版本时输出可能不同,旧 cache 不可复用。
|
|
57
61
|
const hash = createHash('sha256')
|
|
58
|
-
.update(`${model || ''}\n${system || ''}\n${prompt || ''}\n${cwd || ''}\n${isoStr}`)
|
|
62
|
+
.update(`${model || ''}\n${system || ''}\n${prompt || ''}\n${cwd || ''}\n${isoStr}\n${executor || ''}\n${runtimeFingerprint || ''}`)
|
|
59
63
|
.digest('hex')
|
|
60
64
|
.slice(0, 16);
|
|
61
|
-
return `
|
|
65
|
+
return `v4:${hash}`;
|
|
62
66
|
}
|
|
63
67
|
//# sourceMappingURL=cache.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../../src/eval-core/cache.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../../src/eval-core/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGzC,MAAM,UAAU,GAAG,qBAAqB,CAAC;AAEzC,MAAM,UAAU,WAAW,CAAC,QAAgB;IAC1C,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAE5C,IAAI,KAAK,GAA+B,EAAE,CAAC;IAC3C,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;QACtD,CAAC;QAAC,MAAM,CAAC;YACP,KAAK,GAAG,EAAE,CAAC;QACb,CAAC;IACH,CAAC;IAED,IAAI,KAAK,GAAG,KAAK,CAAC;IAElB,OAAO;QACL,GAAG,CAAC,GAAW;YACb,OAAO,KAAK,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;QAC5B,CAAC;QAED,GAAG,CAAC,GAAW,EAAE,KAAiB;YAChC,MAAM,SAAS,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC;YAC/B,OAAO,SAAS,CAAC,KAAK,CAAC;YACvB,OAAO,SAAS,CAAC,SAAS,CAAC;YAC3B,KAAK,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;YACvB,KAAK,GAAG,IAAI,CAAC;QACf,CAAC;QAED,IAAI;YACF,IAAI,CAAC,KAAK;gBAAE,OAAO;YACnB,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YACxD,KAAK,GAAG,KAAK,CAAC;QAChB,CAAC;QAED,IAAI;YACF,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC;QACnC,CAAC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,QAAQ,CACtB,KAAa,EACb,MAAc,EACd,MAAc,EACd,GAAmB,EACnB,aAAwB,EACxB,QAAiB,EACjB,kBAA2B;IAE3B,mEAAmE;IACnE,2CAA2C;IAC3C,MAAM,MAAM,GAAG,aAAa,KAAK,SAAS;QACxC,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC9C,mEAAmE;IACnE,uCAAuC;IACvC,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC;SAC9B,MAAM,CAAC,GAAG,KAAK,IAAI,EAAE,KAAK,MAAM,IAAI,EAAE,KAAK,MAAM,IAAI,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,MAAM,KAAK,QAAQ,IAAI,EAAE,KAAK,kBAAkB,IAAI,EAAE,EAAE,CAAC;SACnI,MAAM,CAAC,KAAK,CAAC;SACb,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAChB,OAAO,MAAM,IAAI,EAAE,CAAC;AACtB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Lang, Report } from '../types/index.js';
|
|
2
|
+
export interface ComparabilityWarning {
|
|
3
|
+
code: string;
|
|
4
|
+
severity: 'warning';
|
|
5
|
+
zh: string;
|
|
6
|
+
en: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function reportComparabilityWarnings(report: Report): ComparabilityWarning[];
|
|
9
|
+
export declare function crossReportComparabilityWarnings(before: Report, after: Report): ComparabilityWarning[];
|
|
10
|
+
export declare function formatComparabilityWarnings(warnings: ComparabilityWarning[], lang?: Lang): string;
|
|
11
|
+
//# sourceMappingURL=comparability.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comparability.d.ts","sourceRoot":"","sources":["../../../src/eval-core/comparability.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,EAAkE,MAAM,mBAAmB,CAAC;AAEtH,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,SAAS,CAAC;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;CACZ;AA0JD,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,MAAM,GAAG,oBAAoB,EAAE,CAoDlF;AAED,wBAAgB,gCAAgC,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,oBAAoB,EAAE,CAwLtG;AAED,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,oBAAoB,EAAE,EAAE,IAAI,GAAE,IAAW,GAAG,MAAM,CAMvG"}
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
function stableStringify(value) {
|
|
2
|
+
if (value === null || typeof value !== 'object')
|
|
3
|
+
return JSON.stringify(value);
|
|
4
|
+
if (Array.isArray(value))
|
|
5
|
+
return '[' + value.map(stableStringify).join(',') + ']';
|
|
6
|
+
const entries = Object.keys(value).sort();
|
|
7
|
+
return '{' + entries.map((k) => JSON.stringify(k) + ':' + stableStringify(value[k])).join(',') + '}';
|
|
8
|
+
}
|
|
9
|
+
function runtimeLabel(runtime) {
|
|
10
|
+
if (!runtime)
|
|
11
|
+
return 'missing';
|
|
12
|
+
const bits = [`${runtime.executor}:${runtime.model}`, `fp=${runtime.fingerprint}`];
|
|
13
|
+
if (runtime.binary?.version)
|
|
14
|
+
bits.push(`binary=${runtime.binary.version}`);
|
|
15
|
+
if (runtime.sdk?.version)
|
|
16
|
+
bits.push(`sdk=${runtime.sdk.version}`);
|
|
17
|
+
return bits.join(' ');
|
|
18
|
+
}
|
|
19
|
+
function push(warnings, code, zh, en) {
|
|
20
|
+
warnings.push({ code, severity: 'warning', zh, en });
|
|
21
|
+
}
|
|
22
|
+
function sorted(values) {
|
|
23
|
+
return [...(values ?? [])].sort();
|
|
24
|
+
}
|
|
25
|
+
function hasJudge(meta) {
|
|
26
|
+
return Boolean(meta.judgeModels && meta.judgeModels.length > 0 && !meta.noJudge);
|
|
27
|
+
}
|
|
28
|
+
function judgeKeys(meta) {
|
|
29
|
+
return (meta.judgeModels ?? []).map((j) => `${j.executor}:${j.model}`);
|
|
30
|
+
}
|
|
31
|
+
function effectiveJudgeRepeat(meta) {
|
|
32
|
+
return meta.judgeRepeat ?? 1;
|
|
33
|
+
}
|
|
34
|
+
function packageReasons(pkg, label) {
|
|
35
|
+
if (!pkg)
|
|
36
|
+
return [`${label} missing`];
|
|
37
|
+
const reasons = [];
|
|
38
|
+
if (pkg.error)
|
|
39
|
+
reasons.push(`${label} probe failed`);
|
|
40
|
+
if (!pkg.version)
|
|
41
|
+
reasons.push(`${label} version missing`);
|
|
42
|
+
return reasons;
|
|
43
|
+
}
|
|
44
|
+
function runtimeUnverifiableReasons(runtime) {
|
|
45
|
+
const reasons = [];
|
|
46
|
+
if (runtime.kind === 'api')
|
|
47
|
+
return reasons;
|
|
48
|
+
if (runtime.kind === 'agent-sdk') {
|
|
49
|
+
reasons.push(...packageReasons(runtime.sdk, 'sdk'));
|
|
50
|
+
}
|
|
51
|
+
const binary = runtime.binary;
|
|
52
|
+
if (!binary) {
|
|
53
|
+
reasons.push('binary missing');
|
|
54
|
+
return reasons;
|
|
55
|
+
}
|
|
56
|
+
if (binary.error)
|
|
57
|
+
reasons.push('binary probe failed');
|
|
58
|
+
if (binary.source === 'path') {
|
|
59
|
+
if (!binary.path)
|
|
60
|
+
reasons.push('binary path missing');
|
|
61
|
+
if (!binary.version)
|
|
62
|
+
reasons.push('binary version missing');
|
|
63
|
+
}
|
|
64
|
+
else if (binary.source === 'bundled') {
|
|
65
|
+
if (!binary.version && !binary.package?.version)
|
|
66
|
+
reasons.push('bundled binary version missing');
|
|
67
|
+
if (binary.package)
|
|
68
|
+
reasons.push(...packageReasons(binary.package, 'bundled package').filter((r) => r !== 'bundled package missing'));
|
|
69
|
+
}
|
|
70
|
+
else if (binary.source === 'unknown') {
|
|
71
|
+
reasons.push('binary source unknown');
|
|
72
|
+
}
|
|
73
|
+
if (runtime.kind === 'unknown')
|
|
74
|
+
reasons.push('runtime kind unknown');
|
|
75
|
+
return [...new Set(reasons)];
|
|
76
|
+
}
|
|
77
|
+
function pushRuntimeUnverifiable(warnings, code, zhSubject, enSubject, runtime) {
|
|
78
|
+
if (!runtime)
|
|
79
|
+
return;
|
|
80
|
+
const reasons = runtimeUnverifiableReasons(runtime);
|
|
81
|
+
if (reasons.length === 0)
|
|
82
|
+
return;
|
|
83
|
+
push(warnings, code, `${zhSubject} runtime 指纹不可完全审计: ${runtimeLabel(runtime)} (${reasons.join('; ')})。`, `${enSubject} runtime fingerprint is not fully verifiable: ${runtimeLabel(runtime)} (${reasons.join('; ')}).`);
|
|
84
|
+
}
|
|
85
|
+
function runtimeMapKeys(meta) {
|
|
86
|
+
return sorted(meta.executorRuntimes ? Object.keys(meta.executorRuntimes) : undefined);
|
|
87
|
+
}
|
|
88
|
+
function reportExecutorRuntimeWarnings(report, warnings) {
|
|
89
|
+
const runtimes = report.meta.executorRuntimes;
|
|
90
|
+
if (runtimes && Object.keys(runtimes).length > 0) {
|
|
91
|
+
const missing = report.meta.variants.filter((variant) => !runtimes[variant]);
|
|
92
|
+
if (missing.length > 0) {
|
|
93
|
+
push(warnings, 'executor_runtime_missing', `报告缺少部分 variant 的 executor runtime 指纹: ${missing.join(', ')}。`, `Report is missing executor runtime fingerprints for variants: ${missing.join(', ')}.`);
|
|
94
|
+
}
|
|
95
|
+
for (const [variant, runtime] of Object.entries(runtimes)) {
|
|
96
|
+
pushRuntimeUnverifiable(warnings, 'executor_runtime_unverifiable', `variant ${variant} executor`, `Variant ${variant} executor`, runtime);
|
|
97
|
+
}
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (!report.meta.executorRuntime) {
|
|
101
|
+
push(warnings, 'executor_runtime_missing', '报告缺少 executor runtime 指纹;无法审计 binary / SDK 版本,跨报告严格比较需谨慎。', 'Report is missing executor runtime fingerprint; binary / SDK versions cannot be audited for strict cross-report comparison.');
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (report.meta.variants.length > 1) {
|
|
105
|
+
push(warnings, 'executor_runtimes_missing', '报告缺少 per-variant executor runtime 指纹;多 variant run 无法审计每个分组实际使用的 binary / SDK 版本。', 'Report is missing per-variant executor runtime fingerprints; multi-variant runs cannot audit each group’s actual binary / SDK version.');
|
|
106
|
+
}
|
|
107
|
+
pushRuntimeUnverifiable(warnings, 'executor_runtime_unverifiable', 'executor', 'Executor', report.meta.executorRuntime);
|
|
108
|
+
}
|
|
109
|
+
function countSampleHashMismatches(a, b) {
|
|
110
|
+
const ah = a.meta.sampleHashes;
|
|
111
|
+
const bh = b.meta.sampleHashes;
|
|
112
|
+
if (!ah || !bh)
|
|
113
|
+
return null;
|
|
114
|
+
let mismatched = 0;
|
|
115
|
+
let missing = 0;
|
|
116
|
+
let common = 0;
|
|
117
|
+
const ids = new Set([...Object.keys(ah), ...Object.keys(bh)]);
|
|
118
|
+
for (const id of ids) {
|
|
119
|
+
if (ah[id] == null || bh[id] == null) {
|
|
120
|
+
missing++;
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
common++;
|
|
124
|
+
if (ah[id] !== bh[id])
|
|
125
|
+
mismatched++;
|
|
126
|
+
}
|
|
127
|
+
return { mismatched, missing, common };
|
|
128
|
+
}
|
|
129
|
+
export function reportComparabilityWarnings(report) {
|
|
130
|
+
const warnings = [];
|
|
131
|
+
reportExecutorRuntimeWarnings(report, warnings);
|
|
132
|
+
if (hasJudge(report.meta) && !report.meta.judgePromptHash) {
|
|
133
|
+
push(warnings, 'judge_prompt_hash_missing', '报告缺少评委提示词指纹;无法确认 LLM 评分语义是否一致。', 'Report is missing judge prompt fingerprint; LLM scoring semantics cannot be verified.');
|
|
134
|
+
}
|
|
135
|
+
// Judge runtime audit: every entry in meta.judgeModels carries its own runtime fingerprint.
|
|
136
|
+
// Skip when noJudge=true (judge didn't run, runtime intentionally absent).
|
|
137
|
+
if (hasJudge(report.meta)) {
|
|
138
|
+
const entries = report.meta.judgeModels ?? [];
|
|
139
|
+
const missing = entries.filter((e) => !e.runtime).map((e) => `${e.executor}:${e.model}`);
|
|
140
|
+
if (missing.length > 0) {
|
|
141
|
+
push(warnings, 'judge_runtime_missing', entries.length === 1
|
|
142
|
+
? '报告缺少评委 runtime 指纹;无法审计评委 executor 的 binary / SDK 版本。'
|
|
143
|
+
: `多评委 ensemble 缺少 runtime 指纹: ${missing.join(', ')}。`, entries.length === 1
|
|
144
|
+
? 'Report is missing judge runtime fingerprint; judge executor binary / SDK versions cannot be audited.'
|
|
145
|
+
: `Multi-judge ensemble is missing runtime fingerprints: ${missing.join(', ')}.`);
|
|
146
|
+
}
|
|
147
|
+
for (const e of entries) {
|
|
148
|
+
if (!e.runtime)
|
|
149
|
+
continue;
|
|
150
|
+
const label = entries.length === 1 ? '评委' : `评委 ${e.executor}:${e.model}`;
|
|
151
|
+
const enLabel = entries.length === 1 ? 'Judge' : `Judge ${e.executor}:${e.model}`;
|
|
152
|
+
pushRuntimeUnverifiable(warnings, 'judge_runtime_unverifiable', label, enLabel, e.runtime);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (!report.meta.sampleHashes) {
|
|
156
|
+
push(warnings, 'sample_hashes_missing', '报告缺少用例指纹;无法确认不同 run 是否测的是同一组用例。', 'Report is missing sample fingerprints; runs cannot be verified to use the same test cases.');
|
|
157
|
+
}
|
|
158
|
+
if (!report.meta.skillIsolation) {
|
|
159
|
+
push(warnings, 'skill_isolation_missing', '报告缺少 skill isolation 快照;baseline 是否被 skill 污染不可审计。', 'Report is missing skill-isolation snapshot; baseline contamination cannot be audited.');
|
|
160
|
+
}
|
|
161
|
+
return warnings;
|
|
162
|
+
}
|
|
163
|
+
export function crossReportComparabilityWarnings(before, after) {
|
|
164
|
+
const warnings = [];
|
|
165
|
+
const b = before.meta;
|
|
166
|
+
const a = after.meta;
|
|
167
|
+
if (b.model !== a.model) {
|
|
168
|
+
push(warnings, 'model_mismatch', `任务执行模型不同: ${b.model} → ${a.model}。`, `Task execution model changed: ${b.model} → ${a.model}.`);
|
|
169
|
+
}
|
|
170
|
+
if (b.executor !== a.executor) {
|
|
171
|
+
push(warnings, 'executor_mismatch', `executor 不同: ${b.executor} → ${a.executor}。`, `Executor changed: ${b.executor} → ${a.executor}.`);
|
|
172
|
+
}
|
|
173
|
+
const bExecutorRuntimeKeys = runtimeMapKeys(b);
|
|
174
|
+
const aExecutorRuntimeKeys = runtimeMapKeys(a);
|
|
175
|
+
const hasExecutorRuntimeMap = bExecutorRuntimeKeys.length > 0 || aExecutorRuntimeKeys.length > 0;
|
|
176
|
+
if (hasExecutorRuntimeMap) {
|
|
177
|
+
const keys = sorted([...new Set([...b.variants, ...a.variants, ...bExecutorRuntimeKeys, ...aExecutorRuntimeKeys])]);
|
|
178
|
+
const missing = keys.filter((key) => !b.executorRuntimes?.[key] || !a.executorRuntimes?.[key]);
|
|
179
|
+
if (missing.length > 0) {
|
|
180
|
+
push(warnings, 'executor_runtime_missing', `至少一份报告缺少 per-variant executor runtime 指纹: ${missing.join(', ')}。`, `At least one report is missing per-variant executor runtime fingerprints: ${missing.join(', ')}.`);
|
|
181
|
+
}
|
|
182
|
+
for (const key of keys) {
|
|
183
|
+
const beforeRuntime = b.executorRuntimes?.[key];
|
|
184
|
+
const afterRuntime = a.executorRuntimes?.[key];
|
|
185
|
+
if (beforeRuntime?.fingerprint && afterRuntime?.fingerprint && beforeRuntime.fingerprint !== afterRuntime.fingerprint) {
|
|
186
|
+
push(warnings, 'executor_runtime_mismatch', `variant ${key} executor runtime 指纹不同: ${runtimeLabel(beforeRuntime)} → ${runtimeLabel(afterRuntime)}。`, `Variant ${key} executor runtime fingerprint changed: ${runtimeLabel(beforeRuntime)} → ${runtimeLabel(afterRuntime)}.`);
|
|
187
|
+
}
|
|
188
|
+
pushRuntimeUnverifiable(warnings, 'executor_runtime_unverifiable', `before variant ${key} executor`, `Before variant ${key} executor`, beforeRuntime);
|
|
189
|
+
pushRuntimeUnverifiable(warnings, 'executor_runtime_unverifiable', `after variant ${key} executor`, `After variant ${key} executor`, afterRuntime);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
else if (b.executorRuntime?.fingerprint && a.executorRuntime?.fingerprint) {
|
|
193
|
+
if (b.executorRuntime.fingerprint !== a.executorRuntime.fingerprint) {
|
|
194
|
+
push(warnings, 'executor_runtime_mismatch', `executor runtime 指纹不同: ${runtimeLabel(b.executorRuntime)} → ${runtimeLabel(a.executorRuntime)}。`, `Executor runtime fingerprint changed: ${runtimeLabel(b.executorRuntime)} → ${runtimeLabel(a.executorRuntime)}.`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
push(warnings, 'executor_runtime_missing', '至少一份报告缺少 executor runtime 指纹;无法确认 binary / SDK 版本一致。', 'At least one report is missing executor runtime fingerprint; binary / SDK version parity cannot be verified.');
|
|
199
|
+
}
|
|
200
|
+
if (!hasExecutorRuntimeMap) {
|
|
201
|
+
pushRuntimeUnverifiable(warnings, 'executor_runtime_unverifiable', 'before executor', 'Before executor', b.executorRuntime);
|
|
202
|
+
pushRuntimeUnverifiable(warnings, 'executor_runtime_unverifiable', 'after executor', 'After executor', a.executorRuntime);
|
|
203
|
+
}
|
|
204
|
+
// Judge comparability — only meaningful when at least one report actually ran a judge.
|
|
205
|
+
// - 都 noJudge: skip 整段(judge 没跑,任何比较都误导;两份 noJudge 报告 judge 视角下完全可比)
|
|
206
|
+
// - 一边 noJudge 一边 judged: 给 judge_presence_mismatch 一条明确警告,跳过 runtime/model 比较
|
|
207
|
+
// (这两份报告本身就不在同一个测量框架,详细 runtime / model diff 是噪声)
|
|
208
|
+
// - 两边都 judged: 走 model / repeat / runtime full audit
|
|
209
|
+
const bJudged = hasJudge(b);
|
|
210
|
+
const aJudged = hasJudge(a);
|
|
211
|
+
if (!bJudged && !aJudged) {
|
|
212
|
+
// 都没跑评委,judge audit 完全跳过;evaluationFramework / skillIsolation 等非 judge 项继续往下走。
|
|
213
|
+
}
|
|
214
|
+
else if (bJudged !== aJudged) {
|
|
215
|
+
push(warnings, 'judge_presence_mismatch', `评委开关不同: ${bJudged ? 'judged' : 'no-judge'} → ${aJudged ? 'judged' : 'no-judge'}。两次跑测的测量框架不一致(LLM 维度缺失),不可在 judge 维度做严格对比。`, `Judge presence differs: ${bJudged ? 'judged' : 'no-judge'} → ${aJudged ? 'judged' : 'no-judge'}. The runs are in different measurement frames (LLM dimension absent on one side); judge-axis comparison is not meaningful.`);
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
// 两边都 judged — single judge 是 ensemble 的 1-entry degenerate case,同一段代码处理。
|
|
219
|
+
const bKeys = sorted(judgeKeys(b));
|
|
220
|
+
const aKeys = sorted(judgeKeys(a));
|
|
221
|
+
if (stableStringify(bKeys) !== stableStringify(aKeys)) {
|
|
222
|
+
const isSingle = bKeys.length <= 1 && aKeys.length <= 1;
|
|
223
|
+
push(warnings, isSingle ? 'judge_model_mismatch' : 'judge_models_mismatch', isSingle
|
|
224
|
+
? `评委模型不同: ${bKeys[0] ?? 'none'} → ${aKeys[0] ?? 'none'}。`
|
|
225
|
+
: `多评委 ensemble 配置不同: ${bKeys.join(', ') || 'none'} → ${aKeys.join(', ') || 'none'}。`, isSingle
|
|
226
|
+
? `Judge model changed: ${bKeys[0] ?? 'none'} → ${aKeys[0] ?? 'none'}.`
|
|
227
|
+
: `Multi-judge ensemble changed: ${bKeys.join(', ') || 'none'} → ${aKeys.join(', ') || 'none'}.`);
|
|
228
|
+
}
|
|
229
|
+
if (effectiveJudgeRepeat(b) !== effectiveJudgeRepeat(a)) {
|
|
230
|
+
push(warnings, 'judge_repeat_mismatch', `每条用例评委评价次数不同: ${effectiveJudgeRepeat(b)} → ${effectiveJudgeRepeat(a)}。`, `Judge repeat count changed: ${effectiveJudgeRepeat(b)} → ${effectiveJudgeRepeat(a)}.`);
|
|
231
|
+
}
|
|
232
|
+
// Per-judge runtime fingerprint comparison. Iterate the union of judges across both reports.
|
|
233
|
+
const judgeUnion = sorted([...new Set([...bKeys, ...aKeys])]);
|
|
234
|
+
if (judgeUnion.length > 0) {
|
|
235
|
+
const bRuntimeByKey = new Map((b.judgeModels ?? []).map((e) => [`${e.executor}:${e.model}`, e.runtime]));
|
|
236
|
+
const aRuntimeByKey = new Map((a.judgeModels ?? []).map((e) => [`${e.executor}:${e.model}`, e.runtime]));
|
|
237
|
+
const missing = judgeUnion.filter((key) => !bRuntimeByKey.get(key) || !aRuntimeByKey.get(key));
|
|
238
|
+
if (missing.length > 0) {
|
|
239
|
+
const isSingle = judgeUnion.length === 1;
|
|
240
|
+
push(warnings, 'judge_runtime_missing', isSingle
|
|
241
|
+
? '至少一份报告缺少评委 runtime 指纹;无法确认评委 binary / SDK 版本一致。'
|
|
242
|
+
: `至少一份报告缺少多评委 runtime 指纹: ${missing.join(', ')}。`, isSingle
|
|
243
|
+
? 'At least one report is missing judge runtime fingerprint; judge binary / SDK version parity cannot be verified.'
|
|
244
|
+
: `At least one report is missing multi-judge runtime fingerprints: ${missing.join(', ')}.`);
|
|
245
|
+
}
|
|
246
|
+
for (const key of judgeUnion) {
|
|
247
|
+
const beforeRuntime = bRuntimeByKey.get(key);
|
|
248
|
+
const afterRuntime = aRuntimeByKey.get(key);
|
|
249
|
+
if (beforeRuntime?.fingerprint && afterRuntime?.fingerprint && beforeRuntime.fingerprint !== afterRuntime.fingerprint) {
|
|
250
|
+
push(warnings, 'judge_runtime_mismatch', judgeUnion.length === 1
|
|
251
|
+
? `评委 runtime 指纹不同: ${runtimeLabel(beforeRuntime)} → ${runtimeLabel(afterRuntime)}。`
|
|
252
|
+
: `评委 ${key} runtime 指纹不同: ${runtimeLabel(beforeRuntime)} → ${runtimeLabel(afterRuntime)}。`, judgeUnion.length === 1
|
|
253
|
+
? `Judge runtime fingerprint changed: ${runtimeLabel(beforeRuntime)} → ${runtimeLabel(afterRuntime)}.`
|
|
254
|
+
: `Judge ${key} runtime fingerprint changed: ${runtimeLabel(beforeRuntime)} → ${runtimeLabel(afterRuntime)}.`);
|
|
255
|
+
}
|
|
256
|
+
const labelPrefix = judgeUnion.length === 1 ? '评委' : `评委 ${key}`;
|
|
257
|
+
const enLabelPrefix = judgeUnion.length === 1 ? 'judge' : `judge ${key}`;
|
|
258
|
+
pushRuntimeUnverifiable(warnings, 'judge_runtime_unverifiable', `before ${labelPrefix}`, `Before ${enLabelPrefix}`, beforeRuntime);
|
|
259
|
+
pushRuntimeUnverifiable(warnings, 'judge_runtime_unverifiable', `after ${labelPrefix}`, `After ${enLabelPrefix}`, afterRuntime);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
// Prompt hash 只在两边都 judged 时比较;一边 noJudge 一边 judged 由上面 judge_presence_mismatch 已覆盖,
|
|
264
|
+
// 两边 noJudge 时双方都没 hash 也不报错。
|
|
265
|
+
if (bJudged && aJudged) {
|
|
266
|
+
if (!b.judgePromptHash || !a.judgePromptHash) {
|
|
267
|
+
push(warnings, 'judge_prompt_hash_missing', `至少一份报告缺少评委提示词指纹: ${b.judgePromptHash ?? 'missing'} → ${a.judgePromptHash ?? 'missing'}。`, `At least one report is missing judge prompt hash: ${b.judgePromptHash ?? 'missing'} → ${a.judgePromptHash ?? 'missing'}.`);
|
|
268
|
+
}
|
|
269
|
+
else if (b.judgePromptHash !== a.judgePromptHash) {
|
|
270
|
+
push(warnings, 'judge_prompt_hash_mismatch', `评委提示词指纹不同: ${b.judgePromptHash ?? 'missing'} → ${a.judgePromptHash ?? 'missing'}。`, `Judge prompt hash changed: ${b.judgePromptHash ?? 'missing'} → ${a.judgePromptHash ?? 'missing'}.`);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
if ((b.evaluationFramework || a.evaluationFramework) && b.evaluationFramework !== a.evaluationFramework) {
|
|
274
|
+
push(warnings, 'evaluation_framework_mismatch', `统计框架不同: ${b.evaluationFramework ?? 'legacy'} → ${a.evaluationFramework ?? 'legacy'}。`, `Evaluation framework changed: ${b.evaluationFramework ?? 'legacy'} → ${a.evaluationFramework ?? 'legacy'}.`);
|
|
275
|
+
}
|
|
276
|
+
if (stableStringify(b.skillIsolation ?? null) !== stableStringify(a.skillIsolation ?? null)) {
|
|
277
|
+
push(warnings, 'skill_isolation_mismatch', 'skill isolation 快照不同;baseline 污染边界不一致,严格比较需谨慎。', 'Skill-isolation snapshots differ; baseline contamination boundaries are not equivalent.');
|
|
278
|
+
}
|
|
279
|
+
const sampleHashDiff = countSampleHashMismatches(before, after);
|
|
280
|
+
if (!sampleHashDiff) {
|
|
281
|
+
push(warnings, 'sample_hashes_missing', '至少一份报告缺少用例指纹;无法确认两次 run 使用同一组用例。', 'At least one report is missing sample fingerprints; test-case parity cannot be verified.');
|
|
282
|
+
}
|
|
283
|
+
else if (sampleHashDiff.mismatched > 0 || sampleHashDiff.missing > 0) {
|
|
284
|
+
push(warnings, 'sample_hashes_mismatch', `用例指纹不一致: ${sampleHashDiff.mismatched} 条内容变化,${sampleHashDiff.missing} 条只出现在其中一份报告。`, `Sample fingerprints differ: ${sampleHashDiff.mismatched} content mismatches, ${sampleHashDiff.missing} samples only appear in one report.`);
|
|
285
|
+
}
|
|
286
|
+
return warnings;
|
|
287
|
+
}
|
|
288
|
+
export function formatComparabilityWarnings(warnings, lang = 'zh') {
|
|
289
|
+
if (warnings.length === 0)
|
|
290
|
+
return '';
|
|
291
|
+
const header = lang === 'zh'
|
|
292
|
+
? '可比性提示: 以下差异会影响 strict comparison / construct validity'
|
|
293
|
+
: 'Comparability warnings: the following differences affect strict comparison / construct validity';
|
|
294
|
+
return [header, ...warnings.map((warning) => ` - ${lang === 'zh' ? warning.zh : warning.en}`)].join('\n');
|
|
295
|
+
}
|
|
296
|
+
//# sourceMappingURL=comparability.js.map
|