@mastra/pg 1.22.3-alpha.2 → 1.22.3-alpha.4
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 +6 -484
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/docs/references/docs-deployment-workers.md +6 -0
- package/dist/index.cjs +58 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +58 -0
- package/dist/index.js.map +1 -1
- package/dist/storage/domains/observability/v-next/ddl.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/feedback.d.ts +2 -1
- package/dist/storage/domains/observability/v-next/feedback.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/helpers.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/index.d.ts +2 -1
- package/dist/storage/domains/observability/v-next/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/review-status.d.ts +5 -0
- package/dist/storage/domains/observability/v-next/review-status.d.ts.map +1 -0
- package/dist/storage/domains/observability/v-next/signal-schema.d.ts +4 -0
- package/dist/storage/domains/observability/v-next/signal-schema.d.ts.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -8,11 +8,6 @@ PostgreSQL implementation for Mastra, providing both vector similarity search (u
|
|
|
8
8
|
npm install @mastra/pg
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
## Prerequisites
|
|
12
|
-
|
|
13
|
-
- PostgreSQL server with pgvector extension installed (if using vector store)
|
|
14
|
-
- PostgreSQL 11 or higher
|
|
15
|
-
|
|
16
11
|
## Usage
|
|
17
12
|
|
|
18
13
|
### Vector Store
|
|
@@ -31,487 +26,14 @@ const vectorStore = new PgVector({
|
|
|
31
26
|
});
|
|
32
27
|
```
|
|
33
28
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
```typescript
|
|
37
|
-
const vectorStore = new PgVector({
|
|
38
|
-
host: 'localhost',
|
|
39
|
-
port: 5432,
|
|
40
|
-
database: 'mydb',
|
|
41
|
-
user: 'postgres',
|
|
42
|
-
password: 'password',
|
|
43
|
-
});
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
> **Note:** PgVector also supports advanced configurations like Google Cloud SQL Connector via `pg.ClientConfig`.
|
|
47
|
-
|
|
48
|
-
#### Advanced Options
|
|
49
|
-
|
|
50
|
-
```typescript
|
|
51
|
-
const vectorStore = new PgVector({
|
|
52
|
-
connectionString: 'postgresql://user:pass@localhost:5432/db',
|
|
53
|
-
schemaName: 'custom_schema', // Use custom schema (default: public)
|
|
54
|
-
max: 30, // Max pool connections (default: 20)
|
|
55
|
-
idleTimeoutMillis: 60000, // Idle timeout (default: 30000)
|
|
56
|
-
pgPoolOptions: {
|
|
57
|
-
// Additional pg pool options
|
|
58
|
-
connectionTimeoutMillis: 5000,
|
|
59
|
-
allowExitOnIdle: true,
|
|
60
|
-
},
|
|
61
|
-
});
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
#### Usage Example
|
|
65
|
-
|
|
66
|
-
```typescript
|
|
67
|
-
// Create a new table with vector support
|
|
68
|
-
await vectorStore.createIndex({
|
|
69
|
-
indexName: 'my_vectors',
|
|
70
|
-
dimension: 1536,
|
|
71
|
-
metric: 'cosine',
|
|
72
|
-
// Optional: Configure index type and parameters
|
|
73
|
-
indexConfig: {
|
|
74
|
-
type: 'hnsw', // 'ivfflat' (default), 'hnsw', or 'flat'
|
|
75
|
-
hnsw: {
|
|
76
|
-
m: 16, // Number of connections per layer (default: 8)
|
|
77
|
-
efConstruction: 64 // Size of dynamic list (default: 32)
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
// Add vectors
|
|
83
|
-
const ids = await vectorStore.upsert({
|
|
84
|
-
indexName: 'my_vectors',
|
|
85
|
-
vectors: [[0.1, 0.2, ...], [0.3, 0.4, ...]],
|
|
86
|
-
metadata: [{ text: 'doc1' }, { text: 'doc2' }],
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
// Query vectors
|
|
90
|
-
const results = await vectorStore.query({
|
|
91
|
-
indexName: 'my_vectors',
|
|
92
|
-
queryVector: [0.1, 0.2, ...],
|
|
93
|
-
topK: 10, // topK
|
|
94
|
-
filter: { text: 'doc1' }, // filter
|
|
95
|
-
includeVector: false, // includeVector
|
|
96
|
-
minScore: 0.5, // minScore
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
// Clean up
|
|
100
|
-
await vectorStore.disconnect();
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
### Storage
|
|
104
|
-
|
|
105
|
-
```typescript
|
|
106
|
-
import { PostgresStore } from '@mastra/pg';
|
|
107
|
-
|
|
108
|
-
const store = new PostgresStore({
|
|
109
|
-
host: 'localhost',
|
|
110
|
-
port: 5432,
|
|
111
|
-
database: 'mastra',
|
|
112
|
-
user: 'postgres',
|
|
113
|
-
password: 'postgres',
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
// Create a thread
|
|
117
|
-
await store.saveThread({
|
|
118
|
-
thread: {
|
|
119
|
-
id: 'thread-123',
|
|
120
|
-
resourceId: 'resource-456',
|
|
121
|
-
title: 'My Thread',
|
|
122
|
-
metadata: { key: 'value' },
|
|
123
|
-
createdAt: new Date(),
|
|
124
|
-
},
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
// Add messages to thread
|
|
128
|
-
await store.saveMessages({
|
|
129
|
-
messages: [
|
|
130
|
-
{
|
|
131
|
-
id: 'msg-789',
|
|
132
|
-
threadId: 'thread-123',
|
|
133
|
-
role: 'user',
|
|
134
|
-
content: { content: 'Hello' },
|
|
135
|
-
resourceId: 'resource-456',
|
|
136
|
-
createdAt: new Date(),
|
|
137
|
-
},
|
|
138
|
-
],
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
// Query threads and messages
|
|
142
|
-
const savedThread = await store.getThreadById({ threadId: 'thread-123' });
|
|
143
|
-
const messages = await store.listMessages({ threadId: 'thread-123' });
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
## Configuration
|
|
147
|
-
|
|
148
|
-
### Connection Methods
|
|
149
|
-
|
|
150
|
-
Both `PgVector` and `PostgresStore` support multiple connection methods:
|
|
151
|
-
|
|
152
|
-
1. **Connection String**
|
|
153
|
-
|
|
154
|
-
```typescript
|
|
155
|
-
{
|
|
156
|
-
connectionString: 'postgresql://user:pass@localhost:5432/db';
|
|
157
|
-
}
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
2. **Host/Port/Database**
|
|
161
|
-
```typescript
|
|
162
|
-
{
|
|
163
|
-
host: 'localhost',
|
|
164
|
-
port: 5432,
|
|
165
|
-
database: 'mydb',
|
|
166
|
-
user: 'postgres',
|
|
167
|
-
password: 'password'
|
|
168
|
-
}
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
> **Advanced:** Also supports `pg.ClientConfig` for use cases like Google Cloud SQL Connector with IAM authentication.
|
|
172
|
-
|
|
173
|
-
### Optional Configuration
|
|
174
|
-
|
|
175
|
-
- `schemaName`: Custom PostgreSQL schema (default: `public`)
|
|
176
|
-
- `ssl`: Enable SSL or provide custom SSL options (`true` | `false` | `ConnectionOptions`)
|
|
177
|
-
- `max`: Maximum pool connections (default: `20`)
|
|
178
|
-
- `idleTimeoutMillis`: Idle connection timeout (default: `30000`)
|
|
179
|
-
- `pgPoolOptions`: Additional pg pool options (PgVector only)
|
|
180
|
-
|
|
181
|
-
### Default Connection Pool Settings
|
|
182
|
-
|
|
183
|
-
- Maximum connections: 20
|
|
184
|
-
- Idle timeout: 30 seconds
|
|
185
|
-
- Connection timeout: 2 seconds
|
|
186
|
-
|
|
187
|
-
## Features
|
|
188
|
-
|
|
189
|
-
### Vector Store Features
|
|
190
|
-
|
|
191
|
-
- Vector similarity search with cosine, euclidean, and dot product (inner) metrics
|
|
192
|
-
- Advanced metadata filtering with MongoDB-like query syntax
|
|
193
|
-
- Minimum score threshold for queries
|
|
194
|
-
- Automatic UUID generation for vectors
|
|
195
|
-
- Table management (create, list, describe, delete, truncate)
|
|
196
|
-
- Configurable vector index types:
|
|
197
|
-
- **IVFFlat** (default): Balanced speed/accuracy, auto-calculates optimal lists parameter
|
|
198
|
-
- **HNSW**: Fastest queries, higher memory usage, best for large datasets
|
|
199
|
-
- **Flat**: No index, 100% accuracy, best for small datasets (<1000 vectors)
|
|
200
|
-
|
|
201
|
-
### Storage Features
|
|
202
|
-
|
|
203
|
-
- Thread and message storage with JSON support
|
|
204
|
-
- Atomic transactions for data consistency
|
|
205
|
-
- Efficient batch operations
|
|
206
|
-
- Rich metadata support
|
|
207
|
-
- Timestamp tracking
|
|
208
|
-
- Cascading deletes
|
|
209
|
-
|
|
210
|
-
## Supported Filter Operators
|
|
211
|
-
|
|
212
|
-
The following filter operators are supported for metadata queries:
|
|
213
|
-
|
|
214
|
-
- Comparison: `$eq`, `$ne`, `$gt`, `$gte`, `$lt`, `$lte`
|
|
215
|
-
- Logical: `$and`, `$or`
|
|
216
|
-
- Array: `$in`, `$nin`
|
|
217
|
-
- Text: `$regex`, `$like`
|
|
218
|
-
|
|
219
|
-
Example filter:
|
|
220
|
-
|
|
221
|
-
```typescript
|
|
222
|
-
{
|
|
223
|
-
$and: [{ age: { $gt: 25 } }, { tags: { $in: ['tag1', 'tag2'] } }];
|
|
224
|
-
}
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
## Vector Index Configuration
|
|
228
|
-
|
|
229
|
-
pgvector supports three index types, each with different performance characteristics:
|
|
230
|
-
|
|
231
|
-
### IVFFlat Index (Default)
|
|
232
|
-
|
|
233
|
-
IVFFlat groups vectors into clusters for efficient searching:
|
|
234
|
-
|
|
235
|
-
```typescript
|
|
236
|
-
await vectorStore.createIndex({
|
|
237
|
-
indexName: 'my_vectors',
|
|
238
|
-
dimension: 1536,
|
|
239
|
-
metric: 'cosine',
|
|
240
|
-
indexConfig: {
|
|
241
|
-
type: 'ivfflat',
|
|
242
|
-
ivf: {
|
|
243
|
-
lists: 1000, // Number of clusters (default: auto-calculated as sqrt(rows) * 2)
|
|
244
|
-
},
|
|
245
|
-
},
|
|
246
|
-
});
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
- **Best for:** Medium to large datasets (10K-1M vectors)
|
|
250
|
-
- **Build time:** Minutes for millions of vectors
|
|
251
|
-
- **Query speed:** Fast (tens of milliseconds)
|
|
252
|
-
- **Memory:** Moderate
|
|
253
|
-
- **Accuracy:** ~95-99%
|
|
254
|
-
|
|
255
|
-
### HNSW Index
|
|
256
|
-
|
|
257
|
-
HNSW builds a graph structure for extremely fast searches:
|
|
258
|
-
|
|
259
|
-
```typescript
|
|
260
|
-
await vectorStore.createIndex({
|
|
261
|
-
indexName: 'my_vectors',
|
|
262
|
-
dimension: 1536,
|
|
263
|
-
metric: 'dotproduct', // Recommended for normalized embeddings (OpenAI, etc.)
|
|
264
|
-
indexConfig: {
|
|
265
|
-
type: 'hnsw',
|
|
266
|
-
hnsw: {
|
|
267
|
-
m: 16, // Connections per layer (default: 8, range: 2-100)
|
|
268
|
-
efConstruction: 64, // Dynamic list size (default: 32, range: 4-1000)
|
|
269
|
-
},
|
|
270
|
-
},
|
|
271
|
-
});
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
- **Best for:** Large datasets (100K+ vectors) requiring fastest searches
|
|
275
|
-
- **Build time:** Can take hours for large datasets
|
|
276
|
-
- **Query speed:** Very fast (milliseconds even for millions)
|
|
277
|
-
- **Memory:** High (can be 2-3x vector size)
|
|
278
|
-
- **Accuracy:** ~99%
|
|
279
|
-
|
|
280
|
-
**Tuning HNSW:**
|
|
281
|
-
|
|
282
|
-
- Higher `m`: Better accuracy, more memory (16-32 for high accuracy)
|
|
283
|
-
- Higher `efConstruction`: Better index quality, slower builds (64-200 for quality)
|
|
284
|
-
|
|
285
|
-
### Flat Index (No Index)
|
|
286
|
-
|
|
287
|
-
Uses sequential scan for 100% accuracy:
|
|
288
|
-
|
|
289
|
-
```typescript
|
|
290
|
-
await vectorStore.createIndex({
|
|
291
|
-
indexName: 'my_vectors',
|
|
292
|
-
dimension: 1536,
|
|
293
|
-
metric: 'cosine',
|
|
294
|
-
indexConfig: {
|
|
295
|
-
type: 'flat',
|
|
296
|
-
},
|
|
297
|
-
});
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
- **Best for:** Small datasets (<1000 vectors) or when 100% accuracy is required
|
|
301
|
-
- **Build time:** None
|
|
302
|
-
- **Query speed:** Slow for large datasets (linear scan)
|
|
303
|
-
- **Memory:** Minimal (just vectors)
|
|
304
|
-
- **Accuracy:** 100%
|
|
305
|
-
|
|
306
|
-
### Distance Metrics
|
|
307
|
-
|
|
308
|
-
Choose the appropriate metric for your embeddings:
|
|
309
|
-
|
|
310
|
-
- **`cosine`** (default): Angular similarity, good for text embeddings
|
|
311
|
-
- **`euclidean`**: L2 distance, for unnormalized embeddings
|
|
312
|
-
- **`dotproduct`**: Dot product, optimal for normalized embeddings (OpenAI, Cohere)
|
|
313
|
-
|
|
314
|
-
### Index Recreation
|
|
315
|
-
|
|
316
|
-
The system automatically detects configuration changes and only rebuilds indexes when necessary, preventing the performance issues from unnecessary recreations.
|
|
317
|
-
|
|
318
|
-
**Important behaviors:**
|
|
319
|
-
|
|
320
|
-
- If no `indexConfig` is provided, existing indexes are preserved as-is
|
|
321
|
-
- If `indexConfig` is provided, indexes are only rebuilt if the configuration differs
|
|
322
|
-
- New indexes default to IVFFlat with cosine distance when no config is specified
|
|
323
|
-
|
|
324
|
-
## Vector Store Methods
|
|
325
|
-
|
|
326
|
-
- `createIndex({indexName, dimension, metric?, indexConfig?, buildIndex?})`: Create a new table with vector support
|
|
327
|
-
- `buildIndex({indexName, metric?, indexConfig?})`: Build or rebuild vector index
|
|
328
|
-
- `upsert({indexName, vectors, metadata?, ids?})`: Add or update vectors
|
|
329
|
-
- `query({indexName, queryVector, topK?, filter?, includeVector?, minScore?})`: Search for similar vectors
|
|
330
|
-
- `updateVector({ indexName, id?, filter?, update })`: Update a single vector by ID or metadata filter
|
|
331
|
-
- `deleteVector({ indexName, id })`: Delete a single vector by ID
|
|
332
|
-
- `deleteVectors({ indexName, ids?, filter? })`: Delete multiple vectors by IDs or metadata filter
|
|
333
|
-
- `listIndexes()`: List all vector-enabled tables
|
|
334
|
-
- `describeIndex(indexName)`: Get table statistics and index configuration
|
|
335
|
-
- `deleteIndex(indexName)`: Delete a table
|
|
336
|
-
- `truncateIndex(indexName)`: Remove all data from a table
|
|
337
|
-
- `disconnect()`: Close all database connections
|
|
338
|
-
|
|
339
|
-
## Storage Methods
|
|
340
|
-
|
|
341
|
-
### Thread Operations
|
|
342
|
-
|
|
343
|
-
- `saveThread({ thread })`: Create or update a thread
|
|
344
|
-
- `getThreadById({ threadId })`: Get a thread by ID
|
|
345
|
-
- `updateThread({ id, title, metadata })`: Update thread title and/or metadata
|
|
346
|
-
- `deleteThread({ threadId })`: Delete a thread and its messages
|
|
347
|
-
- `listThreadsByResourceId({ resourceId, offset, limit, orderBy? })`: List paginated threads for a resource
|
|
348
|
-
|
|
349
|
-
### Message Operations
|
|
350
|
-
|
|
351
|
-
- `saveMessages({ messages })`: Save multiple messages in a transaction
|
|
352
|
-
- `listMessages({ threadId, resourceId?, perPage?, page?, orderBy?, filter? })`: Get messages for a thread with pagination
|
|
353
|
-
- `listMessagesById({ messageIds })`: Get specific messages by their IDs
|
|
354
|
-
- `updateMessages({ messages })`: Update existing messages
|
|
355
|
-
- `deleteMessages(messageIds)`: Delete specific messages
|
|
356
|
-
|
|
357
|
-
### Resource Operations
|
|
358
|
-
|
|
359
|
-
- `getResourceById({ resourceId })`: Get a resource by ID
|
|
360
|
-
- `saveResource({ resource })`: Create or save a resource
|
|
361
|
-
- `updateResource({ resourceId, workingMemory })`: Update resource working memory
|
|
362
|
-
|
|
363
|
-
### Workflow Operations
|
|
364
|
-
|
|
365
|
-
- `persistWorkflowSnapshot({ workflowName, runId, snapshot })`: Save workflow state
|
|
366
|
-
- `loadWorkflowSnapshot({ workflowName, runId })`: Load workflow state
|
|
367
|
-
- `listWorkflowRuns({ workflowName, pagination })`: List workflow runs with pagination
|
|
368
|
-
- `getWorkflowRunById({ workflowName, runId })`: Get a specific workflow run
|
|
369
|
-
- `updateWorkflowState({ workflowName, runId, state })`: Update workflow state
|
|
370
|
-
- `updateWorkflowResults({ workflowName, runId, results })`: Update workflow results
|
|
371
|
-
|
|
372
|
-
### AI Observability Operations
|
|
373
|
-
|
|
374
|
-
- `createSpan(span)`: Create a single AI span
|
|
375
|
-
- `batchCreateSpans({ records })`: Create multiple AI spans
|
|
376
|
-
- `updateSpan({ traceId, spanId, updates })`: Update an AI span
|
|
377
|
-
- `batchUpdateSpans({ updates })`: Update multiple AI spans
|
|
378
|
-
- `getTrace(traceId)`: Get an trace by ID
|
|
379
|
-
- `getTracesPaginated({ ...filters, pagination })`: Get paginated traces with filtering
|
|
380
|
-
- `batchDeleteTraces({ traceIds })`: Delete multiple traces
|
|
29
|
+
## Documentation
|
|
381
30
|
|
|
382
|
-
|
|
31
|
+
- [@mastra/pg documentation](https://mastra.ai/reference/vectors/pg)
|
|
383
32
|
|
|
384
|
-
|
|
385
|
-
- `saveScore(score)`: Save an evaluation score
|
|
386
|
-
- `listScoresByScorerId({ scorerId, pagination })`: List scores by scorer with pagination
|
|
387
|
-
- `listScoresByRunId({ runId, pagination })`: List scores by run with pagination
|
|
388
|
-
- `listScoresByEntityId({ entityId, entityType, pagination })`: List scores by entity with pagination
|
|
389
|
-
- `listScoresBySpan({ traceId, spanId, pagination })`: List scores by span with pagination
|
|
33
|
+
## Changelog
|
|
390
34
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
The PostgreSQL store provides comprehensive index management capabilities to optimize query performance.
|
|
394
|
-
|
|
395
|
-
### Automatic Performance Indexes
|
|
396
|
-
|
|
397
|
-
PostgreSQL storage automatically creates composite indexes during initialization for common query patterns:
|
|
398
|
-
|
|
399
|
-
- `mastra_threads_resourceid_createdat_idx`: (resourceId, createdAt DESC)
|
|
400
|
-
- `mastra_messages_thread_id_createdat_idx`: (thread_id, createdAt DESC)
|
|
401
|
-
- `mastra_traces_name_starttime_idx`: (name, startTime DESC)
|
|
402
|
-
- `mastra_evals_agent_name_created_at_idx`: (agent_name, created_at DESC)
|
|
403
|
-
|
|
404
|
-
These indexes significantly improve performance for filtered queries with sorting.
|
|
405
|
-
|
|
406
|
-
### Creating Custom Indexes
|
|
407
|
-
|
|
408
|
-
Create additional indexes to optimize specific query patterns:
|
|
409
|
-
|
|
410
|
-
```typescript
|
|
411
|
-
// Basic index for common queries
|
|
412
|
-
await store.createIndex({
|
|
413
|
-
name: 'idx_threads_resource',
|
|
414
|
-
table: 'mastra_threads',
|
|
415
|
-
columns: ['resourceId'],
|
|
416
|
-
});
|
|
417
|
-
|
|
418
|
-
// Composite index with sort order for filtering + sorting
|
|
419
|
-
await store.createIndex({
|
|
420
|
-
name: 'idx_messages_composite',
|
|
421
|
-
table: 'mastra_messages',
|
|
422
|
-
columns: ['thread_id', 'createdAt DESC'],
|
|
423
|
-
});
|
|
424
|
-
|
|
425
|
-
// GIN index for JSONB columns (fast JSON queries)
|
|
426
|
-
await store.createIndex({
|
|
427
|
-
name: 'idx_traces_attributes',
|
|
428
|
-
table: 'mastra_traces',
|
|
429
|
-
columns: ['attributes'],
|
|
430
|
-
method: 'gin',
|
|
431
|
-
});
|
|
432
|
-
```
|
|
433
|
-
|
|
434
|
-
For more advanced use cases, you can also use:
|
|
435
|
-
|
|
436
|
-
- `unique: true` for unique constraints
|
|
437
|
-
- `where: 'condition'` for partial indexes
|
|
438
|
-
- `method: 'brin'` for time-series data
|
|
439
|
-
- `storage: { fillfactor: 90 }` for update-heavy tables
|
|
440
|
-
- `concurrent: true` for non-blocking creation (default)
|
|
441
|
-
|
|
442
|
-
### Managing Indexes
|
|
443
|
-
|
|
444
|
-
```typescript
|
|
445
|
-
// List all indexes
|
|
446
|
-
const allIndexes = await store.listIndexes();
|
|
447
|
-
|
|
448
|
-
// List indexes for specific table
|
|
449
|
-
const threadIndexes = await store.listIndexes('mastra_threads');
|
|
450
|
-
|
|
451
|
-
// Get detailed statistics for an index
|
|
452
|
-
const stats = await store.describeIndex('idx_threads_resource');
|
|
453
|
-
console.log(stats);
|
|
454
|
-
// {
|
|
455
|
-
// name: 'idx_threads_resource',
|
|
456
|
-
// table: 'mastra_threads',
|
|
457
|
-
// columns: ['resourceId', 'createdAt'],
|
|
458
|
-
// unique: false,
|
|
459
|
-
// size: '128 KB',
|
|
460
|
-
// definition: 'CREATE INDEX idx_threads_resource...',
|
|
461
|
-
// method: 'btree',
|
|
462
|
-
// scans: 1542, // Number of index scans
|
|
463
|
-
// tuples_read: 45230, // Tuples read via index
|
|
464
|
-
// tuples_fetched: 12050 // Tuples fetched via index
|
|
465
|
-
// }
|
|
466
|
-
|
|
467
|
-
// Drop an index
|
|
468
|
-
await store.dropIndex('idx_threads_status');
|
|
469
|
-
```
|
|
470
|
-
|
|
471
|
-
### Index Types and Use Cases
|
|
472
|
-
|
|
473
|
-
| Index Type | Best For | Storage | Speed |
|
|
474
|
-
| ------------------- | --------------------------------------- | ---------- | -------------------------- |
|
|
475
|
-
| **btree** (default) | Range queries, sorting, general purpose | Moderate | Fast |
|
|
476
|
-
| **hash** | Equality comparisons only | Small | Very fast for `=` |
|
|
477
|
-
| **gin** | JSONB, arrays, full-text search | Large | Fast for contains |
|
|
478
|
-
| **gist** | Geometric data, full-text search | Moderate | Fast for nearest-neighbor |
|
|
479
|
-
| **spgist** | Non-balanced data, text patterns | Small | Fast for specific patterns |
|
|
480
|
-
| **brin** | Large tables with natural ordering | Very small | Fast for ranges |
|
|
481
|
-
|
|
482
|
-
### Index Options
|
|
483
|
-
|
|
484
|
-
- `name` (required): Index name
|
|
485
|
-
- `table` (required): Table name
|
|
486
|
-
- `columns` (required): Array of column names (can include DESC/ASC)
|
|
487
|
-
- `unique`: Create unique index (default: false)
|
|
488
|
-
- `concurrent`: Non-blocking index creation (default: true)
|
|
489
|
-
- `where`: Partial index condition
|
|
490
|
-
- `method`: Index type ('btree' | 'hash' | 'gin' | 'gist' | 'spgist' | 'brin')
|
|
491
|
-
- `opclass`: Operator class for GIN/GIST indexes
|
|
492
|
-
- `storage`: Storage parameters (e.g., { fillfactor: 90 })
|
|
493
|
-
- `tablespace`: Tablespace name for index placement
|
|
494
|
-
|
|
495
|
-
### Monitoring Index Performance
|
|
496
|
-
|
|
497
|
-
```typescript
|
|
498
|
-
// Check index usage statistics
|
|
499
|
-
const stats = await store.describeIndex('idx_threads_resource');
|
|
500
|
-
|
|
501
|
-
// Identify unused indexes
|
|
502
|
-
if (stats.scans === 0) {
|
|
503
|
-
console.log(`Index ${stats.name} is unused - consider removing`);
|
|
504
|
-
await store.dropIndex(stats.name);
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
// Monitor index efficiency
|
|
508
|
-
const efficiency = stats.tuples_fetched / stats.tuples_read;
|
|
509
|
-
if (efficiency < 0.5) {
|
|
510
|
-
console.log(`Index ${stats.name} has low efficiency: ${efficiency}`);
|
|
511
|
-
}
|
|
512
|
-
```
|
|
35
|
+
See the [package changelog](https://github.com/mastra-ai/mastra/blob/main/stores/pg/CHANGELOG.md) for version history and release notes.
|
|
513
36
|
|
|
514
|
-
##
|
|
37
|
+
## Support
|
|
515
38
|
|
|
516
|
-
|
|
517
|
-
- [PostgreSQL Documentation](https://www.postgresql.org/docs/)
|
|
39
|
+
We have an [open community Discord](https://discord.gg/mastra-ai). Come and say hello and let us know if you have any questions or need any help getting things running.
|
package/dist/docs/SKILL.md
CHANGED
|
@@ -39,6 +39,12 @@ Polls storage for due cron schedules and publishes `workflow.start` events. It's
|
|
|
39
39
|
|
|
40
40
|
The scheduler reads declarative `schedule` fields from your workflow definitions automatically. See [Scheduled workflows](https://mastra.ai/docs/workflows/scheduled-workflows) for how to declare schedules.
|
|
41
41
|
|
|
42
|
+
The scheduler only polls storage once a schedule exists. At boot, a process with no declared schedules runs a single `listSchedules()` check. If that check finds no rows, the poll loop never starts, so idle deployments issue no recurring scheduler queries and can scale to zero.
|
|
43
|
+
|
|
44
|
+
When a schedule is created at runtime, Mastra publishes a wake event on the PubSub backend. Any process running the default worker set starts its scheduler in response, which is how a standalone worker discovers schedules created by the API process. This requires both processes to share the same PubSub backend.
|
|
45
|
+
|
|
46
|
+
To poll from startup regardless of whether schedules exist (for example, when your processes don't share a PubSub backend), set `scheduler: { enabled: true }` on the worker or run it with `MASTRA_WORKERS=scheduler`.
|
|
47
|
+
|
|
42
48
|
**Don't run more than one scheduler instance.** Multiple schedulers polling the same storage would fire duplicate events for the same schedule.
|
|
43
49
|
|
|
44
50
|
### Background task worker
|
package/dist/index.cjs
CHANGED
|
@@ -14073,6 +14073,11 @@ const FEEDBACK_EVENT_COLUMNS = [
|
|
|
14073
14073
|
type: "text",
|
|
14074
14074
|
nullable: true
|
|
14075
14075
|
},
|
|
14076
|
+
{
|
|
14077
|
+
name: "reviewStatus",
|
|
14078
|
+
type: "text",
|
|
14079
|
+
defaultSql: "'needs-review'"
|
|
14080
|
+
},
|
|
14076
14081
|
...COMMON_CONTEXT_COLUMNS,
|
|
14077
14082
|
{
|
|
14078
14083
|
name: "tags",
|
|
@@ -14513,6 +14518,10 @@ function additiveColumns(schema) {
|
|
|
14513
14518
|
table: TABLE_SPAN_EVENTS,
|
|
14514
14519
|
column: "isPending",
|
|
14515
14520
|
ddl: `ALTER TABLE ${qualifiedTable(schema, TABLE_SPAN_EVENTS)} ADD COLUMN IF NOT EXISTS "isPending" boolean NOT NULL DEFAULT false`
|
|
14521
|
+
}, {
|
|
14522
|
+
table: TABLE_FEEDBACK_EVENTS,
|
|
14523
|
+
column: "reviewStatus",
|
|
14524
|
+
ddl: `ALTER TABLE ${qualifiedTable(schema, TABLE_FEEDBACK_EVENTS)} ADD COLUMN IF NOT EXISTS "reviewStatus" text NOT NULL DEFAULT 'needs-review'`
|
|
14516
14525
|
}];
|
|
14517
14526
|
}
|
|
14518
14527
|
/** Existence probe for an additive column. Params: schema, table, column. */
|
|
@@ -14835,6 +14844,31 @@ function whereOrEmpty(acc) {
|
|
|
14835
14844
|
return acc.conditions.length ? `WHERE ${acc.conditions.join(" AND ")}` : "";
|
|
14836
14845
|
}
|
|
14837
14846
|
//#endregion
|
|
14847
|
+
//#region src/storage/domains/observability/v-next/review-status.ts
|
|
14848
|
+
const FEEDBACK_REVIEW_STATUSES = ["needs-review", "reviewed"];
|
|
14849
|
+
function isFeedbackReviewStatus(value) {
|
|
14850
|
+
return FEEDBACK_REVIEW_STATUSES.some((status) => status === value);
|
|
14851
|
+
}
|
|
14852
|
+
/** Normalize a stored value to a review status, defaulting legacy/unknown values to `needs-review`. */
|
|
14853
|
+
function coerceFeedbackReviewStatus(value) {
|
|
14854
|
+
return isFeedbackReviewStatus(value) ? value : "needs-review";
|
|
14855
|
+
}
|
|
14856
|
+
function parseUpdateFeedbackReviewStatusArgs(args) {
|
|
14857
|
+
const invalid = (text) => new _mastra_core_error.MastraError({
|
|
14858
|
+
id: "OBSERVABILITY_UPDATE_FEEDBACK_REVIEW_STATUS_INVALID_ARGS",
|
|
14859
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
14860
|
+
category: _mastra_core_error.ErrorCategory.USER,
|
|
14861
|
+
text
|
|
14862
|
+
});
|
|
14863
|
+
if (typeof args !== "object" || args === null) throw invalid("args must be an object");
|
|
14864
|
+
if (typeof args.feedbackId !== "string" || args.feedbackId.length === 0) throw invalid("feedbackId is required");
|
|
14865
|
+
if (!isFeedbackReviewStatus(args.reviewStatus)) throw invalid(`reviewStatus must be one of: ${FEEDBACK_REVIEW_STATUSES.join(", ")}`);
|
|
14866
|
+
return {
|
|
14867
|
+
feedbackId: args.feedbackId,
|
|
14868
|
+
reviewStatus: args.reviewStatus
|
|
14869
|
+
};
|
|
14870
|
+
}
|
|
14871
|
+
//#endregion
|
|
14838
14872
|
//#region src/storage/domains/observability/v-next/helpers.ts
|
|
14839
14873
|
const PROMOTED_KEYS = /* @__PURE__ */ new Set([
|
|
14840
14874
|
"experimentId",
|
|
@@ -15199,6 +15233,7 @@ function feedbackRecordToRow(feedback) {
|
|
|
15199
15233
|
spanId: feedback.spanId ?? null,
|
|
15200
15234
|
feedbackUserId: feedback.feedbackUserId ?? null,
|
|
15201
15235
|
sourceId: feedback.sourceId ?? null,
|
|
15236
|
+
reviewStatus: feedback.reviewStatus ?? "needs-review",
|
|
15202
15237
|
feedbackSource,
|
|
15203
15238
|
feedbackType: feedback.feedbackType,
|
|
15204
15239
|
valueString: typeof feedback.value === "string" ? feedback.value : null,
|
|
@@ -15220,6 +15255,7 @@ function rowToFeedbackRecord(row) {
|
|
|
15220
15255
|
spanId: nullableString(row.spanId),
|
|
15221
15256
|
feedbackUserId: nullableString(row.feedbackUserId),
|
|
15222
15257
|
sourceId: nullableString(row.sourceId),
|
|
15258
|
+
reviewStatus: coerceFeedbackReviewStatus(row.reviewStatus),
|
|
15223
15259
|
feedbackSource,
|
|
15224
15260
|
feedbackType: row.feedbackType,
|
|
15225
15261
|
value: hasNumber ? Number(row.valueNumber) : nullableString(row.valueString) ?? "",
|
|
@@ -15739,6 +15775,10 @@ function applyFeedbackFilters(acc, filters) {
|
|
|
15739
15775
|
acc.conditions.push(`"feedbackUserId" = $${acc.next++}`);
|
|
15740
15776
|
acc.params.push(filters.feedbackUserId);
|
|
15741
15777
|
}
|
|
15778
|
+
if (filters?.reviewStatus) {
|
|
15779
|
+
acc.conditions.push(`"reviewStatus" = $${acc.next++}`);
|
|
15780
|
+
acc.params.push(filters.reviewStatus);
|
|
15781
|
+
}
|
|
15742
15782
|
}
|
|
15743
15783
|
/**
|
|
15744
15784
|
* OLAP queries take an explicit feedbackType / feedbackSource pair as
|
|
@@ -15762,6 +15802,21 @@ async function batchCreateFeedback(client, schema, args) {
|
|
|
15762
15802
|
const insert = buildInsert(schema, TABLE_FEEDBACK_EVENTS, args.feedbacks.map(feedbackRecordToRow));
|
|
15763
15803
|
if (insert) await client.query(insert.text, insert.values);
|
|
15764
15804
|
}
|
|
15805
|
+
async function updateFeedbackReviewStatus(client, schema, args) {
|
|
15806
|
+
const { feedbackId, reviewStatus } = parseUpdateFeedbackReviewStatusArgs(args);
|
|
15807
|
+
const row = await client.oneOrNone(`UPDATE ${qualifiedTable(schema, TABLE_FEEDBACK_EVENTS)}
|
|
15808
|
+
SET "reviewStatus" = $2
|
|
15809
|
+
WHERE "feedbackId" = $1
|
|
15810
|
+
RETURNING ${FEEDBACK_SELECT_COLUMNS}`, [feedbackId, reviewStatus]);
|
|
15811
|
+
if (!row) throw new _mastra_core_error.MastraError({
|
|
15812
|
+
id: "OBSERVABILITY_UPDATE_FEEDBACK_REVIEW_STATUS_NOT_FOUND",
|
|
15813
|
+
domain: _mastra_core_error.ErrorDomain.MASTRA_OBSERVABILITY,
|
|
15814
|
+
category: _mastra_core_error.ErrorCategory.USER,
|
|
15815
|
+
text: "Feedback record not found",
|
|
15816
|
+
details: { feedbackId }
|
|
15817
|
+
});
|
|
15818
|
+
return rowToFeedbackRecord(row);
|
|
15819
|
+
}
|
|
15765
15820
|
async function listFeedback(client, schema, args) {
|
|
15766
15821
|
const { mode, filters, pagination, orderBy, after, limit } = _mastra_core_storage.listFeedbackArgsSchema.parse(args);
|
|
15767
15822
|
const table = qualifiedTable(schema, TABLE_FEEDBACK_EVENTS);
|
|
@@ -17625,6 +17680,9 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
|
|
|
17625
17680
|
async listFeedback(args) {
|
|
17626
17681
|
return this.#run("LIST_FEEDBACK", () => listFeedback(this.#client, this.#schema, args));
|
|
17627
17682
|
}
|
|
17683
|
+
async updateFeedbackReviewStatus(args) {
|
|
17684
|
+
return this.#run("UPDATE_FEEDBACK_REVIEW_STATUS", () => updateFeedbackReviewStatus(this.#client, this.#schema, args), { feedbackId: args.feedbackId });
|
|
17685
|
+
}
|
|
17628
17686
|
async getMetricAggregate(args) {
|
|
17629
17687
|
return this.#run("GET_METRIC_AGGREGATE", () => getMetricAggregate(this.#client, this.#schema, args));
|
|
17630
17688
|
}
|