@mastra/pg 1.31.0-alpha.0 → 1.31.0-alpha.2
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/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/docs/references/docs-memory-semantic-recall.md +2 -2
- package/dist/docs/references/docs-memory-working-memory.md +16 -0
- package/dist/docs/references/reference-memory-memory-class.md +2 -2
- package/dist/docs/references/reference-processors-message-history-processor.md +3 -2
- package/dist/docs/references/reference-processors-semantic-recall-processor.md +2 -3
- package/dist/docs/references/reference-processors-working-memory-processor.md +3 -2
- package/dist/docs/references/reference-rag-chunking-and-embedding.md +1 -1
- package/dist/docs/references/reference-rag-metadata-filters.md +1 -1
- package/dist/docs/references/reference-rag-retrieval.md +7 -5
- package/dist/docs/references/reference-rag-vector-databases.md +1 -1
- package/dist/index.cjs +157 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +157 -28
- package/dist/index.js.map +1 -1
- package/dist/storage/db/index.d.ts +18 -0
- package/dist/storage/db/index.d.ts.map +1 -1
- package/dist/storage/db/schema-snapshot.d.ts +9 -1
- package/dist/storage/db/schema-snapshot.d.ts.map +1 -1
- package/dist/storage/domains/experiments/index.d.ts.map +1 -1
- package/dist/storage/domains/memory/index.d.ts +5 -0
- package/dist/storage/domains/memory/index.d.ts.map +1 -1
- package/dist/storage/domains/workflows/index.d.ts +2 -1
- package/dist/storage/domains/workflows/index.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/docs/SKILL.md
CHANGED
|
@@ -411,11 +411,11 @@ PostgreSQL supports both IVFFlat and HNSW indexes. By default, Mastra creates an
|
|
|
411
411
|
|
|
412
412
|
```typescript
|
|
413
413
|
import { Memory } from '@mastra/memory'
|
|
414
|
-
import {
|
|
414
|
+
import { PostgresStore, PgVector } from '@mastra/pg'
|
|
415
415
|
|
|
416
416
|
const agent = new Agent({
|
|
417
417
|
memory: new Memory({
|
|
418
|
-
storage: new
|
|
418
|
+
storage: new PostgresStore({
|
|
419
419
|
id: 'agent-storage',
|
|
420
420
|
connectionString: process.env.DATABASE_URL,
|
|
421
421
|
}),
|
|
@@ -272,6 +272,22 @@ Schema-based working memory uses **merge semantics**, meaning the agent only nee
|
|
|
272
272
|
- **Set a field to `null` to delete it:** This explicitly removes the field from memory
|
|
273
273
|
- **Arrays are replaced entirely:** When an array field is provided, it replaces the existing array (arrays aren't merged element-by-element)
|
|
274
274
|
|
|
275
|
+
### Concurrent updates
|
|
276
|
+
|
|
277
|
+
With `scope: 'resource'` and a storage adapter that supports atomic merges (currently `@mastra/pg`), each merge runs inside a row-locked database transaction. Concurrent updates to different fields from separate processes are all kept. Other adapters merge in the application, which only serializes writers inside a single process.
|
|
278
|
+
|
|
279
|
+
To merge a partial update from your own code, call `memory.mergeWorkingMemory()`:
|
|
280
|
+
|
|
281
|
+
```typescript
|
|
282
|
+
await memory.mergeWorkingMemory({
|
|
283
|
+
threadId: 'thread-1',
|
|
284
|
+
resourceId: 'user-123',
|
|
285
|
+
workingMemory: { preferences: { tone: 'formal' } },
|
|
286
|
+
})
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
It throws if working memory isn't schema-based and resource-scoped, or if the storage adapter doesn't support atomic merges. Check `await memory.supportsAtomicWorkingMemoryMerge()` first if you need to fall back.
|
|
290
|
+
|
|
275
291
|
## Choosing between template and schema
|
|
276
292
|
|
|
277
293
|
- Use a **template** (Markdown) if you want the agent to maintain memory as a free-form text block, such as a user profile or scratchpad. Templates use **replace semantics**: the agent must provide the complete memory content on each update.
|
|
@@ -101,14 +101,14 @@ export const agent = new Agent({
|
|
|
101
101
|
import { Memory } from '@mastra/memory'
|
|
102
102
|
import { Agent } from '@mastra/core/agent'
|
|
103
103
|
import { ModelRouterEmbeddingModel } from '@mastra/core/llm'
|
|
104
|
-
import {
|
|
104
|
+
import { PostgresStore, PgVector } from '@mastra/pg'
|
|
105
105
|
|
|
106
106
|
export const agent = new Agent({
|
|
107
107
|
name: 'pg-agent',
|
|
108
108
|
instructions: 'You are an agent with optimized PostgreSQL memory.',
|
|
109
109
|
model: 'openai/gpt-5.6-sol',
|
|
110
110
|
memory: new Memory({
|
|
111
|
-
storage: new
|
|
111
|
+
storage: new PostgresStore({
|
|
112
112
|
id: 'pg-agent-storage',
|
|
113
113
|
connectionString: process.env.DATABASE_URL,
|
|
114
114
|
}),
|
|
@@ -40,9 +40,10 @@ const processor = new MessageHistory({
|
|
|
40
40
|
```typescript
|
|
41
41
|
import { Agent } from '@mastra/core/agent'
|
|
42
42
|
import { MessageHistory } from '@mastra/core/processors'
|
|
43
|
-
import {
|
|
43
|
+
import { PostgresStore } from '@mastra/pg'
|
|
44
44
|
|
|
45
|
-
const storage = new
|
|
45
|
+
const storage = new PostgresStore({
|
|
46
|
+
id: 'pg-storage',
|
|
46
47
|
connectionString: process.env.DATABASE_URL,
|
|
47
48
|
})
|
|
48
49
|
|
|
@@ -59,11 +59,10 @@ const processor = new SemanticRecall({
|
|
|
59
59
|
```typescript
|
|
60
60
|
import { Agent } from '@mastra/core/agent'
|
|
61
61
|
import { SemanticRecall, MessageHistory } from '@mastra/core/processors'
|
|
62
|
-
import {
|
|
63
|
-
import { PgVector } from '@mastra/pg'
|
|
62
|
+
import { PostgresStore, PgVector } from '@mastra/pg'
|
|
64
63
|
import { openai } from '@ai-sdk/openai'
|
|
65
64
|
|
|
66
|
-
const storage = new
|
|
65
|
+
const storage = new PostgresStore({
|
|
67
66
|
id: 'pg-storage',
|
|
68
67
|
connectionString: process.env.DATABASE_URL,
|
|
69
68
|
})
|
|
@@ -62,9 +62,10 @@ const processor = new WorkingMemory({
|
|
|
62
62
|
```typescript
|
|
63
63
|
import { Agent } from '@mastra/core/agent'
|
|
64
64
|
import { WorkingMemory, MessageHistory } from '@mastra/core/processors'
|
|
65
|
-
import {
|
|
65
|
+
import { PostgresStore } from '@mastra/pg'
|
|
66
66
|
|
|
67
|
-
const storage = new
|
|
67
|
+
const storage = new PostgresStore({
|
|
68
|
+
id: 'pg-storage',
|
|
68
69
|
connectionString: process.env.DATABASE_URL,
|
|
69
70
|
})
|
|
70
71
|
|
|
@@ -231,7 +231,7 @@ await store.createIndex({
|
|
|
231
231
|
- [libSQL](https://mastra.ai/reference/vectors/libsql)
|
|
232
232
|
- [MongoDB](https://mastra.ai/reference/vectors/mongodb)
|
|
233
233
|
- [OracleDB](https://mastra.ai/reference/vectors/oracledb)
|
|
234
|
-
- [
|
|
234
|
+
- [PgVector](https://mastra.ai/reference/vectors/pg)
|
|
235
235
|
- [Pinecone](https://mastra.ai/reference/vectors/pinecone)
|
|
236
236
|
- [Qdrant](https://mastra.ai/reference/vectors/qdrant)
|
|
237
237
|
- [Upstash](https://mastra.ai/reference/vectors/upstash)
|
|
@@ -50,23 +50,25 @@ console.log(results)
|
|
|
50
50
|
|
|
51
51
|
The `topK` parameter specifies the maximum number of most similar results to return from the vector search.
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
Each result includes an `id`, a similarity `score`, and the stored `metadata`. When you store chunk text in metadata, it's available as `metadata.text`:
|
|
54
54
|
|
|
55
55
|
```ts
|
|
56
56
|
[
|
|
57
57
|
{
|
|
58
|
-
|
|
58
|
+
id: 'chunk-1',
|
|
59
59
|
score: 0.89,
|
|
60
|
-
metadata: { source: 'article1.txt' },
|
|
60
|
+
metadata: { text: 'Climate change poses significant challenges...', source: 'article1.txt' },
|
|
61
61
|
},
|
|
62
62
|
{
|
|
63
|
-
|
|
63
|
+
id: 'chunk-2',
|
|
64
64
|
score: 0.82,
|
|
65
|
-
metadata: { source: 'article1.txt' },
|
|
65
|
+
metadata: { text: 'Rising temperatures affect crop yields...', source: 'article1.txt' },
|
|
66
66
|
},
|
|
67
67
|
]
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
+
Results can also include `vector` when you set `includeVector: true`, and `document` when the store returns the original document (Chroma only).
|
|
71
|
+
|
|
70
72
|
## Advanced retrieval options
|
|
71
73
|
|
|
72
74
|
### Metadata Filtering
|
package/dist/index.cjs
CHANGED
|
@@ -2499,7 +2499,8 @@ async function loadSchemaSnapshot(client, schemaName) {
|
|
|
2499
2499
|
const schema = schemaName || "public";
|
|
2500
2500
|
const [tableRows, columnRows, indexRows] = await Promise.all([
|
|
2501
2501
|
client.manyOrNone(`SELECT tablename FROM pg_catalog.pg_tables WHERE schemaname = $1`, [schema]),
|
|
2502
|
-
client.manyOrNone(`SELECT c.relname AS table_name, a.attname AS column_name, format_type(a.atttypid, a.atttypmod) AS data_type
|
|
2502
|
+
client.manyOrNone(`SELECT c.relname AS table_name, a.attname AS column_name, format_type(a.atttypid, a.atttypmod) AS data_type,
|
|
2503
|
+
a.attnotnull AS not_null
|
|
2503
2504
|
FROM pg_catalog.pg_class c
|
|
2504
2505
|
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
2505
2506
|
JOIN pg_catalog.pg_attribute a ON a.attrelid = c.oid
|
|
@@ -2507,7 +2508,7 @@ async function loadSchemaSnapshot(client, schemaName) {
|
|
|
2507
2508
|
AND c.relkind IN ('r', 'p')
|
|
2508
2509
|
AND a.attnum > 0
|
|
2509
2510
|
AND NOT a.attisdropped`, [schema]),
|
|
2510
|
-
client.manyOrNone(`SELECT c.relname AS indexname, i.indisreplident AS is_replica_identity, i.indisprimary AS is_primary
|
|
2511
|
+
client.manyOrNone(`SELECT c.relname AS indexname, i.indisreplident AS is_replica_identity, i.indisprimary AS is_primary, i.indisvalid AS is_valid
|
|
2511
2512
|
FROM pg_catalog.pg_index i
|
|
2512
2513
|
JOIN pg_catalog.pg_class c ON c.oid = i.indexrelid
|
|
2513
2514
|
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
@@ -2515,6 +2516,7 @@ async function loadSchemaSnapshot(client, schemaName) {
|
|
|
2515
2516
|
]);
|
|
2516
2517
|
const columns = /* @__PURE__ */ new Map();
|
|
2517
2518
|
const columnTypes = /* @__PURE__ */ new Map();
|
|
2519
|
+
const notNullColumns = /* @__PURE__ */ new Map();
|
|
2518
2520
|
for (const row of columnRows) {
|
|
2519
2521
|
let set = columns.get(row.table_name);
|
|
2520
2522
|
if (!set) {
|
|
@@ -2528,12 +2530,22 @@ async function loadSchemaSnapshot(client, schemaName) {
|
|
|
2528
2530
|
columnTypes.set(row.table_name, types);
|
|
2529
2531
|
}
|
|
2530
2532
|
types.set(row.column_name, row.data_type);
|
|
2533
|
+
if (row.not_null) {
|
|
2534
|
+
let notNull = notNullColumns.get(row.table_name);
|
|
2535
|
+
if (!notNull) {
|
|
2536
|
+
notNull = /* @__PURE__ */ new Set();
|
|
2537
|
+
notNullColumns.set(row.table_name, notNull);
|
|
2538
|
+
}
|
|
2539
|
+
notNull.add(row.column_name);
|
|
2540
|
+
}
|
|
2531
2541
|
}
|
|
2532
2542
|
const indexes = /* @__PURE__ */ new Set();
|
|
2533
2543
|
const replicaIdentityIndexes = /* @__PURE__ */ new Set();
|
|
2534
2544
|
const primaryKeyIndexes = /* @__PURE__ */ new Set();
|
|
2545
|
+
const invalidIndexes = /* @__PURE__ */ new Set();
|
|
2535
2546
|
for (const row of indexRows) {
|
|
2536
|
-
indexes.add(row.indexname);
|
|
2547
|
+
if (row.is_valid) indexes.add(row.indexname);
|
|
2548
|
+
else invalidIndexes.add(row.indexname);
|
|
2537
2549
|
if (row.is_replica_identity) replicaIdentityIndexes.add(row.indexname);
|
|
2538
2550
|
if (row.is_primary) primaryKeyIndexes.add(row.indexname.toLowerCase());
|
|
2539
2551
|
}
|
|
@@ -2542,9 +2554,11 @@ async function loadSchemaSnapshot(client, schemaName) {
|
|
|
2542
2554
|
tables: new Set(tableRows.map((r) => r.tablename)),
|
|
2543
2555
|
columns,
|
|
2544
2556
|
columnTypes,
|
|
2557
|
+
notNullColumns,
|
|
2545
2558
|
indexes,
|
|
2546
2559
|
replicaIdentityIndexes,
|
|
2547
|
-
primaryKeyIndexes
|
|
2560
|
+
primaryKeyIndexes,
|
|
2561
|
+
invalidIndexes
|
|
2548
2562
|
};
|
|
2549
2563
|
}
|
|
2550
2564
|
//#endregion
|
|
@@ -3619,6 +3633,33 @@ MIGRATION REQUIRED: Duplicate spans detected in ${duplicateInfo.tableName}\n====
|
|
|
3619
3633
|
this.columnTypeCache.delete(tableName);
|
|
3620
3634
|
}
|
|
3621
3635
|
}
|
|
3636
|
+
/**
|
|
3637
|
+
* Drops NOT NULL from `columns` on `tableName` where it is still set. Answered
|
|
3638
|
+
* from the init snapshot when one is installed, so a converged schema issues
|
|
3639
|
+
* no query and never takes the ACCESS EXCLUSIVE lock.
|
|
3640
|
+
*/
|
|
3641
|
+
async dropNotNull({ tableName, columns }) {
|
|
3642
|
+
const parsedColumns = columns.map((c) => (0, _mastra_core_utils.parseSqlIdentifier)(c, "column name"));
|
|
3643
|
+
const snapshot = this.schemaSnapshot;
|
|
3644
|
+
let toAlter;
|
|
3645
|
+
if (snapshot) {
|
|
3646
|
+
const notNull = snapshot.notNullColumns.get(tableName);
|
|
3647
|
+
toAlter = parsedColumns.filter((c) => notNull?.has(c));
|
|
3648
|
+
} else toAlter = (await this.client.any(`SELECT column_name FROM information_schema.columns
|
|
3649
|
+
WHERE table_schema = $1 AND table_name = $2 AND column_name = ANY($3) AND is_nullable = 'NO'`, [
|
|
3650
|
+
this.schemaName || "public",
|
|
3651
|
+
tableName,
|
|
3652
|
+
parsedColumns
|
|
3653
|
+
])).map((r) => r.column_name);
|
|
3654
|
+
if (toAlter.length === 0) return;
|
|
3655
|
+
const fullTableName = getTableName$6({
|
|
3656
|
+
indexName: tableName,
|
|
3657
|
+
schemaName: getSchemaName$6(this.schemaName)
|
|
3658
|
+
});
|
|
3659
|
+
await this.client.none(`ALTER TABLE ${fullTableName} ${toAlter.map((c) => `ALTER COLUMN "${c}" DROP NOT NULL`).join(", ")}`);
|
|
3660
|
+
const notNull = snapshot?.notNullColumns.get(tableName);
|
|
3661
|
+
for (const c of toAlter) notNull?.delete(c);
|
|
3662
|
+
}
|
|
3622
3663
|
async load({ tableName, keys }) {
|
|
3623
3664
|
try {
|
|
3624
3665
|
const keyEntries = Object.entries(keys).map(([key, value]) => [(0, _mastra_core_utils.parseSqlIdentifier)(key, "column name"), value]);
|
|
@@ -3692,11 +3733,8 @@ MIGRATION REQUIRED: Duplicate spans detected in ${duplicateInfo.tableName}\n====
|
|
|
3692
3733
|
schemaName: getSchemaName$6(this.schemaName)
|
|
3693
3734
|
});
|
|
3694
3735
|
const snapshot = this.schemaSnapshot;
|
|
3695
|
-
if (snapshot)
|
|
3696
|
-
|
|
3697
|
-
} else if (await this.client.oneOrNone(`SELECT 1 FROM pg_indexes
|
|
3698
|
-
WHERE indexname = $1
|
|
3699
|
-
AND schemaname = $2`, [name, schemaName])) return;
|
|
3736
|
+
if (snapshot?.indexes.has(name)) return;
|
|
3737
|
+
if (!await this.prepareIndexBuild(name, schemaName)) return;
|
|
3700
3738
|
const uniqueStr = unique ? "UNIQUE " : "";
|
|
3701
3739
|
const concurrentStr = concurrent ? "CONCURRENTLY " : "";
|
|
3702
3740
|
const methodStr = method !== "btree" ? `USING ${method} ` : "";
|
|
@@ -3715,16 +3753,9 @@ MIGRATION REQUIRED: Duplicate spans detected in ${duplicateInfo.tableName}\n====
|
|
|
3715
3753
|
let withStr = "";
|
|
3716
3754
|
if (storage && Object.keys(storage).length > 0) withStr = ` WITH (${Object.entries(storage).map(([key, value]) => `${key} = ${value}`).join(", ")})`;
|
|
3717
3755
|
const sql = `CREATE ${uniqueStr}INDEX ${concurrentStr}${`"${(0, _mastra_core_utils.parseSqlIdentifier)(name, "index name")}"`} ON ${fullTableName} ${methodStr}(${columnsStr})${withStr}${tablespaceStr}${whereStr}`;
|
|
3718
|
-
await this.
|
|
3756
|
+
await this.timedIndexBuild(name, sql);
|
|
3719
3757
|
snapshot?.indexes.add(name);
|
|
3720
3758
|
} catch (error) {
|
|
3721
|
-
if (error instanceof Error && error.message.includes("CONCURRENTLY")) {
|
|
3722
|
-
const retryOptions = {
|
|
3723
|
-
...options,
|
|
3724
|
-
concurrent: false
|
|
3725
|
-
};
|
|
3726
|
-
return this.createIndex(retryOptions);
|
|
3727
|
-
}
|
|
3728
3759
|
throw new _mastra_core_error.MastraError({
|
|
3729
3760
|
id: (0, _mastra_core_storage.createStorageErrorId)("PG", "INDEX_CREATE", "FAILED"),
|
|
3730
3761
|
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
@@ -3748,9 +3779,43 @@ MIGRATION REQUIRED: Duplicate spans detected in ${duplicateInfo.tableName}\n====
|
|
|
3748
3779
|
async createIndexFromStatement(indexName, sql) {
|
|
3749
3780
|
const snapshot = this.schemaSnapshot;
|
|
3750
3781
|
if (snapshot?.indexes.has(indexName)) return;
|
|
3751
|
-
await this.
|
|
3782
|
+
if (!await this.prepareIndexBuild(indexName, this.schemaName || "public")) return;
|
|
3783
|
+
await this.timedIndexBuild(indexName, sql);
|
|
3752
3784
|
snapshot?.indexes.add(indexName);
|
|
3753
3785
|
}
|
|
3786
|
+
/**
|
|
3787
|
+
* Decides whether `indexName` still needs building. Returns false when a valid
|
|
3788
|
+
* index already exists, or when another session is currently building it.
|
|
3789
|
+
* An invalid index left by an interrupted `CREATE INDEX CONCURRENTLY` is
|
|
3790
|
+
* dropped (concurrently) so the caller rebuilds it — `IF NOT EXISTS` would
|
|
3791
|
+
* otherwise keep the broken index forever.
|
|
3792
|
+
*/
|
|
3793
|
+
async prepareIndexBuild(indexName, schemaName) {
|
|
3794
|
+
const snapshot = this.schemaSnapshot;
|
|
3795
|
+
if (snapshot && !snapshot.invalidIndexes.has(indexName)) return true;
|
|
3796
|
+
const existing = await this.client.oneOrNone(`SELECT i.indisvalid AS is_valid,
|
|
3797
|
+
EXISTS (SELECT 1 FROM pg_stat_progress_create_index p WHERE p.index_relid = c.oid) AS building
|
|
3798
|
+
FROM pg_catalog.pg_index i
|
|
3799
|
+
JOIN pg_catalog.pg_class c ON c.oid = i.indexrelid
|
|
3800
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
3801
|
+
WHERE c.relname = $1 AND n.nspname = $2`, [indexName, schemaName]);
|
|
3802
|
+
if (!existing) return true;
|
|
3803
|
+
if (existing.is_valid) return false;
|
|
3804
|
+
if (existing.building) {
|
|
3805
|
+
this.logger.warn(`Index ${indexName} is being built by another session; skipping`);
|
|
3806
|
+
return false;
|
|
3807
|
+
}
|
|
3808
|
+
this.logger.warn(`Dropping invalid index ${indexName} left by an interrupted build; rebuilding it`);
|
|
3809
|
+
const quotedIndexName = `"${(0, _mastra_core_utils.parseSqlIdentifier)(indexName, "index name")}"`;
|
|
3810
|
+
await this.client.none(`DROP INDEX CONCURRENTLY IF EXISTS ${getSchemaName$6(this.schemaName)}.${quotedIndexName}`);
|
|
3811
|
+
return true;
|
|
3812
|
+
}
|
|
3813
|
+
async timedIndexBuild(indexName, sql) {
|
|
3814
|
+
const startedAt = Date.now();
|
|
3815
|
+
this.logger.info(`Creating index ${indexName}`);
|
|
3816
|
+
await this.client.none(sql);
|
|
3817
|
+
this.logger.info(`Created index ${indexName} in ${Date.now() - startedAt}ms`);
|
|
3818
|
+
}
|
|
3754
3819
|
async dropIndex(indexName) {
|
|
3755
3820
|
try {
|
|
3756
3821
|
const schemaName = this.schemaName || "public";
|
|
@@ -7085,6 +7150,10 @@ var ExperimentsPG = class ExperimentsPG extends _mastra_core_storage.Experiments
|
|
|
7085
7150
|
"scorerIds"
|
|
7086
7151
|
]
|
|
7087
7152
|
});
|
|
7153
|
+
await this.#db.dropNotNull({
|
|
7154
|
+
tableName: _mastra_core_storage.TABLE_EXPERIMENTS,
|
|
7155
|
+
columns: ["targetType", "targetId"]
|
|
7156
|
+
});
|
|
7088
7157
|
await this.#db.alterTable({
|
|
7089
7158
|
tableName: _mastra_core_storage.TABLE_EXPERIMENT_RESULTS,
|
|
7090
7159
|
schema: _mastra_core_storage.EXPERIMENT_RESULTS_SCHEMA,
|
|
@@ -10495,6 +10564,7 @@ function dedupeMessagesForSave(messages) {
|
|
|
10495
10564
|
var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
|
|
10496
10565
|
supportsPartialThreadUpdate = true;
|
|
10497
10566
|
supportsObservationalMemory = true;
|
|
10567
|
+
supportsAtomicWorkingMemoryMerge = true;
|
|
10498
10568
|
supportsObservationalMemoryHistorySearch = true;
|
|
10499
10569
|
/**
|
|
10500
10570
|
* Retention-eligible tables. `threads`, `messages`, and `resources` all anchor
|
|
@@ -11841,6 +11911,57 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
|
|
|
11841
11911
|
metadata: metadataJson ? JSON.parse(metadataJson) : resource.metadata
|
|
11842
11912
|
};
|
|
11843
11913
|
}
|
|
11914
|
+
async mergeResourceWorkingMemory({ resourceId, merge }) {
|
|
11915
|
+
const tableName = getTableName$3({
|
|
11916
|
+
indexName: _mastra_core_storage.TABLE_RESOURCES,
|
|
11917
|
+
schemaName: getSchemaName$3(this.#schema)
|
|
11918
|
+
});
|
|
11919
|
+
const callbackError = {};
|
|
11920
|
+
try {
|
|
11921
|
+
return await this.#db.client.tx(async (t) => {
|
|
11922
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
11923
|
+
await t.none(`INSERT INTO ${tableName} (id, metadata, "createdAt", "createdAtZ", "updatedAt", "updatedAtZ")
|
|
11924
|
+
VALUES ($1, $2, $3, $4, $5, $6) ON CONFLICT (id) DO NOTHING`, [
|
|
11925
|
+
resourceId,
|
|
11926
|
+
toPgJson({}),
|
|
11927
|
+
now,
|
|
11928
|
+
now,
|
|
11929
|
+
now,
|
|
11930
|
+
now
|
|
11931
|
+
]);
|
|
11932
|
+
await t.one(`SELECT id FROM ${tableName} WHERE id = $1 FOR UPDATE`, [resourceId]);
|
|
11933
|
+
const existing = await this.#getResourceById(t, resourceId);
|
|
11934
|
+
let workingMemory;
|
|
11935
|
+
try {
|
|
11936
|
+
workingMemory = merge(existing.workingMemory ?? void 0);
|
|
11937
|
+
} catch (error) {
|
|
11938
|
+
callbackError.error = error;
|
|
11939
|
+
throw error;
|
|
11940
|
+
}
|
|
11941
|
+
const updatedAt = /* @__PURE__ */ new Date();
|
|
11942
|
+
const updatedAtStr = updatedAt.toISOString();
|
|
11943
|
+
await t.none(`UPDATE ${tableName} SET "workingMemory" = $1, "updatedAt" = $2, "updatedAtZ" = $3 WHERE id = $4`, [
|
|
11944
|
+
workingMemory,
|
|
11945
|
+
updatedAtStr,
|
|
11946
|
+
updatedAtStr,
|
|
11947
|
+
resourceId
|
|
11948
|
+
]);
|
|
11949
|
+
return {
|
|
11950
|
+
...existing,
|
|
11951
|
+
workingMemory,
|
|
11952
|
+
updatedAt
|
|
11953
|
+
};
|
|
11954
|
+
});
|
|
11955
|
+
} catch (error) {
|
|
11956
|
+
if (error === callbackError.error || error instanceof _mastra_core_error.MastraError) throw error;
|
|
11957
|
+
throw new _mastra_core_error.MastraError({
|
|
11958
|
+
id: (0, _mastra_core_storage.createStorageErrorId)("PG", "MERGE_RESOURCE_WORKING_MEMORY", "FAILED"),
|
|
11959
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
11960
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
|
|
11961
|
+
details: { resourceId }
|
|
11962
|
+
}, error);
|
|
11963
|
+
}
|
|
11964
|
+
}
|
|
11844
11965
|
async updateResource({ resourceId, workingMemory, metadata }) {
|
|
11845
11966
|
const existingResource = await this.#getResourceById(this.#db.client, resourceId);
|
|
11846
11967
|
if (!existingResource) {
|
|
@@ -23208,12 +23329,17 @@ function workflowSnapshotStatusIndexName(schemaName) {
|
|
|
23208
23329
|
/**
|
|
23209
23330
|
* Expression index on `(workflow_name, snapshot->>'status', "createdAt" DESC)` so
|
|
23210
23331
|
* listWorkflowRuns() status filters can use an index instead of scanning every snapshot.
|
|
23332
|
+
*
|
|
23333
|
+
* `concurrently` is set by init, which may build the index on a large existing table while
|
|
23334
|
+
* other processes keep writing to it. The exported DDL stays plain because migration
|
|
23335
|
+
* tooling often wraps statements in a transaction, where CONCURRENTLY is rejected.
|
|
23211
23336
|
*/
|
|
23212
|
-
function workflowSnapshotStatusIndexSQL(indexName, schemaName) {
|
|
23213
|
-
|
|
23337
|
+
function workflowSnapshotStatusIndexSQL(indexName, schemaName, concurrently = false) {
|
|
23338
|
+
const tableName = getTableName({
|
|
23214
23339
|
indexName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
|
|
23215
23340
|
schemaName: getSchemaName(schemaName)
|
|
23216
|
-
})
|
|
23341
|
+
});
|
|
23342
|
+
return `CREATE INDEX ${concurrently ? "CONCURRENTLY " : ""}IF NOT EXISTS "${indexName}" ON ${tableName} (workflow_name, (snapshot ->> 'status'), "createdAt" DESC)`;
|
|
23217
23343
|
}
|
|
23218
23344
|
/** Base name (before any schema prefix) of the expression index backing the threadId filter. */
|
|
23219
23345
|
const WORKFLOW_SNAPSHOT_THREAD_ID_INDEX = "mastra_workflow_snapshot_threadid_idx";
|
|
@@ -23250,12 +23376,14 @@ const WORKFLOW_SNAPSHOT_THREAD_ID_EXPR = `COALESCE(jsonb_path_query_first(snapsh
|
|
|
23250
23376
|
/**
|
|
23251
23377
|
* Expression index on the snapshot-embedded thread id so listWorkflowRuns() threadId filters
|
|
23252
23378
|
* (Agent.listSuspendedRuns) can use an index instead of detoasting every snapshot.
|
|
23379
|
+
* See {@link workflowSnapshotStatusIndexSQL} for `concurrently`.
|
|
23253
23380
|
*/
|
|
23254
|
-
function workflowSnapshotThreadIdIndexSQL(indexName, schemaName) {
|
|
23255
|
-
|
|
23381
|
+
function workflowSnapshotThreadIdIndexSQL(indexName, schemaName, concurrently = false) {
|
|
23382
|
+
const tableName = getTableName({
|
|
23256
23383
|
indexName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
|
|
23257
23384
|
schemaName: getSchemaName(schemaName)
|
|
23258
|
-
})
|
|
23385
|
+
});
|
|
23386
|
+
return `CREATE INDEX ${concurrently ? "CONCURRENTLY " : ""}IF NOT EXISTS "${indexName}" ON ${tableName} ((${WORKFLOW_SNAPSHOT_THREAD_ID_EXPR}))`;
|
|
23259
23387
|
}
|
|
23260
23388
|
var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorage {
|
|
23261
23389
|
#db;
|
|
@@ -23352,13 +23480,13 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
|
|
|
23352
23480
|
if (await this.#db.getColumnType(_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT, "snapshot") !== "jsonb") return;
|
|
23353
23481
|
const indexName = workflowSnapshotStatusIndexName(this.#schema);
|
|
23354
23482
|
try {
|
|
23355
|
-
await this.#db.createIndexFromStatement(indexName, workflowSnapshotStatusIndexSQL(indexName, this.#schema));
|
|
23483
|
+
await this.#db.createIndexFromStatement(indexName, workflowSnapshotStatusIndexSQL(indexName, this.#schema, true));
|
|
23356
23484
|
} catch (error) {
|
|
23357
23485
|
this.logger?.warn?.(`Failed to create index ${indexName}:`, error);
|
|
23358
23486
|
}
|
|
23359
23487
|
const threadIdIndexName = workflowSnapshotThreadIdIndexName(this.#schema);
|
|
23360
23488
|
try {
|
|
23361
|
-
await this.#db.createIndexFromStatement(threadIdIndexName, workflowSnapshotThreadIdIndexSQL(threadIdIndexName, this.#schema));
|
|
23489
|
+
await this.#db.createIndexFromStatement(threadIdIndexName, workflowSnapshotThreadIdIndexSQL(threadIdIndexName, this.#schema, true));
|
|
23362
23490
|
} catch (error) {
|
|
23363
23491
|
this.logger?.warn?.(`Failed to create index ${threadIdIndexName}:`, error);
|
|
23364
23492
|
}
|
|
@@ -23429,7 +23557,7 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
|
|
|
23429
23557
|
async dangerouslyClearAll() {
|
|
23430
23558
|
await this.#db.clearTable({ tableName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT });
|
|
23431
23559
|
}
|
|
23432
|
-
async updateWorkflowResults({ workflowName, runId, stepId, result, requestContext }) {
|
|
23560
|
+
async updateWorkflowResults({ workflowName, runId, stepId, result, requestContext, state }) {
|
|
23433
23561
|
try {
|
|
23434
23562
|
return await this.#db.client.tx(async (t) => {
|
|
23435
23563
|
const tableName = getTableName({
|
|
@@ -23460,7 +23588,8 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
|
|
|
23460
23588
|
snapshot,
|
|
23461
23589
|
stepId,
|
|
23462
23590
|
result,
|
|
23463
|
-
requestContext
|
|
23591
|
+
requestContext,
|
|
23592
|
+
state
|
|
23464
23593
|
});
|
|
23465
23594
|
const now = /* @__PURE__ */ new Date();
|
|
23466
23595
|
const sanitizedSnapshot = toPgJson(snapshot);
|