@mastra/pg 0.1.5-alpha.0 → 0.1.5-alpha.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/.turbo/turbo-build.log +5 -5
- package/CHANGELOG.md +8 -0
- package/README.md +16 -10
- package/dist/_tsup-dts-rollup.d.ts +149 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +90 -30
- package/docker-compose.perf.yaml +21 -0
- package/package.json +5 -2
- package/src/vector/index.test.ts +144 -47
- package/src/vector/index.ts +131 -34
- package/src/vector/performance.helpers.ts +286 -0
- package/src/vector/types.ts +16 -0
- package/src/vector/vector.performance.test.ts +371 -0
- package/vitest.config.ts +1 -0
- package/vitest.perf.config.ts +8 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
|
|
2
|
-
> @mastra/pg@0.1.5-alpha.
|
|
2
|
+
> @mastra/pg@0.1.5-alpha.1 build /home/runner/work/mastra/mastra/stores/pg
|
|
3
3
|
> tsup src/index.ts --format esm --experimental-dts --clean --treeshake
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
6
6
|
[34mCLI[39m Using tsconfig: tsconfig.json
|
|
7
7
|
[34mCLI[39m tsup v8.3.6
|
|
8
8
|
[34mTSC[39m Build start
|
|
9
|
-
[32mTSC[39m ⚡️ Build success in
|
|
9
|
+
[32mTSC[39m ⚡️ Build success in 9745ms
|
|
10
10
|
[34mDTS[39m Build start
|
|
11
11
|
[34mCLI[39m Target: es2022
|
|
12
12
|
Analysis will use the bundled TypeScript version 5.7.3
|
|
13
13
|
[36mWriting package typings: /home/runner/work/mastra/mastra/stores/pg/dist/_tsup-dts-rollup.d.ts[39m
|
|
14
|
-
[32mDTS[39m ⚡️ Build success in
|
|
14
|
+
[32mDTS[39m ⚡️ Build success in 6324ms
|
|
15
15
|
[34mCLI[39m Cleaning output folder
|
|
16
16
|
[34mESM[39m Build start
|
|
17
|
-
[32mESM[39m [1mdist/index.js [22m[
|
|
18
|
-
[32mESM[39m ⚡️ Build success in
|
|
17
|
+
[32mESM[39m [1mdist/index.js [22m[32m32.67 KB[39m
|
|
18
|
+
[32mESM[39m ⚡️ Build success in 1005ms
|
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -56,7 +56,7 @@ const store = new PostgresStore({
|
|
|
56
56
|
port: 5432,
|
|
57
57
|
database: 'mastra',
|
|
58
58
|
user: 'postgres',
|
|
59
|
-
password: 'postgres'
|
|
59
|
+
password: 'postgres',
|
|
60
60
|
});
|
|
61
61
|
|
|
62
62
|
// Create a thread
|
|
@@ -64,17 +64,19 @@ await store.saveThread({
|
|
|
64
64
|
id: 'thread-123',
|
|
65
65
|
resourceId: 'resource-456',
|
|
66
66
|
title: 'My Thread',
|
|
67
|
-
metadata: { key: 'value' }
|
|
67
|
+
metadata: { key: 'value' },
|
|
68
68
|
});
|
|
69
69
|
|
|
70
70
|
// Add messages to thread
|
|
71
|
-
await store.saveMessages([
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}]
|
|
71
|
+
await store.saveMessages([
|
|
72
|
+
{
|
|
73
|
+
id: 'msg-789',
|
|
74
|
+
threadId: 'thread-123',
|
|
75
|
+
role: 'user',
|
|
76
|
+
type: 'text',
|
|
77
|
+
content: [{ type: 'text', text: 'Hello' }],
|
|
78
|
+
},
|
|
79
|
+
]);
|
|
78
80
|
|
|
79
81
|
// Query threads and messages
|
|
80
82
|
const savedThread = await store.getThread('thread-123');
|
|
@@ -97,14 +99,18 @@ Connection pool settings:
|
|
|
97
99
|
## Features
|
|
98
100
|
|
|
99
101
|
### Vector Store Features
|
|
102
|
+
|
|
100
103
|
- Vector similarity search with cosine, euclidean, and dot product metrics
|
|
101
104
|
- Advanced metadata filtering with MongoDB-like query syntax
|
|
102
105
|
- Minimum score threshold for queries
|
|
103
106
|
- Automatic UUID generation for vectors
|
|
104
107
|
- Table management (create, list, describe, delete, truncate)
|
|
105
|
-
- Uses pgvector's IVFFLAT indexing with 100 lists
|
|
108
|
+
- Uses pgvector's IVFFLAT indexing with 100 lists by default
|
|
109
|
+
- Supports HNSW indexing with configurable parameters
|
|
110
|
+
- Supports flat indexing
|
|
106
111
|
|
|
107
112
|
### Storage Features
|
|
113
|
+
|
|
108
114
|
- Thread and message storage with JSON support
|
|
109
115
|
- Atomic transactions for data consistency
|
|
110
116
|
- Efficient batch operations
|
|
@@ -19,8 +19,47 @@ import { StorageThreadType } from '@mastra/core/memory';
|
|
|
19
19
|
import { TABLE_NAMES } from '@mastra/core/storage';
|
|
20
20
|
import { WorkflowRunState } from '@mastra/core/workflows';
|
|
21
21
|
|
|
22
|
+
export declare const baseTestConfigs: {
|
|
23
|
+
smokeTests: {
|
|
24
|
+
dimension: number;
|
|
25
|
+
size: number;
|
|
26
|
+
k: number;
|
|
27
|
+
queryCount: number;
|
|
28
|
+
}[];
|
|
29
|
+
'64': {
|
|
30
|
+
dimension: number;
|
|
31
|
+
size: number;
|
|
32
|
+
k: number;
|
|
33
|
+
queryCount: number;
|
|
34
|
+
}[];
|
|
35
|
+
'384': {
|
|
36
|
+
dimension: number;
|
|
37
|
+
size: number;
|
|
38
|
+
k: number;
|
|
39
|
+
queryCount: number;
|
|
40
|
+
}[];
|
|
41
|
+
'1024': {
|
|
42
|
+
dimension: number;
|
|
43
|
+
size: number;
|
|
44
|
+
k: number;
|
|
45
|
+
queryCount: number;
|
|
46
|
+
}[];
|
|
47
|
+
stressTests: {
|
|
48
|
+
dimension: number;
|
|
49
|
+
size: number;
|
|
50
|
+
k: number;
|
|
51
|
+
queryCount: number;
|
|
52
|
+
}[];
|
|
53
|
+
};
|
|
54
|
+
|
|
22
55
|
export declare function buildFilterQuery(filter: Filter, minScore: number): FilterResult;
|
|
23
56
|
|
|
57
|
+
export declare const calculateRecall: (actual: number[], expected: number[], k: number) => number;
|
|
58
|
+
|
|
59
|
+
export declare const calculateTimeout: (dimension: number, size: number, k: number) => number;
|
|
60
|
+
|
|
61
|
+
export declare function cosineSimilarity(a: number[], b: number[]): number;
|
|
62
|
+
|
|
24
63
|
export declare const FILTER_OPERATORS: Record<string, OperatorFn>;
|
|
25
64
|
|
|
26
65
|
declare type FilterOperator = {
|
|
@@ -34,8 +73,60 @@ export declare interface FilterResult {
|
|
|
34
73
|
values: any[];
|
|
35
74
|
}
|
|
36
75
|
|
|
76
|
+
export declare const findNearestBruteForce: (query: number[], vectors: number[][], k: number) => number[];
|
|
77
|
+
|
|
78
|
+
export declare const formatTable: (data: any[], columns: string[]) => string;
|
|
79
|
+
|
|
80
|
+
export declare const generateClusteredVectors: (count: number, dim: number, numClusters?: number) => number[][];
|
|
81
|
+
|
|
82
|
+
export declare const generateRandomVectors: (count: number, dim: number) => number[][];
|
|
83
|
+
|
|
84
|
+
export declare const generateSkewedVectors: (count: number, dim: number) => number[][];
|
|
85
|
+
|
|
86
|
+
export declare const getHNSWConfig: (indexConfig: IndexConfig) => {
|
|
87
|
+
m: number;
|
|
88
|
+
efConstruction: number;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export declare function getIndexDescription({ type, hnsw, }: {
|
|
92
|
+
type: IndexType;
|
|
93
|
+
hnsw: {
|
|
94
|
+
m: number;
|
|
95
|
+
efConstruction: number;
|
|
96
|
+
};
|
|
97
|
+
}): string;
|
|
98
|
+
|
|
99
|
+
export declare const getListCount: (indexConfig: IndexConfig, size: number) => number | undefined;
|
|
100
|
+
|
|
101
|
+
export declare function getSearchEf(k: number, m: number): {
|
|
102
|
+
default: number;
|
|
103
|
+
lower: number;
|
|
104
|
+
higher: number;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export declare const groupBy: <T, K extends keyof T>(array: T[], key: K | ((item: T) => string), reducer?: (group: T[]) => any) => Record<string, any>;
|
|
108
|
+
|
|
37
109
|
export declare const handleKey: (key: string) => string;
|
|
38
110
|
|
|
111
|
+
declare interface HNSWConfig {
|
|
112
|
+
m?: number;
|
|
113
|
+
efConstruction?: number;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export declare interface IndexConfig {
|
|
117
|
+
type?: IndexType;
|
|
118
|
+
ivf?: IVFConfig;
|
|
119
|
+
hnsw?: HNSWConfig;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export declare type IndexType = 'ivfflat' | 'hnsw' | 'flat';
|
|
123
|
+
|
|
124
|
+
declare interface IVFConfig {
|
|
125
|
+
lists?: number;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export declare function measureLatency<T>(fn: () => Promise<T>): Promise<[number, T]>;
|
|
129
|
+
|
|
39
130
|
declare type OperatorFn = (key: string, paramIndex: number, value?: any) => FilterOperator;
|
|
40
131
|
|
|
41
132
|
export declare type OperatorType = BasicOperator | NumericOperator | ArrayOperator | ElementOperator | LogicalOperator | '$contains' | Exclude<RegexOperator, '$options'>;
|
|
@@ -57,15 +148,34 @@ export declare class PGFilterTranslator extends BaseFilterTranslator {
|
|
|
57
148
|
private translateRegexPattern;
|
|
58
149
|
}
|
|
59
150
|
|
|
151
|
+
declare interface PGIndexStats extends IndexStats {
|
|
152
|
+
type: IndexType;
|
|
153
|
+
config: {
|
|
154
|
+
m?: number;
|
|
155
|
+
efConstruction?: number;
|
|
156
|
+
lists?: number;
|
|
157
|
+
probes?: number;
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
export { PGIndexStats }
|
|
161
|
+
export { PGIndexStats as PGIndexStats_alias_1 }
|
|
162
|
+
|
|
60
163
|
declare class PgVector extends MastraVector {
|
|
61
164
|
private pool;
|
|
165
|
+
private indexCache;
|
|
62
166
|
constructor(connectionString: string);
|
|
63
167
|
transformFilter(filter?: Filter): Filter;
|
|
64
|
-
|
|
168
|
+
getIndexInfo(indexName: string): Promise<PGIndexStats>;
|
|
169
|
+
query(indexName: string, queryVector: number[], topK?: number, filter?: Filter, includeVector?: boolean, minScore?: number, // Optional minimum score threshold
|
|
170
|
+
options?: {
|
|
171
|
+
ef?: number;
|
|
172
|
+
probes?: number;
|
|
173
|
+
}): Promise<QueryResult[]>;
|
|
65
174
|
upsert(indexName: string, vectors: number[][], metadata?: Record<string, any>[], ids?: string[]): Promise<string[]>;
|
|
66
|
-
createIndex(indexName: string, dimension: number, metric?: 'cosine' | 'euclidean' | 'dotproduct'): Promise<void>;
|
|
175
|
+
createIndex(indexName: string, dimension: number, metric?: 'cosine' | 'euclidean' | 'dotproduct', indexConfig?: IndexConfig, defineIndex?: boolean): Promise<void>;
|
|
176
|
+
defineIndex(indexName: string, metric: "cosine" | "euclidean" | "dotproduct" | undefined, indexConfig: IndexConfig): Promise<void>;
|
|
67
177
|
listIndexes(): Promise<string[]>;
|
|
68
|
-
describeIndex(indexName: string): Promise<
|
|
178
|
+
describeIndex(indexName: string): Promise<PGIndexStats>;
|
|
69
179
|
deleteIndex(indexName: string): Promise<void>;
|
|
70
180
|
truncateIndex(indexName: string): Promise<void>;
|
|
71
181
|
disconnect(): Promise<void>;
|
|
@@ -151,4 +261,40 @@ declare class PostgresStore extends MastraStorage {
|
|
|
151
261
|
export { PostgresStore }
|
|
152
262
|
export { PostgresStore as PostgresStore_alias_1 }
|
|
153
263
|
|
|
264
|
+
export declare interface TestConfig {
|
|
265
|
+
dimension: number;
|
|
266
|
+
size: number;
|
|
267
|
+
k: number;
|
|
268
|
+
queryCount: number;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export declare interface TestResult {
|
|
272
|
+
distribution: string;
|
|
273
|
+
dimension: number;
|
|
274
|
+
type: IndexType;
|
|
275
|
+
size: number;
|
|
276
|
+
k?: number;
|
|
277
|
+
metrics: {
|
|
278
|
+
recall?: number;
|
|
279
|
+
minRecall?: number;
|
|
280
|
+
maxRecall?: number;
|
|
281
|
+
latency?: {
|
|
282
|
+
p50: number;
|
|
283
|
+
p95: number;
|
|
284
|
+
lists?: number;
|
|
285
|
+
vectorsPerList?: number;
|
|
286
|
+
m?: number;
|
|
287
|
+
ef?: number;
|
|
288
|
+
};
|
|
289
|
+
clustering?: {
|
|
290
|
+
numLists?: number;
|
|
291
|
+
avgVectorsPerList?: number;
|
|
292
|
+
recommendedLists?: number;
|
|
293
|
+
distribution?: string;
|
|
294
|
+
};
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
export declare function warmupQuery(vectorDB: PgVector, indexName: string, dimension: number, k: number): Promise<void>;
|
|
299
|
+
|
|
154
300
|
export { }
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -286,6 +286,7 @@ function buildFilterQuery(filter, minScore) {
|
|
|
286
286
|
// src/vector/index.ts
|
|
287
287
|
var PgVector = class extends MastraVector {
|
|
288
288
|
pool;
|
|
289
|
+
indexCache = /* @__PURE__ */ new Map();
|
|
289
290
|
constructor(connectionString) {
|
|
290
291
|
super();
|
|
291
292
|
const basePool = new pg.Pool({
|
|
@@ -310,12 +311,27 @@ var PgVector = class extends MastraVector {
|
|
|
310
311
|
const translatedFilter = pgFilter.translate(filter ?? {});
|
|
311
312
|
return translatedFilter;
|
|
312
313
|
}
|
|
313
|
-
async
|
|
314
|
+
async getIndexInfo(indexName) {
|
|
315
|
+
if (!this.indexCache.has(indexName)) {
|
|
316
|
+
this.indexCache.set(indexName, await this.describeIndex(indexName));
|
|
317
|
+
}
|
|
318
|
+
return this.indexCache.get(indexName);
|
|
319
|
+
}
|
|
320
|
+
async query(indexName, queryVector, topK = 10, filter, includeVector = false, minScore = 0, options) {
|
|
314
321
|
const client = await this.pool.connect();
|
|
315
322
|
try {
|
|
316
323
|
const vectorStr = `[${queryVector.join(",")}]`;
|
|
317
324
|
const translatedFilter = this.transformFilter(filter);
|
|
318
325
|
const { sql: filterQuery, values: filterValues } = buildFilterQuery(translatedFilter, minScore);
|
|
326
|
+
const indexInfo = await this.getIndexInfo(indexName);
|
|
327
|
+
if (indexInfo.type === "hnsw") {
|
|
328
|
+
const calculatedEf = options?.ef ?? Math.max(topK, (indexInfo?.config?.m ?? 16) * topK);
|
|
329
|
+
const searchEf = Math.min(1e3, Math.max(1, calculatedEf));
|
|
330
|
+
await client.query(`SET LOCAL hnsw.ef_search = ${searchEf}`);
|
|
331
|
+
}
|
|
332
|
+
if (indexInfo.type === "ivfflat" && options?.probes) {
|
|
333
|
+
await client.query(`SET LOCAL ivfflat.probes = ${options.probes}`);
|
|
334
|
+
}
|
|
319
335
|
const query = `
|
|
320
336
|
WITH vector_scores AS (
|
|
321
337
|
SELECT
|
|
@@ -349,13 +365,13 @@ var PgVector = class extends MastraVector {
|
|
|
349
365
|
const vectorIds = ids || vectors.map(() => crypto.randomUUID());
|
|
350
366
|
for (let i = 0; i < vectors.length; i++) {
|
|
351
367
|
const query = `
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
368
|
+
INSERT INTO ${indexName} (vector_id, embedding, metadata)
|
|
369
|
+
VALUES ($1, $2::vector, $3::jsonb)
|
|
370
|
+
ON CONFLICT (vector_id)
|
|
371
|
+
DO UPDATE SET
|
|
372
|
+
embedding = $2::vector,
|
|
373
|
+
metadata = $3::jsonb
|
|
374
|
+
RETURNING embedding::text
|
|
359
375
|
`;
|
|
360
376
|
await client.query(query, [vectorIds[i], `[${vectors[i]?.join(",")}]`, JSON.stringify(metadata?.[i] || {})]);
|
|
361
377
|
}
|
|
@@ -368,7 +384,7 @@ var PgVector = class extends MastraVector {
|
|
|
368
384
|
client.release();
|
|
369
385
|
}
|
|
370
386
|
}
|
|
371
|
-
async createIndex(indexName, dimension, metric = "cosine") {
|
|
387
|
+
async createIndex(indexName, dimension, metric = "cosine", indexConfig = {}, defineIndex = true) {
|
|
372
388
|
const client = await this.pool.connect();
|
|
373
389
|
try {
|
|
374
390
|
if (!indexName.match(/^[a-zA-Z_][a-zA-Z0-9_]*$/)) {
|
|
@@ -394,13 +410,9 @@ var PgVector = class extends MastraVector {
|
|
|
394
410
|
metadata JSONB DEFAULT '{}'::jsonb
|
|
395
411
|
);
|
|
396
412
|
`);
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
ON public.${indexName}
|
|
401
|
-
USING ivfflat (embedding ${indexMethod})
|
|
402
|
-
WITH (lists = 100);
|
|
403
|
-
`);
|
|
413
|
+
if (defineIndex) {
|
|
414
|
+
await this.defineIndex(indexName, metric, indexConfig);
|
|
415
|
+
}
|
|
404
416
|
} catch (error) {
|
|
405
417
|
console.error("Failed to create vector table:", error);
|
|
406
418
|
throw error;
|
|
@@ -408,6 +420,46 @@ var PgVector = class extends MastraVector {
|
|
|
408
420
|
client.release();
|
|
409
421
|
}
|
|
410
422
|
}
|
|
423
|
+
async defineIndex(indexName, metric = "cosine", indexConfig) {
|
|
424
|
+
const client = await this.pool.connect();
|
|
425
|
+
try {
|
|
426
|
+
await client.query(`DROP INDEX IF EXISTS ${indexName}_vector_idx`);
|
|
427
|
+
if (indexConfig.type === "flat") return;
|
|
428
|
+
const metricOp = metric === "cosine" ? "vector_cosine_ops" : metric === "euclidean" ? "vector_l2_ops" : "vector_ip_ops";
|
|
429
|
+
let indexSQL;
|
|
430
|
+
if (indexConfig.type === "hnsw") {
|
|
431
|
+
const m = indexConfig.hnsw?.m ?? 8;
|
|
432
|
+
const efConstruction = indexConfig.hnsw?.efConstruction ?? 32;
|
|
433
|
+
indexSQL = `
|
|
434
|
+
CREATE INDEX ${indexName}_vector_idx
|
|
435
|
+
ON ${indexName}
|
|
436
|
+
USING hnsw (embedding ${metricOp})
|
|
437
|
+
WITH (
|
|
438
|
+
m = ${m},
|
|
439
|
+
ef_construction = ${efConstruction}
|
|
440
|
+
)
|
|
441
|
+
`;
|
|
442
|
+
} else {
|
|
443
|
+
let lists;
|
|
444
|
+
if (indexConfig.ivf?.lists) {
|
|
445
|
+
lists = indexConfig.ivf.lists;
|
|
446
|
+
} else {
|
|
447
|
+
const size = (await client.query(`SELECT COUNT(*) FROM ${indexName}`)).rows[0].count;
|
|
448
|
+
lists = Math.max(100, Math.min(4e3, Math.floor(Math.sqrt(size) * 2)));
|
|
449
|
+
}
|
|
450
|
+
indexSQL = `
|
|
451
|
+
CREATE INDEX ${indexName}_vector_idx
|
|
452
|
+
ON ${indexName}
|
|
453
|
+
USING ivfflat (embedding ${metricOp})
|
|
454
|
+
WITH (lists = ${lists});
|
|
455
|
+
`;
|
|
456
|
+
}
|
|
457
|
+
await client.query(indexSQL);
|
|
458
|
+
this.indexCache.delete(indexName);
|
|
459
|
+
} finally {
|
|
460
|
+
client.release();
|
|
461
|
+
}
|
|
462
|
+
}
|
|
411
463
|
async listIndexes() {
|
|
412
464
|
const client = await this.pool.connect();
|
|
413
465
|
try {
|
|
@@ -436,9 +488,10 @@ var PgVector = class extends MastraVector {
|
|
|
436
488
|
SELECT COUNT(*) as count
|
|
437
489
|
FROM ${indexName};
|
|
438
490
|
`;
|
|
439
|
-
const
|
|
491
|
+
const indexQuery = `
|
|
440
492
|
SELECT
|
|
441
493
|
am.amname as index_method,
|
|
494
|
+
pg_get_indexdef(i.indexrelid) as index_def,
|
|
442
495
|
opclass.opcname as operator_class
|
|
443
496
|
FROM pg_index i
|
|
444
497
|
JOIN pg_class c ON i.indexrelid = c.oid
|
|
@@ -446,26 +499,33 @@ var PgVector = class extends MastraVector {
|
|
|
446
499
|
JOIN pg_opclass opclass ON i.indclass[0] = opclass.oid
|
|
447
500
|
WHERE c.relname = '${indexName}_vector_idx';
|
|
448
501
|
`;
|
|
449
|
-
const [dimResult, countResult,
|
|
502
|
+
const [dimResult, countResult, indexResult] = await Promise.all([
|
|
450
503
|
client.query(dimensionQuery, [indexName]),
|
|
451
504
|
client.query(countQuery),
|
|
452
|
-
client.query(
|
|
505
|
+
client.query(indexQuery)
|
|
453
506
|
]);
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
507
|
+
const { index_method, index_def, operator_class } = indexResult.rows[0] || {
|
|
508
|
+
index_method: "flat",
|
|
509
|
+
index_def: "",
|
|
510
|
+
operator_class: "cosine"
|
|
511
|
+
};
|
|
512
|
+
const metric = operator_class.includes("l2") ? "euclidean" : operator_class.includes("ip") ? "dotproduct" : "cosine";
|
|
513
|
+
const config = {};
|
|
514
|
+
if (index_method === "hnsw") {
|
|
515
|
+
const m = index_def.match(/m\s*=\s*'?(\d+)'?/)?.[1];
|
|
516
|
+
const efConstruction = index_def.match(/ef_construction\s*=\s*'?(\d+)'?/)?.[1];
|
|
517
|
+
if (m) config.m = parseInt(m);
|
|
518
|
+
if (efConstruction) config.efConstruction = parseInt(efConstruction);
|
|
519
|
+
} else if (index_method === "ivfflat") {
|
|
520
|
+
const lists = index_def.match(/lists\s*=\s*'?(\d+)'?/)?.[1];
|
|
521
|
+
if (lists) config.lists = parseInt(lists);
|
|
464
522
|
}
|
|
465
523
|
return {
|
|
466
524
|
dimension: dimResult.rows[0].dimension,
|
|
467
525
|
count: parseInt(countResult.rows[0].count),
|
|
468
|
-
metric
|
|
526
|
+
metric,
|
|
527
|
+
type: index_method,
|
|
528
|
+
config
|
|
469
529
|
};
|
|
470
530
|
} catch (e) {
|
|
471
531
|
await client.query("ROLLBACK");
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
services:
|
|
2
|
+
db:
|
|
3
|
+
image: pgvector/pgvector:pg16
|
|
4
|
+
container_name: 'pg-perf-test-db'
|
|
5
|
+
ports:
|
|
6
|
+
- '5435:5432'
|
|
7
|
+
environment:
|
|
8
|
+
POSTGRES_USER: ${POSTGRES_USER:-postgres}
|
|
9
|
+
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres}
|
|
10
|
+
POSTGRES_DB: ${POSTGRES_DB:-mastra}
|
|
11
|
+
shm_size: 1gb
|
|
12
|
+
command:
|
|
13
|
+
- "postgres"
|
|
14
|
+
- "-c"
|
|
15
|
+
- "shared_buffers=512MB"
|
|
16
|
+
- "-c"
|
|
17
|
+
- "maintenance_work_mem=1024MB"
|
|
18
|
+
- "-c"
|
|
19
|
+
- "work_mem=512MB"
|
|
20
|
+
tmpfs:
|
|
21
|
+
- /var/lib/postgresql/data
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mastra/pg",
|
|
3
|
-
"version": "0.1.5-alpha.
|
|
3
|
+
"version": "0.1.5-alpha.1",
|
|
4
4
|
"description": "Postgres provider for Mastra - includes both vector and db storage capabilities",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"pg": "^8.13.1",
|
|
19
19
|
"pg-promise": "^11.5.4",
|
|
20
|
-
"@mastra/core": "^0.4.2-alpha.
|
|
20
|
+
"@mastra/core": "^0.4.2-alpha.1"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@microsoft/api-extractor": "^7.49.2",
|
|
@@ -32,7 +32,10 @@
|
|
|
32
32
|
"build:watch": "pnpm build --watch",
|
|
33
33
|
"pretest": "docker compose up -d && (for i in $(seq 1 30); do docker compose exec -T db pg_isready -U postgres && break || (sleep 1; [ $i -eq 30 ] && exit 1); done)",
|
|
34
34
|
"test": "vitest run",
|
|
35
|
+
"pretest:perf": "docker compose -f docker-compose.perf.yaml up -d && (for i in $(seq 1 30); do docker compose -f docker-compose.perf.yaml exec -T db pg_isready -U postgres && break || (sleep 1; [ $i -eq 30 ] && exit 1); done)",
|
|
36
|
+
"test:perf": "NODE_OPTIONS='--max-old-space-size=16384' vitest run -c vitest.perf.config.ts",
|
|
35
37
|
"posttest": "docker compose down -v",
|
|
38
|
+
"posttest:perf": "docker compose -f docker-compose.perf.yaml down -v",
|
|
36
39
|
"pretest:watch": "docker compose up -d",
|
|
37
40
|
"test:watch": "vitest watch",
|
|
38
41
|
"posttest:watch": "docker compose down -v"
|