knodin 0.7.4 → 0.7.6

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 (104) hide show
  1. package/README.md +4 -1
  2. package/dist/bin/cli.js +108 -1859
  3. package/dist/bin/launcher.js +3 -10
  4. package/dist/chunks/chunk-DMQAGX77.js +654 -0
  5. package/dist/chunks/chunk-F4Z3Z766.js +4 -0
  6. package/dist/chunks/chunk-SIJAQVSX.js +3 -0
  7. package/dist/chunks/chunk-X6M4HUUE.js +2 -0
  8. package/dist/chunks/chunk-YPRMY2LP.js +8 -0
  9. package/dist/chunks/pure-compression-cli-4TA2TQD5.js +5 -0
  10. package/dist/chunks/server-7EDF4CBY.js +14 -0
  11. package/dist/chunks/structural-fast-path-KD5KQSPX.js +4 -0
  12. package/dist/src/init-progress-worker.js +2 -106
  13. package/dist/src/repair-progress-worker.js +2 -120
  14. package/docs/INSTALLATION.md +17 -1
  15. package/docs/releases/0.7.6.md +25 -0
  16. package/package.json +1 -110
  17. package/dist/src/agent-integration.js +0 -238
  18. package/dist/src/artifact-refresh.js +0 -81
  19. package/dist/src/cli-args.js +0 -274
  20. package/dist/src/cli-model.js +0 -358
  21. package/dist/src/codeflow-replay.js +0 -80
  22. package/dist/src/compact-structural.js +0 -96
  23. package/dist/src/competitive-cold-mcp.js +0 -40
  24. package/dist/src/competitive-constraints.js +0 -20
  25. package/dist/src/competitive-manifest.js +0 -411
  26. package/dist/src/competitive-measurement.js +0 -183
  27. package/dist/src/competitive-runner.js +0 -487
  28. package/dist/src/competitive-sandbox.js +0 -108
  29. package/dist/src/context-export.js +0 -422
  30. package/dist/src/context.js +0 -102
  31. package/dist/src/diagnostics.js +0 -449
  32. package/dist/src/docs-sections.js +0 -141
  33. package/dist/src/doctor.js +0 -380
  34. package/dist/src/engine/ann-hnsw.js +0 -271
  35. package/dist/src/engine/embeddings.js +0 -193
  36. package/dist/src/engine/file-walker.js +0 -43
  37. package/dist/src/engine/git-history.js +0 -289
  38. package/dist/src/engine/index.js +0 -13377
  39. package/dist/src/engine/perf.js +0 -115
  40. package/dist/src/engine/prune.js +0 -112
  41. package/dist/src/engine/scip-import.js +0 -408
  42. package/dist/src/engine/source-policy.js +0 -69
  43. package/dist/src/engine/sqlite.js +0 -71
  44. package/dist/src/engine/symbol-delete.js +0 -58
  45. package/dist/src/execution-profile.js +0 -203
  46. package/dist/src/failure-diagnosis.js +0 -649
  47. package/dist/src/fleet.js +0 -7
  48. package/dist/src/git-executable.js +0 -31
  49. package/dist/src/graph-query-health.js +0 -115
  50. package/dist/src/hook-manager-integration.js +0 -156
  51. package/dist/src/index-activity.js +0 -125
  52. package/dist/src/init-progress.js +0 -155
  53. package/dist/src/init.js +0 -1254
  54. package/dist/src/lifecycle-health.js +0 -250
  55. package/dist/src/lsp-readonly.js +0 -217
  56. package/dist/src/output-compression.js +0 -629
  57. package/dist/src/output-telemetry.js +0 -363
  58. package/dist/src/pr-triage.js +0 -638
  59. package/dist/src/progressive-evidence.js +0 -473
  60. package/dist/src/pure-compression-cli.js +0 -101
  61. package/dist/src/relationship-adapters.js +0 -370
  62. package/dist/src/release-attestation.js +0 -533
  63. package/dist/src/release-preflight.js +0 -510
  64. package/dist/src/repair-progress.js +0 -262
  65. package/dist/src/repository-init-process.js +0 -173
  66. package/dist/src/repository-management.js +0 -1231
  67. package/dist/src/response-budget.js +0 -194
  68. package/dist/src/server.js +0 -73
  69. package/dist/src/structural-fast-path.js +0 -338
  70. package/dist/src/structural-snapshot.js +0 -33
  71. package/dist/src/system-config.js +0 -615
  72. package/dist/src/terminal-help.js +0 -83
  73. package/dist/src/tools/knodin-tools.js +0 -1529
  74. package/dist/src/update-ceremony.js +0 -158
  75. package/dist/src/update-policy.js +0 -944
  76. package/dist/src/update-trust.js +0 -503
  77. package/dist/src/version.js +0 -13
  78. package/dist/src/visualization.js +0 -162
  79. package/dist/src/wait-for-fresh.js +0 -98
  80. package/dist/src/worktree-lifecycle.js +0 -231
  81. package/docs/CONTAINED-EXECUTION.md +0 -77
  82. package/docs/DIAGNOSTICS.md +0 -45
  83. package/docs/GIT-HISTORY-REVIEW.md +0 -39
  84. package/docs/PROGRESSIVE-EVIDENCE.md +0 -37
  85. package/docs/PT-ACCESS-RECOMMENDATION.md +0 -91
  86. package/docs/RELEASE-0.3-EVIDENCE.md +0 -73
  87. package/docs/SCIP-IMPORT.md +0 -57
  88. package/docs/SIGNED-UPDATES.md +0 -151
  89. package/docs/TELEMETRY.md +0 -46
  90. package/docs/assets/knodin-favicon.svg +0 -4
  91. package/docs/releases/0.3.0.md +0 -46
  92. package/docs/releases/0.4.0.md +0 -68
  93. package/docs/releases/0.4.1.md +0 -28
  94. package/docs/releases/0.4.2.md +0 -27
  95. package/docs/releases/0.4.3.md +0 -23
  96. package/docs/releases/0.5.0.md +0 -29
  97. package/docs/releases/0.5.1.md +0 -17
  98. package/docs/releases/0.6.0.md +0 -18
  99. package/docs/releases/0.7.0.md +0 -24
  100. package/docs/releases/0.7.1.md +0 -21
  101. package/docs/releases/0.7.2.md +0 -21
  102. package/docs/releases/0.7.3.md +0 -23
  103. package/docs/releases/0.7.4.md +0 -17
  104. package/schemas/release-attestation-v1.schema.json +0 -210
@@ -1,183 +0,0 @@
1
- import childProcess from "node:child_process";
2
- import fs from "node:fs";
3
- /**
4
- * Twenty warm observations are the minimum that gives nearest-rank p95 a
5
- * distinct tail observation instead of aliasing a three-sample median.
6
- */
7
- export const MIN_WARM_SAMPLES = 20;
8
- export const DEFAULT_WARMUP_COUNT = 3;
9
- export const DEFAULT_COLD_SAMPLES = 5;
10
- export const DEFAULT_CALL_TIMEOUT_MS = 30_000;
11
- function percentile(sorted, fraction) {
12
- const index = Math.max(0, Math.ceil(fraction * sorted.length) - 1);
13
- return sorted[index];
14
- }
15
- export function summarizeLatencySamples(mode, samplesMs, warmupCount) {
16
- if (!Number.isInteger(warmupCount) || warmupCount < 0)
17
- throw new Error("Warm-up count must be a non-negative integer");
18
- if (samplesMs.length === 0)
19
- throw new Error("At least one latency sample is required");
20
- if (mode === "warm" && samplesMs.length < MIN_WARM_SAMPLES) {
21
- throw new Error(`Warm measurements require at least ${MIN_WARM_SAMPLES} samples for a meaningful p95`);
22
- }
23
- if (samplesMs.some((sample) => !Number.isFinite(sample) || sample < 0))
24
- throw new Error("Latency samples must be finite non-negative numbers");
25
- const raw = [...samplesMs];
26
- const sorted = [...raw].sort((left, right) => left - right);
27
- const p50Ms = percentile(sorted, 0.5);
28
- return {
29
- mode,
30
- warmupCount,
31
- sampleCount: raw.length,
32
- samplesMs: raw,
33
- p50Ms,
34
- p95Ms: percentile(sorted, 0.95),
35
- minMs: sorted[0],
36
- maxMs: sorted.at(-1),
37
- medianMs: p50Ms,
38
- };
39
- }
40
- export async function sampleOperation(operation, options) {
41
- const mode = options.mode ?? "warm";
42
- const warmupCount = mode === "warm" ? (options.warmupCount ?? DEFAULT_WARMUP_COUNT) : 0;
43
- const sampleCount = options.sampleCount ?? (mode === "warm" ? MIN_WARM_SAMPLES : DEFAULT_COLD_SAMPLES);
44
- let value;
45
- const call = async (kind, index) => {
46
- const timeoutMs = options.timeoutMs ?? DEFAULT_CALL_TIMEOUT_MS;
47
- let timer;
48
- try {
49
- return await Promise.race([
50
- operation(),
51
- new Promise((_, reject) => {
52
- timer = setTimeout(async () => {
53
- try {
54
- if (options.onTimeout)
55
- await options.onTimeout(kind, index);
56
- else
57
- reapDescendants(process.pid);
58
- }
59
- catch {
60
- // Cleanup failure must not replace or strand the timeout result.
61
- }
62
- finally {
63
- reject(new Error(`Competitive ${kind} call ${index + 1} timed out after ${timeoutMs}ms`));
64
- }
65
- }, timeoutMs);
66
- timer.unref?.();
67
- }),
68
- ]);
69
- }
70
- finally {
71
- if (timer)
72
- clearTimeout(timer);
73
- }
74
- };
75
- for (let index = 0; index < warmupCount; index++) {
76
- await options.beforeSample?.("warmup", index);
77
- value = await call("warmup", index);
78
- }
79
- const samplesMs = [];
80
- for (let index = 0; index < sampleCount; index++) {
81
- await options.beforeSample?.("sample", index);
82
- const startedAt = performance.now();
83
- value = await call("sample", index);
84
- samplesMs.push(Number((performance.now() - startedAt).toFixed(3)));
85
- }
86
- return {
87
- value: value,
88
- measurement: summarizeLatencySamples(mode, samplesMs, warmupCount),
89
- };
90
- }
91
- export function reapDescendants(rootPid) {
92
- const output = childProcess.spawnSync("ps", ["-axo", "pid=,ppid="]).stdout.toString();
93
- const processes = output
94
- .split("\n")
95
- .map((line) => line.trim().split(/\s+/).map(Number))
96
- .filter(([pid, parentPid]) => Number.isInteger(pid) && Number.isInteger(parentPid));
97
- const descendants = new Set([rootPid]);
98
- for (let changed = true; changed;) {
99
- changed = false;
100
- for (const [pid, parentPid] of processes) {
101
- if (!descendants.has(pid) && descendants.has(parentPid)) {
102
- descendants.add(pid);
103
- changed = true;
104
- }
105
- }
106
- }
107
- descendants.delete(rootPid);
108
- for (const signal of ["-TERM", "-KILL"]) {
109
- for (const pid of [...descendants].reverse()) {
110
- childProcess.spawnSync("/bin/kill", [signal, String(pid)]);
111
- }
112
- }
113
- }
114
- export function createCheckpointingResults(outputPath) {
115
- const results = [];
116
- results.push = (...items) => {
117
- const length = Array.prototype.push.apply(results, items);
118
- const temporary = `${outputPath}.partial-${process.pid}`;
119
- fs.writeFileSync(temporary, `${JSON.stringify({
120
- schemaVersion: 2,
121
- status: "partial",
122
- terminal: false,
123
- partial: true,
124
- generatedAt: new Date().toISOString(),
125
- results,
126
- }, null, 2)}\n`);
127
- fs.renameSync(temporary, outputPath);
128
- return length;
129
- };
130
- return results;
131
- }
132
- function emptyOwner() {
133
- return { rssBytes: 0, pids: [] };
134
- }
135
- export function attributeProcessTree(processes, rootPid, patterns, phase, rootOwner = "harness") {
136
- const descendants = new Set([rootPid]);
137
- for (let changed = true; changed;) {
138
- changed = false;
139
- for (const process of processes) {
140
- if (!descendants.has(process.pid) && descendants.has(process.parentPid)) {
141
- descendants.add(process.pid);
142
- changed = true;
143
- }
144
- }
145
- }
146
- const result = {
147
- phase,
148
- harness: emptyOwner(),
149
- knodin: emptyOwner(),
150
- competitor: emptyOwner(),
151
- unknown: emptyOwner(),
152
- };
153
- for (const process of processes) {
154
- if (!descendants.has(process.pid))
155
- continue;
156
- const owner = process.pid === rootPid
157
- ? rootOwner
158
- : patterns.knodin.some((pattern) => pattern.test(process.command))
159
- ? "knodin"
160
- : patterns.competitor.some((pattern) => pattern.test(process.command))
161
- ? "competitor"
162
- : "unknown";
163
- result[owner].rssBytes += process.rssBytes;
164
- result[owner].pids.push(process.pid);
165
- }
166
- return result;
167
- }
168
- export function mergeResourcePeaks(left, right) {
169
- const merged = {
170
- phase: right.phase,
171
- harness: emptyOwner(),
172
- knodin: emptyOwner(),
173
- competitor: emptyOwner(),
174
- unknown: emptyOwner(),
175
- };
176
- for (const owner of ["harness", "knodin", "competitor", "unknown"]) {
177
- merged[owner] =
178
- right[owner].rssBytes >= left[owner].rssBytes
179
- ? { ...right[owner], pids: [...right[owner].pids] }
180
- : { ...left[owner], pids: [...left[owner].pids] };
181
- }
182
- return merged;
183
- }
@@ -1,487 +0,0 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
- import { COMPETITIVE_MANIFEST, } from "./competitive-manifest.js";
4
- const RESERVED_CASE_KEYS = new Set([
5
- "id",
6
- "axis",
7
- "parameters",
8
- "note",
9
- "blocked",
10
- "mutation",
11
- "knodin",
12
- "oracle",
13
- "caseContract",
14
- "partial",
15
- ]);
16
- function responseOf(side) {
17
- if (!side || side.ok !== true)
18
- return null;
19
- const response = side.response;
20
- return typeof response === "string" && response.length > 0 ? response : null;
21
- }
22
- function normalizedWords(value) {
23
- return new Set(value
24
- .toLowerCase()
25
- .split(/[^a-z0-9_.-]+/)
26
- .filter((word) => word.length >= 4));
27
- }
28
- function runCheckedInOracle(oracleId, test, competitor) {
29
- const contenderResponse = responseOf(findCompetitorSide(test, competitor));
30
- const knodinResponse = responseOf(test.knodin);
31
- if (!contenderResponse || !knodinResponse) {
32
- return {
33
- id: oracleId,
34
- status: "unavailable",
35
- reason: "Both successful response payloads are required for the checked-in oracle.",
36
- };
37
- }
38
- const bothContain = (pattern) => pattern.test(contenderResponse) && pattern.test(knodinResponse);
39
- let passed = false;
40
- switch (oracleId) {
41
- case "contains-create-engine":
42
- passed = bothContain(/create[\s_-]*engine/i);
43
- break;
44
- case "create-engine-definition":
45
- passed = bothContain(/create[\s_-]*engine/i) && bothContain(/src\/engine\/index\.ts/i);
46
- break;
47
- case "create-engine-callers":
48
- passed = bothContain(/create[\s_-]*engine/i) && bothContain(/\bmain\b/i);
49
- break;
50
- case "create-engine-callees":
51
- passed = bothContain(/create[\s_-]*engine/i) && bothContain(/withStaleness/i);
52
- break;
53
- case "create-engine-both":
54
- passed =
55
- bothContain(/create[\s_-]*engine/i) &&
56
- bothContain(/\bmain\b/i) &&
57
- bothContain(/withStaleness/i);
58
- break;
59
- case "freshness-search":
60
- passed = bothContain(/index/i) && bothContain(/fresh(?:ness)?/i);
61
- break;
62
- case "engine-package":
63
- passed = bothContain(/engine/i) && bothContain(/(?:package|module|community)/i);
64
- break;
65
- case "indexed-status": {
66
- const indexed = (response) => /(?:total_files|indexedFiles)"?\s*:\s*[1-9]\d*/i.test(response);
67
- passed = indexed(contenderResponse) && indexed(knodinResponse);
68
- break;
69
- }
70
- case "contains-index":
71
- passed = bothContain(/index|fresh|status/i);
72
- break;
73
- case "pack-two-files":
74
- passed =
75
- bothContain(/src\/context\.ts/i) &&
76
- bothContain(/src\/cli-args\.ts/i) &&
77
- !/(?:^|\/)[^/\n]*\.spec\.ts/i.test(contenderResponse) &&
78
- !/(?:^|\/)[^/\n]*\.spec\.ts/i.test(knodinResponse);
79
- break;
80
- case "packed-read": {
81
- const contenderWords = normalizedWords(contenderResponse);
82
- const knodinWords = normalizedWords(knodinResponse);
83
- const shared = [...contenderWords].filter((word) => knodinWords.has(word));
84
- passed = shared.length >= 2;
85
- break;
86
- }
87
- case "grep-create-engine":
88
- passed = bothContain(/createEngine/i);
89
- break;
90
- case "architecture-facet": {
91
- const facet = String(test.id ?? "")
92
- .replace(/^architecture-/, "")
93
- .replace("_", " ");
94
- passed = facet.length > 0 && bothContain(new RegExp(facet.replace(" ", "[ _-]?"), "i"));
95
- break;
96
- }
97
- }
98
- return {
99
- id: oracleId,
100
- status: passed ? "passed" : "failed",
101
- ...(passed ? {} : { reason: `Checked-in ${oracleId} oracle did not match both responses.` }),
102
- };
103
- }
104
- export function applyCompetitiveCaseContracts(competitor, raw, fixtureRevision) {
105
- const source = raw && typeof raw === "object"
106
- ? raw
107
- : {};
108
- const results = Array.isArray(source.results) ? source.results : [];
109
- const mappings = COMPETITIVE_MANIFEST.find((entry) => entry.id === competitor)?.caseMappings ?? [];
110
- return {
111
- ...source,
112
- results: results.map((test) => {
113
- const caseId = String(test.id ?? "");
114
- const mapping = mappings.find((candidate) => new RegExp(candidate.idPattern).test(caseId));
115
- if (!mapping) {
116
- return {
117
- ...test,
118
- caseContract: {
119
- useCase: String(test.axis ?? test.id ?? "unlabeled competitive case"),
120
- fixtureRevision,
121
- mode: "warm",
122
- scope: {
123
- competitorOperation: "unmatched",
124
- knodinOperation: "unmatched",
125
- dimensions: [],
126
- },
127
- budgets: { bytes: null, tokens: null, items: null },
128
- disposition: {
129
- status: "incomparable",
130
- reason: "No checked-in equivalent-operation mapping exists for this case ID; latency is excluded.",
131
- },
132
- oracle: {
133
- status: "unavailable",
134
- reason: "Correctness is not evaluated for an incomparable case.",
135
- },
136
- },
137
- };
138
- }
139
- const dimensions = [...mapping.scope.dimensions];
140
- const direction = caseId.match(/^trace-(inbound|outbound|both)-/)?.[1];
141
- if (direction)
142
- dimensions.push(`direction=${direction}`);
143
- const facet = caseId.match(/^architecture-([a-z_]+)/)?.[1];
144
- if (facet)
145
- dimensions.push(`facet=${facet}`);
146
- return {
147
- ...test,
148
- caseContract: {
149
- useCase: mapping.useCase,
150
- fixtureRevision,
151
- mode: caseId.endsWith("-cold") ? "cold" : mapping.mode,
152
- scope: { ...mapping.scope, dimensions },
153
- budgets: mapping.budgets,
154
- disposition: { status: "comparable", reason: mapping.reason },
155
- oracle: runCheckedInOracle(mapping.oracleId, test, competitor),
156
- },
157
- };
158
- }),
159
- };
160
- }
161
- export function selectCompetitors(manifest, explicitIds, roadmapIds) {
162
- const known = new Set(manifest.map((entry) => entry.id));
163
- for (const id of explicitIds) {
164
- if (!known.has(id))
165
- throw new Error(`Unknown competitor: ${id}`);
166
- }
167
- const requestedRoadmap = new Set(roadmapIds.map((id) => id.toUpperCase()));
168
- const knownRoadmap = new Set(manifest.flatMap((entry) => [...entry.roadmap]).map((id) => id.toUpperCase()));
169
- for (const id of requestedRoadmap) {
170
- if (!knownRoadmap.has(id))
171
- throw new Error(`Unknown roadmap item: ${id}`);
172
- }
173
- const selected = new Set(explicitIds);
174
- for (const entry of manifest) {
175
- if (entry.roadmap.some((id) => requestedRoadmap.has(id.toUpperCase())))
176
- selected.add(entry.id);
177
- }
178
- if (selected.size === 0 && explicitIds.length === 0 && roadmapIds.length === 0) {
179
- for (const entry of manifest)
180
- selected.add(entry.id);
181
- }
182
- return manifest.filter((entry) => selected.has(entry.id)).map((entry) => entry.id);
183
- }
184
- export function createRunDirectory(root, timestamp, commit) {
185
- const stamp = timestamp.replace(/[-:.]/g, "");
186
- const directory = path.join(root, `${stamp}-${commit.slice(0, 12)}`);
187
- try {
188
- fs.mkdirSync(directory, { recursive: false });
189
- }
190
- catch (error) {
191
- if (error.code === "EEXIST") {
192
- throw new Error(`Competitive run directory already exists: ${directory}`);
193
- }
194
- throw error;
195
- }
196
- return directory;
197
- }
198
- function findCompetitorSide(test, competitor) {
199
- const candidates = [
200
- competitor,
201
- competitor.replaceAll("-", ""),
202
- competitor.replaceAll("-", "_"),
203
- "competitor",
204
- "gitnexus",
205
- "graphify",
206
- "serena",
207
- "grepai",
208
- "repomix",
209
- "aider",
210
- "code2prompt",
211
- "claudeContext",
212
- "codegraph",
213
- ];
214
- for (const key of candidates) {
215
- const value = test[key];
216
- if (value && typeof value === "object" && ("ok" in value || "medianMs" in value)) {
217
- return value;
218
- }
219
- }
220
- for (const [key, value] of Object.entries(test)) {
221
- if (RESERVED_CASE_KEYS.has(key) || !value || typeof value !== "object")
222
- continue;
223
- if ("ok" in value || "medianMs" in value)
224
- return value;
225
- }
226
- return undefined;
227
- }
228
- export function normalizeRawResult(competitor, raw) {
229
- const rawRecord = raw && typeof raw === "object"
230
- ? raw
231
- : {};
232
- const results = Array.isArray(rawRecord.results)
233
- ? (rawRecord.results ?? [])
234
- : [];
235
- const partialRun = rawRecord.partial === true || rawRecord.status === "partial";
236
- let paired = 0;
237
- let knodinFaster = 0;
238
- let competitorFaster = 0;
239
- let competitorErrors = 0;
240
- let knodinErrors = 0;
241
- let blocked = 0;
242
- const ratios = [];
243
- const precisions = [];
244
- const recalls = [];
245
- let oracleCases = 0;
246
- let oraclePassed = 0;
247
- let oracleFailed = 0;
248
- let oracleUnavailable = 0;
249
- let incomparable = 0;
250
- let exactMatches = 0;
251
- let exactMismatches = 0;
252
- let falsePositives = 0;
253
- let hasExactMatch = false;
254
- let hasFalsePositives = false;
255
- let knodinResponseBytes = 0;
256
- let competitorResponseBytes = 0;
257
- let hasKnodinResponseBytes = false;
258
- let hasCompetitorResponseBytes = false;
259
- const estimatedTokens = [];
260
- const peakRssMb = [];
261
- const knodinPeakRssMb = [];
262
- const competitorPeakRssMb = [];
263
- const diskBytes = [];
264
- const modeRatios = {
265
- cold: { p50: [], p95: [] },
266
- warm: { p50: [], p95: [] },
267
- };
268
- const attributedResources = Array.isArray(rawRecord.resources)
269
- ? rawRecord.resources
270
- : [];
271
- for (const resource of attributedResources) {
272
- for (const [owner, target] of [
273
- ["knodin", knodinPeakRssMb],
274
- ["competitor", competitorPeakRssMb],
275
- ]) {
276
- const measurement = resource[owner];
277
- if (measurement &&
278
- typeof measurement === "object" &&
279
- "rssBytes" in measurement &&
280
- typeof measurement.rssBytes === "number" &&
281
- measurement.rssBytes > 0) {
282
- const megabytes = measurement.rssBytes / 1024 / 1024;
283
- target.push(megabytes);
284
- peakRssMb.push(megabytes);
285
- }
286
- }
287
- }
288
- for (const test of results) {
289
- if (test.blocked)
290
- blocked++;
291
- const contender = findCompetitorSide(test, competitor);
292
- const knodin = test.knodin;
293
- if (contender && contender.ok === false)
294
- competitorErrors++;
295
- if (knodin && knodin.ok === false)
296
- knodinErrors++;
297
- const oracle = test.oracle;
298
- const contract = test.caseContract;
299
- if (contract?.disposition.status === "incomparable")
300
- incomparable++;
301
- if (!test.blocked) {
302
- if (contract?.disposition.status === "comparable") {
303
- oracleCases++;
304
- if (contract.oracle.status === "passed")
305
- oraclePassed++;
306
- else if (contract.oracle.status === "failed")
307
- oracleFailed++;
308
- else
309
- oracleUnavailable++;
310
- }
311
- else if (!contract) {
312
- oracleUnavailable++;
313
- }
314
- }
315
- if (oracle && typeof oracle === "object") {
316
- if (contract?.disposition.status !== "comparable")
317
- oracleCases++;
318
- if (typeof oracle.exactMatch === "boolean") {
319
- hasExactMatch = true;
320
- if (oracle.exactMatch)
321
- exactMatches++;
322
- else
323
- exactMismatches++;
324
- }
325
- if (typeof oracle.precision === "number")
326
- precisions.push(oracle.precision);
327
- if (typeof oracle.recall === "number")
328
- recalls.push(oracle.recall);
329
- if (typeof oracle.falsePositives === "number") {
330
- hasFalsePositives = true;
331
- falsePositives += oracle.falsePositives;
332
- }
333
- }
334
- if (typeof knodin?.responseBytes === "number") {
335
- hasKnodinResponseBytes = true;
336
- knodinResponseBytes += knodin.responseBytes;
337
- }
338
- if (typeof contender?.responseBytes === "number") {
339
- hasCompetitorResponseBytes = true;
340
- competitorResponseBytes += contender.responseBytes;
341
- }
342
- for (const value of [knodin?.estimatedTokens, contender?.estimatedTokens])
343
- if (typeof value === "number")
344
- estimatedTokens.push(value);
345
- if (typeof knodin?.peakRssMb === "number") {
346
- peakRssMb.push(knodin.peakRssMb);
347
- knodinPeakRssMb.push(knodin.peakRssMb);
348
- }
349
- if (typeof contender?.peakRssMb === "number") {
350
- peakRssMb.push(contender.peakRssMb);
351
- competitorPeakRssMb.push(contender.peakRssMb);
352
- }
353
- for (const value of [knodin?.diskBytes, contender?.diskBytes])
354
- if (typeof value === "number")
355
- diskBytes.push(value);
356
- if (!partialRun &&
357
- test.partial !== true &&
358
- contract?.disposition.status === "comparable" &&
359
- contract.oracle.status === "passed" &&
360
- contender?.ok === true &&
361
- knodin?.ok === true &&
362
- typeof contender.medianMs === "number" &&
363
- typeof knodin.medianMs === "number" &&
364
- knodin.medianMs > 0) {
365
- const mode = contract.mode;
366
- const contenderP50 = contender.p50Ms ?? contender.medianMs;
367
- const knodinP50 = knodin.p50Ms ?? knodin.medianMs;
368
- paired++;
369
- if (contender.medianMs < knodin.medianMs)
370
- competitorFaster++;
371
- else if (knodin.medianMs < contender.medianMs)
372
- knodinFaster++;
373
- ratios.push(contender.medianMs / knodin.medianMs);
374
- if (knodinP50 > 0)
375
- modeRatios[mode].p50.push(contenderP50 / knodinP50);
376
- if (typeof contender.p95Ms === "number" &&
377
- typeof knodin.p95Ms === "number" &&
378
- knodin.p95Ms > 0) {
379
- modeRatios[mode].p95.push(contender.p95Ms / knodin.p95Ms);
380
- }
381
- }
382
- }
383
- ratios.sort((a, b) => a - b);
384
- const median = (values) => {
385
- const sorted = [...values].sort((left, right) => left - right);
386
- return sorted.length ? sorted[Math.floor(sorted.length / 2)] : null;
387
- };
388
- return {
389
- competitor,
390
- cases: results.length,
391
- paired,
392
- knodinFaster,
393
- competitorFaster,
394
- competitorErrors,
395
- knodinErrors,
396
- blocked,
397
- medianRatio: ratios.length ? ratios[Math.floor(ratios.length / 2)] : null,
398
- correctness: {
399
- oracleCases,
400
- oraclePassed,
401
- oracleFailed,
402
- oracleUnavailable,
403
- incomparable,
404
- exactMatches: hasExactMatch ? exactMatches : null,
405
- exactMismatches: hasExactMatch ? exactMismatches : null,
406
- precision: precisions.length
407
- ? precisions.reduce((sum, value) => sum + value, 0) / precisions.length
408
- : null,
409
- recall: recalls.length
410
- ? recalls.reduce((sum, value) => sum + value, 0) / recalls.length
411
- : null,
412
- falsePositives: hasFalsePositives ? falsePositives : null,
413
- },
414
- performance: {
415
- latencySamples: ratios.length,
416
- modes: Object.fromEntries(["cold", "warm"]
417
- .filter((mode) => modeRatios[mode].p50.length > 0)
418
- .map((mode) => [
419
- mode,
420
- {
421
- paired: modeRatios[mode].p50.length,
422
- p50Ratio: median(modeRatios[mode].p50),
423
- p95Ratio: median(modeRatios[mode].p95),
424
- },
425
- ])),
426
- knodinResponseBytes: hasKnodinResponseBytes ? knodinResponseBytes : null,
427
- competitorResponseBytes: hasCompetitorResponseBytes ? competitorResponseBytes : null,
428
- estimatedTokens: estimatedTokens.length
429
- ? estimatedTokens.reduce((sum, value) => sum + value, 0)
430
- : null,
431
- knodinPeakRssMb: knodinPeakRssMb.length ? Math.max(...knodinPeakRssMb) : null,
432
- competitorPeakRssMb: competitorPeakRssMb.length ? Math.max(...competitorPeakRssMb) : null,
433
- peakRssMb: peakRssMb.length ? Math.max(...peakRssMb) : null,
434
- diskBytes: diskBytes.length ? Math.max(...diskBytes) : null,
435
- },
436
- };
437
- }
438
- export function compareSummaries(baseline, current, latencyTolerance = 0.1) {
439
- if (!Number.isFinite(latencyTolerance) || latencyTolerance < 0) {
440
- throw new Error("Latency tolerance must be a non-negative number");
441
- }
442
- const reasons = [];
443
- if (current.paired < baseline.paired)
444
- reasons.push(`Paired cases fell by ${baseline.paired - current.paired}`);
445
- if (current.knodinFaster < baseline.knodinFaster) {
446
- reasons.push(`knodin wins fell by ${baseline.knodinFaster - current.knodinFaster}`);
447
- }
448
- if (current.knodinErrors > baseline.knodinErrors) {
449
- reasons.push(`knodin errors increased by ${current.knodinErrors - baseline.knodinErrors}`);
450
- }
451
- if (baseline.performance && current.performance) {
452
- for (const mode of ["cold", "warm"]) {
453
- const before = baseline.performance.modes?.[mode];
454
- const after = current.performance.modes?.[mode];
455
- if (!before || !after)
456
- continue;
457
- for (const percentile of ["p50Ratio", "p95Ratio"]) {
458
- const beforeValue = before[percentile];
459
- const afterValue = after[percentile];
460
- if (beforeValue !== null &&
461
- afterValue !== null &&
462
- afterValue < beforeValue * (1 - latencyTolerance)) {
463
- reasons.push(`${mode} ${percentile.replace("Ratio", "")} competitor/knodin ratio fell from ${beforeValue.toFixed(3)} to ${afterValue.toFixed(3)}`);
464
- }
465
- }
466
- }
467
- }
468
- else if (baseline.medianRatio !== null &&
469
- current.medianRatio !== null &&
470
- current.medianRatio < baseline.medianRatio * (1 - latencyTolerance)) {
471
- reasons.push(`Competitor/knodin median ratio fell from ${baseline.medianRatio.toFixed(3)} to ${current.medianRatio.toFixed(3)}`);
472
- }
473
- return {
474
- regressed: reasons.length > 0,
475
- reasons,
476
- deltas: {
477
- paired: current.paired - baseline.paired,
478
- knodinFaster: current.knodinFaster - baseline.knodinFaster,
479
- competitorFaster: current.competitorFaster - baseline.competitorFaster,
480
- competitorErrors: current.competitorErrors - baseline.competitorErrors,
481
- knodinErrors: current.knodinErrors - baseline.knodinErrors,
482
- medianRatio: baseline.medianRatio === null || current.medianRatio === null
483
- ? null
484
- : current.medianRatio - baseline.medianRatio,
485
- },
486
- };
487
- }