agentic-qe 3.6.9 → 3.6.11

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 (117) hide show
  1. package/.claude/skills/.validation/schemas/skill-eval.schema.json +11 -1
  2. package/.claude/skills/pr-review/SKILL.md +2 -2
  3. package/.claude/skills/qcsd-production-swarm/SKILL.md +2781 -0
  4. package/.claude/skills/qcsd-production-swarm/evals/qcsd-production-swarm.yaml +246 -0
  5. package/.claude/skills/qcsd-production-swarm/schemas/output.json +505 -0
  6. package/.claude/skills/qcsd-production-swarm/scripts/validate-config.json +25 -0
  7. package/.claude/skills/skills-manifest.json +5 -5
  8. package/package.json +1 -1
  9. package/scripts/benchmark-hnsw-loading.ts +480 -0
  10. package/scripts/benchmark-kg-assisted.ts +725 -0
  11. package/scripts/collect-production-telemetry.sh +291 -0
  12. package/scripts/detect-skill-conflicts.ts +347 -0
  13. package/scripts/eval-driven-workflow.ts +704 -0
  14. package/scripts/run-skill-eval.ts +210 -10
  15. package/scripts/score-skill-quality.ts +511 -0
  16. package/v3/CHANGELOG.md +44 -0
  17. package/v3/assets/skills/pr-review/SKILL.md +2 -2
  18. package/v3/dist/cli/bundle.js +1526 -700
  19. package/v3/dist/cli/commands/code.d.ts.map +1 -1
  20. package/v3/dist/cli/commands/code.js +9 -85
  21. package/v3/dist/cli/commands/code.js.map +1 -1
  22. package/v3/dist/cli/commands/coverage.d.ts.map +1 -1
  23. package/v3/dist/cli/commands/coverage.js +3 -28
  24. package/v3/dist/cli/commands/coverage.js.map +1 -1
  25. package/v3/dist/cli/commands/hooks.d.ts.map +1 -1
  26. package/v3/dist/cli/commands/hooks.js +143 -2
  27. package/v3/dist/cli/commands/hooks.js.map +1 -1
  28. package/v3/dist/cli/commands/security.d.ts.map +1 -1
  29. package/v3/dist/cli/commands/security.js +3 -29
  30. package/v3/dist/cli/commands/security.js.map +1 -1
  31. package/v3/dist/cli/commands/test.d.ts.map +1 -1
  32. package/v3/dist/cli/commands/test.js +11 -58
  33. package/v3/dist/cli/commands/test.js.map +1 -1
  34. package/v3/dist/cli/utils/file-discovery.d.ts +27 -0
  35. package/v3/dist/cli/utils/file-discovery.d.ts.map +1 -0
  36. package/v3/dist/cli/utils/file-discovery.js +105 -0
  37. package/v3/dist/cli/utils/file-discovery.js.map +1 -0
  38. package/v3/dist/coordination/task-executor.d.ts.map +1 -1
  39. package/v3/dist/coordination/task-executor.js +304 -44
  40. package/v3/dist/coordination/task-executor.js.map +1 -1
  41. package/v3/dist/domains/code-intelligence/coordinator.d.ts.map +1 -1
  42. package/v3/dist/domains/code-intelligence/coordinator.js +8 -1
  43. package/v3/dist/domains/code-intelligence/coordinator.js.map +1 -1
  44. package/v3/dist/domains/code-intelligence/services/metric-collector/index.d.ts.map +1 -1
  45. package/v3/dist/domains/code-intelligence/services/metric-collector/index.js +10 -0
  46. package/v3/dist/domains/code-intelligence/services/metric-collector/index.js.map +1 -1
  47. package/v3/dist/domains/code-intelligence/services/metric-collector/interfaces.d.ts +7 -1
  48. package/v3/dist/domains/code-intelligence/services/metric-collector/interfaces.d.ts.map +1 -1
  49. package/v3/dist/domains/code-intelligence/services/metric-collector/interfaces.js +10 -1
  50. package/v3/dist/domains/code-intelligence/services/metric-collector/interfaces.js.map +1 -1
  51. package/v3/dist/domains/code-intelligence/services/metric-collector/loc-counter.js +34 -10
  52. package/v3/dist/domains/code-intelligence/services/metric-collector/loc-counter.js.map +1 -1
  53. package/v3/dist/domains/coverage-analysis/services/hnsw-index.d.ts +9 -0
  54. package/v3/dist/domains/coverage-analysis/services/hnsw-index.d.ts.map +1 -1
  55. package/v3/dist/domains/coverage-analysis/services/hnsw-index.js +38 -3
  56. package/v3/dist/domains/coverage-analysis/services/hnsw-index.js.map +1 -1
  57. package/v3/dist/domains/test-generation/generators/jest-vitest-generator.d.ts.map +1 -1
  58. package/v3/dist/domains/test-generation/generators/jest-vitest-generator.js +58 -6
  59. package/v3/dist/domains/test-generation/generators/jest-vitest-generator.js.map +1 -1
  60. package/v3/dist/domains/test-generation/generators/mocha-generator.d.ts.map +1 -1
  61. package/v3/dist/domains/test-generation/generators/mocha-generator.js +79 -7
  62. package/v3/dist/domains/test-generation/generators/mocha-generator.js.map +1 -1
  63. package/v3/dist/domains/test-generation/generators/pytest-generator.d.ts +4 -0
  64. package/v3/dist/domains/test-generation/generators/pytest-generator.d.ts.map +1 -1
  65. package/v3/dist/domains/test-generation/generators/pytest-generator.js +77 -10
  66. package/v3/dist/domains/test-generation/generators/pytest-generator.js.map +1 -1
  67. package/v3/dist/domains/test-generation/interfaces/test-generator.interface.d.ts +21 -0
  68. package/v3/dist/domains/test-generation/interfaces/test-generator.interface.d.ts.map +1 -1
  69. package/v3/dist/domains/test-generation/interfaces.d.ts +21 -0
  70. package/v3/dist/domains/test-generation/interfaces.d.ts.map +1 -1
  71. package/v3/dist/domains/test-generation/services/test-generator.d.ts +22 -0
  72. package/v3/dist/domains/test-generation/services/test-generator.d.ts.map +1 -1
  73. package/v3/dist/domains/test-generation/services/test-generator.js +163 -3
  74. package/v3/dist/domains/test-generation/services/test-generator.js.map +1 -1
  75. package/v3/dist/init/init-wizard-hooks.d.ts +8 -1
  76. package/v3/dist/init/init-wizard-hooks.d.ts.map +1 -1
  77. package/v3/dist/init/init-wizard-hooks.js +47 -39
  78. package/v3/dist/init/init-wizard-hooks.js.map +1 -1
  79. package/v3/dist/init/phases/07-hooks.d.ts +11 -1
  80. package/v3/dist/init/phases/07-hooks.d.ts.map +1 -1
  81. package/v3/dist/init/phases/07-hooks.js +46 -50
  82. package/v3/dist/init/phases/07-hooks.js.map +1 -1
  83. package/v3/dist/init/settings-merge.d.ts +35 -0
  84. package/v3/dist/init/settings-merge.d.ts.map +1 -0
  85. package/v3/dist/init/settings-merge.js +140 -0
  86. package/v3/dist/init/settings-merge.js.map +1 -0
  87. package/v3/dist/integrations/agentic-flow/model-router/router.js +1 -1
  88. package/v3/dist/integrations/agentic-flow/model-router/router.js.map +1 -1
  89. package/v3/dist/integrations/agentic-flow/model-router/score-calculator.d.ts.map +1 -1
  90. package/v3/dist/integrations/agentic-flow/model-router/score-calculator.js +18 -3
  91. package/v3/dist/integrations/agentic-flow/model-router/score-calculator.js.map +1 -1
  92. package/v3/dist/integrations/agentic-flow/model-router/signal-collector.d.ts +3 -3
  93. package/v3/dist/integrations/agentic-flow/model-router/signal-collector.d.ts.map +1 -1
  94. package/v3/dist/integrations/agentic-flow/model-router/signal-collector.js +18 -0
  95. package/v3/dist/integrations/agentic-flow/model-router/signal-collector.js.map +1 -1
  96. package/v3/dist/kernel/unified-memory-hnsw.d.ts +29 -0
  97. package/v3/dist/kernel/unified-memory-hnsw.d.ts.map +1 -1
  98. package/v3/dist/kernel/unified-memory-hnsw.js +136 -0
  99. package/v3/dist/kernel/unified-memory-hnsw.js.map +1 -1
  100. package/v3/dist/kernel/unified-memory.d.ts +2 -2
  101. package/v3/dist/kernel/unified-memory.d.ts.map +1 -1
  102. package/v3/dist/kernel/unified-memory.js +7 -9
  103. package/v3/dist/kernel/unified-memory.js.map +1 -1
  104. package/v3/dist/learning/qe-hooks.d.ts.map +1 -1
  105. package/v3/dist/learning/qe-hooks.js +34 -3
  106. package/v3/dist/learning/qe-hooks.js.map +1 -1
  107. package/v3/dist/mcp/bundle.js +1403 -425
  108. package/v3/dist/mcp/handlers/domain-handler-configs.d.ts.map +1 -1
  109. package/v3/dist/mcp/handlers/domain-handler-configs.js +40 -31
  110. package/v3/dist/mcp/handlers/domain-handler-configs.js.map +1 -1
  111. package/v3/dist/mcp/handlers/task-handlers.d.ts.map +1 -1
  112. package/v3/dist/mcp/handlers/task-handlers.js +68 -5
  113. package/v3/dist/mcp/handlers/task-handlers.js.map +1 -1
  114. package/v3/dist/mcp/protocol-server.d.ts.map +1 -1
  115. package/v3/dist/mcp/protocol-server.js +16 -2
  116. package/v3/dist/mcp/protocol-server.js.map +1 -1
  117. package/v3/package.json +1 -1
@@ -0,0 +1,480 @@
1
+ /**
2
+ * HNSW Loading Benchmark
3
+ *
4
+ * Compares vector index build performance across implementations:
5
+ * 1. Pure JS InMemoryHNSWIndex (current, efConstruction=200)
6
+ * 2. Pure JS InMemoryHNSWIndex with reduced efConstruction=50
7
+ * 3. @ruvector/gnn differentiableSearch (Rust/NAPI)
8
+ * 4. Optimized JS with Float32Array deserialization
9
+ *
10
+ * Same scenario for all: 5073 vectors, 768 dimensions, cosine similarity.
11
+ * Measures: build time, search time, memory usage, recall quality.
12
+ */
13
+
14
+ import { performance } from 'perf_hooks';
15
+
16
+ // ============================================================================
17
+ // Scenario Configuration (identical for all implementations)
18
+ // ============================================================================
19
+ const NUM_VECTORS = 5073;
20
+ const DIMENSIONS = 768;
21
+ const NUM_SEARCHES = 100;
22
+ const SEARCH_K = 5;
23
+
24
+ // ============================================================================
25
+ // Generate deterministic test data (same seed for all)
26
+ // ============================================================================
27
+
28
+ function seededRandom(seed: number): () => number {
29
+ let s = seed;
30
+ return () => {
31
+ s = (s * 1664525 + 1013904223) & 0xFFFFFFFF;
32
+ return (s >>> 0) / 0xFFFFFFFF;
33
+ };
34
+ }
35
+
36
+ function generateVectors(count: number, dim: number): { ids: string[]; embeddings: number[][] } {
37
+ const rand = seededRandom(42);
38
+ const ids: string[] = [];
39
+ const embeddings: number[][] = [];
40
+
41
+ for (let i = 0; i < count; i++) {
42
+ ids.push(`code-intelligence:kg:node:${i}`);
43
+ const vec: number[] = new Array(dim);
44
+ let norm = 0;
45
+ for (let d = 0; d < dim; d++) {
46
+ vec[d] = rand() * 2 - 1;
47
+ norm += vec[d] * vec[d];
48
+ }
49
+ // Normalize for cosine similarity
50
+ norm = Math.sqrt(norm);
51
+ for (let d = 0; d < dim; d++) vec[d] /= norm;
52
+ embeddings.push(vec);
53
+ }
54
+
55
+ return { ids, embeddings };
56
+ }
57
+
58
+ function generateQueries(count: number, dim: number): number[][] {
59
+ const rand = seededRandom(12345);
60
+ const queries: number[][] = [];
61
+ for (let i = 0; i < count; i++) {
62
+ const vec: number[] = new Array(dim);
63
+ let norm = 0;
64
+ for (let d = 0; d < dim; d++) {
65
+ vec[d] = rand() * 2 - 1;
66
+ norm += vec[d] * vec[d];
67
+ }
68
+ norm = Math.sqrt(norm);
69
+ for (let d = 0; d < dim; d++) vec[d] /= norm;
70
+ queries.push(vec);
71
+ }
72
+ return queries;
73
+ }
74
+
75
+ // Simulate SQLite buffer format (Float32LE)
76
+ function vectorToBuffer(vec: number[]): Buffer {
77
+ const buf = Buffer.alloc(vec.length * 4);
78
+ for (let i = 0; i < vec.length; i++) buf.writeFloatLE(vec[i], i * 4);
79
+ return buf;
80
+ }
81
+
82
+ // Current slow deserialization
83
+ function bufferToFloatArraySlow(buffer: Buffer, dimensions: number): number[] {
84
+ const arr: number[] = [];
85
+ for (let i = 0; i < dimensions; i++) arr.push(buffer.readFloatLE(i * 4));
86
+ return arr;
87
+ }
88
+
89
+ // Optimized deserialization via Float32Array view
90
+ function bufferToFloatArrayFast(buffer: Buffer, dimensions: number): number[] {
91
+ const f32 = new Float32Array(buffer.buffer, buffer.byteOffset, dimensions);
92
+ return Array.from(f32);
93
+ }
94
+
95
+ // ============================================================================
96
+ // Brute-force ground truth for recall measurement
97
+ // ============================================================================
98
+
99
+ function cosineSim(a: number[], b: number[]): number {
100
+ let dot = 0, na = 0, nb = 0;
101
+ for (let i = 0; i < a.length; i++) {
102
+ dot += a[i] * b[i];
103
+ na += a[i] * a[i];
104
+ nb += b[i] * b[i];
105
+ }
106
+ const denom = Math.sqrt(na) * Math.sqrt(nb);
107
+ return denom === 0 ? 0 : dot / denom;
108
+ }
109
+
110
+ function bruteForceTopK(query: number[], embeddings: number[][], ids: string[], k: number): string[] {
111
+ const scored = ids.map((id, i) => ({ id, score: cosineSim(query, embeddings[i]) }));
112
+ scored.sort((a, b) => b.score - a.score);
113
+ return scored.slice(0, k).map(s => s.id);
114
+ }
115
+
116
+ function recallAtK(predicted: string[], actual: string[]): number {
117
+ const actualSet = new Set(actual);
118
+ let hits = 0;
119
+ for (const p of predicted) {
120
+ if (actualSet.has(p)) hits++;
121
+ }
122
+ return hits / actual.length;
123
+ }
124
+
125
+ // ============================================================================
126
+ // Implementation 1: Pure JS HNSW (current)
127
+ // ============================================================================
128
+
129
+ async function benchmarkPureJS(
130
+ label: string,
131
+ efConstruction: number,
132
+ buffers: Buffer[],
133
+ ids: string[],
134
+ embeddings: number[][],
135
+ queries: number[][],
136
+ groundTruth: string[][]
137
+ ) {
138
+ // Import from compiled output
139
+ const { InMemoryHNSWIndex } = await import('../v3/dist/kernel/unified-memory-hnsw.js');
140
+
141
+ const index = new InMemoryHNSWIndex();
142
+ if (efConstruction !== 200) {
143
+ (index as any).efConstruction = efConstruction;
144
+ }
145
+
146
+ const memBefore = process.memoryUsage().heapUsed;
147
+
148
+ // Measure deserialization + build
149
+ const buildStart = performance.now();
150
+ for (let i = 0; i < buffers.length; i++) {
151
+ const embedding = bufferToFloatArraySlow(buffers[i], DIMENSIONS);
152
+ index.add(ids[i], embedding);
153
+ }
154
+ const buildMs = performance.now() - buildStart;
155
+
156
+ const memAfter = process.memoryUsage().heapUsed;
157
+
158
+ // Measure search
159
+ const searchStart = performance.now();
160
+ const searchResults: string[][] = [];
161
+ for (const q of queries) {
162
+ const results = index.search(q, SEARCH_K);
163
+ searchResults.push(results.map(r => r.id));
164
+ }
165
+ const searchMs = performance.now() - searchStart;
166
+
167
+ // Compute recall
168
+ let totalRecall = 0;
169
+ for (let i = 0; i < queries.length; i++) {
170
+ totalRecall += recallAtK(searchResults[i], groundTruth[i]);
171
+ }
172
+ const avgRecall = totalRecall / queries.length;
173
+
174
+ return {
175
+ label,
176
+ buildMs: Math.round(buildMs),
177
+ searchMs: Math.round(searchMs),
178
+ avgSearchMs: (searchMs / queries.length).toFixed(2),
179
+ memoryMB: Math.round((memAfter - memBefore) / 1024 / 1024),
180
+ recall: (avgRecall * 100).toFixed(1),
181
+ indexSize: index.size(),
182
+ };
183
+ }
184
+
185
+ // ============================================================================
186
+ // Implementation 2: Pure JS with Fast Deserialization
187
+ // ============================================================================
188
+
189
+ async function benchmarkPureJSFastDeser(
190
+ buffers: Buffer[],
191
+ ids: string[],
192
+ embeddings: number[][],
193
+ queries: number[][],
194
+ groundTruth: string[][]
195
+ ) {
196
+ const { InMemoryHNSWIndex } = await import('../v3/dist/kernel/unified-memory-hnsw.js');
197
+ const index = new InMemoryHNSWIndex();
198
+
199
+ const memBefore = process.memoryUsage().heapUsed;
200
+
201
+ const buildStart = performance.now();
202
+ for (let i = 0; i < buffers.length; i++) {
203
+ const embedding = bufferToFloatArrayFast(buffers[i], DIMENSIONS);
204
+ index.add(ids[i], embedding);
205
+ }
206
+ const buildMs = performance.now() - buildStart;
207
+
208
+ const memAfter = process.memoryUsage().heapUsed;
209
+
210
+ const searchStart = performance.now();
211
+ const searchResults: string[][] = [];
212
+ for (const q of queries) {
213
+ const results = index.search(q, SEARCH_K);
214
+ searchResults.push(results.map(r => r.id));
215
+ }
216
+ const searchMs = performance.now() - searchStart;
217
+
218
+ let totalRecall = 0;
219
+ for (let i = 0; i < queries.length; i++) {
220
+ totalRecall += recallAtK(searchResults[i], groundTruth[i]);
221
+ }
222
+
223
+ return {
224
+ label: 'Pure JS (efC=200) + Float32Array deser',
225
+ buildMs: Math.round(buildMs),
226
+ searchMs: Math.round(searchMs),
227
+ avgSearchMs: (searchMs / queries.length).toFixed(2),
228
+ memoryMB: Math.round((memAfter - memBefore) / 1024 / 1024),
229
+ recall: ((totalRecall / queries.length) * 100).toFixed(1),
230
+ indexSize: index.size(),
231
+ };
232
+ }
233
+
234
+ // ============================================================================
235
+ // Implementation 3: @ruvector/gnn (Rust/NAPI)
236
+ // ============================================================================
237
+
238
+ async function benchmarkRuvector(
239
+ buffers: Buffer[],
240
+ ids: string[],
241
+ embeddings: number[][],
242
+ queries: number[][],
243
+ groundTruth: string[][]
244
+ ) {
245
+ const { differentiableSearch, init } = await import('@ruvector/gnn');
246
+ init();
247
+
248
+ const memBefore = process.memoryUsage().heapUsed;
249
+
250
+ // ruvector doesn't have a persistent index - it does brute-force differentiable search
251
+ // Build = converting buffers to Float32Arrays (the "index" is just the array of embeddings)
252
+ const buildStart = performance.now();
253
+ const indexedVectors: Float32Array[] = [];
254
+ for (let i = 0; i < buffers.length; i++) {
255
+ const f32 = new Float32Array(buffers[i].buffer, buffers[i].byteOffset, DIMENSIONS);
256
+ indexedVectors.push(new Float32Array(f32)); // copy since buffer may be reused
257
+ }
258
+ const buildMs = performance.now() - buildStart;
259
+
260
+ const memAfter = process.memoryUsage().heapUsed;
261
+
262
+ // Search
263
+ const searchStart = performance.now();
264
+ const searchResults: string[][] = [];
265
+ for (const q of queries) {
266
+ const queryF32 = new Float32Array(q);
267
+ const result = differentiableSearch(
268
+ queryF32 as unknown as number[],
269
+ indexedVectors as unknown as number[][],
270
+ SEARCH_K,
271
+ 1.0
272
+ );
273
+ searchResults.push(result.indices.map(idx => ids[idx]));
274
+ }
275
+ const searchMs = performance.now() - searchStart;
276
+
277
+ let totalRecall = 0;
278
+ for (let i = 0; i < queries.length; i++) {
279
+ totalRecall += recallAtK(searchResults[i], groundTruth[i]);
280
+ }
281
+
282
+ return {
283
+ label: '@ruvector/gnn differentiableSearch (Rust)',
284
+ buildMs: Math.round(buildMs),
285
+ searchMs: Math.round(searchMs),
286
+ avgSearchMs: (searchMs / queries.length).toFixed(2),
287
+ memoryMB: Math.round((memAfter - memBefore) / 1024 / 1024),
288
+ recall: ((totalRecall / queries.length) * 100).toFixed(1),
289
+ indexSize: indexedVectors.length,
290
+ };
291
+ }
292
+
293
+ // ============================================================================
294
+ // Implementation 4: Optimized JS HNSW (reduced efConstruction + fast deser)
295
+ // ============================================================================
296
+
297
+ async function benchmarkOptimizedJS(
298
+ buffers: Buffer[],
299
+ ids: string[],
300
+ embeddings: number[][],
301
+ queries: number[][],
302
+ groundTruth: string[][]
303
+ ) {
304
+ const { InMemoryHNSWIndex } = await import('../v3/dist/kernel/unified-memory-hnsw.js');
305
+ const index = new InMemoryHNSWIndex();
306
+ (index as any).efConstruction = 50;
307
+
308
+ const memBefore = process.memoryUsage().heapUsed;
309
+
310
+ const buildStart = performance.now();
311
+ for (let i = 0; i < buffers.length; i++) {
312
+ const embedding = bufferToFloatArrayFast(buffers[i], DIMENSIONS);
313
+ index.add(ids[i], embedding);
314
+ }
315
+ const buildMs = performance.now() - buildStart;
316
+
317
+ const memAfter = process.memoryUsage().heapUsed;
318
+
319
+ const searchStart = performance.now();
320
+ const searchResults: string[][] = [];
321
+ for (const q of queries) {
322
+ const results = index.search(q, SEARCH_K);
323
+ searchResults.push(results.map(r => r.id));
324
+ }
325
+ const searchMs = performance.now() - searchStart;
326
+
327
+ let totalRecall = 0;
328
+ for (let i = 0; i < queries.length; i++) {
329
+ totalRecall += recallAtK(searchResults[i], groundTruth[i]);
330
+ }
331
+
332
+ return {
333
+ label: 'Optimized JS (efC=50 + Float32Array deser)',
334
+ buildMs: Math.round(buildMs),
335
+ searchMs: Math.round(searchMs),
336
+ avgSearchMs: (searchMs / queries.length).toFixed(2),
337
+ memoryMB: Math.round((memAfter - memBefore) / 1024 / 1024),
338
+ recall: ((totalRecall / queries.length) * 100).toFixed(1),
339
+ indexSize: index.size(),
340
+ };
341
+ }
342
+
343
+ // ============================================================================
344
+ // Deserialization-only micro benchmark
345
+ // ============================================================================
346
+
347
+ function benchmarkDeserialization(buffers: Buffer[]) {
348
+ // Slow path
349
+ const slowStart = performance.now();
350
+ for (const buf of buffers) {
351
+ bufferToFloatArraySlow(buf, DIMENSIONS);
352
+ }
353
+ const slowMs = performance.now() - slowStart;
354
+
355
+ // Fast path
356
+ const fastStart = performance.now();
357
+ for (const buf of buffers) {
358
+ bufferToFloatArrayFast(buf, DIMENSIONS);
359
+ }
360
+ const fastMs = performance.now() - fastStart;
361
+
362
+ return { slowMs: Math.round(slowMs), fastMs: Math.round(fastMs) };
363
+ }
364
+
365
+ // ============================================================================
366
+ // Main
367
+ // ============================================================================
368
+
369
+ async function main() {
370
+ console.log('='.repeat(70));
371
+ console.log('HNSW Loading Benchmark');
372
+ console.log(`Scenario: ${NUM_VECTORS} vectors × ${DIMENSIONS} dims, ${NUM_SEARCHES} searches, k=${SEARCH_K}`);
373
+ console.log('='.repeat(70));
374
+
375
+ // Generate data
376
+ console.log('\nGenerating test data...');
377
+ const { ids, embeddings } = generateVectors(NUM_VECTORS, DIMENSIONS);
378
+ const queries = generateQueries(NUM_SEARCHES, DIMENSIONS);
379
+
380
+ // Convert to SQLite buffer format (simulates reading from DB)
381
+ const buffers = embeddings.map(vectorToBuffer);
382
+
383
+ // Compute brute-force ground truth
384
+ console.log('Computing ground truth (brute force)...');
385
+ const groundTruth = queries.map(q => bruteForceTopK(q, embeddings, ids, SEARCH_K));
386
+
387
+ // Deserialization micro-benchmark
388
+ console.log('\n--- Deserialization Benchmark ---');
389
+ const deser = benchmarkDeserialization(buffers);
390
+ console.log(` readFloatLE loop: ${deser.slowMs}ms`);
391
+ console.log(` Float32Array view: ${deser.fastMs}ms`);
392
+ console.log(` Speedup: ${(deser.slowMs / deser.fastMs).toFixed(1)}x`);
393
+
394
+ // Results table
395
+ const results: any[] = [];
396
+
397
+ // 1. Current implementation (Pure JS, efC=200, slow deser)
398
+ console.log('\n[1/5] Pure JS HNSW (efC=200, readFloatLE) — current baseline...');
399
+ results.push(await benchmarkPureJS(
400
+ 'Pure JS (efC=200) + readFloatLE [CURRENT]',
401
+ 200, buffers, ids, embeddings, queries, groundTruth
402
+ ));
403
+ console.log(` Build: ${results[results.length-1].buildMs}ms`);
404
+
405
+ // Force GC between tests
406
+ if (global.gc) global.gc();
407
+
408
+ // 2. Pure JS with fast deserialization
409
+ console.log('\n[2/5] Pure JS HNSW (efC=200, Float32Array)...');
410
+ results.push(await benchmarkPureJSFastDeser(buffers, ids, embeddings, queries, groundTruth));
411
+ console.log(` Build: ${results[results.length-1].buildMs}ms`);
412
+
413
+ if (global.gc) global.gc();
414
+
415
+ // 3. Optimized JS (efC=50 + fast deser)
416
+ console.log('\n[3/5] Optimized JS HNSW (efC=50, Float32Array)...');
417
+ results.push(await benchmarkOptimizedJS(buffers, ids, embeddings, queries, groundTruth));
418
+ console.log(` Build: ${results[results.length-1].buildMs}ms`);
419
+
420
+ if (global.gc) global.gc();
421
+
422
+ // 4. Pure JS with efC=50 but slow deser
423
+ console.log('\n[4/5] Pure JS HNSW (efC=50, readFloatLE)...');
424
+ results.push(await benchmarkPureJS(
425
+ 'Pure JS (efC=50) + readFloatLE',
426
+ 50, buffers, ids, embeddings, queries, groundTruth
427
+ ));
428
+ console.log(` Build: ${results[results.length-1].buildMs}ms`);
429
+
430
+ if (global.gc) global.gc();
431
+
432
+ // 5. @ruvector/gnn
433
+ console.log('\n[5/5] @ruvector/gnn differentiableSearch (Rust/NAPI)...');
434
+ try {
435
+ results.push(await benchmarkRuvector(buffers, ids, embeddings, queries, groundTruth));
436
+ console.log(` Build: ${results[results.length-1].buildMs}ms, Search: ${results[results.length-1].searchMs}ms`);
437
+ } catch (e) {
438
+ console.log(` SKIPPED: ${(e as Error).message}`);
439
+ }
440
+
441
+ // Print results table
442
+ console.log('\n' + '='.repeat(70));
443
+ console.log('RESULTS');
444
+ console.log('='.repeat(70));
445
+ console.log('');
446
+ console.log(
447
+ 'Implementation'.padEnd(48) +
448
+ 'Build(ms)'.padStart(10) +
449
+ 'Search(ms)'.padStart(11) +
450
+ 'Avg/q(ms)'.padStart(10) +
451
+ 'Mem(MB)'.padStart(8) +
452
+ 'Recall%'.padStart(8)
453
+ );
454
+ console.log('-'.repeat(95));
455
+
456
+ for (const r of results) {
457
+ console.log(
458
+ r.label.padEnd(48) +
459
+ String(r.buildMs).padStart(10) +
460
+ String(r.searchMs).padStart(11) +
461
+ String(r.avgSearchMs).padStart(10) +
462
+ String(r.memoryMB).padStart(8) +
463
+ String(r.recall + '%').padStart(8)
464
+ );
465
+ }
466
+
467
+ console.log('-'.repeat(95));
468
+
469
+ // Speedup summary
470
+ if (results.length >= 2) {
471
+ const baseline = results[0].buildMs;
472
+ console.log('\nSpeedup vs current baseline:');
473
+ for (let i = 1; i < results.length; i++) {
474
+ const speedup = (baseline / results[i].buildMs).toFixed(1);
475
+ console.log(` ${results[i].label}: ${speedup}x faster build`);
476
+ }
477
+ }
478
+ }
479
+
480
+ main().catch(console.error);