@mastra/pg 1.20.1-alpha.0 → 1.20.1-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/CHANGELOG.md +32 -0
- package/dist/docs/SKILL.md +2 -2
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/docs/references/docs-deployment-workers.md +4 -4
- package/dist/docs/references/docs-memory-working-memory.md +1 -1
- package/dist/docs/references/{docs-storage-overview.md → docs-storage.md} +1 -1
- package/dist/docs/references/integrations-databases-postgresql.md +1 -0
- package/dist/docs/references/reference-memory-memory-class.md +1 -0
- package/dist/index.cjs +129 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +129 -13
- package/dist/index.js.map +1 -1
- package/dist/storage/db/index.d.ts +12 -0
- package/dist/storage/db/index.d.ts.map +1 -1
- package/dist/storage/db/schema-snapshot.d.ts +2 -0
- package/dist/storage/db/schema-snapshot.d.ts.map +1 -1
- package/dist/storage/domains/agents/index.d.ts.map +1 -1
- package/dist/storage/domains/workflows/index.d.ts.map +1 -1
- package/dist/vector/index.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -541,6 +541,7 @@ function buildFilterQuery(filter, minScore, topK) {
|
|
|
541
541
|
}
|
|
542
542
|
//#endregion
|
|
543
543
|
//#region src/vector/index.ts
|
|
544
|
+
const MAX_UPSERT_ROWS_PER_STATEMENT = Math.floor(65535 / 3);
|
|
544
545
|
var PgVector = class extends MastraVector {
|
|
545
546
|
pool;
|
|
546
547
|
describeIndexCache = /* @__PURE__ */ new Map();
|
|
@@ -933,23 +934,41 @@ var PgVector = class extends MastraVector {
|
|
|
933
934
|
const indexInfo = await this.getIndexInfo({ indexName });
|
|
934
935
|
const qualifiedVectorType = this.getVectorTypeName(indexInfo.vectorType, indexInfo.dimension);
|
|
935
936
|
const ops = this.getVectorOps(indexInfo.vectorType, indexInfo.metric ?? "cosine");
|
|
936
|
-
for (let i = 0; i < vectors.length; i++) {
|
|
937
|
+
if (new Set(vectorIds).size !== vectorIds.length) for (let i = 0; i < vectors.length; i++) {
|
|
937
938
|
const vectorStr = ops.formatVector(vectors[i], indexInfo.dimension);
|
|
938
939
|
const query = `
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
`;
|
|
940
|
+
INSERT INTO ${tableName} (vector_id, embedding, metadata)
|
|
941
|
+
VALUES ($1, $2::${qualifiedVectorType}, $3::jsonb)
|
|
942
|
+
ON CONFLICT (vector_id)
|
|
943
|
+
DO UPDATE SET
|
|
944
|
+
embedding = $2::${qualifiedVectorType},
|
|
945
|
+
metadata = $3::jsonb
|
|
946
|
+
`;
|
|
947
947
|
await client.query(query, [
|
|
948
948
|
vectorIds[i],
|
|
949
949
|
vectorStr,
|
|
950
950
|
JSON.stringify(metadata?.[i] || {})
|
|
951
951
|
]);
|
|
952
952
|
}
|
|
953
|
+
else for (let start = 0; start < vectors.length; start += MAX_UPSERT_ROWS_PER_STATEMENT) {
|
|
954
|
+
const end = Math.min(start + MAX_UPSERT_ROWS_PER_STATEMENT, vectors.length);
|
|
955
|
+
const rows = [];
|
|
956
|
+
const values = [];
|
|
957
|
+
for (let i = start; i < end; i++) {
|
|
958
|
+
const base = values.length;
|
|
959
|
+
rows.push(`($${base + 1}, $${base + 2}::${qualifiedVectorType}, $${base + 3}::jsonb)`);
|
|
960
|
+
values.push(vectorIds[i], ops.formatVector(vectors[i], indexInfo.dimension), JSON.stringify(metadata?.[i] || {}));
|
|
961
|
+
}
|
|
962
|
+
const query = `
|
|
963
|
+
INSERT INTO ${tableName} (vector_id, embedding, metadata)
|
|
964
|
+
VALUES ${rows.join(", ")}
|
|
965
|
+
ON CONFLICT (vector_id)
|
|
966
|
+
DO UPDATE SET
|
|
967
|
+
embedding = EXCLUDED.embedding,
|
|
968
|
+
metadata = EXCLUDED.metadata
|
|
969
|
+
`;
|
|
970
|
+
await client.query(query, values);
|
|
971
|
+
}
|
|
953
972
|
await client.query("COMMIT");
|
|
954
973
|
this.logger?.debug(`Upserted ${vectors.length} vectors to ${indexName}`, {
|
|
955
974
|
indexName,
|
|
@@ -2152,7 +2171,7 @@ async function loadSchemaSnapshot(client, schemaName) {
|
|
|
2152
2171
|
const schema = schemaName || "public";
|
|
2153
2172
|
const [tableRows, columnRows, indexRows] = await Promise.all([
|
|
2154
2173
|
client.manyOrNone(`SELECT tablename FROM pg_catalog.pg_tables WHERE schemaname = $1`, [schema]),
|
|
2155
|
-
client.manyOrNone(`SELECT c.relname AS table_name, a.attname AS column_name
|
|
2174
|
+
client.manyOrNone(`SELECT c.relname AS table_name, a.attname AS column_name, format_type(a.atttypid, a.atttypmod) AS data_type
|
|
2156
2175
|
FROM pg_catalog.pg_class c
|
|
2157
2176
|
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
2158
2177
|
JOIN pg_catalog.pg_attribute a ON a.attrelid = c.oid
|
|
@@ -2167,6 +2186,7 @@ async function loadSchemaSnapshot(client, schemaName) {
|
|
|
2167
2186
|
WHERE n.nspname = $1`, [schema])
|
|
2168
2187
|
]);
|
|
2169
2188
|
const columns = /* @__PURE__ */ new Map();
|
|
2189
|
+
const columnTypes = /* @__PURE__ */ new Map();
|
|
2170
2190
|
for (const row of columnRows) {
|
|
2171
2191
|
let set = columns.get(row.table_name);
|
|
2172
2192
|
if (!set) {
|
|
@@ -2174,6 +2194,12 @@ async function loadSchemaSnapshot(client, schemaName) {
|
|
|
2174
2194
|
columns.set(row.table_name, set);
|
|
2175
2195
|
}
|
|
2176
2196
|
set.add(row.column_name);
|
|
2197
|
+
let types = columnTypes.get(row.table_name);
|
|
2198
|
+
if (!types) {
|
|
2199
|
+
types = /* @__PURE__ */ new Map();
|
|
2200
|
+
columnTypes.set(row.table_name, types);
|
|
2201
|
+
}
|
|
2202
|
+
types.set(row.column_name, row.data_type);
|
|
2177
2203
|
}
|
|
2178
2204
|
const indexes = /* @__PURE__ */ new Set();
|
|
2179
2205
|
const replicaIdentityIndexes = /* @__PURE__ */ new Set();
|
|
@@ -2187,6 +2213,7 @@ async function loadSchemaSnapshot(client, schemaName) {
|
|
|
2187
2213
|
schemaName: schema,
|
|
2188
2214
|
tables: new Set(tableRows.map((r) => r.tablename)),
|
|
2189
2215
|
columns,
|
|
2216
|
+
columnTypes,
|
|
2190
2217
|
indexes,
|
|
2191
2218
|
replicaIdentityIndexes,
|
|
2192
2219
|
primaryKeyIndexes
|
|
@@ -2440,6 +2467,8 @@ var PgDB = class extends MastraBase {
|
|
|
2440
2467
|
skipDefaultIndexes;
|
|
2441
2468
|
/** Cache of actual table columns: tableName -> Set<columnName> */
|
|
2442
2469
|
tableColumnsCache = /* @__PURE__ */ new Map();
|
|
2470
|
+
/** Cache of column Postgres data types: tableName -> columnName -> data_type */
|
|
2471
|
+
columnTypeCache = /* @__PURE__ */ new Map();
|
|
2443
2472
|
constructor(config) {
|
|
2444
2473
|
super({
|
|
2445
2474
|
component: "STORAGE",
|
|
@@ -2514,7 +2543,9 @@ var PgDB = class extends MastraBase {
|
|
|
2514
2543
|
}
|
|
2515
2544
|
}
|
|
2516
2545
|
this.tableColumnsCache.delete(oldName);
|
|
2546
|
+
this.columnTypeCache.delete(oldName);
|
|
2517
2547
|
this.tableColumnsCache.delete(newName);
|
|
2548
|
+
this.columnTypeCache.delete(newName);
|
|
2518
2549
|
}
|
|
2519
2550
|
/**
|
|
2520
2551
|
* Records an out-of-band `DROP TABLE` in the init snapshot. See
|
|
@@ -2530,8 +2561,10 @@ var PgDB = class extends MastraBase {
|
|
|
2530
2561
|
if (snapshot) {
|
|
2531
2562
|
snapshot.tables.delete(tableName);
|
|
2532
2563
|
snapshot.columns.delete(tableName);
|
|
2564
|
+
snapshot.columnTypes.delete(tableName);
|
|
2533
2565
|
}
|
|
2534
2566
|
this.tableColumnsCache.delete(tableName);
|
|
2567
|
+
this.columnTypeCache.delete(tableName);
|
|
2535
2568
|
}
|
|
2536
2569
|
/**
|
|
2537
2570
|
* Records an out-of-band `ALTER TABLE … ADD COLUMN` in the init snapshot.
|
|
@@ -2541,6 +2574,7 @@ var PgDB = class extends MastraBase {
|
|
|
2541
2574
|
const snapshot = this.schemaSnapshot;
|
|
2542
2575
|
if (snapshot) this.snapshotColumns(snapshot, tableName).add(column);
|
|
2543
2576
|
this.tableColumnsCache.delete(tableName);
|
|
2577
|
+
this.columnTypeCache.delete(tableName);
|
|
2544
2578
|
}
|
|
2545
2579
|
/**
|
|
2546
2580
|
* Gets the set of column names that actually exist in the database table.
|
|
@@ -2583,6 +2617,50 @@ var PgDB = class extends MastraBase {
|
|
|
2583
2617
|
]);
|
|
2584
2618
|
}
|
|
2585
2619
|
/**
|
|
2620
|
+
* Returns the Postgres data type of a column (e.g. `jsonb`, `json`, `text`),
|
|
2621
|
+
* or null when the table or column does not exist.
|
|
2622
|
+
*
|
|
2623
|
+
* Answered from the init snapshot when one is installed, so a warm `init()`
|
|
2624
|
+
* issues no catalog probe. Outside init, results are cached per instance and
|
|
2625
|
+
* the cache is invalidated alongside {@link tableColumnsCache} whenever DDL
|
|
2626
|
+
* changes a table.
|
|
2627
|
+
*/
|
|
2628
|
+
async getColumnType(table, column) {
|
|
2629
|
+
const snapshot = this.schemaSnapshot;
|
|
2630
|
+
if (snapshot) {
|
|
2631
|
+
const types = snapshot.columnTypes.get(table);
|
|
2632
|
+
const known = types?.get(column) ?? types?.get(column.toLowerCase());
|
|
2633
|
+
if (known) return known;
|
|
2634
|
+
if (types) return null;
|
|
2635
|
+
}
|
|
2636
|
+
const cached = this.columnTypeCache.get(table)?.get(column);
|
|
2637
|
+
if (cached !== void 0) return cached;
|
|
2638
|
+
const schema = this.schemaName || "public";
|
|
2639
|
+
const dataType = (await this.client.oneOrNone(`SELECT data_type FROM information_schema.columns WHERE table_schema = $1 AND table_name = $2 AND (column_name = $3 OR column_name = $4)`, [
|
|
2640
|
+
schema,
|
|
2641
|
+
table,
|
|
2642
|
+
column,
|
|
2643
|
+
column.toLowerCase()
|
|
2644
|
+
]))?.data_type ?? null;
|
|
2645
|
+
if (dataType) {
|
|
2646
|
+
if (snapshot) {
|
|
2647
|
+
let snapshotTypes = snapshot.columnTypes.get(table);
|
|
2648
|
+
if (!snapshotTypes) {
|
|
2649
|
+
snapshotTypes = /* @__PURE__ */ new Map();
|
|
2650
|
+
snapshot.columnTypes.set(table, snapshotTypes);
|
|
2651
|
+
}
|
|
2652
|
+
snapshotTypes.set(column, dataType);
|
|
2653
|
+
}
|
|
2654
|
+
let types = this.columnTypeCache.get(table);
|
|
2655
|
+
if (!types) {
|
|
2656
|
+
types = /* @__PURE__ */ new Map();
|
|
2657
|
+
this.columnTypeCache.set(table, types);
|
|
2658
|
+
}
|
|
2659
|
+
types.set(column, dataType);
|
|
2660
|
+
}
|
|
2661
|
+
return dataType;
|
|
2662
|
+
}
|
|
2663
|
+
/**
|
|
2586
2664
|
* Prepares values for insertion, handling JSONB columns by stringifying them
|
|
2587
2665
|
*/
|
|
2588
2666
|
prepareValuesForInsert(record, tableName) {
|
|
@@ -2781,6 +2859,7 @@ MIGRATION REQUIRED: Duplicate spans detected in ${duplicateInfo.tableName}\n====
|
|
|
2781
2859
|
}, error);
|
|
2782
2860
|
} finally {
|
|
2783
2861
|
this.tableColumnsCache.delete(tableName);
|
|
2862
|
+
this.columnTypeCache.delete(tableName);
|
|
2784
2863
|
}
|
|
2785
2864
|
}
|
|
2786
2865
|
async setupTimestampTriggers(tableName) {
|
|
@@ -3103,6 +3182,7 @@ MIGRATION REQUIRED: Duplicate spans detected in ${duplicateInfo.tableName}\n====
|
|
|
3103
3182
|
}, error);
|
|
3104
3183
|
} finally {
|
|
3105
3184
|
this.tableColumnsCache.delete(tableName);
|
|
3185
|
+
this.columnTypeCache.delete(tableName);
|
|
3106
3186
|
}
|
|
3107
3187
|
}
|
|
3108
3188
|
async load({ tableName, keys }) {
|
|
@@ -3166,6 +3246,7 @@ MIGRATION REQUIRED: Duplicate spans detected in ${duplicateInfo.tableName}\n====
|
|
|
3166
3246
|
}, error);
|
|
3167
3247
|
} finally {
|
|
3168
3248
|
this.tableColumnsCache.delete(tableName);
|
|
3249
|
+
this.columnTypeCache.delete(tableName);
|
|
3169
3250
|
}
|
|
3170
3251
|
}
|
|
3171
3252
|
async createIndex(options) {
|
|
@@ -3717,6 +3798,7 @@ var AgentsPG = class AgentsPG extends AgentsStorage {
|
|
|
3717
3798
|
"workspace",
|
|
3718
3799
|
"skills",
|
|
3719
3800
|
"skillsFormat",
|
|
3801
|
+
"durable",
|
|
3720
3802
|
"browser",
|
|
3721
3803
|
"toolProviders"
|
|
3722
3804
|
]
|
|
@@ -4196,10 +4278,10 @@ var AgentsPG = class AgentsPG extends AgentsStorage {
|
|
|
4196
4278
|
"defaultOptions", workflows, agents, "integrationTools", "toolProviders",
|
|
4197
4279
|
"inputProcessors", "outputProcessors", memory, scorers,
|
|
4198
4280
|
"mcpClients", "requestContextSchema", workspace, skills, "skillsFormat",
|
|
4199
|
-
browser,
|
|
4281
|
+
durable, browser,
|
|
4200
4282
|
"changedFields", "changeMessage",
|
|
4201
4283
|
"createdAt", "createdAtZ"
|
|
4202
|
-
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27)`, [
|
|
4284
|
+
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28)`, [
|
|
4203
4285
|
input.id,
|
|
4204
4286
|
input.agentId,
|
|
4205
4287
|
input.versionNumber,
|
|
@@ -4222,6 +4304,7 @@ var AgentsPG = class AgentsPG extends AgentsStorage {
|
|
|
4222
4304
|
input.workspace ? JSON.stringify(input.workspace) : null,
|
|
4223
4305
|
input.skills ? JSON.stringify(input.skills) : null,
|
|
4224
4306
|
input.skillsFormat ?? null,
|
|
4307
|
+
input.durable !== void 0 ? JSON.stringify(input.durable) : null,
|
|
4225
4308
|
input.browser ? JSON.stringify(input.browser) : null,
|
|
4226
4309
|
input.changedFields ? JSON.stringify(input.changedFields) : null,
|
|
4227
4310
|
input.changeMessage ?? null,
|
|
@@ -4450,6 +4533,7 @@ var AgentsPG = class AgentsPG extends AgentsStorage {
|
|
|
4450
4533
|
workspace: parseJsonResilient(row.workspace, "workspace"),
|
|
4451
4534
|
skills: parseJsonResilient(row.skills, "skills"),
|
|
4452
4535
|
skillsFormat: row.skillsFormat,
|
|
4536
|
+
durable: parseJsonResilient(row.durable, "durable"),
|
|
4453
4537
|
browser: parseJsonResilient(row.browser, "browser"),
|
|
4454
4538
|
changedFields: parseJsonResilient(row.changedFields, "changedFields"),
|
|
4455
4539
|
changeMessage: row.changeMessage,
|
|
@@ -19298,6 +19382,29 @@ function getTableName({ indexName, schemaName }) {
|
|
|
19298
19382
|
const quotedIndexName = `"${indexName}"`;
|
|
19299
19383
|
return schemaName ? `${schemaName}.${quotedIndexName}` : quotedIndexName;
|
|
19300
19384
|
}
|
|
19385
|
+
/** Base name (before any schema prefix) of the expression index backing the status filter. */
|
|
19386
|
+
const WORKFLOW_SNAPSHOT_STATUS_INDEX = "mastra_workflow_snapshot_name_status_createdat_idx";
|
|
19387
|
+
/**
|
|
19388
|
+
* Schema-prefixed name of the status index, lowercased and truncated the same way Postgres
|
|
19389
|
+
* stores it, so the init snapshot's index set answers "does it exist?" without a probe or a
|
|
19390
|
+
* no-op `CREATE INDEX` (schema-prefixed names routinely exceed the 63-byte limit).
|
|
19391
|
+
*/
|
|
19392
|
+
function workflowSnapshotStatusIndexName(schemaName) {
|
|
19393
|
+
return buildConstraintName({
|
|
19394
|
+
baseName: WORKFLOW_SNAPSHOT_STATUS_INDEX,
|
|
19395
|
+
schemaName: schemaName && schemaName !== "public" ? schemaName : void 0
|
|
19396
|
+
});
|
|
19397
|
+
}
|
|
19398
|
+
/**
|
|
19399
|
+
* Expression index on `(workflow_name, snapshot->>'status', "createdAt" DESC)` so
|
|
19400
|
+
* listWorkflowRuns() status filters can use an index instead of scanning every snapshot.
|
|
19401
|
+
*/
|
|
19402
|
+
function workflowSnapshotStatusIndexSQL(indexName, schemaName) {
|
|
19403
|
+
return `CREATE INDEX IF NOT EXISTS "${indexName}" ON ${getTableName({
|
|
19404
|
+
indexName: TABLE_WORKFLOW_SNAPSHOT,
|
|
19405
|
+
schemaName: getSchemaName(schemaName)
|
|
19406
|
+
})} (workflow_name, (snapshot ->> 'status'), "createdAt" DESC)`;
|
|
19407
|
+
}
|
|
19301
19408
|
/**
|
|
19302
19409
|
* Sanitizes JSON string for PostgreSQL jsonb:
|
|
19303
19410
|
* - Removes problematic Unicode sequences:
|
|
@@ -19380,6 +19487,7 @@ var WorkflowsPG = class WorkflowsPG extends WorkflowsStorage {
|
|
|
19380
19487
|
includeAllConstraints: true
|
|
19381
19488
|
}));
|
|
19382
19489
|
for (const idx of WorkflowsPG.getDefaultIndexDefs(schemaPrefix)) statements.push(generateIndexSQL(idx, schemaName));
|
|
19490
|
+
statements.push(`${workflowSnapshotStatusIndexSQL(workflowSnapshotStatusIndexName(parsedSchema), schemaName)};`);
|
|
19383
19491
|
return statements;
|
|
19384
19492
|
}
|
|
19385
19493
|
/**
|
|
@@ -19399,6 +19507,13 @@ var WorkflowsPG = class WorkflowsPG extends WorkflowsStorage {
|
|
|
19399
19507
|
} catch (error) {
|
|
19400
19508
|
this.logger?.warn?.(`Failed to create index ${indexDef.name}:`, error);
|
|
19401
19509
|
}
|
|
19510
|
+
if (await this.#db.getColumnType(TABLE_WORKFLOW_SNAPSHOT, "snapshot") !== "jsonb") return;
|
|
19511
|
+
const indexName = workflowSnapshotStatusIndexName(this.#schema);
|
|
19512
|
+
try {
|
|
19513
|
+
await this.#db.createIndexFromStatement(indexName, workflowSnapshotStatusIndexSQL(indexName, this.#schema));
|
|
19514
|
+
} catch (error) {
|
|
19515
|
+
this.logger?.warn?.(`Failed to create index ${indexName}:`, error);
|
|
19516
|
+
}
|
|
19402
19517
|
}
|
|
19403
19518
|
async init() {
|
|
19404
19519
|
await this.#db.createTable({
|
|
@@ -19688,7 +19803,8 @@ var WorkflowsPG = class WorkflowsPG extends WorkflowsStorage {
|
|
|
19688
19803
|
paramIndex++;
|
|
19689
19804
|
}
|
|
19690
19805
|
if (status) {
|
|
19691
|
-
|
|
19806
|
+
const statusExpr = await this.#db.getColumnType(TABLE_WORKFLOW_SNAPSHOT, "snapshot") === "jsonb" ? `snapshot ->> 'status'` : `regexp_replace(snapshot::text, '\\\\u(0000|[Dd][89A-Fa-f][0-9A-Fa-f]{2})', '', 'g')::jsonb ->> 'status'`;
|
|
19807
|
+
conditions.push(`${statusExpr} = $${paramIndex}`);
|
|
19692
19808
|
values.push(status);
|
|
19693
19809
|
paramIndex++;
|
|
19694
19810
|
}
|