cozo-memory 1.0.9 → 1.1.1
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 +36 -7
- package/dist/cli-commands.js +32 -0
- package/dist/cli.js +125 -0
- package/dist/compare-embeddings.js +402 -0
- package/dist/download-pplx-embed.js +151 -0
- package/dist/embedding-service.js +79 -7
- package/dist/eval-suite.js +7 -1
- package/dist/hybrid-search.js +87 -8
- package/dist/index.js +259 -18
- package/dist/reranker-service.js +125 -0
- package/dist/test-multi-level-memory.js +80 -0
- package/package.json +5 -3
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
require("dotenv/config");
|
|
37
|
+
const embedding_service_1 = require("./embedding-service");
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
// Test data - verschiedene Szenarien
|
|
41
|
+
const TEST_QUERIES = [
|
|
42
|
+
"What is machine learning?",
|
|
43
|
+
"How do neural networks work?",
|
|
44
|
+
"Explain quantum computing",
|
|
45
|
+
"What are the benefits of TypeScript?",
|
|
46
|
+
"How to optimize database queries?",
|
|
47
|
+
"Best practices for API design",
|
|
48
|
+
"Understanding distributed systems",
|
|
49
|
+
"Introduction to graph databases",
|
|
50
|
+
"Microservices architecture patterns",
|
|
51
|
+
"Cloud computing fundamentals"
|
|
52
|
+
];
|
|
53
|
+
const TEST_DOCUMENTS = [
|
|
54
|
+
"Machine learning is a subset of artificial intelligence that enables systems to learn and improve from experience without being explicitly programmed.",
|
|
55
|
+
"Neural networks are computing systems inspired by biological neural networks that constitute animal brains. They consist of interconnected nodes or neurons.",
|
|
56
|
+
"Quantum computing uses quantum-mechanical phenomena such as superposition and entanglement to perform operations on data.",
|
|
57
|
+
"TypeScript is a strongly typed programming language that builds on JavaScript, giving you better tooling at any scale.",
|
|
58
|
+
"Database query optimization involves analyzing and improving query performance through indexing, query rewriting, and execution plan analysis.",
|
|
59
|
+
"API design best practices include using RESTful principles, proper versioning, clear documentation, and consistent error handling.",
|
|
60
|
+
"Distributed systems are computing systems whose components are located on different networked computers, which communicate and coordinate their actions.",
|
|
61
|
+
"Graph databases use graph structures with nodes, edges, and properties to represent and store data, ideal for connected data.",
|
|
62
|
+
"Microservices architecture is an approach to developing a single application as a suite of small services, each running in its own process.",
|
|
63
|
+
"Cloud computing delivers computing services including servers, storage, databases, networking, software, analytics, and intelligence over the Internet."
|
|
64
|
+
];
|
|
65
|
+
// Cosine similarity berechnen
|
|
66
|
+
function cosineSimilarity(a, b) {
|
|
67
|
+
let dotProduct = 0;
|
|
68
|
+
let normA = 0;
|
|
69
|
+
let normB = 0;
|
|
70
|
+
for (let i = 0; i < a.length; i++) {
|
|
71
|
+
dotProduct += a[i] * b[i];
|
|
72
|
+
normA += a[i] * a[i];
|
|
73
|
+
normB += b[i] * b[i];
|
|
74
|
+
}
|
|
75
|
+
return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
|
|
76
|
+
}
|
|
77
|
+
// Test 1: Embedding-Geschwindigkeit
|
|
78
|
+
async function testEmbeddingSpeed(service, modelName) {
|
|
79
|
+
console.log(`\n${'='.repeat(70)}`);
|
|
80
|
+
console.log(`TEST 1: Embedding-Geschwindigkeit - ${modelName}`);
|
|
81
|
+
console.log('='.repeat(70));
|
|
82
|
+
const times = [];
|
|
83
|
+
// Warmup
|
|
84
|
+
await service.embed("warmup");
|
|
85
|
+
// Single embeddings
|
|
86
|
+
for (const query of TEST_QUERIES) {
|
|
87
|
+
const start = performance.now();
|
|
88
|
+
await service.embed(query);
|
|
89
|
+
const end = performance.now();
|
|
90
|
+
times.push(end - start);
|
|
91
|
+
}
|
|
92
|
+
const avgTime = times.reduce((a, b) => a + b, 0) / times.length;
|
|
93
|
+
const minTime = Math.min(...times);
|
|
94
|
+
const maxTime = Math.max(...times);
|
|
95
|
+
console.log(`\nSingle Embedding Performance:`);
|
|
96
|
+
console.log(` Average: ${avgTime.toFixed(2)} ms`);
|
|
97
|
+
console.log(` Min: ${minTime.toFixed(2)} ms`);
|
|
98
|
+
console.log(` Max: ${maxTime.toFixed(2)} ms`);
|
|
99
|
+
return { avgTime, minTime, maxTime };
|
|
100
|
+
}
|
|
101
|
+
// Test 2: Batch-Performance
|
|
102
|
+
async function testBatchPerformance(service, modelName) {
|
|
103
|
+
console.log(`\n${'='.repeat(70)}`);
|
|
104
|
+
console.log(`TEST 2: Batch-Performance - ${modelName}`);
|
|
105
|
+
console.log('='.repeat(70));
|
|
106
|
+
const start = performance.now();
|
|
107
|
+
await service.embedBatch(TEST_DOCUMENTS);
|
|
108
|
+
const end = performance.now();
|
|
109
|
+
const totalTime = end - start;
|
|
110
|
+
const avgPerDoc = totalTime / TEST_DOCUMENTS.length;
|
|
111
|
+
console.log(`\nBatch Embedding (${TEST_DOCUMENTS.length} documents):`);
|
|
112
|
+
console.log(` Total time: ${totalTime.toFixed(2)} ms`);
|
|
113
|
+
console.log(` Avg per doc: ${avgPerDoc.toFixed(2)} ms`);
|
|
114
|
+
console.log(` Throughput: ${(1000 / avgPerDoc).toFixed(2)} docs/sec`);
|
|
115
|
+
return { totalTime, avgPerDoc };
|
|
116
|
+
}
|
|
117
|
+
// Test 3: Semantic Similarity Quality
|
|
118
|
+
async function testSemanticSimilarity(service, modelName) {
|
|
119
|
+
console.log(`\n${'='.repeat(70)}`);
|
|
120
|
+
console.log(`TEST 3: Semantic Similarity Quality - ${modelName}`);
|
|
121
|
+
console.log('='.repeat(70));
|
|
122
|
+
// Embed queries and documents
|
|
123
|
+
const queryEmbeddings = await service.embedBatch(TEST_QUERIES);
|
|
124
|
+
const docEmbeddings = await service.embedBatch(TEST_DOCUMENTS);
|
|
125
|
+
// Expected matches (query index -> document index)
|
|
126
|
+
const expectedMatches = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
|
127
|
+
let correctMatches = 0;
|
|
128
|
+
const similarities = [];
|
|
129
|
+
console.log(`\nTop Match for each Query:`);
|
|
130
|
+
for (let i = 0; i < TEST_QUERIES.length; i++) {
|
|
131
|
+
const queryEmb = queryEmbeddings[i];
|
|
132
|
+
// Calculate similarities with all documents
|
|
133
|
+
const sims = docEmbeddings.map((docEmb, idx) => ({
|
|
134
|
+
idx,
|
|
135
|
+
similarity: cosineSimilarity(queryEmb, docEmb)
|
|
136
|
+
}));
|
|
137
|
+
// Sort by similarity
|
|
138
|
+
sims.sort((a, b) => b.similarity - a.similarity);
|
|
139
|
+
const topMatch = sims[0];
|
|
140
|
+
const isCorrect = topMatch.idx === expectedMatches[i];
|
|
141
|
+
if (isCorrect)
|
|
142
|
+
correctMatches++;
|
|
143
|
+
similarities.push(topMatch.similarity);
|
|
144
|
+
console.log(` Q${i}: "${TEST_QUERIES[i].substring(0, 40)}..."`);
|
|
145
|
+
console.log(` → Doc ${topMatch.idx} (sim: ${topMatch.similarity.toFixed(4)}) ${isCorrect ? '✓' : '✗'}`);
|
|
146
|
+
}
|
|
147
|
+
const accuracy = (correctMatches / TEST_QUERIES.length) * 100;
|
|
148
|
+
const avgSimilarity = similarities.reduce((a, b) => a + b, 0) / similarities.length;
|
|
149
|
+
console.log(`\nResults:`);
|
|
150
|
+
console.log(` Accuracy: ${accuracy.toFixed(1)}% (${correctMatches}/${TEST_QUERIES.length})`);
|
|
151
|
+
console.log(` Avg Similarity: ${avgSimilarity.toFixed(4)}`);
|
|
152
|
+
return { accuracy, avgSimilarity, correctMatches };
|
|
153
|
+
}
|
|
154
|
+
// Test 4: Long Context Handling
|
|
155
|
+
async function testLongContext(service, modelName) {
|
|
156
|
+
console.log(`\n${'='.repeat(70)}`);
|
|
157
|
+
console.log(`TEST 4: Long Context Handling - ${modelName}`);
|
|
158
|
+
console.log('='.repeat(70));
|
|
159
|
+
const shortText = "Machine learning is AI.";
|
|
160
|
+
const mediumText = TEST_DOCUMENTS[0]; // ~150 chars
|
|
161
|
+
const longText = TEST_DOCUMENTS.join(" "); // ~1000 chars
|
|
162
|
+
const veryLongText = longText.repeat(5); // ~5000 chars
|
|
163
|
+
const tests = [
|
|
164
|
+
{ name: "Short (~20 chars)", text: shortText },
|
|
165
|
+
{ name: "Medium (~150 chars)", text: mediumText },
|
|
166
|
+
{ name: "Long (~1000 chars)", text: longText },
|
|
167
|
+
{ name: "Very Long (~5000 chars)", text: veryLongText }
|
|
168
|
+
];
|
|
169
|
+
console.log(`\nContext Length Performance:`);
|
|
170
|
+
const results = [];
|
|
171
|
+
for (const test of tests) {
|
|
172
|
+
const start = performance.now();
|
|
173
|
+
await service.embed(test.text);
|
|
174
|
+
const end = performance.now();
|
|
175
|
+
const time = end - start;
|
|
176
|
+
console.log(` ${test.name.padEnd(25)} ${time.toFixed(2)} ms`);
|
|
177
|
+
results.push({ name: test.name, time });
|
|
178
|
+
}
|
|
179
|
+
return results;
|
|
180
|
+
}
|
|
181
|
+
// Test 5: Cache Performance
|
|
182
|
+
async function testCachePerformance(service, modelName) {
|
|
183
|
+
console.log(`\n${'='.repeat(70)}`);
|
|
184
|
+
console.log(`TEST 5: Cache Performance - ${modelName}`);
|
|
185
|
+
console.log('='.repeat(70));
|
|
186
|
+
const testText = "Test cache performance";
|
|
187
|
+
// First call (cold)
|
|
188
|
+
const start1 = performance.now();
|
|
189
|
+
await service.embed(testText);
|
|
190
|
+
const end1 = performance.now();
|
|
191
|
+
const coldTime = end1 - start1;
|
|
192
|
+
// Second call (cached)
|
|
193
|
+
const start2 = performance.now();
|
|
194
|
+
await service.embed(testText);
|
|
195
|
+
const end2 = performance.now();
|
|
196
|
+
const cachedTime = end2 - start2;
|
|
197
|
+
const speedup = coldTime / cachedTime;
|
|
198
|
+
console.log(`\nCache Hit Performance:`);
|
|
199
|
+
console.log(` Cold (first call): ${coldTime.toFixed(2)} ms`);
|
|
200
|
+
console.log(` Cached (second): ${cachedTime.toFixed(2)} ms`);
|
|
201
|
+
console.log(` Speedup: ${speedup.toFixed(1)}x faster`);
|
|
202
|
+
const stats = service.getCacheStats();
|
|
203
|
+
console.log(`\nCache Statistics:`);
|
|
204
|
+
console.log(` Size: ${stats.size}/${stats.maxSize}`);
|
|
205
|
+
console.log(` Model: ${stats.model}`);
|
|
206
|
+
console.log(` Dims: ${stats.dimensions}`);
|
|
207
|
+
return { coldTime, cachedTime, speedup };
|
|
208
|
+
}
|
|
209
|
+
// Test 6: Memory Usage
|
|
210
|
+
async function testMemoryUsage(service, modelName) {
|
|
211
|
+
console.log(`\n${'='.repeat(70)}`);
|
|
212
|
+
console.log(`TEST 6: Memory Usage - ${modelName}`);
|
|
213
|
+
console.log('='.repeat(70));
|
|
214
|
+
const memBefore = process.memoryUsage();
|
|
215
|
+
// Embed a batch
|
|
216
|
+
await service.embedBatch(TEST_DOCUMENTS);
|
|
217
|
+
const memAfter = process.memoryUsage();
|
|
218
|
+
const heapUsedMB = (memAfter.heapUsed - memBefore.heapUsed) / 1024 / 1024;
|
|
219
|
+
const rssMB = (memAfter.rss - memBefore.rss) / 1024 / 1024;
|
|
220
|
+
console.log(`\nMemory Usage (after batch embedding):`);
|
|
221
|
+
console.log(` Heap Used: ${heapUsedMB.toFixed(2)} MB`);
|
|
222
|
+
console.log(` RSS: ${rssMB.toFixed(2)} MB`);
|
|
223
|
+
console.log(` Total Heap: ${(memAfter.heapTotal / 1024 / 1024).toFixed(2)} MB`);
|
|
224
|
+
return { heapUsedMB, rssMB };
|
|
225
|
+
}
|
|
226
|
+
// Main comparison function
|
|
227
|
+
async function runSingleModelTest(modelId) {
|
|
228
|
+
console.log('\n' + '█'.repeat(70));
|
|
229
|
+
console.log(`TESTING MODEL: ${modelId}`);
|
|
230
|
+
console.log('█'.repeat(70));
|
|
231
|
+
const service = new embedding_service_1.EmbeddingService();
|
|
232
|
+
const results = {
|
|
233
|
+
model: modelId,
|
|
234
|
+
timestamp: new Date().toISOString()
|
|
235
|
+
};
|
|
236
|
+
// Run tests
|
|
237
|
+
results.speed = await testEmbeddingSpeed(service, modelId);
|
|
238
|
+
results.batch = await testBatchPerformance(service, modelId);
|
|
239
|
+
results.similarity = await testSemanticSimilarity(service, modelId);
|
|
240
|
+
results.longContext = await testLongContext(service, modelId);
|
|
241
|
+
results.cache = await testCachePerformance(service, modelId);
|
|
242
|
+
results.memory = await testMemoryUsage(service, modelId);
|
|
243
|
+
// Save results
|
|
244
|
+
const resultsPath = path.join(__dirname, '..', `embedding-results-${modelId.replace(/\//g, '-')}.json`);
|
|
245
|
+
fs.writeFileSync(resultsPath, JSON.stringify(results, null, 2));
|
|
246
|
+
console.log(`\n✓ Results saved to: ${resultsPath}`);
|
|
247
|
+
return results;
|
|
248
|
+
}
|
|
249
|
+
async function compareResults() {
|
|
250
|
+
console.log('\n' + '█'.repeat(70));
|
|
251
|
+
console.log('LOADING AND COMPARING RESULTS');
|
|
252
|
+
console.log('█'.repeat(70));
|
|
253
|
+
const bgeFile = path.join(__dirname, '..', 'embedding-results-Xenova-bge-m3.json');
|
|
254
|
+
const pplxFile = path.join(__dirname, '..', 'embedding-results-perplexity-ai-pplx-embed-v1-0.6b.json');
|
|
255
|
+
if (!fs.existsSync(bgeFile)) {
|
|
256
|
+
console.error('\n✗ BGE-M3 results not found!');
|
|
257
|
+
console.log('Please run: EMBEDDING_MODEL=Xenova/bge-m3 npm run compare-embeddings');
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
if (!fs.existsSync(pplxFile)) {
|
|
261
|
+
console.error('\n✗ pplx-embed results not found!');
|
|
262
|
+
console.log('Please run: EMBEDDING_MODEL=perplexity-ai/pplx-embed-v1-0.6b npm run compare-embeddings');
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
const bge = JSON.parse(fs.readFileSync(bgeFile, 'utf-8'));
|
|
266
|
+
const pplx = JSON.parse(fs.readFileSync(pplxFile, 'utf-8'));
|
|
267
|
+
// Print comparison summary
|
|
268
|
+
console.log(`\n\n${'█'.repeat(70)}`);
|
|
269
|
+
console.log('COMPARISON SUMMARY');
|
|
270
|
+
console.log('█'.repeat(70));
|
|
271
|
+
console.log(`\n1. EMBEDDING SPEED (lower is better)`);
|
|
272
|
+
console.log(` BGE-M3: ${bge.speed.avgTime.toFixed(2)} ms`);
|
|
273
|
+
console.log(` pplx-embed: ${pplx.speed.avgTime.toFixed(2)} ms`);
|
|
274
|
+
const speedDiff = ((pplx.speed.avgTime - bge.speed.avgTime) / bge.speed.avgTime * 100);
|
|
275
|
+
console.log(` Winner: ${bge.speed.avgTime < pplx.speed.avgTime ? 'BGE-M3' : 'pplx-embed'} (${Math.abs(speedDiff).toFixed(1)}% ${speedDiff > 0 ? 'slower' : 'faster'})`);
|
|
276
|
+
console.log(`\n2. BATCH THROUGHPUT (higher is better)`);
|
|
277
|
+
const bgeThroughput = 1000 / bge.batch.avgPerDoc;
|
|
278
|
+
const pplxThroughput = 1000 / pplx.batch.avgPerDoc;
|
|
279
|
+
console.log(` BGE-M3: ${bgeThroughput.toFixed(2)} docs/sec`);
|
|
280
|
+
console.log(` pplx-embed: ${pplxThroughput.toFixed(2)} docs/sec`);
|
|
281
|
+
console.log(` Winner: ${bgeThroughput > pplxThroughput ? 'BGE-M3' : 'pplx-embed'}`);
|
|
282
|
+
console.log(`\n3. SEMANTIC SIMILARITY ACCURACY (higher is better)`);
|
|
283
|
+
console.log(` BGE-M3: ${bge.similarity.accuracy.toFixed(1)}% (${bge.similarity.correctMatches}/${TEST_QUERIES.length})`);
|
|
284
|
+
console.log(` pplx-embed: ${pplx.similarity.accuracy.toFixed(1)}% (${pplx.similarity.correctMatches}/${TEST_QUERIES.length})`);
|
|
285
|
+
console.log(` Winner: ${bge.similarity.accuracy > pplx.similarity.accuracy ? 'BGE-M3' : 'pplx-embed'} ${pplx.similarity.accuracy > bge.similarity.accuracy ? '🏆' : ''}`);
|
|
286
|
+
console.log(`\n4. AVERAGE SIMILARITY SCORE (higher is better)`);
|
|
287
|
+
console.log(` BGE-M3: ${bge.similarity.avgSimilarity.toFixed(4)}`);
|
|
288
|
+
console.log(` pplx-embed: ${pplx.similarity.avgSimilarity.toFixed(4)}`);
|
|
289
|
+
const simDiff = ((pplx.similarity.avgSimilarity - bge.similarity.avgSimilarity) / bge.similarity.avgSimilarity * 100);
|
|
290
|
+
console.log(` Winner: ${bge.similarity.avgSimilarity > pplx.similarity.avgSimilarity ? 'BGE-M3' : 'pplx-embed'} (${Math.abs(simDiff).toFixed(1)}% ${simDiff > 0 ? 'higher' : 'lower'})`);
|
|
291
|
+
console.log(`\n5. CACHE SPEEDUP (higher is better)`);
|
|
292
|
+
console.log(` BGE-M3: ${bge.cache.speedup.toFixed(1)}x`);
|
|
293
|
+
console.log(` pplx-embed: ${pplx.cache.speedup.toFixed(1)}x`);
|
|
294
|
+
console.log(`\n6. MEMORY USAGE (lower is better)`);
|
|
295
|
+
console.log(` BGE-M3: ${bge.memory.heapUsedMB.toFixed(2)} MB heap`);
|
|
296
|
+
console.log(` pplx-embed: ${pplx.memory.heapUsedMB.toFixed(2)} MB heap`);
|
|
297
|
+
console.log(` Winner: ${bge.memory.heapUsedMB < pplx.memory.heapUsedMB ? 'BGE-M3' : 'pplx-embed'}`);
|
|
298
|
+
// Score calculation
|
|
299
|
+
let bgeScore = 0;
|
|
300
|
+
let pplxScore = 0;
|
|
301
|
+
if (bge.speed.avgTime < pplx.speed.avgTime)
|
|
302
|
+
bgeScore++;
|
|
303
|
+
else
|
|
304
|
+
pplxScore++;
|
|
305
|
+
if (bgeThroughput > pplxThroughput)
|
|
306
|
+
bgeScore++;
|
|
307
|
+
else
|
|
308
|
+
pplxScore++;
|
|
309
|
+
if (bge.similarity.accuracy > pplx.similarity.accuracy)
|
|
310
|
+
bgeScore++;
|
|
311
|
+
else
|
|
312
|
+
pplxScore++;
|
|
313
|
+
if (bge.similarity.avgSimilarity > pplx.similarity.avgSimilarity)
|
|
314
|
+
bgeScore++;
|
|
315
|
+
else
|
|
316
|
+
pplxScore++;
|
|
317
|
+
if (bge.memory.heapUsedMB < pplx.memory.heapUsedMB)
|
|
318
|
+
bgeScore++;
|
|
319
|
+
else
|
|
320
|
+
pplxScore++;
|
|
321
|
+
// Overall winner
|
|
322
|
+
console.log(`\n${'='.repeat(70)}`);
|
|
323
|
+
console.log('OVERALL SCORE');
|
|
324
|
+
console.log('='.repeat(70));
|
|
325
|
+
console.log(`\n BGE-M3: ${bgeScore}/5 wins`);
|
|
326
|
+
console.log(` pplx-embed: ${pplxScore}/5 wins`);
|
|
327
|
+
console.log(`\n${'='.repeat(70)}`);
|
|
328
|
+
console.log('RECOMMENDATION');
|
|
329
|
+
console.log('='.repeat(70));
|
|
330
|
+
if (pplxScore > bgeScore) {
|
|
331
|
+
console.log(`\n✓ pplx-embed-v1-0.6b is RECOMMENDED 🏆`);
|
|
332
|
+
console.log(` Reasons:`);
|
|
333
|
+
if (pplx.similarity.accuracy > bge.similarity.accuracy) {
|
|
334
|
+
console.log(` ✓ Better semantic similarity accuracy (+${(pplx.similarity.accuracy - bge.similarity.accuracy).toFixed(1)}%)`);
|
|
335
|
+
}
|
|
336
|
+
if (pplx.similarity.avgSimilarity > bge.similarity.avgSimilarity) {
|
|
337
|
+
console.log(` ✓ Higher quality embeddings (+${(simDiff).toFixed(1)}%)`);
|
|
338
|
+
}
|
|
339
|
+
console.log(` ✓ 32K context length (vs 8K for BGE-M3)`);
|
|
340
|
+
console.log(` ✓ Better MTEB benchmark scores`);
|
|
341
|
+
}
|
|
342
|
+
else if (bgeScore > pplxScore) {
|
|
343
|
+
console.log(`\n✓ BGE-M3 is RECOMMENDED 🏆`);
|
|
344
|
+
console.log(` Reasons:`);
|
|
345
|
+
if (bge.speed.avgTime < pplx.speed.avgTime) {
|
|
346
|
+
console.log(` ✓ Faster embedding speed (-${Math.abs(speedDiff).toFixed(1)}%)`);
|
|
347
|
+
}
|
|
348
|
+
if (bge.memory.heapUsedMB < pplx.memory.heapUsedMB) {
|
|
349
|
+
console.log(` ✓ Lower memory usage`);
|
|
350
|
+
}
|
|
351
|
+
console.log(` ✓ Automatic download (no manual setup)`);
|
|
352
|
+
console.log(` ✓ Proven stability`);
|
|
353
|
+
}
|
|
354
|
+
else {
|
|
355
|
+
console.log(`\n⚖ BOTH MODELS ARE EQUALLY COMPETITIVE`);
|
|
356
|
+
console.log(` Choose based on your priorities:`);
|
|
357
|
+
console.log(` - pplx-embed: Better quality, longer context (32K)`);
|
|
358
|
+
console.log(` - BGE-M3: Faster, easier setup, automatic download`);
|
|
359
|
+
}
|
|
360
|
+
console.log('\n' + '█'.repeat(70));
|
|
361
|
+
console.log('COMPARISON COMPLETE');
|
|
362
|
+
console.log('█'.repeat(70) + '\n');
|
|
363
|
+
}
|
|
364
|
+
// Main entry point
|
|
365
|
+
async function main() {
|
|
366
|
+
const currentModel = process.env.EMBEDDING_MODEL || "Xenova/bge-m3";
|
|
367
|
+
// Check if we should compare existing results
|
|
368
|
+
const args = process.argv.slice(2);
|
|
369
|
+
if (args.includes('--compare')) {
|
|
370
|
+
await compareResults();
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
console.log('\n' + '█'.repeat(70));
|
|
374
|
+
console.log('EMBEDDING MODEL BENCHMARK');
|
|
375
|
+
console.log('█'.repeat(70));
|
|
376
|
+
console.log(`\nCurrent model: ${currentModel}`);
|
|
377
|
+
console.log('\nThis will run a comprehensive test suite including:');
|
|
378
|
+
console.log(' 1. Embedding speed');
|
|
379
|
+
console.log(' 2. Batch performance');
|
|
380
|
+
console.log(' 3. Semantic similarity quality');
|
|
381
|
+
console.log(' 4. Long context handling');
|
|
382
|
+
console.log(' 5. Cache performance');
|
|
383
|
+
console.log(' 6. Memory usage');
|
|
384
|
+
console.log('\nEstimated time: 2-3 minutes\n');
|
|
385
|
+
await runSingleModelTest(currentModel);
|
|
386
|
+
console.log('\n' + '='.repeat(70));
|
|
387
|
+
console.log('NEXT STEPS');
|
|
388
|
+
console.log('='.repeat(70));
|
|
389
|
+
if (currentModel === 'Xenova/bge-m3') {
|
|
390
|
+
console.log('\nTo test pplx-embed, run:');
|
|
391
|
+
console.log(' EMBEDDING_MODEL=perplexity-ai/pplx-embed-v1-0.6b npm run compare-embeddings');
|
|
392
|
+
}
|
|
393
|
+
else if (currentModel === 'perplexity-ai/pplx-embed-v1-0.6b') {
|
|
394
|
+
console.log('\nTo test BGE-M3, run:');
|
|
395
|
+
console.log(' EMBEDDING_MODEL=Xenova/bge-m3 npm run compare-embeddings');
|
|
396
|
+
}
|
|
397
|
+
console.log('\nTo compare both results, run:');
|
|
398
|
+
console.log(' npm run compare-embeddings -- --compare');
|
|
399
|
+
console.log();
|
|
400
|
+
}
|
|
401
|
+
// Run
|
|
402
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
require("dotenv/config");
|
|
37
|
+
const https = __importStar(require("https"));
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const transformers_1 = require("@xenova/transformers");
|
|
41
|
+
// Configure cache path
|
|
42
|
+
const CACHE_DIR = path.resolve('./.cache');
|
|
43
|
+
transformers_1.env.cacheDir = CACHE_DIR;
|
|
44
|
+
const MODEL_ID = "perplexity-ai/pplx-embed-v1-0.6b";
|
|
45
|
+
const BASE_URL = `https://huggingface.co/${MODEL_ID}/resolve/main/onnx`;
|
|
46
|
+
// Model variant to download (quantized is recommended for smaller size)
|
|
47
|
+
const USE_QUANTIZED = true; // Set to false for FP32 full precision
|
|
48
|
+
// Files to download based on variant
|
|
49
|
+
const FILES = USE_QUANTIZED ? [
|
|
50
|
+
{ name: 'model_quantized.onnx', size: '614 KB' },
|
|
51
|
+
{ name: 'model_quantized.onnx_data', size: '706 MB' }
|
|
52
|
+
] : [
|
|
53
|
+
{ name: 'model.onnx', size: '520 KB' },
|
|
54
|
+
{ name: 'model.onnx_data', size: '2.09 GB' },
|
|
55
|
+
{ name: 'model.onnx_data_1', size: '306 MB' }
|
|
56
|
+
];
|
|
57
|
+
// Target directory
|
|
58
|
+
const targetDir = path.join(CACHE_DIR, 'perplexity-ai', 'pplx-embed-v1-0.6b', 'onnx');
|
|
59
|
+
function downloadFile(url, dest) {
|
|
60
|
+
return new Promise((resolve, reject) => {
|
|
61
|
+
const file = fs.createWriteStream(dest);
|
|
62
|
+
https.get(url, (response) => {
|
|
63
|
+
if (response.statusCode === 302 || response.statusCode === 301) {
|
|
64
|
+
// Follow redirect
|
|
65
|
+
const redirectUrl = response.headers.location;
|
|
66
|
+
if (redirectUrl) {
|
|
67
|
+
file.close();
|
|
68
|
+
fs.unlinkSync(dest);
|
|
69
|
+
return downloadFile(redirectUrl, dest).then(resolve).catch(reject);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const totalSize = parseInt(response.headers['content-length'] || '0', 10);
|
|
73
|
+
let downloadedSize = 0;
|
|
74
|
+
response.on('data', (chunk) => {
|
|
75
|
+
downloadedSize += chunk.length;
|
|
76
|
+
const progress = ((downloadedSize / totalSize) * 100).toFixed(2);
|
|
77
|
+
process.stdout.write(`\r Progress: ${progress}% (${(downloadedSize / 1024 / 1024).toFixed(2)} MB / ${(totalSize / 1024 / 1024).toFixed(2)} MB)`);
|
|
78
|
+
});
|
|
79
|
+
response.pipe(file);
|
|
80
|
+
file.on('finish', () => {
|
|
81
|
+
file.close();
|
|
82
|
+
console.log('\n ✓ Download complete');
|
|
83
|
+
resolve();
|
|
84
|
+
});
|
|
85
|
+
}).on('error', (err) => {
|
|
86
|
+
fs.unlinkSync(dest);
|
|
87
|
+
reject(err);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
async function downloadPplxEmbed() {
|
|
92
|
+
console.log('='.repeat(70));
|
|
93
|
+
console.log('Downloading Perplexity pplx-embed-v1-0.6b ONNX files');
|
|
94
|
+
console.log(`Variant: ${USE_QUANTIZED ? 'INT8 Quantized (Recommended)' : 'FP32 Full Precision'}`);
|
|
95
|
+
console.log('='.repeat(70));
|
|
96
|
+
console.log();
|
|
97
|
+
// Create target directory
|
|
98
|
+
if (!fs.existsSync(targetDir)) {
|
|
99
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
100
|
+
console.log(`✓ Created directory: ${targetDir}`);
|
|
101
|
+
}
|
|
102
|
+
console.log();
|
|
103
|
+
console.log('Files to download:');
|
|
104
|
+
FILES.forEach(f => console.log(` - ${f.name} (${f.size})`));
|
|
105
|
+
console.log();
|
|
106
|
+
console.log(`Total size: ${USE_QUANTIZED ? '~706 MB' : '~2.5 GB'}`);
|
|
107
|
+
console.log(`This may take ${USE_QUANTIZED ? '3-10' : '10-30'} minutes depending on your internet connection.`);
|
|
108
|
+
console.log();
|
|
109
|
+
if (USE_QUANTIZED) {
|
|
110
|
+
console.log('ℹ Using INT8 quantized model (recommended):');
|
|
111
|
+
console.log(' ✓ 3x smaller than FP32 (~706 MB vs ~2.4 GB)');
|
|
112
|
+
console.log(' ✓ Minimal quality loss (~1.5% MTEB drop)');
|
|
113
|
+
console.log(' ✓ Faster inference');
|
|
114
|
+
console.log();
|
|
115
|
+
console.log(' To use FP32 instead, edit src/download-pplx-embed.ts');
|
|
116
|
+
console.log(' and set USE_QUANTIZED = false');
|
|
117
|
+
console.log();
|
|
118
|
+
}
|
|
119
|
+
// Download each file
|
|
120
|
+
for (const file of FILES) {
|
|
121
|
+
const filePath = path.join(targetDir, file.name);
|
|
122
|
+
// Skip if already exists
|
|
123
|
+
if (fs.existsSync(filePath)) {
|
|
124
|
+
console.log(`⊘ Skipping ${file.name} (already exists)`);
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
console.log(`⬇ Downloading ${file.name} (${file.size})...`);
|
|
128
|
+
const url = `${BASE_URL}/${file.name}`;
|
|
129
|
+
try {
|
|
130
|
+
await downloadFile(url, filePath);
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
console.error(`✗ Failed to download ${file.name}:`, error.message);
|
|
134
|
+
console.error(' Please download manually from:');
|
|
135
|
+
console.error(` ${url}`);
|
|
136
|
+
process.exit(1);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
console.log();
|
|
140
|
+
console.log('='.repeat(70));
|
|
141
|
+
console.log('✓ All files downloaded successfully!');
|
|
142
|
+
console.log('='.repeat(70));
|
|
143
|
+
console.log();
|
|
144
|
+
console.log('You can now use the model by setting in .env:');
|
|
145
|
+
console.log(' EMBEDDING_MODEL=perplexity-ai/pplx-embed-v1-0.6b');
|
|
146
|
+
console.log();
|
|
147
|
+
console.log('Then start the server with:');
|
|
148
|
+
console.log(' npm run start');
|
|
149
|
+
console.log();
|
|
150
|
+
}
|
|
151
|
+
downloadPplxEmbed().catch(console.error);
|