monomind 1.18.9 → 1.18.10
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/package.json +1 -1
- package/packages/@monomind/cli/dist/src/commands/hooks-coverage-commands.d.ts +1 -2
- package/packages/@monomind/cli/dist/src/commands/hooks-coverage-commands.js +1 -2
- package/packages/@monomind/cli/dist/src/commands/hooks-coverage-gaps.d.ts +1 -2
- package/packages/@monomind/cli/dist/src/commands/hooks-coverage-gaps.js +2 -117
- package/packages/@monomind/cli/dist/src/commands/hooks.js +1 -2
- package/packages/@monomind/cli/dist/src/commands/index.d.ts +0 -1
- package/packages/@monomind/cli/dist/src/commands/index.js +0 -1
- package/packages/@monomind/cli/dist/src/init/executor.d.ts +3 -0
- package/packages/@monomind/cli/dist/src/init/executor.js +20 -111
- package/packages/@monomind/cli/dist/src/mcp-client.js +0 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/index.d.ts +0 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/index.js +0 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/quality/coverage-analysis/track-trends.d.ts +4 -4
- package/packages/@monomind/cli/dist/src/mcp-tools/quality/test-generation/suggest-tests.d.ts +4 -4
- package/packages/@monomind/cli/package.json +1 -1
- package/packages/@monomind/cli/dist/src/benchmarks/benchmark-runner.d.ts +0 -134
- package/packages/@monomind/cli/dist/src/benchmarks/benchmark-runner.js +0 -340
- package/packages/@monomind/cli/dist/src/benchmarks/metric-evaluators.d.ts +0 -36
- package/packages/@monomind/cli/dist/src/benchmarks/metric-evaluators.js +0 -125
- package/packages/@monomind/cli/dist/src/commands/benchmark.d.ts +0 -10
- package/packages/@monomind/cli/dist/src/commands/benchmark.js +0 -586
- package/packages/@monomind/cli/dist/src/commands/migrate.d.ts +0 -8
- package/packages/@monomind/cli/dist/src/commands/migrate.js +0 -789
- package/packages/@monomind/cli/dist/src/commands/monovector/backup.d.ts +0 -11
- package/packages/@monomind/cli/dist/src/commands/monovector/backup.js +0 -752
- package/packages/@monomind/cli/dist/src/commands/monovector/benchmark.d.ts +0 -11
- package/packages/@monomind/cli/dist/src/commands/monovector/benchmark.js +0 -502
- package/packages/@monomind/cli/dist/src/commands/monovector/import.d.ts +0 -18
- package/packages/@monomind/cli/dist/src/commands/monovector/import.js +0 -374
- package/packages/@monomind/cli/dist/src/commands/monovector/index.d.ts +0 -29
- package/packages/@monomind/cli/dist/src/commands/monovector/index.js +0 -129
- package/packages/@monomind/cli/dist/src/commands/monovector/init.d.ts +0 -11
- package/packages/@monomind/cli/dist/src/commands/monovector/init.js +0 -481
- package/packages/@monomind/cli/dist/src/commands/monovector/migrate.d.ts +0 -11
- package/packages/@monomind/cli/dist/src/commands/monovector/migrate.js +0 -500
- package/packages/@monomind/cli/dist/src/commands/monovector/optimize.d.ts +0 -11
- package/packages/@monomind/cli/dist/src/commands/monovector/optimize.js +0 -515
- package/packages/@monomind/cli/dist/src/commands/monovector/setup.d.ts +0 -18
- package/packages/@monomind/cli/dist/src/commands/monovector/setup.js +0 -775
- package/packages/@monomind/cli/dist/src/commands/monovector/status.d.ts +0 -11
- package/packages/@monomind/cli/dist/src/commands/monovector/status.js +0 -491
- package/packages/@monomind/cli/dist/src/commands/progress.d.ts +0 -11
- package/packages/@monomind/cli/dist/src/commands/progress.js +0 -261
- package/packages/@monomind/cli/dist/src/mcp-tools/progress-tools.d.ts +0 -14
- package/packages/@monomind/cli/dist/src/mcp-tools/progress-tools.js +0 -353
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CLI MonoVector Benchmark Command
|
|
3
|
-
* Performance benchmarking for MonoVector PostgreSQL Bridge
|
|
4
|
-
*/
|
|
5
|
-
import type { Command } from '../../types.js';
|
|
6
|
-
/**
|
|
7
|
-
* MonoVector benchmark command
|
|
8
|
-
*/
|
|
9
|
-
export declare const benchmarkCommand: Command;
|
|
10
|
-
export default benchmarkCommand;
|
|
11
|
-
//# sourceMappingURL=benchmark.d.ts.map
|
|
@@ -1,502 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CLI MonoVector Benchmark Command
|
|
3
|
-
* Performance benchmarking for MonoVector PostgreSQL Bridge
|
|
4
|
-
*/
|
|
5
|
-
import { output } from '../../output.js';
|
|
6
|
-
import { confirm } from '../../prompt.js';
|
|
7
|
-
/**
|
|
8
|
-
* Get PostgreSQL connection config from context
|
|
9
|
-
*/
|
|
10
|
-
function getConnectionConfig(ctx) {
|
|
11
|
-
return {
|
|
12
|
-
host: ctx.flags.host || process.env.PGHOST || 'localhost',
|
|
13
|
-
port: parseInt(ctx.flags.port || process.env.PGPORT || '5432', 10),
|
|
14
|
-
database: ctx.flags.database || process.env.PGDATABASE || '',
|
|
15
|
-
user: ctx.flags.user || process.env.PGUSER || 'postgres',
|
|
16
|
-
password: ctx.flags.password || process.env.PGPASSWORD || '',
|
|
17
|
-
ssl: ctx.flags.ssl || process.env.PGSSLMODE === 'require',
|
|
18
|
-
schema: ctx.flags.schema || 'monomind',
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Generate random vector
|
|
23
|
-
*/
|
|
24
|
-
function generateRandomVector(dimensions) {
|
|
25
|
-
const vector = [];
|
|
26
|
-
for (let i = 0; i < dimensions; i++) {
|
|
27
|
-
vector.push(Math.random() * 2 - 1);
|
|
28
|
-
}
|
|
29
|
-
// Normalize
|
|
30
|
-
const magnitude = Math.sqrt(vector.reduce((sum, v) => sum + v * v, 0));
|
|
31
|
-
return vector.map(v => v / magnitude);
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Calculate percentile from sorted array
|
|
35
|
-
*/
|
|
36
|
-
function percentile(sortedArr, p) {
|
|
37
|
-
const index = Math.ceil((p / 100) * sortedArr.length) - 1;
|
|
38
|
-
return sortedArr[Math.max(0, index)];
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* MonoVector benchmark command
|
|
42
|
-
*/
|
|
43
|
-
export const benchmarkCommand = {
|
|
44
|
-
name: 'benchmark',
|
|
45
|
-
description: 'Performance benchmarking',
|
|
46
|
-
options: [
|
|
47
|
-
{
|
|
48
|
-
name: 'vectors',
|
|
49
|
-
short: 'n',
|
|
50
|
-
description: 'Number of test vectors',
|
|
51
|
-
type: 'number',
|
|
52
|
-
default: 10000,
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
name: 'dimensions',
|
|
56
|
-
description: 'Vector dimensions',
|
|
57
|
-
type: 'number',
|
|
58
|
-
default: 1536,
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
name: 'queries',
|
|
62
|
-
short: 'q',
|
|
63
|
-
description: 'Number of test queries',
|
|
64
|
-
type: 'number',
|
|
65
|
-
default: 100,
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
name: 'k',
|
|
69
|
-
description: 'Top-k results to retrieve',
|
|
70
|
-
type: 'number',
|
|
71
|
-
default: 10,
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
name: 'metric',
|
|
75
|
-
short: 'm',
|
|
76
|
-
description: 'Distance metric',
|
|
77
|
-
type: 'string',
|
|
78
|
-
default: 'cosine',
|
|
79
|
-
choices: ['cosine', 'l2', 'inner'],
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
name: 'index',
|
|
83
|
-
short: 'i',
|
|
84
|
-
description: 'Index type to test',
|
|
85
|
-
type: 'string',
|
|
86
|
-
default: 'hnsw',
|
|
87
|
-
choices: ['hnsw', 'ivfflat', 'none'],
|
|
88
|
-
},
|
|
89
|
-
{
|
|
90
|
-
name: 'batch-size',
|
|
91
|
-
description: 'Batch size for inserts',
|
|
92
|
-
type: 'number',
|
|
93
|
-
default: 1000,
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
name: 'cleanup',
|
|
97
|
-
description: 'Clean up test data after benchmark',
|
|
98
|
-
type: 'boolean',
|
|
99
|
-
default: true,
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
name: 'host',
|
|
103
|
-
short: 'h',
|
|
104
|
-
description: 'PostgreSQL host',
|
|
105
|
-
type: 'string',
|
|
106
|
-
default: 'localhost',
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
name: 'port',
|
|
110
|
-
short: 'p',
|
|
111
|
-
description: 'PostgreSQL port',
|
|
112
|
-
type: 'number',
|
|
113
|
-
default: 5432,
|
|
114
|
-
},
|
|
115
|
-
{
|
|
116
|
-
name: 'database',
|
|
117
|
-
short: 'd',
|
|
118
|
-
description: 'Database name',
|
|
119
|
-
type: 'string',
|
|
120
|
-
},
|
|
121
|
-
{
|
|
122
|
-
name: 'user',
|
|
123
|
-
short: 'u',
|
|
124
|
-
description: 'Database user',
|
|
125
|
-
type: 'string',
|
|
126
|
-
},
|
|
127
|
-
{
|
|
128
|
-
name: 'password',
|
|
129
|
-
description: 'Database password',
|
|
130
|
-
type: 'string',
|
|
131
|
-
},
|
|
132
|
-
{
|
|
133
|
-
name: 'ssl',
|
|
134
|
-
description: 'Enable SSL',
|
|
135
|
-
type: 'boolean',
|
|
136
|
-
default: false,
|
|
137
|
-
},
|
|
138
|
-
{
|
|
139
|
-
name: 'schema',
|
|
140
|
-
short: 's',
|
|
141
|
-
description: 'Schema name',
|
|
142
|
-
type: 'string',
|
|
143
|
-
default: 'monomind',
|
|
144
|
-
},
|
|
145
|
-
],
|
|
146
|
-
examples: [
|
|
147
|
-
{ command: 'monomind monovector benchmark', description: 'Run default benchmark' },
|
|
148
|
-
{ command: 'monomind monovector benchmark --vectors 50000', description: 'Benchmark with 50k vectors' },
|
|
149
|
-
{ command: 'monomind monovector benchmark --index ivfflat', description: 'Test IVFFlat index' },
|
|
150
|
-
{ command: 'monomind monovector benchmark --dimensions 768 --metric l2', description: 'Custom dimensions and metric' },
|
|
151
|
-
],
|
|
152
|
-
action: async (ctx) => {
|
|
153
|
-
const config = getConnectionConfig(ctx);
|
|
154
|
-
// Clamp numeric inputs to safe ranges to prevent DoS via OOM
|
|
155
|
-
const numVectors = Math.min(Math.max(1, parseInt(ctx.flags.vectors || '10000', 10)), 1_000_000);
|
|
156
|
-
const dimensions = Math.min(Math.max(1, parseInt(ctx.flags.dimensions || '1536', 10)), 65536);
|
|
157
|
-
const numQueries = Math.min(Math.max(1, parseInt(ctx.flags.queries || '100', 10)), 10_000);
|
|
158
|
-
const topK = Math.min(Math.max(1, parseInt(ctx.flags.k || '10', 10)), 10_000);
|
|
159
|
-
// Validate metric and indexType against allowlists to prevent SQL injection
|
|
160
|
-
const VALID_METRICS = ['cosine', 'l2', 'inner'];
|
|
161
|
-
const VALID_INDEX_TYPES = ['hnsw', 'ivfflat', 'none'];
|
|
162
|
-
const rawMetric = ctx.flags.metric || 'cosine';
|
|
163
|
-
const rawIndexType = ctx.flags.index || 'hnsw';
|
|
164
|
-
const metric = VALID_METRICS.includes(rawMetric) ? rawMetric : 'cosine';
|
|
165
|
-
const indexType = VALID_INDEX_TYPES.includes(rawIndexType) ? rawIndexType : 'hnsw';
|
|
166
|
-
const batchSize = Math.min(Math.max(1, parseInt(ctx.flags['batch-size'] || '1000', 10)), 100_000);
|
|
167
|
-
// Validate schema identifier against safe pattern to prevent SQL injection
|
|
168
|
-
const rawSchema = config.schema || 'monomind';
|
|
169
|
-
if (!/^[a-zA-Z_][a-zA-Z0-9_]{0,62}$/.test(rawSchema)) {
|
|
170
|
-
output.printError('Invalid schema name. Only alphanumeric characters and underscores are allowed.');
|
|
171
|
-
return { success: false, exitCode: 1 };
|
|
172
|
-
}
|
|
173
|
-
config.schema = rawSchema;
|
|
174
|
-
const cleanup = ctx.flags.cleanup !== false;
|
|
175
|
-
output.writeln();
|
|
176
|
-
output.writeln(output.bold('MonoVector Performance Benchmark'));
|
|
177
|
-
output.writeln(output.dim('='.repeat(60)));
|
|
178
|
-
output.writeln();
|
|
179
|
-
if (!config.database) {
|
|
180
|
-
output.printError('Database name is required. Use --database or -d flag, or set PGDATABASE env.');
|
|
181
|
-
return { success: false, exitCode: 1 };
|
|
182
|
-
}
|
|
183
|
-
// Show benchmark configuration
|
|
184
|
-
output.writeln(output.highlight('Benchmark Configuration:'));
|
|
185
|
-
output.printTable({
|
|
186
|
-
columns: [
|
|
187
|
-
{ key: 'setting', header: 'Setting', width: 20 },
|
|
188
|
-
{ key: 'value', header: 'Value', width: 20 },
|
|
189
|
-
],
|
|
190
|
-
data: [
|
|
191
|
-
{ setting: 'Vectors', value: numVectors.toLocaleString() },
|
|
192
|
-
{ setting: 'Dimensions', value: dimensions.toLocaleString() },
|
|
193
|
-
{ setting: 'Queries', value: numQueries.toLocaleString() },
|
|
194
|
-
{ setting: 'Top-K', value: topK.toLocaleString() },
|
|
195
|
-
{ setting: 'Metric', value: metric },
|
|
196
|
-
{ setting: 'Index Type', value: indexType.toUpperCase() },
|
|
197
|
-
{ setting: 'Batch Size', value: batchSize.toLocaleString() },
|
|
198
|
-
],
|
|
199
|
-
});
|
|
200
|
-
output.writeln();
|
|
201
|
-
// Confirm large benchmarks
|
|
202
|
-
if (numVectors >= 50000 && ctx.interactive) {
|
|
203
|
-
const confirmRun = await confirm({
|
|
204
|
-
message: `This will insert ${numVectors.toLocaleString()} vectors. Continue?`,
|
|
205
|
-
default: true,
|
|
206
|
-
});
|
|
207
|
-
if (!confirmRun) {
|
|
208
|
-
output.printInfo('Benchmark cancelled');
|
|
209
|
-
return { success: false, exitCode: 0 };
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
const spinner = output.createSpinner({ text: 'Connecting to PostgreSQL...', spinner: 'dots' });
|
|
213
|
-
spinner.start();
|
|
214
|
-
const results = {
|
|
215
|
-
config: { numVectors, dimensions, numQueries, topK, metric, indexType },
|
|
216
|
-
insert: {},
|
|
217
|
-
query: {},
|
|
218
|
-
memory: {},
|
|
219
|
-
};
|
|
220
|
-
try {
|
|
221
|
-
// Import pg
|
|
222
|
-
let pg = null;
|
|
223
|
-
try {
|
|
224
|
-
pg = await import('pg');
|
|
225
|
-
}
|
|
226
|
-
catch {
|
|
227
|
-
spinner.fail('PostgreSQL driver not found');
|
|
228
|
-
output.printError('Install pg package: npm install pg');
|
|
229
|
-
return { success: false, exitCode: 1 };
|
|
230
|
-
}
|
|
231
|
-
const client = new pg.Client({
|
|
232
|
-
host: config.host,
|
|
233
|
-
port: config.port,
|
|
234
|
-
database: config.database,
|
|
235
|
-
user: config.user,
|
|
236
|
-
password: config.password,
|
|
237
|
-
ssl: config.ssl ? { rejectUnauthorized: false } : false,
|
|
238
|
-
});
|
|
239
|
-
await client.connect();
|
|
240
|
-
spinner.succeed('Connected to PostgreSQL');
|
|
241
|
-
// Detect vector extension type: prefer monovector, fall back to pgvector
|
|
242
|
-
let vectorTypeName = 'vector';
|
|
243
|
-
const monovectorCheck = await client.query(`
|
|
244
|
-
SELECT extname FROM pg_extension WHERE extname = 'monovector'
|
|
245
|
-
`);
|
|
246
|
-
if (monovectorCheck.rows.length > 0) {
|
|
247
|
-
vectorTypeName = 'monovector';
|
|
248
|
-
}
|
|
249
|
-
// Create benchmark table
|
|
250
|
-
const benchmarkTable = `${config.schema}.benchmark_${Date.now()}`;
|
|
251
|
-
spinner.setText('Creating benchmark table...');
|
|
252
|
-
spinner.start();
|
|
253
|
-
await client.query(`
|
|
254
|
-
CREATE TABLE ${benchmarkTable} (
|
|
255
|
-
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
256
|
-
embedding ${vectorTypeName}(${dimensions}),
|
|
257
|
-
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
258
|
-
)
|
|
259
|
-
`);
|
|
260
|
-
spinner.succeed('Benchmark table created');
|
|
261
|
-
// Insert vectors
|
|
262
|
-
spinner.setText(`Inserting ${numVectors.toLocaleString()} vectors...`);
|
|
263
|
-
spinner.start();
|
|
264
|
-
const insertStart = Date.now();
|
|
265
|
-
let insertedCount = 0;
|
|
266
|
-
for (let batch = 0; batch < Math.ceil(numVectors / batchSize); batch++) {
|
|
267
|
-
const batchStart = batch * batchSize;
|
|
268
|
-
const batchEnd = Math.min(batchStart + batchSize, numVectors);
|
|
269
|
-
const batchVectors = [];
|
|
270
|
-
for (let i = batchStart; i < batchEnd; i++) {
|
|
271
|
-
const vector = generateRandomVector(dimensions);
|
|
272
|
-
batchVectors.push(`('[${vector.join(',')}]')`);
|
|
273
|
-
}
|
|
274
|
-
await client.query(`
|
|
275
|
-
INSERT INTO ${benchmarkTable} (embedding)
|
|
276
|
-
VALUES ${batchVectors.join(',')}
|
|
277
|
-
`);
|
|
278
|
-
insertedCount = batchEnd;
|
|
279
|
-
spinner.setText(`Inserting vectors... ${insertedCount.toLocaleString()}/${numVectors.toLocaleString()}`);
|
|
280
|
-
}
|
|
281
|
-
const insertDuration = Date.now() - insertStart;
|
|
282
|
-
const insertThroughput = Math.round(numVectors / (insertDuration / 1000));
|
|
283
|
-
results.insert = {
|
|
284
|
-
totalTime: insertDuration,
|
|
285
|
-
throughput: insertThroughput,
|
|
286
|
-
vectorsInserted: numVectors,
|
|
287
|
-
};
|
|
288
|
-
spinner.succeed(`Inserted ${numVectors.toLocaleString()} vectors in ${(insertDuration / 1000).toFixed(2)}s (${insertThroughput.toLocaleString()} vectors/sec)`);
|
|
289
|
-
// Create index
|
|
290
|
-
if (indexType !== 'none') {
|
|
291
|
-
spinner.setText(`Creating ${indexType.toUpperCase()} index...`);
|
|
292
|
-
spinner.start();
|
|
293
|
-
const indexStart = Date.now();
|
|
294
|
-
const opsPrefix = vectorTypeName === 'monovector' ? 'monovector' : 'vector';
|
|
295
|
-
const metricOp = metric === 'cosine' ? `${opsPrefix}_cosine_ops` :
|
|
296
|
-
metric === 'l2' ? `${opsPrefix}_l2_ops` : `${opsPrefix}_ip_ops`;
|
|
297
|
-
if (indexType === 'hnsw') {
|
|
298
|
-
await client.query(`
|
|
299
|
-
CREATE INDEX idx_benchmark_hnsw
|
|
300
|
-
ON ${benchmarkTable}
|
|
301
|
-
USING hnsw (embedding ${metricOp})
|
|
302
|
-
WITH (m = 16, ef_construction = 64)
|
|
303
|
-
`);
|
|
304
|
-
}
|
|
305
|
-
else if (indexType === 'ivfflat') {
|
|
306
|
-
// Need to train IVFFlat with existing data
|
|
307
|
-
const lists = Math.max(100, Math.floor(numVectors / 1000));
|
|
308
|
-
await client.query(`
|
|
309
|
-
CREATE INDEX idx_benchmark_ivfflat
|
|
310
|
-
ON ${benchmarkTable}
|
|
311
|
-
USING ivfflat (embedding ${metricOp})
|
|
312
|
-
WITH (lists = ${lists})
|
|
313
|
-
`);
|
|
314
|
-
}
|
|
315
|
-
const indexDuration = Date.now() - indexStart;
|
|
316
|
-
results.insert.indexTime = indexDuration;
|
|
317
|
-
spinner.succeed(`${indexType.toUpperCase()} index created in ${(indexDuration / 1000).toFixed(2)}s`);
|
|
318
|
-
// Set search parameters
|
|
319
|
-
if (indexType === 'hnsw') {
|
|
320
|
-
await client.query(`SET hnsw.ef_search = 100`);
|
|
321
|
-
}
|
|
322
|
-
else if (indexType === 'ivfflat') {
|
|
323
|
-
await client.query(`SET ivfflat.probes = 10`);
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
// Run queries
|
|
327
|
-
spinner.setText(`Running ${numQueries} queries...`);
|
|
328
|
-
spinner.start();
|
|
329
|
-
const queryLatencies = [];
|
|
330
|
-
const distanceOp = metric === 'cosine' ? '<=>' :
|
|
331
|
-
metric === 'l2' ? '<->' : '<#>';
|
|
332
|
-
for (let q = 0; q < numQueries; q++) {
|
|
333
|
-
const queryVector = generateRandomVector(dimensions);
|
|
334
|
-
const queryStart = Date.now();
|
|
335
|
-
await client.query(`
|
|
336
|
-
SELECT id, embedding ${distanceOp} '[${queryVector.join(',')}]' as distance
|
|
337
|
-
FROM ${benchmarkTable}
|
|
338
|
-
ORDER BY embedding ${distanceOp} '[${queryVector.join(',')}]'
|
|
339
|
-
LIMIT ${topK}
|
|
340
|
-
`);
|
|
341
|
-
queryLatencies.push(Date.now() - queryStart);
|
|
342
|
-
if (q % 10 === 0) {
|
|
343
|
-
spinner.setText(`Running queries... ${q + 1}/${numQueries}`);
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
// Calculate query statistics
|
|
347
|
-
queryLatencies.sort((a, b) => a - b);
|
|
348
|
-
const avgLatency = queryLatencies.reduce((a, b) => a + b, 0) / queryLatencies.length;
|
|
349
|
-
const p50 = percentile(queryLatencies, 50);
|
|
350
|
-
const p95 = percentile(queryLatencies, 95);
|
|
351
|
-
const p99 = percentile(queryLatencies, 99);
|
|
352
|
-
const minLatency = queryLatencies[0];
|
|
353
|
-
const maxLatency = queryLatencies[queryLatencies.length - 1];
|
|
354
|
-
const qps = Math.round(1000 / avgLatency);
|
|
355
|
-
results.query = {
|
|
356
|
-
totalQueries: numQueries,
|
|
357
|
-
avgLatency,
|
|
358
|
-
p50,
|
|
359
|
-
p95,
|
|
360
|
-
p99,
|
|
361
|
-
minLatency,
|
|
362
|
-
maxLatency,
|
|
363
|
-
qps,
|
|
364
|
-
};
|
|
365
|
-
spinner.succeed(`Completed ${numQueries} queries`);
|
|
366
|
-
// Get memory usage
|
|
367
|
-
spinner.setText('Analyzing memory usage...');
|
|
368
|
-
spinner.start();
|
|
369
|
-
const sizeResult = await client.query(`
|
|
370
|
-
SELECT
|
|
371
|
-
pg_relation_size('${benchmarkTable}') as table_size,
|
|
372
|
-
pg_total_relation_size('${benchmarkTable}') as total_size,
|
|
373
|
-
pg_indexes_size('${benchmarkTable}') as index_size
|
|
374
|
-
`);
|
|
375
|
-
const tableSize = parseInt(sizeResult.rows[0].table_size, 10);
|
|
376
|
-
const totalSize = parseInt(sizeResult.rows[0].total_size, 10);
|
|
377
|
-
const indexSize = parseInt(sizeResult.rows[0].index_size, 10);
|
|
378
|
-
const bytesPerVector = totalSize / numVectors;
|
|
379
|
-
results.memory = {
|
|
380
|
-
tableSize,
|
|
381
|
-
indexSize,
|
|
382
|
-
totalSize,
|
|
383
|
-
bytesPerVector,
|
|
384
|
-
vectorDimensions: dimensions,
|
|
385
|
-
};
|
|
386
|
-
spinner.succeed('Memory analysis complete');
|
|
387
|
-
// Calculate recall (if we have ground truth)
|
|
388
|
-
// For now, we'll estimate based on index type
|
|
389
|
-
let estimatedRecall = 1.0;
|
|
390
|
-
if (indexType === 'hnsw') {
|
|
391
|
-
estimatedRecall = 0.99; // HNSW typically achieves 99%+ recall with default params
|
|
392
|
-
}
|
|
393
|
-
else if (indexType === 'ivfflat') {
|
|
394
|
-
estimatedRecall = 0.95; // IVFFlat typically 95% with probes=10
|
|
395
|
-
}
|
|
396
|
-
results.query.estimatedRecall = estimatedRecall;
|
|
397
|
-
// Cleanup
|
|
398
|
-
if (cleanup) {
|
|
399
|
-
spinner.setText('Cleaning up benchmark data...');
|
|
400
|
-
spinner.start();
|
|
401
|
-
await client.query(`DROP TABLE IF EXISTS ${benchmarkTable}`);
|
|
402
|
-
spinner.succeed('Benchmark data cleaned up');
|
|
403
|
-
}
|
|
404
|
-
else {
|
|
405
|
-
output.printInfo(`Benchmark table retained: ${benchmarkTable}`);
|
|
406
|
-
}
|
|
407
|
-
await client.end();
|
|
408
|
-
// Display results
|
|
409
|
-
output.writeln();
|
|
410
|
-
output.writeln(output.bold('Benchmark Results'));
|
|
411
|
-
output.writeln(output.dim('-'.repeat(60)));
|
|
412
|
-
output.writeln();
|
|
413
|
-
// Insert performance
|
|
414
|
-
output.writeln(output.highlight('Insert Performance:'));
|
|
415
|
-
output.printTable({
|
|
416
|
-
columns: [
|
|
417
|
-
{ key: 'metric', header: 'Metric', width: 25 },
|
|
418
|
-
{ key: 'value', header: 'Value', width: 25 },
|
|
419
|
-
],
|
|
420
|
-
data: [
|
|
421
|
-
{ metric: 'Total Vectors', value: numVectors.toLocaleString() },
|
|
422
|
-
{ metric: 'Total Time', value: `${(insertDuration / 1000).toFixed(2)}s` },
|
|
423
|
-
{ metric: 'Throughput', value: `${insertThroughput.toLocaleString()} vectors/sec` },
|
|
424
|
-
{ metric: 'Index Build Time', value: results.insert.indexTime
|
|
425
|
-
? `${(results.insert.indexTime / 1000).toFixed(2)}s`
|
|
426
|
-
: 'N/A' },
|
|
427
|
-
],
|
|
428
|
-
});
|
|
429
|
-
output.writeln();
|
|
430
|
-
// Query performance
|
|
431
|
-
output.writeln(output.highlight('Query Performance:'));
|
|
432
|
-
output.printTable({
|
|
433
|
-
columns: [
|
|
434
|
-
{ key: 'metric', header: 'Metric', width: 25 },
|
|
435
|
-
{ key: 'value', header: 'Value', width: 25 },
|
|
436
|
-
],
|
|
437
|
-
data: [
|
|
438
|
-
{ metric: 'Total Queries', value: numQueries.toLocaleString() },
|
|
439
|
-
{ metric: 'Avg Latency', value: `${avgLatency.toFixed(2)}ms` },
|
|
440
|
-
{ metric: 'P50 Latency', value: `${p50.toFixed(2)}ms` },
|
|
441
|
-
{ metric: 'P95 Latency', value: `${p95.toFixed(2)}ms` },
|
|
442
|
-
{ metric: 'P99 Latency', value: `${p99.toFixed(2)}ms` },
|
|
443
|
-
{ metric: 'Min/Max Latency', value: `${minLatency.toFixed(2)}ms / ${maxLatency.toFixed(2)}ms` },
|
|
444
|
-
{ metric: 'Queries/Second', value: qps.toLocaleString() },
|
|
445
|
-
{ metric: 'Estimated Recall@K', value: `${(estimatedRecall * 100).toFixed(1)}%` },
|
|
446
|
-
],
|
|
447
|
-
});
|
|
448
|
-
output.writeln();
|
|
449
|
-
// Memory usage
|
|
450
|
-
output.writeln(output.highlight('Memory Usage:'));
|
|
451
|
-
const formatBytes = (b) => {
|
|
452
|
-
if (b < 1024)
|
|
453
|
-
return `${b} B`;
|
|
454
|
-
if (b < 1024 * 1024)
|
|
455
|
-
return `${(b / 1024).toFixed(2)} KB`;
|
|
456
|
-
if (b < 1024 * 1024 * 1024)
|
|
457
|
-
return `${(b / 1024 / 1024).toFixed(2)} MB`;
|
|
458
|
-
return `${(b / 1024 / 1024 / 1024).toFixed(2)} GB`;
|
|
459
|
-
};
|
|
460
|
-
output.printTable({
|
|
461
|
-
columns: [
|
|
462
|
-
{ key: 'metric', header: 'Metric', width: 25 },
|
|
463
|
-
{ key: 'value', header: 'Value', width: 25 },
|
|
464
|
-
],
|
|
465
|
-
data: [
|
|
466
|
-
{ metric: 'Table Size', value: formatBytes(tableSize) },
|
|
467
|
-
{ metric: 'Index Size', value: formatBytes(indexSize) },
|
|
468
|
-
{ metric: 'Total Size', value: formatBytes(totalSize) },
|
|
469
|
-
{ metric: 'Bytes per Vector', value: `${bytesPerVector.toFixed(2)} bytes` },
|
|
470
|
-
],
|
|
471
|
-
});
|
|
472
|
-
output.writeln();
|
|
473
|
-
// Summary box
|
|
474
|
-
const grade = qps >= 1000 ? 'Excellent' :
|
|
475
|
-
qps >= 500 ? 'Good' :
|
|
476
|
-
qps >= 100 ? 'Fair' : 'Needs Optimization';
|
|
477
|
-
const gradeColor = qps >= 1000 ? output.success.bind(output) :
|
|
478
|
-
qps >= 500 ? output.highlight.bind(output) :
|
|
479
|
-
qps >= 100 ? output.warning.bind(output) : output.error.bind(output);
|
|
480
|
-
output.printBox([
|
|
481
|
-
`Performance Grade: ${gradeColor(grade)}`,
|
|
482
|
-
'',
|
|
483
|
-
`Throughput: ${insertThroughput.toLocaleString()} inserts/sec, ${qps.toLocaleString()} queries/sec`,
|
|
484
|
-
`Latency: ${avgLatency.toFixed(2)}ms avg, ${p99.toFixed(2)}ms p99`,
|
|
485
|
-
`Memory: ${formatBytes(bytesPerVector)} per ${dimensions}-dim vector`,
|
|
486
|
-
`Recall: ~${(estimatedRecall * 100).toFixed(0)}% @ k=${topK}`,
|
|
487
|
-
'',
|
|
488
|
-
indexType === 'hnsw' ? 'HNSW index provides excellent recall with good performance.' :
|
|
489
|
-
indexType === 'ivfflat' ? 'IVFFlat index balances memory usage and query speed.' :
|
|
490
|
-
'No index: exact search provides 100% recall but slower queries.',
|
|
491
|
-
].join('\n'), 'Summary');
|
|
492
|
-
return { success: true, data: results };
|
|
493
|
-
}
|
|
494
|
-
catch (error) {
|
|
495
|
-
spinner.fail('Benchmark failed');
|
|
496
|
-
output.printError(error instanceof Error ? error.message : String(error));
|
|
497
|
-
return { success: false, exitCode: 1 };
|
|
498
|
-
}
|
|
499
|
-
},
|
|
500
|
-
};
|
|
501
|
-
export default benchmarkCommand;
|
|
502
|
-
//# sourceMappingURL=benchmark.js.map
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CLI MonoVector Import Command
|
|
3
|
-
* Import data from sql.js/JSON memory to MonoVector PostgreSQL
|
|
4
|
-
*
|
|
5
|
-
* Usage:
|
|
6
|
-
* npx monomind monovector import --input memory-export.json
|
|
7
|
-
* npx monomind monovector import --from-memory
|
|
8
|
-
* npx monomind monovector import --input data.json --batch-size 100
|
|
9
|
-
*
|
|
10
|
-
* https://github.com/monoes/monomind
|
|
11
|
-
*/
|
|
12
|
-
import type { Command } from '../../types.js';
|
|
13
|
-
/**
|
|
14
|
-
* MonoVector Import command - import from sql.js/JSON to PostgreSQL
|
|
15
|
-
*/
|
|
16
|
-
export declare const importCommand: Command;
|
|
17
|
-
export default importCommand;
|
|
18
|
-
//# sourceMappingURL=import.d.ts.map
|