@mastra/pg 1.33.0-alpha.2 → 1.33.0-alpha.3
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/index.cjs +128 -57
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +128 -57
- package/dist/index.js.map +1 -1
- package/dist/storage/db/index.d.ts +14 -0
- package/dist/storage/db/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/index.d.ts +2 -1
- package/dist/storage/domains/observability/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/thread-query.d.ts +0 -2
- package/dist/storage/domains/observability/v-next/thread-query.d.ts.map +1 -1
- package/dist/storage/domains/workflows/index.d.ts +9 -3
- package/dist/storage/domains/workflows/index.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -3996,6 +3996,42 @@ MIGRATION REQUIRED: Duplicate spans detected in ${duplicateInfo.tableName}\n====
|
|
|
3996
3996
|
}, error);
|
|
3997
3997
|
}
|
|
3998
3998
|
}
|
|
3999
|
+
/**
|
|
4000
|
+
* Runs one UPDATE per record in a transaction, changing a row only while every
|
|
4001
|
+
* column in `nullColumns` is still NULL. Never inserts. Returns, in input order,
|
|
4002
|
+
* whether each update changed a row.
|
|
4003
|
+
*/
|
|
4004
|
+
async batchUpdateWhereNull({ tableName, updates, nullColumns }) {
|
|
4005
|
+
const nullConditions = nullColumns.map((column) => ` AND "${parseSqlIdentifier(column, "column name")}" IS NULL`).join("");
|
|
4006
|
+
try {
|
|
4007
|
+
return await this.client.tx(async (tx) => {
|
|
4008
|
+
const applied = [];
|
|
4009
|
+
for (const update of updates) {
|
|
4010
|
+
const query = await this.buildUpdateQuery({
|
|
4011
|
+
tableName,
|
|
4012
|
+
...update
|
|
4013
|
+
});
|
|
4014
|
+
if (!query) {
|
|
4015
|
+
applied.push(false);
|
|
4016
|
+
continue;
|
|
4017
|
+
}
|
|
4018
|
+
const rows = await tx.any(`${query.sql}${nullConditions} RETURNING 1`, query.values);
|
|
4019
|
+
applied.push(rows.length > 0);
|
|
4020
|
+
}
|
|
4021
|
+
return applied;
|
|
4022
|
+
});
|
|
4023
|
+
} catch (error) {
|
|
4024
|
+
throw new MastraError({
|
|
4025
|
+
id: createStorageErrorId("PG", "BATCH_UPDATE_WHERE_NULL", "FAILED"),
|
|
4026
|
+
domain: ErrorDomain.STORAGE,
|
|
4027
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
4028
|
+
details: {
|
|
4029
|
+
tableName,
|
|
4030
|
+
numberOfRecords: updates.length
|
|
4031
|
+
}
|
|
4032
|
+
}, error);
|
|
4033
|
+
}
|
|
4034
|
+
}
|
|
3999
4035
|
async batchDelete({ tableName, keys }) {
|
|
4000
4036
|
try {
|
|
4001
4037
|
if (keys.length === 0) return;
|
|
@@ -4116,9 +4152,14 @@ MIGRATION REQUIRED: Duplicate spans detected in ${duplicateInfo.tableName}\n====
|
|
|
4116
4152
|
columns: [column]
|
|
4117
4153
|
});
|
|
4118
4154
|
}
|
|
4119
|
-
async executeUpdate(client,
|
|
4155
|
+
async executeUpdate(client, args) {
|
|
4156
|
+
const query = await this.buildUpdateQuery(args);
|
|
4157
|
+
if (!query) return;
|
|
4158
|
+
await client.none(query.sql, query.values);
|
|
4159
|
+
}
|
|
4160
|
+
async buildUpdateQuery({ tableName, keys, data }) {
|
|
4120
4161
|
const filteredData = await this.filterRecordToKnownColumns(tableName, data);
|
|
4121
|
-
if (Object.keys(filteredData).length === 0) return;
|
|
4162
|
+
if (Object.keys(filteredData).length === 0) return null;
|
|
4122
4163
|
const setColumns = [];
|
|
4123
4164
|
const setValues = [];
|
|
4124
4165
|
let paramIndex = 1;
|
|
@@ -4134,12 +4175,13 @@ MIGRATION REQUIRED: Duplicate spans detected in ${duplicateInfo.tableName}\n====
|
|
|
4134
4175
|
whereConditions.push(`"${parsedKey}" = $${paramIndex++}`);
|
|
4135
4176
|
whereValues.push(this.prepareValue(value, key, tableName));
|
|
4136
4177
|
});
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4178
|
+
return {
|
|
4179
|
+
sql: `UPDATE ${getTableName$6({
|
|
4180
|
+
indexName: tableName,
|
|
4181
|
+
schemaName: getSchemaName$6(this.schemaName)
|
|
4182
|
+
})} SET ${setColumns.join(", ")} WHERE ${whereConditions.join(" AND ")}`,
|
|
4183
|
+
values: [...setValues, ...whereValues]
|
|
4184
|
+
};
|
|
4143
4185
|
}
|
|
4144
4186
|
};
|
|
4145
4187
|
// src/storage/domains/utils.ts
|
|
@@ -12015,7 +12057,7 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
|
|
|
12015
12057
|
indexName: OM_TABLE,
|
|
12016
12058
|
schemaName: quotedSchemaName
|
|
12017
12059
|
});
|
|
12018
|
-
const idxPrefix = schemaPrefix ?
|
|
12060
|
+
const idxPrefix = schemaPrefix ? schemaPrefix : "";
|
|
12019
12061
|
statements.push(`CREATE INDEX IF NOT EXISTS "${idxPrefix}idx_om_lookup_key" ON ${fullOmTableName} ("lookupKey");`);
|
|
12020
12062
|
}
|
|
12021
12063
|
for (const idx of MemoryPG.getDefaultIndexDefs(schemaPrefix)) statements.push(generateIndexSQL(idx, schemaName));
|
|
@@ -15689,30 +15731,33 @@ var ObservabilityPG = class ObservabilityPG extends ObservabilityStorage {
|
|
|
15689
15731
|
}, error);
|
|
15690
15732
|
}
|
|
15691
15733
|
}
|
|
15734
|
+
#toSpanUpdates(records) {
|
|
15735
|
+
return records.map((record) => {
|
|
15736
|
+
const data = { ...record.updates };
|
|
15737
|
+
if (data.endedAt instanceof Date) {
|
|
15738
|
+
const endedAt = data.endedAt.toISOString();
|
|
15739
|
+
data.endedAt = endedAt;
|
|
15740
|
+
data.endedAtZ = endedAt;
|
|
15741
|
+
}
|
|
15742
|
+
if (data.startedAt instanceof Date) {
|
|
15743
|
+
const startedAt = data.startedAt.toISOString();
|
|
15744
|
+
data.startedAt = startedAt;
|
|
15745
|
+
data.startedAtZ = startedAt;
|
|
15746
|
+
}
|
|
15747
|
+
return {
|
|
15748
|
+
keys: {
|
|
15749
|
+
spanId: record.spanId,
|
|
15750
|
+
traceId: record.traceId
|
|
15751
|
+
},
|
|
15752
|
+
data
|
|
15753
|
+
};
|
|
15754
|
+
});
|
|
15755
|
+
}
|
|
15692
15756
|
async batchUpdateSpans(args) {
|
|
15693
15757
|
try {
|
|
15694
15758
|
return this.#db.batchUpdate({
|
|
15695
15759
|
tableName: TABLE_SPANS,
|
|
15696
|
-
updates: args.records
|
|
15697
|
-
const data = { ...record.updates };
|
|
15698
|
-
if (data.endedAt instanceof Date) {
|
|
15699
|
-
const endedAt = data.endedAt.toISOString();
|
|
15700
|
-
data.endedAt = endedAt;
|
|
15701
|
-
data.endedAtZ = endedAt;
|
|
15702
|
-
}
|
|
15703
|
-
if (data.startedAt instanceof Date) {
|
|
15704
|
-
const startedAt = data.startedAt.toISOString();
|
|
15705
|
-
data.startedAt = startedAt;
|
|
15706
|
-
data.startedAtZ = startedAt;
|
|
15707
|
-
}
|
|
15708
|
-
return {
|
|
15709
|
-
keys: {
|
|
15710
|
-
spanId: record.spanId,
|
|
15711
|
-
traceId: record.traceId
|
|
15712
|
-
},
|
|
15713
|
-
data
|
|
15714
|
-
};
|
|
15715
|
-
})
|
|
15760
|
+
updates: this.#toSpanUpdates(args.records)
|
|
15716
15761
|
});
|
|
15717
15762
|
} catch (error) {
|
|
15718
15763
|
throw new MastraError({
|
|
@@ -15722,6 +15767,25 @@ var ObservabilityPG = class ObservabilityPG extends ObservabilityStorage {
|
|
|
15722
15767
|
}, error);
|
|
15723
15768
|
}
|
|
15724
15769
|
}
|
|
15770
|
+
async batchUpdateUnendedSpans(args) {
|
|
15771
|
+
try {
|
|
15772
|
+
const applied = await this.#db.batchUpdateWhereNull({
|
|
15773
|
+
tableName: TABLE_SPANS,
|
|
15774
|
+
updates: this.#toSpanUpdates(args.records),
|
|
15775
|
+
nullColumns: ["endedAt"]
|
|
15776
|
+
});
|
|
15777
|
+
return { updated: args.records.filter((_, index) => applied[index]).map(({ traceId, spanId }) => ({
|
|
15778
|
+
traceId,
|
|
15779
|
+
spanId
|
|
15780
|
+
})) };
|
|
15781
|
+
} catch (error) {
|
|
15782
|
+
throw new MastraError({
|
|
15783
|
+
id: createStorageErrorId("PG", "BATCH_UPDATE_UNENDED_SPANS", "FAILED"),
|
|
15784
|
+
domain: ErrorDomain.STORAGE,
|
|
15785
|
+
category: ErrorCategory.USER
|
|
15786
|
+
}, error);
|
|
15787
|
+
}
|
|
15788
|
+
}
|
|
15725
15789
|
async batchDeleteTraces(args) {
|
|
15726
15790
|
this.assertUnscopedBatchDeleteTraces(args);
|
|
15727
15791
|
try {
|
|
@@ -20560,7 +20624,7 @@ async function queryThreadRows(client, schema, plan, timeoutMs, strategy = "auto
|
|
|
20560
20624
|
return `"${field}" ${field === "organizationId" || field === "resourceId" ? "IS NOT DISTINCT FROM" : "="} $${values.length}`;
|
|
20561
20625
|
}).join(" AND ")})`;
|
|
20562
20626
|
}));
|
|
20563
|
-
const payloads = await run(`SELECT "organizationId", "traceId", "cursorId",
|
|
20627
|
+
const payloads = await run(`SELECT "organizationId", "traceId", "cursorId", ${contextFields.map((field) => `"${field}"`).join(", ")},
|
|
20564
20628
|
CASE WHEN "spanType" = 'agent_run' THEN "timeToFirstChunkMs" END AS "timeToFirstChunkMs",
|
|
20565
20629
|
CASE WHEN error IS NULL THEN 'success' ELSE 'error' END AS status,
|
|
20566
20630
|
${[
|
|
@@ -20575,30 +20639,10 @@ async function queryThreadRows(client, schema, plan, timeoutMs, strategy = "auto
|
|
|
20575
20639
|
String(version)
|
|
20576
20640
|
]);
|
|
20577
20641
|
const byRoot = new Map(payloads.map((row) => [key(row.organizationId, row.traceId, row.cursorId), row]));
|
|
20578
|
-
const agents = page.filter((row) => byRoot.get(key(row.organizationId, row.firstTraceId, row.firstCursorId))?.spanType === "agent_run");
|
|
20579
|
-
const models = /* @__PURE__ */ new Map();
|
|
20580
|
-
if (agents.length) {
|
|
20581
|
-
const modelValues = [];
|
|
20582
|
-
const rows = await run(`SELECT DISTINCT ON ("organizationId", "traceId") "organizationId", "traceId",
|
|
20583
|
-
CASE WHEN jsonb_typeof(attributes -> 'model') = 'string' THEN attributes ->> 'model' END AS model,
|
|
20584
|
-
CASE WHEN jsonb_typeof(attributes -> 'provider') = 'string' THEN attributes ->> 'provider' END AS provider
|
|
20585
|
-
FROM ${table} WHERE "spanType" = 'model_generation' AND NOT "isPending" AND (${agents.map((row) => `(${Object.entries({
|
|
20586
|
-
organizationId: row.organizationId,
|
|
20587
|
-
resourceId: row.resourceId,
|
|
20588
|
-
traceId: row.firstTraceId,
|
|
20589
|
-
parentSpanId: row.firstSpanId
|
|
20590
|
-
}).map(([field, value]) => {
|
|
20591
|
-
modelValues.push(value);
|
|
20592
|
-
return `"${field}" ${field === "organizationId" || field === "resourceId" ? "IS NOT DISTINCT FROM" : "="} $${modelValues.length}`;
|
|
20593
|
-
}).join(" AND ")})`).join(" OR ")})
|
|
20594
|
-
ORDER BY "organizationId", "traceId", "startedAt", "spanId" COLLATE "C", "cursorId" DESC`, modelValues);
|
|
20595
|
-
for (const row of rows) models.set(JSON.stringify([row.organizationId, row.traceId]), row);
|
|
20596
|
-
}
|
|
20597
20642
|
const threads = page.flatMap((row) => {
|
|
20598
20643
|
const first = byRoot.get(key(row.organizationId, row.firstTraceId, row.firstCursorId));
|
|
20599
20644
|
const last = byRoot.get(key(row.organizationId, row.lastTraceId, row.lastCursorId));
|
|
20600
20645
|
if (!first || !last) return [];
|
|
20601
|
-
const model = models.get(JSON.stringify([row.organizationId, row.firstTraceId]));
|
|
20602
20646
|
return [coreStorage.threadQueryRowSchema.parse({
|
|
20603
20647
|
organizationId: row.organizationId,
|
|
20604
20648
|
threadId: row.threadId,
|
|
@@ -20610,8 +20654,6 @@ async function queryThreadRows(client, schema, plan, timeoutMs, strategy = "auto
|
|
|
20610
20654
|
endedAt: row.endedAt.toISOString(),
|
|
20611
20655
|
traceCount: Number(row.traceCount),
|
|
20612
20656
|
timeToFirstChunkMs: first.timeToFirstChunkMs,
|
|
20613
|
-
model: model?.model ?? null,
|
|
20614
|
-
provider: model?.provider ?? null,
|
|
20615
20657
|
inputPreview: readPreview(first.inputPreview, first.input),
|
|
20616
20658
|
outputPreview: readPreview(last.outputPreview, last.output),
|
|
20617
20659
|
errorPreview: readPreview(last.errorPreview, last.error)
|
|
@@ -25595,6 +25637,13 @@ var WorkflowsPG = class WorkflowsPG extends WorkflowsStorage {
|
|
|
25595
25637
|
supportsConcurrentUpdates() {
|
|
25596
25638
|
return true;
|
|
25597
25639
|
}
|
|
25640
|
+
supportsGuardedSnapshotWrites() {
|
|
25641
|
+
return true;
|
|
25642
|
+
}
|
|
25643
|
+
/** SQL for a snapshot's status. Legacy json/text columns can hold escapes that fail a jsonb cast (#11563). */
|
|
25644
|
+
async #statusExpression(column) {
|
|
25645
|
+
return await this.#db.getColumnType(TABLE_WORKFLOW_SNAPSHOT, "snapshot") === "jsonb" ? `${column} ->> 'status'` : `regexp_replace(${column}::text, '\\\\u(0000|[Dd][89A-Fa-f][0-9A-Fa-f]{2})', '', 'g')::jsonb ->> 'status'`;
|
|
25646
|
+
}
|
|
25598
25647
|
parseWorkflowRun(row) {
|
|
25599
25648
|
let parsedSnapshot = row.snapshot;
|
|
25600
25649
|
if (typeof parsedSnapshot === "string") try {
|
|
@@ -25990,20 +26039,22 @@ var WorkflowsPG = class WorkflowsPG extends WorkflowsStorage {
|
|
|
25990
26039
|
}, error);
|
|
25991
26040
|
}
|
|
25992
26041
|
}
|
|
25993
|
-
async persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot, createdAt, updatedAt, fence }) {
|
|
26042
|
+
async persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot, createdAt, updatedAt, fence, expectedStatus }) {
|
|
25994
26043
|
try {
|
|
25995
26044
|
const now = /* @__PURE__ */ new Date();
|
|
25996
26045
|
const createdAtValue = createdAt ? createdAt : now;
|
|
25997
26046
|
const updatedAtValue = updatedAt ? updatedAt : now;
|
|
25998
26047
|
const sanitizedSnapshot = toPgJson(snapshot);
|
|
25999
|
-
|
|
26048
|
+
const guard = expectedStatus === void 0 ? "" : ` WHERE ${await this.#statusExpression("t.snapshot")} = ANY($9::text[])`;
|
|
26049
|
+
return await withRunFence(this.#db.client, this.#runOwnersTable(), resolveRunFence(this, fence, runId), "persistWorkflowSnapshot", (q) => q.oneOrNone(`INSERT INTO ${getTableName({
|
|
26000
26050
|
indexName: TABLE_WORKFLOW_SNAPSHOT,
|
|
26001
26051
|
schemaName: getSchemaName(this.#schema)
|
|
26002
26052
|
})} AS t
|
|
26003
26053
|
(workflow_name, run_id, "resourceId", snapshot, "createdAt", "updatedAt", "createdAtZ", "updatedAtZ")
|
|
26004
26054
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
|
26005
26055
|
ON CONFLICT (workflow_name, run_id) DO UPDATE
|
|
26006
|
-
SET "resourceId" = COALESCE($3, t."resourceId"), snapshot = $4, "updatedAt" = $6, "updatedAtZ" = $8
|
|
26056
|
+
SET "resourceId" = COALESCE($3, t."resourceId"), snapshot = $4, "updatedAt" = $6, "updatedAtZ" = $8${guard}
|
|
26057
|
+
RETURNING 1 AS written`, [
|
|
26007
26058
|
workflowName,
|
|
26008
26059
|
runId,
|
|
26009
26060
|
resourceId,
|
|
@@ -26011,8 +26062,9 @@ var WorkflowsPG = class WorkflowsPG extends WorkflowsStorage {
|
|
|
26011
26062
|
createdAtValue,
|
|
26012
26063
|
updatedAtValue,
|
|
26013
26064
|
createdAtValue,
|
|
26014
|
-
updatedAtValue
|
|
26015
|
-
|
|
26065
|
+
updatedAtValue,
|
|
26066
|
+
...expectedStatus === void 0 ? [] : [Array.isArray(expectedStatus) ? expectedStatus : [expectedStatus]]
|
|
26067
|
+
])) !== null;
|
|
26016
26068
|
} catch (error) {
|
|
26017
26069
|
if (isRunFenceConflictError(error)) throw error;
|
|
26018
26070
|
throw new MastraError({
|
|
@@ -26022,6 +26074,25 @@ var WorkflowsPG = class WorkflowsPG extends WorkflowsStorage {
|
|
|
26022
26074
|
}, error);
|
|
26023
26075
|
}
|
|
26024
26076
|
}
|
|
26077
|
+
async loadWorkflowRunStatus({ workflowName, runId }) {
|
|
26078
|
+
try {
|
|
26079
|
+
const statusExpr = await this.#statusExpression("snapshot");
|
|
26080
|
+
return (await this.#db.client.oneOrNone(`SELECT ${statusExpr} AS status FROM ${getTableName({
|
|
26081
|
+
indexName: TABLE_WORKFLOW_SNAPSHOT,
|
|
26082
|
+
schemaName: getSchemaName(this.#schema)
|
|
26083
|
+
})} WHERE "workflow_name" = $1 AND "run_id" = $2 ORDER BY "createdAt" DESC LIMIT 1`, [workflowName, runId]))?.status ?? null;
|
|
26084
|
+
} catch (error) {
|
|
26085
|
+
throw new MastraError({
|
|
26086
|
+
id: createStorageErrorId("PG", "LOAD_WORKFLOW_RUN_STATUS", "FAILED"),
|
|
26087
|
+
domain: ErrorDomain.STORAGE,
|
|
26088
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
26089
|
+
details: {
|
|
26090
|
+
workflowName,
|
|
26091
|
+
runId
|
|
26092
|
+
}
|
|
26093
|
+
}, error);
|
|
26094
|
+
}
|
|
26095
|
+
}
|
|
26025
26096
|
async loadWorkflowSnapshot({ workflowName, runId }) {
|
|
26026
26097
|
try {
|
|
26027
26098
|
const result = await this.#db.load({
|