@ruvector/ruvllm 2.5.5 → 2.5.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.
- package/README.md +282 -282
- package/bin/cli.js +1020 -1020
- package/dist/cjs/benchmarks/embedding-benchmark.js +1 -1
- package/dist/cjs/benchmarks/index.js +1 -1
- package/dist/cjs/benchmarks/model-comparison.js +1 -1
- package/dist/cjs/benchmarks/routing-benchmark.js +1 -1
- package/dist/cjs/contrastive.js +70 -70
- package/dist/cjs/engine.js +13 -13
- package/dist/cjs/export.js +31 -31
- package/dist/cjs/federated.js +1 -1
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/intelligence.js +1 -1
- package/dist/cjs/lora.js +1 -1
- package/dist/cjs/models.js +1 -1
- package/dist/cjs/native.js +1 -1
- package/dist/cjs/session.js +1 -1
- package/dist/cjs/simd.js +1 -1
- package/dist/cjs/sona.d.ts +36 -0
- package/dist/cjs/sona.d.ts.map +1 -1
- package/dist/cjs/sona.js +66 -4
- package/dist/cjs/streaming.js +1 -1
- package/dist/cjs/training.js +1 -1
- package/dist/cjs/types.js +1 -1
- package/dist/esm/benchmarks/embedding-benchmark.js +1 -1
- package/dist/esm/benchmarks/index.js +1 -1
- package/dist/esm/benchmarks/model-comparison.js +1 -1
- package/dist/esm/benchmarks/routing-benchmark.js +1 -1
- package/dist/esm/contrastive.js +70 -70
- package/dist/esm/engine.js +13 -13
- package/dist/esm/export.js +31 -31
- package/dist/esm/federated.js +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/intelligence.js +1 -1
- package/dist/esm/lora.js +1 -1
- package/dist/esm/models.js +1 -1
- package/dist/esm/native.js +1 -1
- package/dist/esm/session.js +1 -1
- package/dist/esm/simd.js +1 -1
- package/dist/esm/sona.d.ts +36 -0
- package/dist/esm/sona.d.ts.map +1 -1
- package/dist/esm/sona.js +66 -4
- package/dist/esm/streaming.js +1 -1
- package/dist/esm/training.js +1 -1
- package/dist/esm/types.js +1 -1
- package/package.json +133 -133
- package/scripts/ensemble-model-compare.js +287 -287
- package/scripts/huggingface/README.md +537 -537
- package/scripts/huggingface/publish.sh +112 -112
- package/scripts/hybrid-model-compare.js +373 -373
- package/scripts/improved-model-compare.js +288 -288
- package/scripts/optimized-model-compare.js +364 -364
- package/scripts/real-model-compare.js +280 -280
- package/scripts/training/agentic-flow-capabilities.json +329 -329
- package/scripts/training/claude-code-synth.js +505 -505
- package/scripts/training/claude-flow-capabilities.json +1322 -1322
- package/scripts/training/claude-hard-negatives.js +222 -222
- package/scripts/training/contrastive-finetune.js +517 -517
- package/scripts/training/ecosystem-triplets.jsonl +1466 -1466
- package/scripts/training/generate-ecosystem-triplets.js +1324 -1324
- package/scripts/training/routing-dataset.js +634 -634
- package/scripts/training/ruvector-capabilities.json +798 -798
- package/scripts/training/validate-ecosystem.js +381 -381
- 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);
|