@ruvector/ruvllm 2.5.6 → 2.5.7

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 (67) hide show
  1. package/README.md +282 -282
  2. package/bin/cli.js +1020 -1020
  3. package/dist/cjs/benchmarks/embedding-benchmark.js +8 -8
  4. package/dist/cjs/benchmarks/index.js +5 -5
  5. package/dist/cjs/benchmarks/model-comparison.js +15 -25
  6. package/dist/cjs/benchmarks/routing-benchmark.js +5 -5
  7. package/dist/cjs/contrastive.js +78 -78
  8. package/dist/cjs/engine.js +13 -13
  9. package/dist/cjs/export.js +31 -31
  10. package/dist/cjs/federated.js +1 -1
  11. package/dist/cjs/index.js +1 -1
  12. package/dist/cjs/intelligence.js +8 -18
  13. package/dist/cjs/lora.js +1 -1
  14. package/dist/cjs/models.js +13 -23
  15. package/dist/cjs/native.js +5 -4
  16. package/dist/cjs/session.js +1 -1
  17. package/dist/cjs/simd.js +1 -1
  18. package/dist/cjs/sona.js +1 -1
  19. package/dist/cjs/streaming.d.ts +1 -0
  20. package/dist/cjs/streaming.d.ts.map +1 -1
  21. package/dist/cjs/streaming.js +3 -3
  22. package/dist/cjs/training.d.ts +32 -4
  23. package/dist/cjs/training.d.ts.map +1 -1
  24. package/dist/cjs/training.js +59 -9
  25. package/dist/cjs/types.js +1 -1
  26. package/dist/esm/benchmarks/embedding-benchmark.js +1 -1
  27. package/dist/esm/benchmarks/index.js +1 -1
  28. package/dist/esm/benchmarks/model-comparison.js +1 -1
  29. package/dist/esm/benchmarks/routing-benchmark.js +1 -1
  30. package/dist/esm/contrastive.js +70 -70
  31. package/dist/esm/engine.js +13 -13
  32. package/dist/esm/export.js +31 -31
  33. package/dist/esm/federated.js +1 -1
  34. package/dist/esm/index.js +1 -1
  35. package/dist/esm/intelligence.js +1 -1
  36. package/dist/esm/lora.js +1 -1
  37. package/dist/esm/models.js +1 -1
  38. package/dist/esm/native.js +1 -1
  39. package/dist/esm/session.js +1 -1
  40. package/dist/esm/simd.js +1 -1
  41. package/dist/esm/sona.js +1 -1
  42. package/dist/esm/streaming.d.ts +1 -0
  43. package/dist/esm/streaming.d.ts.map +1 -1
  44. package/dist/esm/streaming.js +1 -1
  45. package/dist/esm/training.d.ts +32 -4
  46. package/dist/esm/training.d.ts.map +1 -1
  47. package/dist/esm/training.js +59 -9
  48. package/dist/esm/types.js +1 -1
  49. package/package.json +133 -133
  50. package/scripts/ensemble-model-compare.js +287 -287
  51. package/scripts/huggingface/README.md +537 -537
  52. package/scripts/huggingface/publish.sh +112 -112
  53. package/scripts/hybrid-model-compare.js +373 -373
  54. package/scripts/improved-model-compare.js +288 -288
  55. package/scripts/optimized-model-compare.js +364 -364
  56. package/scripts/real-model-compare.js +280 -280
  57. package/scripts/training/agentic-flow-capabilities.json +329 -329
  58. package/scripts/training/claude-code-synth.js +505 -505
  59. package/scripts/training/claude-flow-capabilities.json +1322 -1322
  60. package/scripts/training/claude-hard-negatives.js +222 -222
  61. package/scripts/training/contrastive-finetune.js +517 -517
  62. package/scripts/training/ecosystem-triplets.jsonl +1466 -1466
  63. package/scripts/training/generate-ecosystem-triplets.js +1324 -1324
  64. package/scripts/training/routing-dataset.js +634 -634
  65. package/scripts/training/ruvector-capabilities.json +798 -798
  66. package/scripts/training/validate-ecosystem.js +381 -381
  67. package/scripts/training/validation-results.json +22 -22
@@ -1,373 +1,373 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Hybrid Model Comparison
4
- *
5
- * Combines embedding similarity with keyword boosting.
6
- * This addresses the "reviewer overfit" problem by:
7
- * 1. Computing embedding similarity
8
- * 2. Boosting agents that have keyword matches in the task
9
- * 3. Using weighted combination for final score
10
- */
11
-
12
- const { execSync } = require('child_process');
13
- const { existsSync } = require('fs');
14
- const { join } = require('path');
15
- const { homedir } = require('os');
16
-
17
- const MODELS_DIR = join(homedir(), '.ruvllm', 'models');
18
- const RUVLTRA_MODEL = join(MODELS_DIR, 'ruvltra-claude-code-0.5b-q4_k_m.gguf');
19
- const QWEN_MODEL = join(MODELS_DIR, 'qwen2.5-0.5b-instruct-q4_k_m.gguf');
20
-
21
- // V1 descriptions for embedding
22
- const DESCRIPTIONS_V1 = {
23
- coder: 'implement create write build add code function class component feature',
24
- researcher: 'research find investigate analyze explore search discover examine',
25
- reviewer: 'review check evaluate assess inspect examine code quality',
26
- tester: 'test unit integration e2e coverage mock assertion spec',
27
- architect: 'design architecture schema system structure plan database',
28
- 'security-architect': 'security vulnerability xss injection audit cve authentication',
29
- debugger: 'debug fix bug error issue broken crash exception trace',
30
- documenter: 'document readme jsdoc comment explain describe documentation',
31
- refactorer: 'refactor extract rename consolidate clean restructure simplify',
32
- optimizer: 'optimize performance slow fast cache speed memory latency',
33
- devops: 'deploy ci cd kubernetes docker pipeline container infrastructure',
34
- 'api-docs': 'openapi swagger api documentation graphql schema endpoint',
35
- planner: 'plan estimate prioritize sprint roadmap schedule milestone',
36
- };
37
-
38
- // UNIQUE trigger keywords - words that strongly indicate a specific agent
39
- // Priority-ordered: first match wins for disambiguation
40
- // NOTE: "investigate" takes priority over "slow" for researcher vs optimizer
41
- const TRIGGER_KEYWORDS = {
42
- // Higher priority agents (check these first)
43
- researcher: ['research', 'investigate', 'explore', 'discover', 'best practices', 'patterns', 'analyze', 'look into', 'find out'],
44
- coder: ['implement', 'build', 'create', 'component', 'function', 'typescript', 'react', 'feature', 'write code'],
45
- tester: ['test', 'tests', 'testing', 'unit test', 'integration test', 'e2e', 'coverage', 'spec'],
46
- reviewer: ['review', 'pull request', 'pr', 'code quality', 'code review', 'check code'],
47
- debugger: ['debug', 'fix', 'bug', 'error', 'exception', 'crash', 'trace', 'null pointer', 'memory leak'],
48
- 'security-architect': ['security', 'vulnerability', 'xss', 'injection', 'csrf', 'cve', 'audit', 'exploit'],
49
- refactorer: ['refactor', 'async/await', 'modernize', 'restructure', 'extract', 'legacy'],
50
- // Optimizer: removed "slow" (too generic), added query-specific terms
51
- optimizer: ['optimize', 'performance', 'cache', 'caching', 'speed up', 'latency', 'faster', 'queries', 'reduce time'],
52
- architect: ['design', 'architecture', 'schema', 'structure', 'diagram', 'system design', 'plan architecture'],
53
- documenter: ['jsdoc', 'comment', 'comments', 'readme', 'documentation', 'document', 'explain'],
54
- devops: ['deploy', 'ci/cd', 'kubernetes', 'docker', 'pipeline', 'infrastructure', 'container'],
55
- 'api-docs': ['openapi', 'swagger', 'api doc', 'rest api', 'graphql', 'endpoint'],
56
- planner: ['sprint', 'plan', 'roadmap', 'milestone', 'estimate', 'schedule', 'prioritize'],
57
- };
58
-
59
- // Priority order for disambiguation (when multiple agents match)
60
- const AGENT_PRIORITY = [
61
- 'researcher', // "investigate" wins over "slow"
62
- 'debugger', // "fix" wins over generic terms
63
- 'tester', // "test" is specific
64
- 'security-architect',
65
- 'coder',
66
- 'reviewer',
67
- 'refactorer',
68
- 'optimizer',
69
- 'architect',
70
- 'documenter',
71
- 'devops',
72
- 'api-docs',
73
- 'planner',
74
- ];
75
-
76
- const ROUTING_TESTS = [
77
- { task: 'Implement a binary search function in TypeScript', expected: 'coder' },
78
- { task: 'Write unit tests for the authentication module', expected: 'tester' },
79
- { task: 'Review the pull request for security vulnerabilities', expected: 'reviewer' },
80
- { task: 'Research best practices for React state management', expected: 'researcher' },
81
- { task: 'Design the database schema for user profiles', expected: 'architect' },
82
- { task: 'Fix the null pointer exception in the login handler', expected: 'debugger' },
83
- { task: 'Audit the API endpoints for XSS vulnerabilities', expected: 'security-architect' },
84
- { task: 'Write JSDoc comments for the utility functions', expected: 'documenter' },
85
- { task: 'Refactor the payment module to use async/await', expected: 'refactorer' },
86
- { task: 'Optimize the database queries for the dashboard', expected: 'optimizer' },
87
- { task: 'Set up the CI/CD pipeline for the microservices', expected: 'devops' },
88
- { task: 'Generate OpenAPI documentation for the REST API', expected: 'api-docs' },
89
- { task: 'Create a sprint plan for the next two weeks', expected: 'planner' },
90
- { task: 'Build a React component for user registration', expected: 'coder' },
91
- { task: 'Debug memory leak in the WebSocket handler', expected: 'debugger' },
92
- { task: 'Investigate slow API response times', expected: 'researcher' },
93
- { task: 'Check code for potential race conditions', expected: 'reviewer' },
94
- { task: 'Add integration tests for the payment gateway', expected: 'tester' },
95
- { task: 'Plan the architecture for real-time notifications', expected: 'architect' },
96
- { task: 'Cache the frequently accessed user data', expected: 'optimizer' },
97
- ];
98
-
99
- function getEmbedding(modelPath, text) {
100
- try {
101
- const sanitized = text.replace(/"/g, '\\"').replace(/\n/g, ' ');
102
- const result = execSync(
103
- `llama-embedding -m "${modelPath}" -p "${sanitized}" --embd-output-format json 2>/dev/null`,
104
- { encoding: 'utf-8', maxBuffer: 10 * 1024 * 1024 }
105
- );
106
- const json = JSON.parse(result);
107
- return json.data[json.data.length - 1].embedding;
108
- } catch {
109
- return null;
110
- }
111
- }
112
-
113
- function cosineSimilarity(a, b) {
114
- if (!a || !b || a.length !== b.length) return 0;
115
- let dot = 0, normA = 0, normB = 0;
116
- for (let i = 0; i < a.length; i++) {
117
- dot += a[i] * b[i];
118
- normA += a[i] * a[i];
119
- normB += b[i] * b[i];
120
- }
121
- return dot / (Math.sqrt(normA) * Math.sqrt(normB) || 1);
122
- }
123
-
124
- /**
125
- * Count keyword matches for each agent
126
- */
127
- function getKeywordScores(task) {
128
- const taskLower = task.toLowerCase();
129
- const scores = {};
130
-
131
- for (const [agent, keywords] of Object.entries(TRIGGER_KEYWORDS)) {
132
- let matches = 0;
133
- for (const kw of keywords) {
134
- if (taskLower.includes(kw.toLowerCase())) {
135
- matches++;
136
- }
137
- }
138
- scores[agent] = matches;
139
- }
140
-
141
- return scores;
142
- }
143
-
144
- /**
145
- * Pure embedding routing (baseline)
146
- */
147
- function routeEmbeddingOnly(taskEmbedding, agentEmbeddings) {
148
- let bestAgent = 'coder';
149
- let bestSim = -1;
150
-
151
- for (const [agent, emb] of Object.entries(agentEmbeddings)) {
152
- const sim = cosineSimilarity(taskEmbedding, emb);
153
- if (sim > bestSim) {
154
- bestSim = sim;
155
- bestAgent = agent;
156
- }
157
- }
158
-
159
- return { agent: bestAgent, confidence: bestSim };
160
- }
161
-
162
- /**
163
- * Pure keyword routing
164
- */
165
- function routeKeywordOnly(task) {
166
- const scores = getKeywordScores(task);
167
- let bestAgent = 'coder';
168
- let bestScore = 0;
169
-
170
- for (const [agent, score] of Object.entries(scores)) {
171
- if (score > bestScore) {
172
- bestScore = score;
173
- bestAgent = agent;
174
- }
175
- }
176
-
177
- return { agent: bestAgent, confidence: bestScore };
178
- }
179
-
180
- /**
181
- * Hybrid routing - combine embedding similarity with keyword boost
182
- */
183
- function routeHybrid(task, taskEmbedding, agentEmbeddings, embeddingWeight = 0.6, keywordWeight = 0.4) {
184
- const keywordScores = getKeywordScores(task);
185
-
186
- // Normalize keyword scores to 0-1 range
187
- const maxKeyword = Math.max(...Object.values(keywordScores), 1);
188
- const normalizedKeywords = {};
189
- for (const agent of Object.keys(keywordScores)) {
190
- normalizedKeywords[agent] = keywordScores[agent] / maxKeyword;
191
- }
192
-
193
- let bestAgent = 'coder';
194
- let bestScore = -1;
195
- const allScores = {};
196
-
197
- for (const [agent, emb] of Object.entries(agentEmbeddings)) {
198
- const embSim = cosineSimilarity(taskEmbedding, emb);
199
- const kwScore = normalizedKeywords[agent] || 0;
200
- const combined = embeddingWeight * embSim + keywordWeight * kwScore;
201
- allScores[agent] = { embedding: embSim, keyword: kwScore, combined };
202
-
203
- if (combined > bestScore) {
204
- bestScore = combined;
205
- bestAgent = agent;
206
- }
207
- }
208
-
209
- return { agent: bestAgent, confidence: bestScore, scores: allScores };
210
- }
211
-
212
- /**
213
- * Keyword-first routing - use keywords as primary, embedding as tiebreaker
214
- */
215
- function routeKeywordFirst(task, taskEmbedding, agentEmbeddings) {
216
- const keywordScores = getKeywordScores(task);
217
-
218
- // Find agents with max keyword matches
219
- const maxKw = Math.max(...Object.values(keywordScores));
220
-
221
- if (maxKw > 0) {
222
- // At least one keyword match - use keywords, embedding as tiebreaker
223
- const candidates = Object.entries(keywordScores)
224
- .filter(([_, score]) => score === maxKw)
225
- .map(([agent, _]) => agent);
226
-
227
- if (candidates.length === 1) {
228
- return { agent: candidates[0], confidence: maxKw };
229
- }
230
-
231
- // Multiple candidates with same keyword count - use embedding
232
- let bestAgent = candidates[0];
233
- let bestSim = -1;
234
- for (const agent of candidates) {
235
- const sim = cosineSimilarity(taskEmbedding, agentEmbeddings[agent]);
236
- if (sim > bestSim) {
237
- bestSim = sim;
238
- bestAgent = agent;
239
- }
240
- }
241
- return { agent: bestAgent, confidence: maxKw + bestSim / 10 };
242
- }
243
-
244
- // No keyword matches - fall back to pure embedding
245
- return routeEmbeddingOnly(taskEmbedding, agentEmbeddings);
246
- }
247
-
248
- function runBenchmark(modelPath, routerFn, name) {
249
- const agentEmbeddings = {};
250
- for (const [agent, desc] of Object.entries(DESCRIPTIONS_V1)) {
251
- agentEmbeddings[agent] = getEmbedding(modelPath, desc);
252
- }
253
-
254
- let correct = 0;
255
- const results = [];
256
-
257
- for (const test of ROUTING_TESTS) {
258
- const taskEmb = getEmbedding(modelPath, test.task);
259
- const { agent } = routerFn(test.task, taskEmb, agentEmbeddings);
260
- const isCorrect = agent === test.expected;
261
- if (isCorrect) correct++;
262
- results.push({ task: test.task, expected: test.expected, got: agent, correct: isCorrect });
263
- }
264
-
265
- return { accuracy: correct / ROUTING_TESTS.length, correct, total: ROUTING_TESTS.length, results, name };
266
- }
267
-
268
- async function main() {
269
- console.log('\n╔═══════════════════════════════════════════════════════════════════════════════════╗');
270
- console.log('║ HYBRID ROUTING: Embeddings + Keywords ║');
271
- console.log('╚═══════════════════════════════════════════════════════════════════════════════════╝\n');
272
-
273
- if (!existsSync(RUVLTRA_MODEL)) {
274
- console.error('RuvLTRA model not found.');
275
- process.exit(1);
276
- }
277
-
278
- console.log('Strategies:');
279
- console.log(' 1. Embedding Only (baseline)');
280
- console.log(' 2. Keyword Only (no model)');
281
- console.log(' 3. Hybrid 60/40 (60% embedding, 40% keyword)');
282
- console.log(' 4. Hybrid 40/60 (40% embedding, 60% keyword)');
283
- console.log(' 5. Keyword-First (keywords primary, embedding tiebreaker)\n');
284
-
285
- // RuvLTRA tests
286
- console.log('─────────────────────────────────────────────────────────────────');
287
- console.log(' RUVLTRA RESULTS');
288
- console.log('─────────────────────────────────────────────────────────────────\n');
289
-
290
- const ruvEmbedding = runBenchmark(RUVLTRA_MODEL,
291
- (task, taskEmb, agentEmbs) => routeEmbeddingOnly(taskEmb, agentEmbs),
292
- 'Embedding Only');
293
- console.log(` Embedding Only: ${(ruvEmbedding.accuracy * 100).toFixed(1)}%`);
294
-
295
- const ruvKeyword = runBenchmark(RUVLTRA_MODEL,
296
- (task, taskEmb, agentEmbs) => routeKeywordOnly(task),
297
- 'Keyword Only');
298
- console.log(` Keyword Only: ${(ruvKeyword.accuracy * 100).toFixed(1)}%`);
299
-
300
- const ruvHybrid60 = runBenchmark(RUVLTRA_MODEL,
301
- (task, taskEmb, agentEmbs) => routeHybrid(task, taskEmb, agentEmbs, 0.6, 0.4),
302
- 'Hybrid 60/40');
303
- console.log(` Hybrid 60/40: ${(ruvHybrid60.accuracy * 100).toFixed(1)}%`);
304
-
305
- const ruvHybrid40 = runBenchmark(RUVLTRA_MODEL,
306
- (task, taskEmb, agentEmbs) => routeHybrid(task, taskEmb, agentEmbs, 0.4, 0.6),
307
- 'Hybrid 40/60');
308
- console.log(` Hybrid 40/60: ${(ruvHybrid40.accuracy * 100).toFixed(1)}%`);
309
-
310
- const ruvKwFirst = runBenchmark(RUVLTRA_MODEL,
311
- (task, taskEmb, agentEmbs) => routeKeywordFirst(task, taskEmb, agentEmbs),
312
- 'Keyword-First');
313
- console.log(` Keyword-First: ${(ruvKwFirst.accuracy * 100).toFixed(1)}%`);
314
-
315
- // Qwen tests
316
- console.log('\n─────────────────────────────────────────────────────────────────');
317
- console.log(' QWEN RESULTS');
318
- console.log('─────────────────────────────────────────────────────────────────\n');
319
-
320
- const qwenEmbedding = runBenchmark(QWEN_MODEL,
321
- (task, taskEmb, agentEmbs) => routeEmbeddingOnly(taskEmb, agentEmbs),
322
- 'Embedding Only');
323
- console.log(` Embedding Only: ${(qwenEmbedding.accuracy * 100).toFixed(1)}%`);
324
-
325
- const qwenHybrid60 = runBenchmark(QWEN_MODEL,
326
- (task, taskEmb, agentEmbs) => routeHybrid(task, taskEmb, agentEmbs, 0.6, 0.4),
327
- 'Hybrid 60/40');
328
- console.log(` Hybrid 60/40: ${(qwenHybrid60.accuracy * 100).toFixed(1)}%`);
329
-
330
- const qwenKwFirst = runBenchmark(QWEN_MODEL,
331
- (task, taskEmb, agentEmbs) => routeKeywordFirst(task, taskEmb, agentEmbs),
332
- 'Keyword-First');
333
- console.log(` Keyword-First: ${(qwenKwFirst.accuracy * 100).toFixed(1)}%`);
334
-
335
- // Summary table
336
- console.log('\n═══════════════════════════════════════════════════════════════════════════════════');
337
- console.log(' SUMMARY');
338
- console.log('═══════════════════════════════════════════════════════════════════════════════════\n');
339
-
340
- const fmt = (v) => `${(v * 100).toFixed(1)}%`.padStart(8);
341
-
342
- console.log('┌───────────────────────┬──────────┬──────────┬──────────────────┐');
343
- console.log('│ Strategy │ RuvLTRA │ Qwen │ RuvLTRA vs Qwen │');
344
- console.log('├───────────────────────┼──────────┼──────────┼──────────────────┤');
345
- console.log(`│ Embedding Only │${fmt(ruvEmbedding.accuracy)} │${fmt(qwenEmbedding.accuracy)} │ +${((ruvEmbedding.accuracy - qwenEmbedding.accuracy) * 100).toFixed(1)} pts │`);
346
- console.log(`│ Keyword Only │${fmt(ruvKeyword.accuracy)} │${fmt(ruvKeyword.accuracy)} │ same │`);
347
- console.log(`│ Hybrid 60/40 │${fmt(ruvHybrid60.accuracy)} │${fmt(qwenHybrid60.accuracy)} │ +${((ruvHybrid60.accuracy - qwenHybrid60.accuracy) * 100).toFixed(1)} pts │`);
348
- console.log(`│ Keyword-First │${fmt(ruvKwFirst.accuracy)} │${fmt(qwenKwFirst.accuracy)} │ +${((ruvKwFirst.accuracy - qwenKwFirst.accuracy) * 100).toFixed(1)} pts │`);
349
- console.log('└───────────────────────┴──────────┴──────────┴──────────────────┘');
350
-
351
- // Best results
352
- const ruvBest = [ruvEmbedding, ruvKeyword, ruvHybrid60, ruvHybrid40, ruvKwFirst]
353
- .reduce((a, b) => a.accuracy > b.accuracy ? a : b);
354
-
355
- console.log(`\n BEST RuvLTRA: ${ruvBest.name} = ${(ruvBest.accuracy * 100).toFixed(1)}%`);
356
- console.log(` Improvement over embedding-only: +${((ruvBest.accuracy - ruvEmbedding.accuracy) * 100).toFixed(1)} points`);
357
-
358
- // Show best results details
359
- console.log('\n─────────────────────────────────────────────────────────────────');
360
- console.log(` BEST STRATEGY DETAILS: ${ruvBest.name}`);
361
- console.log('─────────────────────────────────────────────────────────────────\n');
362
-
363
- for (const r of ruvBest.results) {
364
- const mark = r.correct ? '✓' : '✗';
365
- const task = r.task.slice(0, 45).padEnd(45);
366
- const exp = r.expected.padEnd(18);
367
- console.log(`${mark} ${task} ${exp}${r.correct ? '' : '→ ' + r.got}`);
368
- }
369
-
370
- console.log('\n');
371
- }
372
-
373
- main().catch(console.error);
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Hybrid Model Comparison
4
+ *
5
+ * Combines embedding similarity with keyword boosting.
6
+ * This addresses the "reviewer overfit" problem by:
7
+ * 1. Computing embedding similarity
8
+ * 2. Boosting agents that have keyword matches in the task
9
+ * 3. Using weighted combination for final score
10
+ */
11
+
12
+ const { execSync } = require('child_process');
13
+ const { existsSync } = require('fs');
14
+ const { join } = require('path');
15
+ const { homedir } = require('os');
16
+
17
+ const MODELS_DIR = join(homedir(), '.ruvllm', 'models');
18
+ const RUVLTRA_MODEL = join(MODELS_DIR, 'ruvltra-claude-code-0.5b-q4_k_m.gguf');
19
+ const QWEN_MODEL = join(MODELS_DIR, 'qwen2.5-0.5b-instruct-q4_k_m.gguf');
20
+
21
+ // V1 descriptions for embedding
22
+ const DESCRIPTIONS_V1 = {
23
+ coder: 'implement create write build add code function class component feature',
24
+ researcher: 'research find investigate analyze explore search discover examine',
25
+ reviewer: 'review check evaluate assess inspect examine code quality',
26
+ tester: 'test unit integration e2e coverage mock assertion spec',
27
+ architect: 'design architecture schema system structure plan database',
28
+ 'security-architect': 'security vulnerability xss injection audit cve authentication',
29
+ debugger: 'debug fix bug error issue broken crash exception trace',
30
+ documenter: 'document readme jsdoc comment explain describe documentation',
31
+ refactorer: 'refactor extract rename consolidate clean restructure simplify',
32
+ optimizer: 'optimize performance slow fast cache speed memory latency',
33
+ devops: 'deploy ci cd kubernetes docker pipeline container infrastructure',
34
+ 'api-docs': 'openapi swagger api documentation graphql schema endpoint',
35
+ planner: 'plan estimate prioritize sprint roadmap schedule milestone',
36
+ };
37
+
38
+ // UNIQUE trigger keywords - words that strongly indicate a specific agent
39
+ // Priority-ordered: first match wins for disambiguation
40
+ // NOTE: "investigate" takes priority over "slow" for researcher vs optimizer
41
+ const TRIGGER_KEYWORDS = {
42
+ // Higher priority agents (check these first)
43
+ researcher: ['research', 'investigate', 'explore', 'discover', 'best practices', 'patterns', 'analyze', 'look into', 'find out'],
44
+ coder: ['implement', 'build', 'create', 'component', 'function', 'typescript', 'react', 'feature', 'write code'],
45
+ tester: ['test', 'tests', 'testing', 'unit test', 'integration test', 'e2e', 'coverage', 'spec'],
46
+ reviewer: ['review', 'pull request', 'pr', 'code quality', 'code review', 'check code'],
47
+ debugger: ['debug', 'fix', 'bug', 'error', 'exception', 'crash', 'trace', 'null pointer', 'memory leak'],
48
+ 'security-architect': ['security', 'vulnerability', 'xss', 'injection', 'csrf', 'cve', 'audit', 'exploit'],
49
+ refactorer: ['refactor', 'async/await', 'modernize', 'restructure', 'extract', 'legacy'],
50
+ // Optimizer: removed "slow" (too generic), added query-specific terms
51
+ optimizer: ['optimize', 'performance', 'cache', 'caching', 'speed up', 'latency', 'faster', 'queries', 'reduce time'],
52
+ architect: ['design', 'architecture', 'schema', 'structure', 'diagram', 'system design', 'plan architecture'],
53
+ documenter: ['jsdoc', 'comment', 'comments', 'readme', 'documentation', 'document', 'explain'],
54
+ devops: ['deploy', 'ci/cd', 'kubernetes', 'docker', 'pipeline', 'infrastructure', 'container'],
55
+ 'api-docs': ['openapi', 'swagger', 'api doc', 'rest api', 'graphql', 'endpoint'],
56
+ planner: ['sprint', 'plan', 'roadmap', 'milestone', 'estimate', 'schedule', 'prioritize'],
57
+ };
58
+
59
+ // Priority order for disambiguation (when multiple agents match)
60
+ const AGENT_PRIORITY = [
61
+ 'researcher', // "investigate" wins over "slow"
62
+ 'debugger', // "fix" wins over generic terms
63
+ 'tester', // "test" is specific
64
+ 'security-architect',
65
+ 'coder',
66
+ 'reviewer',
67
+ 'refactorer',
68
+ 'optimizer',
69
+ 'architect',
70
+ 'documenter',
71
+ 'devops',
72
+ 'api-docs',
73
+ 'planner',
74
+ ];
75
+
76
+ const ROUTING_TESTS = [
77
+ { task: 'Implement a binary search function in TypeScript', expected: 'coder' },
78
+ { task: 'Write unit tests for the authentication module', expected: 'tester' },
79
+ { task: 'Review the pull request for security vulnerabilities', expected: 'reviewer' },
80
+ { task: 'Research best practices for React state management', expected: 'researcher' },
81
+ { task: 'Design the database schema for user profiles', expected: 'architect' },
82
+ { task: 'Fix the null pointer exception in the login handler', expected: 'debugger' },
83
+ { task: 'Audit the API endpoints for XSS vulnerabilities', expected: 'security-architect' },
84
+ { task: 'Write JSDoc comments for the utility functions', expected: 'documenter' },
85
+ { task: 'Refactor the payment module to use async/await', expected: 'refactorer' },
86
+ { task: 'Optimize the database queries for the dashboard', expected: 'optimizer' },
87
+ { task: 'Set up the CI/CD pipeline for the microservices', expected: 'devops' },
88
+ { task: 'Generate OpenAPI documentation for the REST API', expected: 'api-docs' },
89
+ { task: 'Create a sprint plan for the next two weeks', expected: 'planner' },
90
+ { task: 'Build a React component for user registration', expected: 'coder' },
91
+ { task: 'Debug memory leak in the WebSocket handler', expected: 'debugger' },
92
+ { task: 'Investigate slow API response times', expected: 'researcher' },
93
+ { task: 'Check code for potential race conditions', expected: 'reviewer' },
94
+ { task: 'Add integration tests for the payment gateway', expected: 'tester' },
95
+ { task: 'Plan the architecture for real-time notifications', expected: 'architect' },
96
+ { task: 'Cache the frequently accessed user data', expected: 'optimizer' },
97
+ ];
98
+
99
+ function getEmbedding(modelPath, text) {
100
+ try {
101
+ const sanitized = text.replace(/"/g, '\\"').replace(/\n/g, ' ');
102
+ const result = execSync(
103
+ `llama-embedding -m "${modelPath}" -p "${sanitized}" --embd-output-format json 2>/dev/null`,
104
+ { encoding: 'utf-8', maxBuffer: 10 * 1024 * 1024 }
105
+ );
106
+ const json = JSON.parse(result);
107
+ return json.data[json.data.length - 1].embedding;
108
+ } catch {
109
+ return null;
110
+ }
111
+ }
112
+
113
+ function cosineSimilarity(a, b) {
114
+ if (!a || !b || a.length !== b.length) return 0;
115
+ let dot = 0, normA = 0, normB = 0;
116
+ for (let i = 0; i < a.length; i++) {
117
+ dot += a[i] * b[i];
118
+ normA += a[i] * a[i];
119
+ normB += b[i] * b[i];
120
+ }
121
+ return dot / (Math.sqrt(normA) * Math.sqrt(normB) || 1);
122
+ }
123
+
124
+ /**
125
+ * Count keyword matches for each agent
126
+ */
127
+ function getKeywordScores(task) {
128
+ const taskLower = task.toLowerCase();
129
+ const scores = {};
130
+
131
+ for (const [agent, keywords] of Object.entries(TRIGGER_KEYWORDS)) {
132
+ let matches = 0;
133
+ for (const kw of keywords) {
134
+ if (taskLower.includes(kw.toLowerCase())) {
135
+ matches++;
136
+ }
137
+ }
138
+ scores[agent] = matches;
139
+ }
140
+
141
+ return scores;
142
+ }
143
+
144
+ /**
145
+ * Pure embedding routing (baseline)
146
+ */
147
+ function routeEmbeddingOnly(taskEmbedding, agentEmbeddings) {
148
+ let bestAgent = 'coder';
149
+ let bestSim = -1;
150
+
151
+ for (const [agent, emb] of Object.entries(agentEmbeddings)) {
152
+ const sim = cosineSimilarity(taskEmbedding, emb);
153
+ if (sim > bestSim) {
154
+ bestSim = sim;
155
+ bestAgent = agent;
156
+ }
157
+ }
158
+
159
+ return { agent: bestAgent, confidence: bestSim };
160
+ }
161
+
162
+ /**
163
+ * Pure keyword routing
164
+ */
165
+ function routeKeywordOnly(task) {
166
+ const scores = getKeywordScores(task);
167
+ let bestAgent = 'coder';
168
+ let bestScore = 0;
169
+
170
+ for (const [agent, score] of Object.entries(scores)) {
171
+ if (score > bestScore) {
172
+ bestScore = score;
173
+ bestAgent = agent;
174
+ }
175
+ }
176
+
177
+ return { agent: bestAgent, confidence: bestScore };
178
+ }
179
+
180
+ /**
181
+ * Hybrid routing - combine embedding similarity with keyword boost
182
+ */
183
+ function routeHybrid(task, taskEmbedding, agentEmbeddings, embeddingWeight = 0.6, keywordWeight = 0.4) {
184
+ const keywordScores = getKeywordScores(task);
185
+
186
+ // Normalize keyword scores to 0-1 range
187
+ const maxKeyword = Math.max(...Object.values(keywordScores), 1);
188
+ const normalizedKeywords = {};
189
+ for (const agent of Object.keys(keywordScores)) {
190
+ normalizedKeywords[agent] = keywordScores[agent] / maxKeyword;
191
+ }
192
+
193
+ let bestAgent = 'coder';
194
+ let bestScore = -1;
195
+ const allScores = {};
196
+
197
+ for (const [agent, emb] of Object.entries(agentEmbeddings)) {
198
+ const embSim = cosineSimilarity(taskEmbedding, emb);
199
+ const kwScore = normalizedKeywords[agent] || 0;
200
+ const combined = embeddingWeight * embSim + keywordWeight * kwScore;
201
+ allScores[agent] = { embedding: embSim, keyword: kwScore, combined };
202
+
203
+ if (combined > bestScore) {
204
+ bestScore = combined;
205
+ bestAgent = agent;
206
+ }
207
+ }
208
+
209
+ return { agent: bestAgent, confidence: bestScore, scores: allScores };
210
+ }
211
+
212
+ /**
213
+ * Keyword-first routing - use keywords as primary, embedding as tiebreaker
214
+ */
215
+ function routeKeywordFirst(task, taskEmbedding, agentEmbeddings) {
216
+ const keywordScores = getKeywordScores(task);
217
+
218
+ // Find agents with max keyword matches
219
+ const maxKw = Math.max(...Object.values(keywordScores));
220
+
221
+ if (maxKw > 0) {
222
+ // At least one keyword match - use keywords, embedding as tiebreaker
223
+ const candidates = Object.entries(keywordScores)
224
+ .filter(([_, score]) => score === maxKw)
225
+ .map(([agent, _]) => agent);
226
+
227
+ if (candidates.length === 1) {
228
+ return { agent: candidates[0], confidence: maxKw };
229
+ }
230
+
231
+ // Multiple candidates with same keyword count - use embedding
232
+ let bestAgent = candidates[0];
233
+ let bestSim = -1;
234
+ for (const agent of candidates) {
235
+ const sim = cosineSimilarity(taskEmbedding, agentEmbeddings[agent]);
236
+ if (sim > bestSim) {
237
+ bestSim = sim;
238
+ bestAgent = agent;
239
+ }
240
+ }
241
+ return { agent: bestAgent, confidence: maxKw + bestSim / 10 };
242
+ }
243
+
244
+ // No keyword matches - fall back to pure embedding
245
+ return routeEmbeddingOnly(taskEmbedding, agentEmbeddings);
246
+ }
247
+
248
+ function runBenchmark(modelPath, routerFn, name) {
249
+ const agentEmbeddings = {};
250
+ for (const [agent, desc] of Object.entries(DESCRIPTIONS_V1)) {
251
+ agentEmbeddings[agent] = getEmbedding(modelPath, desc);
252
+ }
253
+
254
+ let correct = 0;
255
+ const results = [];
256
+
257
+ for (const test of ROUTING_TESTS) {
258
+ const taskEmb = getEmbedding(modelPath, test.task);
259
+ const { agent } = routerFn(test.task, taskEmb, agentEmbeddings);
260
+ const isCorrect = agent === test.expected;
261
+ if (isCorrect) correct++;
262
+ results.push({ task: test.task, expected: test.expected, got: agent, correct: isCorrect });
263
+ }
264
+
265
+ return { accuracy: correct / ROUTING_TESTS.length, correct, total: ROUTING_TESTS.length, results, name };
266
+ }
267
+
268
+ async function main() {
269
+ console.log('\n╔═══════════════════════════════════════════════════════════════════════════════════╗');
270
+ console.log('║ HYBRID ROUTING: Embeddings + Keywords ║');
271
+ console.log('╚═══════════════════════════════════════════════════════════════════════════════════╝\n');
272
+
273
+ if (!existsSync(RUVLTRA_MODEL)) {
274
+ console.error('RuvLTRA model not found.');
275
+ process.exit(1);
276
+ }
277
+
278
+ console.log('Strategies:');
279
+ console.log(' 1. Embedding Only (baseline)');
280
+ console.log(' 2. Keyword Only (no model)');
281
+ console.log(' 3. Hybrid 60/40 (60% embedding, 40% keyword)');
282
+ console.log(' 4. Hybrid 40/60 (40% embedding, 60% keyword)');
283
+ console.log(' 5. Keyword-First (keywords primary, embedding tiebreaker)\n');
284
+
285
+ // RuvLTRA tests
286
+ console.log('─────────────────────────────────────────────────────────────────');
287
+ console.log(' RUVLTRA RESULTS');
288
+ console.log('─────────────────────────────────────────────────────────────────\n');
289
+
290
+ const ruvEmbedding = runBenchmark(RUVLTRA_MODEL,
291
+ (task, taskEmb, agentEmbs) => routeEmbeddingOnly(taskEmb, agentEmbs),
292
+ 'Embedding Only');
293
+ console.log(` Embedding Only: ${(ruvEmbedding.accuracy * 100).toFixed(1)}%`);
294
+
295
+ const ruvKeyword = runBenchmark(RUVLTRA_MODEL,
296
+ (task, taskEmb, agentEmbs) => routeKeywordOnly(task),
297
+ 'Keyword Only');
298
+ console.log(` Keyword Only: ${(ruvKeyword.accuracy * 100).toFixed(1)}%`);
299
+
300
+ const ruvHybrid60 = runBenchmark(RUVLTRA_MODEL,
301
+ (task, taskEmb, agentEmbs) => routeHybrid(task, taskEmb, agentEmbs, 0.6, 0.4),
302
+ 'Hybrid 60/40');
303
+ console.log(` Hybrid 60/40: ${(ruvHybrid60.accuracy * 100).toFixed(1)}%`);
304
+
305
+ const ruvHybrid40 = runBenchmark(RUVLTRA_MODEL,
306
+ (task, taskEmb, agentEmbs) => routeHybrid(task, taskEmb, agentEmbs, 0.4, 0.6),
307
+ 'Hybrid 40/60');
308
+ console.log(` Hybrid 40/60: ${(ruvHybrid40.accuracy * 100).toFixed(1)}%`);
309
+
310
+ const ruvKwFirst = runBenchmark(RUVLTRA_MODEL,
311
+ (task, taskEmb, agentEmbs) => routeKeywordFirst(task, taskEmb, agentEmbs),
312
+ 'Keyword-First');
313
+ console.log(` Keyword-First: ${(ruvKwFirst.accuracy * 100).toFixed(1)}%`);
314
+
315
+ // Qwen tests
316
+ console.log('\n─────────────────────────────────────────────────────────────────');
317
+ console.log(' QWEN RESULTS');
318
+ console.log('─────────────────────────────────────────────────────────────────\n');
319
+
320
+ const qwenEmbedding = runBenchmark(QWEN_MODEL,
321
+ (task, taskEmb, agentEmbs) => routeEmbeddingOnly(taskEmb, agentEmbs),
322
+ 'Embedding Only');
323
+ console.log(` Embedding Only: ${(qwenEmbedding.accuracy * 100).toFixed(1)}%`);
324
+
325
+ const qwenHybrid60 = runBenchmark(QWEN_MODEL,
326
+ (task, taskEmb, agentEmbs) => routeHybrid(task, taskEmb, agentEmbs, 0.6, 0.4),
327
+ 'Hybrid 60/40');
328
+ console.log(` Hybrid 60/40: ${(qwenHybrid60.accuracy * 100).toFixed(1)}%`);
329
+
330
+ const qwenKwFirst = runBenchmark(QWEN_MODEL,
331
+ (task, taskEmb, agentEmbs) => routeKeywordFirst(task, taskEmb, agentEmbs),
332
+ 'Keyword-First');
333
+ console.log(` Keyword-First: ${(qwenKwFirst.accuracy * 100).toFixed(1)}%`);
334
+
335
+ // Summary table
336
+ console.log('\n═══════════════════════════════════════════════════════════════════════════════════');
337
+ console.log(' SUMMARY');
338
+ console.log('═══════════════════════════════════════════════════════════════════════════════════\n');
339
+
340
+ const fmt = (v) => `${(v * 100).toFixed(1)}%`.padStart(8);
341
+
342
+ console.log('┌───────────────────────┬──────────┬──────────┬──────────────────┐');
343
+ console.log('│ Strategy │ RuvLTRA │ Qwen │ RuvLTRA vs Qwen │');
344
+ console.log('├───────────────────────┼──────────┼──────────┼──────────────────┤');
345
+ console.log(`│ Embedding Only │${fmt(ruvEmbedding.accuracy)} │${fmt(qwenEmbedding.accuracy)} │ +${((ruvEmbedding.accuracy - qwenEmbedding.accuracy) * 100).toFixed(1)} pts │`);
346
+ console.log(`│ Keyword Only │${fmt(ruvKeyword.accuracy)} │${fmt(ruvKeyword.accuracy)} │ same │`);
347
+ console.log(`│ Hybrid 60/40 │${fmt(ruvHybrid60.accuracy)} │${fmt(qwenHybrid60.accuracy)} │ +${((ruvHybrid60.accuracy - qwenHybrid60.accuracy) * 100).toFixed(1)} pts │`);
348
+ console.log(`│ Keyword-First │${fmt(ruvKwFirst.accuracy)} │${fmt(qwenKwFirst.accuracy)} │ +${((ruvKwFirst.accuracy - qwenKwFirst.accuracy) * 100).toFixed(1)} pts │`);
349
+ console.log('└───────────────────────┴──────────┴──────────┴──────────────────┘');
350
+
351
+ // Best results
352
+ const ruvBest = [ruvEmbedding, ruvKeyword, ruvHybrid60, ruvHybrid40, ruvKwFirst]
353
+ .reduce((a, b) => a.accuracy > b.accuracy ? a : b);
354
+
355
+ console.log(`\n BEST RuvLTRA: ${ruvBest.name} = ${(ruvBest.accuracy * 100).toFixed(1)}%`);
356
+ console.log(` Improvement over embedding-only: +${((ruvBest.accuracy - ruvEmbedding.accuracy) * 100).toFixed(1)} points`);
357
+
358
+ // Show best results details
359
+ console.log('\n─────────────────────────────────────────────────────────────────');
360
+ console.log(` BEST STRATEGY DETAILS: ${ruvBest.name}`);
361
+ console.log('─────────────────────────────────────────────────────────────────\n');
362
+
363
+ for (const r of ruvBest.results) {
364
+ const mark = r.correct ? '✓' : '✗';
365
+ const task = r.task.slice(0, 45).padEnd(45);
366
+ const exp = r.expected.padEnd(18);
367
+ console.log(`${mark} ${task} ${exp}${r.correct ? '' : '→ ' + r.got}`);
368
+ }
369
+
370
+ console.log('\n');
371
+ }
372
+
373
+ main().catch(console.error);