oh-my-knowledge 0.18.0 → 0.20.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.
Files changed (113) hide show
  1. package/README.md +596 -326
  2. package/README.zh.md +917 -0
  3. package/dist/src/analysis/failure-clusterer.d.ts +96 -0
  4. package/dist/src/analysis/failure-clusterer.d.ts.map +1 -0
  5. package/dist/src/analysis/failure-clusterer.js +298 -0
  6. package/dist/src/analysis/failure-clusterer.js.map +1 -0
  7. package/dist/src/analysis/sample-diagnostics.d.ts +78 -0
  8. package/dist/src/analysis/sample-diagnostics.d.ts.map +1 -0
  9. package/dist/src/analysis/sample-diagnostics.js +259 -0
  10. package/dist/src/analysis/sample-diagnostics.js.map +1 -0
  11. package/dist/src/analysis/saturation.d.ts +85 -0
  12. package/dist/src/analysis/saturation.d.ts.map +1 -0
  13. package/dist/src/analysis/saturation.js +174 -0
  14. package/dist/src/analysis/saturation.js.map +1 -0
  15. package/dist/src/cli.js +772 -18
  16. package/dist/src/cli.js.map +1 -1
  17. package/dist/src/eval-core/bootstrap.d.ts +72 -0
  18. package/dist/src/eval-core/bootstrap.d.ts.map +1 -0
  19. package/dist/src/eval-core/bootstrap.js +174 -0
  20. package/dist/src/eval-core/bootstrap.js.map +1 -0
  21. package/dist/src/eval-core/evaluation-execution.d.ts +15 -1
  22. package/dist/src/eval-core/evaluation-execution.d.ts.map +1 -1
  23. package/dist/src/eval-core/evaluation-execution.js +37 -3
  24. package/dist/src/eval-core/evaluation-execution.js.map +1 -1
  25. package/dist/src/eval-core/evaluation-job.d.ts +10 -2
  26. package/dist/src/eval-core/evaluation-job.d.ts.map +1 -1
  27. package/dist/src/eval-core/evaluation-job.js +9 -1
  28. package/dist/src/eval-core/evaluation-job.js.map +1 -1
  29. package/dist/src/eval-core/evaluation-reporting.d.ts.map +1 -1
  30. package/dist/src/eval-core/evaluation-reporting.js +90 -0
  31. package/dist/src/eval-core/evaluation-reporting.js.map +1 -1
  32. package/dist/src/eval-core/schema.d.ts.map +1 -1
  33. package/dist/src/eval-core/schema.js +69 -0
  34. package/dist/src/eval-core/schema.js.map +1 -1
  35. package/dist/src/eval-core/verdict.d.ts +74 -0
  36. package/dist/src/eval-core/verdict.d.ts.map +1 -0
  37. package/dist/src/eval-core/verdict.js +283 -0
  38. package/dist/src/eval-core/verdict.js.map +1 -0
  39. package/dist/src/eval-workflows/each-evaluation-workflow.d.ts +16 -3
  40. package/dist/src/eval-workflows/each-evaluation-workflow.d.ts.map +1 -1
  41. package/dist/src/eval-workflows/each-evaluation-workflow.js +10 -2
  42. package/dist/src/eval-workflows/each-evaluation-workflow.js.map +1 -1
  43. package/dist/src/eval-workflows/evaluation-pipeline.d.ts +17 -1
  44. package/dist/src/eval-workflows/evaluation-pipeline.d.ts.map +1 -1
  45. package/dist/src/eval-workflows/evaluation-pipeline.js +45 -3
  46. package/dist/src/eval-workflows/evaluation-pipeline.js.map +1 -1
  47. package/dist/src/eval-workflows/run-evaluation.d.ts +23 -2
  48. package/dist/src/eval-workflows/run-evaluation.d.ts.map +1 -1
  49. package/dist/src/eval-workflows/run-evaluation.js +104 -4
  50. package/dist/src/eval-workflows/run-evaluation.js.map +1 -1
  51. package/dist/src/grading/assertions.d.ts +16 -0
  52. package/dist/src/grading/assertions.d.ts.map +1 -1
  53. package/dist/src/grading/assertions.js +385 -111
  54. package/dist/src/grading/assertions.js.map +1 -1
  55. package/dist/src/grading/debias-validate.d.ts +84 -0
  56. package/dist/src/grading/debias-validate.d.ts.map +1 -0
  57. package/dist/src/grading/debias-validate.js +173 -0
  58. package/dist/src/grading/debias-validate.js.map +1 -0
  59. package/dist/src/grading/gold-cli.d.ts +88 -0
  60. package/dist/src/grading/gold-cli.d.ts.map +1 -0
  61. package/dist/src/grading/gold-cli.js +251 -0
  62. package/dist/src/grading/gold-cli.js.map +1 -0
  63. package/dist/src/grading/gold-dataset.d.ts +73 -0
  64. package/dist/src/grading/gold-dataset.d.ts.map +1 -0
  65. package/dist/src/grading/gold-dataset.js +161 -0
  66. package/dist/src/grading/gold-dataset.js.map +1 -0
  67. package/dist/src/grading/human-gold.d.ts +102 -0
  68. package/dist/src/grading/human-gold.d.ts.map +1 -0
  69. package/dist/src/grading/human-gold.js +188 -0
  70. package/dist/src/grading/human-gold.js.map +1 -0
  71. package/dist/src/grading/index.d.ts +27 -2
  72. package/dist/src/grading/index.d.ts.map +1 -1
  73. package/dist/src/grading/index.js +36 -18
  74. package/dist/src/grading/index.js.map +1 -1
  75. package/dist/src/grading/judge.d.ts +65 -2
  76. package/dist/src/grading/judge.d.ts.map +1 -1
  77. package/dist/src/grading/judge.js +280 -23
  78. package/dist/src/grading/judge.js.map +1 -1
  79. package/dist/src/inputs/eval-config.js +19 -0
  80. package/dist/src/inputs/eval-config.js.map +1 -1
  81. package/dist/src/observability/{production-analyzer.d.ts → skill-health-analyzer.d.ts} +24 -2
  82. package/dist/src/observability/skill-health-analyzer.d.ts.map +1 -0
  83. package/dist/src/observability/{production-analyzer.js → skill-health-analyzer.js} +61 -6
  84. package/dist/src/observability/skill-health-analyzer.js.map +1 -0
  85. package/dist/src/observability/trace-adapter.d.ts.map +1 -1
  86. package/dist/src/observability/trace-adapter.js +27 -1
  87. package/dist/src/observability/trace-adapter.js.map +1 -1
  88. package/dist/src/renderer/html-renderer.d.ts.map +1 -1
  89. package/dist/src/renderer/html-renderer.js +40 -6
  90. package/dist/src/renderer/html-renderer.js.map +1 -1
  91. package/dist/src/renderer/layout.d.ts.map +1 -1
  92. package/dist/src/renderer/layout.js +138 -4
  93. package/dist/src/renderer/layout.js.map +1 -1
  94. package/dist/src/renderer/skill-health-renderer.d.ts +2 -2
  95. package/dist/src/renderer/skill-health-renderer.d.ts.map +1 -1
  96. package/dist/src/renderer/skill-health-renderer.js +39 -4
  97. package/dist/src/renderer/skill-health-renderer.js.map +1 -1
  98. package/dist/src/renderer/summary.d.ts +28 -1
  99. package/dist/src/renderer/summary.d.ts.map +1 -1
  100. package/dist/src/renderer/summary.js +322 -8
  101. package/dist/src/renderer/summary.js.map +1 -1
  102. package/dist/src/renderer/table.d.ts.map +1 -1
  103. package/dist/src/renderer/table.js +63 -2
  104. package/dist/src/renderer/table.js.map +1 -1
  105. package/dist/src/server/report-server.d.ts +2 -1
  106. package/dist/src/server/report-server.d.ts.map +1 -1
  107. package/dist/src/server/report-server.js +397 -2
  108. package/dist/src/server/report-server.js.map +1 -1
  109. package/dist/src/types.d.ts +247 -0
  110. package/dist/src/types.d.ts.map +1 -1
  111. package/package.json +24 -6
  112. package/dist/src/observability/production-analyzer.d.ts.map +0 -1
  113. package/dist/src/observability/production-analyzer.js.map +0 -1
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Failure clustering — turn a list of "what failed" into "why it failed".
3
+ *
4
+ * Why this exists
5
+ * ---------------
6
+ * Senior-engineer pain point: 50 samples ran, 14 failed. Reading 14 failure
7
+ * cases one by one is tedious; the user wants the tool to say "8 of these
8
+ * are tool-call errors, 4 are refusals, 2 are formatting errors", with a
9
+ * suggested fix per cluster.
10
+ *
11
+ * This module:
12
+ *
13
+ * 1. Selects failed samples from a report (compositeScore < threshold OR
14
+ * ok=false on at least one variant).
15
+ * 2. Builds a compact failure description per sample (failed assertion
16
+ * types + judge reason snippet + tool failure pattern).
17
+ * 3. Sends the full set to a single LLM call asking it to (a) propose
18
+ * N cluster labels, (b) assign each failure to a cluster, (c) suggest
19
+ * a fix per cluster.
20
+ * 4. Returns the structured cluster report. Pure function — no I/O,
21
+ * accepts the executor as an argument.
22
+ *
23
+ * Design choice: single-call LLM clustering rather than embedding-based
24
+ * k-means. Reasons:
25
+ *
26
+ * - Failure descriptions are short (~50-200 tokens each), so 14 of them
27
+ * fit comfortably in one prompt. A 50-failure batch is also fine.
28
+ * - The LLM can produce HUMAN-READABLE cluster labels ("tool call error",
29
+ * "premature refusal") rather than numeric centroids — exactly what
30
+ * the user needs to act on.
31
+ * - One call = one judge cost = predictable. Embedding + clustering
32
+ * needs N+1 calls minimum.
33
+ * - Acceptable inaccuracy: clusters are advisory, not load-bearing. We
34
+ * document this clearly and surface raw failures alongside.
35
+ *
36
+ * The clustering prompt is structured to be JSON-only output for parsing
37
+ * stability. We do NOT include length-debias instructions — clustering is
38
+ * a categorization task, not a quality scoring task, so the length-debias
39
+ * directive is inapplicable.
40
+ */
41
+ import type { ExecutorFn, Report } from '../types.js';
42
+ export interface FailureClusterRequest {
43
+ report: Report;
44
+ /** Executor for the clustering LLM call. */
45
+ executor: ExecutorFn;
46
+ /** Model id to use for clustering. */
47
+ judgeModel: string;
48
+ /** Max number of clusters. Default 5; the LLM may produce fewer. */
49
+ maxClusters?: number;
50
+ /** compositeScore < this OR ok=false counts as failure. Default 3. */
51
+ failureThreshold?: number;
52
+ /** Cap on how many failures to feed the LLM (selects worst by score). Default 50. */
53
+ maxFailuresFed?: number;
54
+ }
55
+ export interface FailureCase {
56
+ sample_id: string;
57
+ variant: string;
58
+ /** Composite score (0 if errored). */
59
+ score: number;
60
+ /** Whether the executor itself errored (ok=false). */
61
+ errored: boolean;
62
+ /** Description summarizing what went wrong, fed to the LLM. */
63
+ description: string;
64
+ }
65
+ export interface FailureCluster {
66
+ /** Human-readable cluster label, in the report's primary language. */
67
+ label: string;
68
+ /** Free-text root cause analysis from the LLM. */
69
+ rootCause: string;
70
+ /** Suggested fix the user can apply. */
71
+ suggestedFix: string;
72
+ /** Sample IDs (with variant) belonging to this cluster. */
73
+ members: Array<{
74
+ sample_id: string;
75
+ variant: string;
76
+ }>;
77
+ }
78
+ export interface FailureClusterReport {
79
+ /** All failures considered (after threshold + cap). */
80
+ failures: FailureCase[];
81
+ /** Clusters returned by the LLM, sorted by member count desc. */
82
+ clusters: FailureCluster[];
83
+ /** Failures the LLM didn't put in any cluster (label = "other"). */
84
+ unclassified: Array<{
85
+ sample_id: string;
86
+ variant: string;
87
+ }>;
88
+ /** USD cost of the clustering call. */
89
+ clusterCostUSD: number;
90
+ /** Truncation flag — set when more failures existed than maxFailuresFed. */
91
+ truncated: boolean;
92
+ totalFailures: number;
93
+ }
94
+ export declare function clusterFailures(req: FailureClusterRequest): Promise<FailureClusterReport>;
95
+ export declare function formatFailureClusterReport(r: FailureClusterReport): string;
96
+ //# sourceMappingURL=failure-clusterer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"failure-clusterer.d.ts","sourceRoot":"","sources":["../../../src/analysis/failure-clusterer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,EAA8B,MAAM,aAAa,CAAC;AAElF,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,4CAA4C;IAC5C,QAAQ,EAAE,UAAU,CAAC;IACrB,sCAAsC;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,oEAAoE;IACpE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sEAAsE;IACtE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,qFAAqF;IACrF,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,OAAO,EAAE,OAAO,CAAC;IACjB,+DAA+D;IAC/D,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,SAAS,EAAE,MAAM,CAAC;IAClB,wCAAwC;IACxC,YAAY,EAAE,MAAM,CAAC;IACrB,2DAA2D;IAC3D,OAAO,EAAE,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACxD;AAED,MAAM,WAAW,oBAAoB;IACnC,uDAAuD;IACvD,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,iEAAiE;IACjE,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,oEAAoE;IACpE,YAAY,EAAE,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5D,uCAAuC;IACvC,cAAc,EAAE,MAAM,CAAC;IACvB,4EAA4E;IAC5E,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;CACvB;AAeD,wBAAsB,eAAe,CAAC,GAAG,EAAE,qBAAqB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAkF/F;AAwID,wBAAgB,0BAA0B,CAAC,CAAC,EAAE,oBAAoB,GAAG,MAAM,CAsC1E"}
@@ -0,0 +1,298 @@
1
+ /**
2
+ * Failure clustering — turn a list of "what failed" into "why it failed".
3
+ *
4
+ * Why this exists
5
+ * ---------------
6
+ * Senior-engineer pain point: 50 samples ran, 14 failed. Reading 14 failure
7
+ * cases one by one is tedious; the user wants the tool to say "8 of these
8
+ * are tool-call errors, 4 are refusals, 2 are formatting errors", with a
9
+ * suggested fix per cluster.
10
+ *
11
+ * This module:
12
+ *
13
+ * 1. Selects failed samples from a report (compositeScore < threshold OR
14
+ * ok=false on at least one variant).
15
+ * 2. Builds a compact failure description per sample (failed assertion
16
+ * types + judge reason snippet + tool failure pattern).
17
+ * 3. Sends the full set to a single LLM call asking it to (a) propose
18
+ * N cluster labels, (b) assign each failure to a cluster, (c) suggest
19
+ * a fix per cluster.
20
+ * 4. Returns the structured cluster report. Pure function — no I/O,
21
+ * accepts the executor as an argument.
22
+ *
23
+ * Design choice: single-call LLM clustering rather than embedding-based
24
+ * k-means. Reasons:
25
+ *
26
+ * - Failure descriptions are short (~50-200 tokens each), so 14 of them
27
+ * fit comfortably in one prompt. A 50-failure batch is also fine.
28
+ * - The LLM can produce HUMAN-READABLE cluster labels ("tool call error",
29
+ * "premature refusal") rather than numeric centroids — exactly what
30
+ * the user needs to act on.
31
+ * - One call = one judge cost = predictable. Embedding + clustering
32
+ * needs N+1 calls minimum.
33
+ * - Acceptable inaccuracy: clusters are advisory, not load-bearing. We
34
+ * document this clearly and surface raw failures alongside.
35
+ *
36
+ * The clustering prompt is structured to be JSON-only output for parsing
37
+ * stability. We do NOT include length-debias instructions — clustering is
38
+ * a categorization task, not a quality scoring task, so the length-debias
39
+ * directive is inapplicable.
40
+ */
41
+ const DEFAULT_MAX_CLUSTERS = 5;
42
+ const DEFAULT_FAILURE_THRESHOLD = 3;
43
+ const DEFAULT_MAX_FED = 50;
44
+ export async function clusterFailures(req) {
45
+ const { report, executor, judgeModel, maxClusters = DEFAULT_MAX_CLUSTERS, failureThreshold = DEFAULT_FAILURE_THRESHOLD, maxFailuresFed = DEFAULT_MAX_FED, } = req;
46
+ const allFailures = collectFailures(report, failureThreshold);
47
+ // Sort by severity (errored > low score) then take up to maxFailuresFed.
48
+ allFailures.sort((a, b) => {
49
+ if (a.errored !== b.errored)
50
+ return a.errored ? -1 : 1;
51
+ return a.score - b.score;
52
+ });
53
+ const truncated = allFailures.length > maxFailuresFed;
54
+ const fed = allFailures.slice(0, maxFailuresFed);
55
+ if (fed.length === 0) {
56
+ return {
57
+ failures: [], clusters: [], unclassified: [],
58
+ clusterCostUSD: 0, truncated: false, totalFailures: 0,
59
+ };
60
+ }
61
+ // Need at least 2 failures for clustering to be meaningful — with 1 we
62
+ // simply emit a single trivial cluster without calling the LLM.
63
+ if (fed.length === 1) {
64
+ return {
65
+ failures: fed,
66
+ clusters: [{
67
+ label: '单条失败',
68
+ rootCause: '只有 1 条失败,无法聚类',
69
+ suggestedFix: '直接看下面的失败描述',
70
+ members: [{ sample_id: fed[0].sample_id, variant: fed[0].variant }],
71
+ }],
72
+ unclassified: [],
73
+ clusterCostUSD: 0,
74
+ truncated: false,
75
+ totalFailures: 1,
76
+ };
77
+ }
78
+ const prompt = buildClusterPrompt(fed, maxClusters);
79
+ const result = await executor({
80
+ model: judgeModel,
81
+ system: '你是 LLM 评测失败案例分类员。逐条分类,只返回 JSON。',
82
+ prompt,
83
+ });
84
+ if (!result.ok) {
85
+ return {
86
+ failures: fed,
87
+ clusters: [],
88
+ unclassified: fed.map((f) => ({ sample_id: f.sample_id, variant: f.variant })),
89
+ clusterCostUSD: result.costUSD || 0,
90
+ truncated,
91
+ totalFailures: allFailures.length,
92
+ };
93
+ }
94
+ const parsed = parseClusterResponse(result.output ?? '');
95
+ const clusters = normalizeClusters(parsed, fed);
96
+ // Anything fed but not assigned to a cluster lands in unclassified.
97
+ const claimed = new Set();
98
+ for (const c of clusters) {
99
+ for (const m of c.members)
100
+ claimed.add(`${m.sample_id}::${m.variant}`);
101
+ }
102
+ const unclassified = fed
103
+ .filter((f) => !claimed.has(`${f.sample_id}::${f.variant}`))
104
+ .map((f) => ({ sample_id: f.sample_id, variant: f.variant }));
105
+ clusters.sort((a, b) => b.members.length - a.members.length);
106
+ return {
107
+ failures: fed,
108
+ clusters,
109
+ unclassified,
110
+ clusterCostUSD: result.costUSD || 0,
111
+ truncated,
112
+ totalFailures: allFailures.length,
113
+ };
114
+ }
115
+ function collectFailures(report, threshold) {
116
+ const variants = report.meta?.variants ?? [];
117
+ const out = [];
118
+ for (const entry of report.results ?? []) {
119
+ for (const v of variants) {
120
+ const r = entry.variants?.[v];
121
+ if (!r)
122
+ continue;
123
+ const score = typeof r.compositeScore === 'number' ? r.compositeScore : 0;
124
+ const errored = r.ok === false;
125
+ if (!errored && score >= threshold)
126
+ continue;
127
+ out.push({
128
+ sample_id: entry.sample_id,
129
+ variant: v,
130
+ score,
131
+ errored,
132
+ description: describeFailure(entry, v, r),
133
+ });
134
+ }
135
+ }
136
+ return out;
137
+ }
138
+ /**
139
+ * Build a compact textual description of one failure for the clustering LLM.
140
+ * Keeps it under ~250 tokens so 50 failures fit in a single ~12k token prompt.
141
+ */
142
+ function describeFailure(entry, variant, r) {
143
+ const parts = [];
144
+ if (r.ok === false) {
145
+ parts.push(`EXEC_ERROR: ${truncate(r.error ?? 'unknown error', 120)}`);
146
+ }
147
+ if (typeof r.compositeScore === 'number' && r.compositeScore < 3) {
148
+ parts.push(`compositeScore=${r.compositeScore.toFixed(2)}`);
149
+ }
150
+ // Failed assertions — top 5 to keep size bounded.
151
+ const failed = (r.assertions?.details ?? [])
152
+ .filter((d) => !d.passed)
153
+ .slice(0, 5)
154
+ .map((d) => `${d.type}(${truncate(String(d.value), 40)})`);
155
+ if (failed.length > 0) {
156
+ parts.push(`failed_assertions=[${failed.join(', ')}]`);
157
+ }
158
+ // Judge reasoning snippet.
159
+ if (r.llmReason)
160
+ parts.push(`judge_reason="${truncate(r.llmReason, 200)}"`);
161
+ // Tool failures (agent traces).
162
+ const toolFailures = (r.toolCalls ?? []).filter((tc) => !tc.success).slice(0, 3);
163
+ if (toolFailures.length > 0) {
164
+ parts.push(`failed_tools=[${toolFailures.map((tc) => `${tc.tool}: ${truncate(String(tc.output ?? ''), 80)}`).join('; ')}]`);
165
+ }
166
+ // Output preview if nothing else informative.
167
+ if (parts.length === 0 && r.outputPreview) {
168
+ parts.push(`output_preview="${truncate(r.outputPreview, 200)}"`);
169
+ }
170
+ return `[${entry.sample_id}@${variant}] ${parts.join(' | ')}`;
171
+ }
172
+ function truncate(s, n) {
173
+ if (s.length <= n)
174
+ return s;
175
+ return s.slice(0, n - 1) + '…';
176
+ }
177
+ function buildClusterPrompt(failures, maxClusters) {
178
+ const lines = [];
179
+ lines.push(`分析下面 ${failures.length} 条 LLM 评测失败案例,把它们分到 ≤ ${maxClusters} 个 cluster。`);
180
+ lines.push('');
181
+ lines.push('## 失败案例 (每行一条):');
182
+ for (const f of failures) {
183
+ lines.push(f.description);
184
+ }
185
+ lines.push('');
186
+ lines.push('## 任务');
187
+ lines.push(`1. 根据失败模式提出最多 ${maxClusters} 个 cluster (例如:工具调用错误 / 拒答 / 格式错 / 事实错 / 其它)`);
188
+ lines.push('2. 把每条失败分到一个 cluster (用 sample_id@variant 引用)');
189
+ lines.push('3. 每个 cluster 给出根因 + 一条具体修复建议');
190
+ lines.push('');
191
+ lines.push('## 返回格式 (严格 JSON,不要 markdown 代码块):');
192
+ lines.push('{');
193
+ lines.push(' "clusters": [');
194
+ lines.push(' {');
195
+ lines.push(' "label": "<简短中文标签>",');
196
+ lines.push(' "rootCause": "<根因说明,1-2 句>",');
197
+ lines.push(' "suggestedFix": "<修复建议,1-2 句,要可立即操作>",');
198
+ lines.push(' "members": [{"sample_id": "...", "variant": "..."}, ...]');
199
+ lines.push(' }');
200
+ lines.push(' ]');
201
+ lines.push('}');
202
+ return lines.join('\n');
203
+ }
204
+ function parseClusterResponse(text) {
205
+ const trimmed = text.trim();
206
+ // Tolerate accidental ```json fences from the model.
207
+ const cleaned = trimmed.replace(/^```(?:json)?\s*/i, '').replace(/\s*```$/i, '');
208
+ const match = cleaned.match(/\{[\s\S]*\}/);
209
+ if (!match)
210
+ return { clusters: [] };
211
+ try {
212
+ return JSON.parse(match[0]);
213
+ }
214
+ catch {
215
+ return { clusters: [] };
216
+ }
217
+ }
218
+ function normalizeClusters(parsed, fed) {
219
+ const validIds = new Set(fed.map((f) => `${f.sample_id}::${f.variant}`));
220
+ const clusters = parsed.clusters ?? [];
221
+ const out = [];
222
+ for (const c of clusters) {
223
+ const label = typeof c.label === 'string' && c.label ? c.label : '未命名';
224
+ const rootCause = typeof c.rootCause === 'string' ? c.rootCause : '';
225
+ const suggestedFix = typeof c.suggestedFix === 'string' ? c.suggestedFix : '';
226
+ const memberArr = Array.isArray(c.members) ? c.members : [];
227
+ const members = [];
228
+ for (const m of memberArr) {
229
+ let sample_id;
230
+ let variant;
231
+ if (typeof m === 'string') {
232
+ // Tolerate "sample_id@variant" string form.
233
+ const parts = m.split('@');
234
+ if (parts.length === 2) {
235
+ sample_id = parts[0];
236
+ variant = parts[1];
237
+ }
238
+ }
239
+ else if (m && typeof m === 'object') {
240
+ sample_id = typeof m.sample_id === 'string' ? m.sample_id : undefined;
241
+ variant = typeof m.variant === 'string' ? m.variant : undefined;
242
+ }
243
+ if (!sample_id || !variant)
244
+ continue;
245
+ if (!validIds.has(`${sample_id}::${variant}`))
246
+ continue; // hallucinated member
247
+ members.push({ sample_id, variant });
248
+ }
249
+ if (members.length > 0) {
250
+ out.push({ label, rootCause, suggestedFix, members });
251
+ }
252
+ }
253
+ return out;
254
+ }
255
+ export function formatFailureClusterReport(r) {
256
+ const lines = [];
257
+ lines.push('');
258
+ lines.push(` 失败聚类 — ${r.totalFailures} 条失败${r.truncated ? ` (取最差 ${r.failures.length} 条做聚类)` : ''}`);
259
+ lines.push('');
260
+ if (r.clusters.length === 0) {
261
+ lines.push(' (聚类失败,以下是原始失败列表)');
262
+ for (const f of r.failures.slice(0, 10)) {
263
+ lines.push(` ${f.sample_id}@${f.variant}: ${truncate(f.description, 120)}`);
264
+ }
265
+ if (r.failures.length > 10)
266
+ lines.push(` ... 还有 ${r.failures.length - 10} 条`);
267
+ lines.push('');
268
+ return lines.join('\n');
269
+ }
270
+ for (const c of r.clusters) {
271
+ lines.push(` [${c.label}] ${c.members.length} 条`);
272
+ if (c.rootCause)
273
+ lines.push(` 根因: ${c.rootCause}`);
274
+ if (c.suggestedFix)
275
+ lines.push(` 建议: ${c.suggestedFix}`);
276
+ const display = c.members.slice(0, 5);
277
+ for (const m of display) {
278
+ lines.push(` - ${m.sample_id}@${m.variant}`);
279
+ }
280
+ if (c.members.length > display.length) {
281
+ lines.push(` ... 还有 ${c.members.length - display.length} 条`);
282
+ }
283
+ lines.push('');
284
+ }
285
+ if (r.unclassified.length > 0) {
286
+ lines.push(` [未分类] ${r.unclassified.length} 条`);
287
+ for (const m of r.unclassified.slice(0, 5)) {
288
+ lines.push(` - ${m.sample_id}@${m.variant}`);
289
+ }
290
+ if (r.unclassified.length > 5)
291
+ lines.push(` ... 还有 ${r.unclassified.length - 5} 条`);
292
+ lines.push('');
293
+ }
294
+ lines.push(` 聚类调用 cost: $${r.clusterCostUSD.toFixed(6)}`);
295
+ lines.push('');
296
+ return lines.join('\n');
297
+ }
298
+ //# sourceMappingURL=failure-clusterer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"failure-clusterer.js","sourceRoot":"","sources":["../../../src/analysis/failure-clusterer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AA+DH,MAAM,oBAAoB,GAAG,CAAC,CAAC;AAC/B,MAAM,yBAAyB,GAAG,CAAC,CAAC;AACpC,MAAM,eAAe,GAAG,EAAE,CAAC;AAE3B,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,GAA0B;IAC9D,MAAM,EACJ,MAAM,EAAE,QAAQ,EAAE,UAAU,EAC5B,WAAW,GAAG,oBAAoB,EAClC,gBAAgB,GAAG,yBAAyB,EAC5C,cAAc,GAAG,eAAe,GACjC,GAAG,GAAG,CAAC;IAER,MAAM,WAAW,GAAG,eAAe,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAC9D,yEAAyE;IACzE,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACxB,IAAI,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO;YAAE,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACvD,OAAO,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC;IAC3B,CAAC,CAAC,CAAC;IACH,MAAM,SAAS,GAAG,WAAW,CAAC,MAAM,GAAG,cAAc,CAAC;IACtD,MAAM,GAAG,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC;IAEjD,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrB,OAAO;YACL,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE;YAC5C,cAAc,EAAE,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC;SACtD,CAAC;IACJ,CAAC;IAED,uEAAuE;IACvE,gEAAgE;IAChE,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrB,OAAO;YACL,QAAQ,EAAE,GAAG;YACb,QAAQ,EAAE,CAAC;oBACT,KAAK,EAAE,MAAM;oBACb,SAAS,EAAE,eAAe;oBAC1B,YAAY,EAAE,YAAY;oBAC1B,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;iBACpE,CAAC;YACF,YAAY,EAAE,EAAE;YAChB,cAAc,EAAE,CAAC;YACjB,SAAS,EAAE,KAAK;YAChB,aAAa,EAAE,CAAC;SACjB,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,kBAAkB,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IACpD,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC;QAC5B,KAAK,EAAE,UAAU;QACjB,MAAM,EAAE,iCAAiC;QACzC,MAAM;KACP,CAAC,CAAC;IAEH,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QACf,OAAO;YACL,QAAQ,EAAE,GAAG;YACb,QAAQ,EAAE,EAAE;YACZ,YAAY,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;YAC9E,cAAc,EAAE,MAAM,CAAC,OAAO,IAAI,CAAC;YACnC,SAAS;YACT,aAAa,EAAE,WAAW,CAAC,MAAM;SAClC,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,oBAAoB,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;IACzD,MAAM,QAAQ,GAAG,iBAAiB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAEhD,oEAAoE;IACpE,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO;YAAE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,SAAS,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;IACzE,CAAC;IACD,MAAM,YAAY,GAAG,GAAG;SACrB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,SAAS,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;SAC3D,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAEhE,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAE7D,OAAO;QACL,QAAQ,EAAE,GAAG;QACb,QAAQ;QACR,YAAY;QACZ,cAAc,EAAE,MAAM,CAAC,OAAO,IAAI,CAAC;QACnC,SAAS;QACT,aAAa,EAAE,WAAW,CAAC,MAAM;KAClC,CAAC;AACJ,CAAC;AAED,SAAS,eAAe,CAAC,MAAc,EAAE,SAAiB;IACxD,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,EAAE,QAAQ,IAAI,EAAE,CAAC;IAC7C,MAAM,GAAG,GAAkB,EAAE,CAAC;IAC9B,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;QACzC,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;YACzB,MAAM,CAAC,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC;YAC9B,IAAI,CAAC,CAAC;gBAAE,SAAS;YACjB,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,cAAc,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;YAC1E,MAAM,OAAO,GAAG,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC;YAC/B,IAAI,CAAC,OAAO,IAAI,KAAK,IAAI,SAAS;gBAAE,SAAS;YAC7C,GAAG,CAAC,IAAI,CAAC;gBACP,SAAS,EAAE,KAAK,CAAC,SAAS;gBAC1B,OAAO,EAAE,CAAC;gBACV,KAAK;gBACL,OAAO;gBACP,WAAW,EAAE,eAAe,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;aAC1C,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;GAGG;AACH,SAAS,eAAe,CAAC,KAAkB,EAAE,OAAe,EAAE,CAAgB;IAC5E,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,CAAC,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;QACnB,KAAK,CAAC,IAAI,CAAC,eAAe,QAAQ,CAAC,CAAC,CAAC,KAAK,IAAI,eAAe,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IACzE,CAAC;IACD,IAAI,OAAO,CAAC,CAAC,cAAc,KAAK,QAAQ,IAAI,CAAC,CAAC,cAAc,GAAG,CAAC,EAAE,CAAC;QACjE,KAAK,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC9D,CAAC;IACD,kDAAkD;IAClD,MAAM,MAAM,GAAG,CAAC,CAAC,CAAC,UAAU,EAAE,OAAO,IAAI,EAAE,CAAC;SACzC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;SACxB,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;SACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC;IAC7D,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,KAAK,CAAC,IAAI,CAAC,sBAAsB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACzD,CAAC;IACD,2BAA2B;IAC3B,IAAI,CAAC,CAAC,SAAS;QAAE,KAAK,CAAC,IAAI,CAAC,iBAAiB,QAAQ,CAAC,CAAC,CAAC,SAAS,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;IAC5E,gCAAgC;IAChC,MAAM,YAAY,GAAG,CAAC,CAAC,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACjF,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,KAAK,CAAC,IAAI,CAAC,iBAAiB,YAAY,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,EAAE,CAAC,IAAI,KAAK,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC9H,CAAC;IACD,8CAA8C;IAC9C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC,aAAa,EAAE,CAAC;QAC1C,KAAK,CAAC,IAAI,CAAC,mBAAmB,QAAQ,CAAC,CAAC,CAAC,aAAa,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;IACnE,CAAC;IACD,OAAO,IAAI,KAAK,CAAC,SAAS,IAAI,OAAO,KAAK,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;AAChE,CAAC;AAED,SAAS,QAAQ,CAAC,CAAS,EAAE,CAAS;IACpC,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC;IAC5B,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC;AACjC,CAAC;AAED,SAAS,kBAAkB,CAAC,QAAuB,EAAE,WAAmB;IACtE,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,CAAC,IAAI,CAAC,QAAQ,QAAQ,CAAC,MAAM,yBAAyB,WAAW,aAAa,CAAC,CAAC;IACrF,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;IAC9B,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;IAC5B,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACpB,KAAK,CAAC,IAAI,CAAC,iBAAiB,WAAW,8CAA8C,CAAC,CAAC;IACvF,KAAK,CAAC,IAAI,CAAC,+CAA+C,CAAC,CAAC;IAC5D,KAAK,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;IAC5C,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;IACjD,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChB,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;IAC9B,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACpB,KAAK,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;IACzC,KAAK,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;IACjD,KAAK,CAAC,IAAI,CAAC,8CAA8C,CAAC,CAAC;IAC3D,KAAK,CAAC,IAAI,CAAC,gEAAgE,CAAC,CAAC;IAC7E,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACpB,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClB,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,SAAS,oBAAoB,CAAC,IAAY;IACxC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAC5B,qDAAqD;IACrD,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,mBAAmB,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IACjF,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IAC3C,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IACpC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAyB,CAAC;IACtD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IAC1B,CAAC;AACH,CAAC;AAED,SAAS,iBAAiB,CAAC,MAA4B,EAAE,GAAkB;IACzE,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,SAAS,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACzE,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC;IACvC,MAAM,GAAG,GAAqB,EAAE,CAAC;IACjC,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC;QACvE,MAAM,SAAS,GAAG,OAAO,CAAC,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;QACrE,MAAM,YAAY,GAAG,OAAO,CAAC,CAAC,YAAY,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9E,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5D,MAAM,OAAO,GAA8B,EAAE,CAAC;QAC9C,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;YAC1B,IAAI,SAA6B,CAAC;YAClC,IAAI,OAA2B,CAAC;YAChC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;gBAC1B,4CAA4C;gBAC5C,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBAC3B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBAAC,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;oBAAC,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;gBAAC,CAAC;YACvE,CAAC;iBAAM,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;gBACtC,SAAS,GAAG,OAAO,CAAC,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;gBACtE,OAAO,GAAG,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;YAClE,CAAC;YACD,IAAI,CAAC,SAAS,IAAI,CAAC,OAAO;gBAAE,SAAS;YACrC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,SAAS,KAAK,OAAO,EAAE,CAAC;gBAAE,SAAS,CAAC,sBAAsB;YAC/E,OAAO,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC;QACvC,CAAC;QACD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC,CAAC;QACxD,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,CAAuB;IAChE,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,aAAa,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,MAAM,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACtG,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,KAAK,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;QACjC,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;YACxC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;QACjF,CAAC;QACD,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,GAAG,EAAE;YAAE,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,QAAQ,CAAC,MAAM,GAAG,EAAE,IAAI,CAAC,CAAC;QACjF,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC3B,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC;QACnD,IAAI,CAAC,CAAC,SAAS;YAAE,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC;QACtD,IAAI,CAAC,CAAC,YAAY;YAAE,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC;QAC5D,MAAM,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACtC,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACxB,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;QACpD,CAAC;QACD,IAAI,CAAC,CAAC,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;YACtC,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC;QACpE,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IACD,IAAI,CAAC,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9B,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,YAAY,CAAC,MAAM,IAAI,CAAC,CAAC;QACjD,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;YAC3C,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;QACpD,CAAC;QACD,IAAI,CAAC,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC;YAAE,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,CAAC;QACzF,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC3D,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Per-sample quality diagnostics.
3
+ *
4
+ * Why this exists
5
+ * ---------------
6
+ * `report-diagnostics.ts` answers "what's wrong with the report overall" —
7
+ * aggregate-level insights about assertions and variant comparisons. This
8
+ * module answers a complementary question: "which specific samples in my
9
+ * eval-samples set are problematic and should be fixed before I trust any
10
+ * conclusion drawn on this report?"
11
+ *
12
+ * Senior-engineer pain point: 100 eval-samples typically have 20-40 that
13
+ * (a) all variants get the same score on (no discriminative power), or
14
+ * (b) are near-duplicates of another sample (data redundancy inflating N), or
15
+ * (c) have ambiguous rubric (judge gives wildly different scores on repeats), or
16
+ * (d) are cost / latency outliers (one sample spikes the budget by itself), or
17
+ * (e) fail across the board (likely broken sample, not a model deficiency).
18
+ *
19
+ * The CLI `omk bench diagnose <reportId>` surfaces these so users can iterate
20
+ * the SAMPLE SET as much as they iterate the SKILL.
21
+ *
22
+ * Output shape: a flat list of issues, each tagged with severity and the
23
+ * minimal evidence needed to act ("here are the matching prompts"). Issues are
24
+ * sorted by severity then by sample_id so the report is stable across runs.
25
+ */
26
+ import type { Report, Sample } from '../types.js';
27
+ export type SampleIssueKind = 'flat_scores' | 'all_pass' | 'all_fail' | 'near_duplicate' | 'ambiguous_rubric' | 'cost_outlier' | 'latency_outlier' | 'error_prone';
28
+ export interface SampleIssue {
29
+ sample_id: string;
30
+ severity: 'error' | 'warning' | 'info';
31
+ kind: SampleIssueKind;
32
+ message: string;
33
+ /** Minimal evidence to make the issue actionable. */
34
+ evidence: Record<string, unknown>;
35
+ }
36
+ export interface SampleDiagnosticReport {
37
+ /** All issues sorted by (severity desc, sample_id asc). */
38
+ issues: SampleIssue[];
39
+ /** 0-100 quality score for the sample set as a whole. Lower = more issues. */
40
+ healthScore: number;
41
+ /** Sample IDs grouped by issue kind for quick consumption. */
42
+ byKind: Partial<Record<SampleIssueKind, string[]>>;
43
+ /** Quick-glance counters. */
44
+ totals: {
45
+ samples: number;
46
+ flagged: number;
47
+ errors: number;
48
+ warnings: number;
49
+ infos: number;
50
+ };
51
+ }
52
+ export interface DiagnoseOptions {
53
+ /** Minimum ROUGE-1 between two prompts to flag near-duplicate. Default 0.7. */
54
+ duplicateRouge?: number;
55
+ /** Judge stddev threshold (across judgeRepeat samples) to flag ambiguous. Default 1.0. */
56
+ ambiguousStddev?: number;
57
+ /** Cost outlier multiplier vs median. Default 3 (≥ 3× median is flagged). */
58
+ costOutlierK?: number;
59
+ /** Latency outlier multiplier vs median. Default 3. */
60
+ latencyOutlierK?: number;
61
+ /** Score-spread threshold below which a sample is "flat". Default 0.5. */
62
+ flatThreshold?: number;
63
+ /**
64
+ * Original Sample objects, used for near-duplicate detection. When omitted
65
+ * (Report alone) the duplicate check is skipped — Report doesn't carry
66
+ * prompts, only score/cost/latency. The CLI handler reads samples via
67
+ * report.meta.request.samplesPath and passes them in.
68
+ */
69
+ samples?: Sample[];
70
+ }
71
+ export declare function diagnoseSamples(report: Report, options?: DiagnoseOptions): SampleDiagnosticReport;
72
+ /**
73
+ * Plain-text formatter for `omk bench diagnose <reportId>`.
74
+ */
75
+ export declare function formatSampleDiagnostics(diag: SampleDiagnosticReport, options?: {
76
+ topN?: number;
77
+ }): string;
78
+ //# sourceMappingURL=sample-diagnostics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sample-diagnostics.d.ts","sourceRoot":"","sources":["../../../src/analysis/sample-diagnostics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,EAAE,MAAM,aAAa,CAAC;AAG/D,MAAM,MAAM,eAAe,GACvB,aAAa,GACb,UAAU,GACV,UAAU,GACV,gBAAgB,GAChB,kBAAkB,GAClB,cAAc,GACd,iBAAiB,GACjB,aAAa,CAAC;AAElB,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;IACvC,IAAI,EAAE,eAAe,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,sBAAsB;IACrC,2DAA2D;IAC3D,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,8EAA8E;IAC9E,WAAW,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;IACnD,6BAA6B;IAC7B,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/F;AAED,MAAM,WAAW,eAAe;IAC9B,+EAA+E;IAC/E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,0FAA0F;IAC1F,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,6EAA6E;IAC7E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uDAAuD;IACvD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,0EAA0E;IAC1E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAYD,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,sBAAsB,CAmKrG;AAsBD;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,sBAAsB,EAAE,OAAO,GAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,MAAM,CAoD7G"}