@mastra/pg 0.12.3 → 0.12.4-alpha.0
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 +7 -7
- package/CHANGELOG.md +26 -0
- package/dist/_tsup-dts-rollup.d.cts +346 -63
- package/dist/_tsup-dts-rollup.d.ts +346 -63
- package/dist/index.cjs +1610 -1117
- package/dist/index.js +1611 -1118
- package/package.json +3 -3
- package/src/storage/domains/legacy-evals/index.ts +151 -0
- package/src/storage/domains/memory/index.ts +900 -0
- package/src/storage/domains/operations/index.ts +368 -0
- package/src/storage/domains/scores/index.ts +231 -0
- package/src/storage/domains/traces/index.ts +160 -0
- package/src/storage/domains/utils.ts +12 -0
- package/src/storage/domains/workflows/index.ts +253 -0
- package/src/storage/index.test.ts +5 -2389
- package/src/storage/index.ts +157 -1545
- package/src/storage/test-utils.ts +368 -0
- package/src/vector/index.test.ts +89 -0
- package/src/vector/index.ts +3 -0
package/dist/index.js
CHANGED
|
@@ -5,9 +5,9 @@ import { Mutex } from 'async-mutex';
|
|
|
5
5
|
import pg from 'pg';
|
|
6
6
|
import xxhash from 'xxhash-wasm';
|
|
7
7
|
import { BaseFilterTranslator } from '@mastra/core/vector/filter';
|
|
8
|
-
import {
|
|
9
|
-
import { MastraStorage, TABLE_EVALS, TABLE_TRACES, TABLE_WORKFLOW_SNAPSHOT, TABLE_THREADS, TABLE_MESSAGES, TABLE_RESOURCES } from '@mastra/core/storage';
|
|
8
|
+
import { MastraStorage, StoreOperations, TABLE_WORKFLOW_SNAPSHOT, ScoresStorage, TABLE_SCORERS, TracesStorage, safelyParseJSON, TABLE_TRACES, WorkflowsStorage, LegacyEvalsStorage, MemoryStorage, resolveMessageLimit, TABLE_RESOURCES, TABLE_EVALS, TABLE_THREADS, TABLE_MESSAGES } from '@mastra/core/storage';
|
|
10
9
|
import pgPromise from 'pg-promise';
|
|
10
|
+
import { MessageList } from '@mastra/core/agent';
|
|
11
11
|
|
|
12
12
|
// src/vector/index.ts
|
|
13
13
|
var PGFilterTranslator = class extends BaseFilterTranslator {
|
|
@@ -482,6 +482,7 @@ var PgVector = class extends MastraVector {
|
|
|
482
482
|
}
|
|
483
483
|
const client = await this.pool.connect();
|
|
484
484
|
try {
|
|
485
|
+
await client.query("BEGIN");
|
|
485
486
|
const vectorStr = `[${queryVector.join(",")}]`;
|
|
486
487
|
const translatedFilter = this.transformFilter(filter);
|
|
487
488
|
const { sql: filterQuery, values: filterValues } = buildFilterQuery(translatedFilter, minScore, topK);
|
|
@@ -511,6 +512,7 @@ var PgVector = class extends MastraVector {
|
|
|
511
512
|
ORDER BY score DESC
|
|
512
513
|
LIMIT $2`;
|
|
513
514
|
const result = await client.query(query, filterValues);
|
|
515
|
+
await client.query("COMMIT");
|
|
514
516
|
return result.rows.map(({ id, score, metadata, embedding }) => ({
|
|
515
517
|
id,
|
|
516
518
|
score,
|
|
@@ -518,6 +520,7 @@ var PgVector = class extends MastraVector {
|
|
|
518
520
|
...includeVector && embedding && { vector: JSON.parse(embedding) }
|
|
519
521
|
}));
|
|
520
522
|
} catch (error) {
|
|
523
|
+
await client.query("ROLLBACK");
|
|
521
524
|
const mastraError = new MastraError(
|
|
522
525
|
{
|
|
523
526
|
id: "MASTRA_STORAGE_PG_VECTOR_QUERY_FAILED",
|
|
@@ -1096,77 +1099,55 @@ var PgVector = class extends MastraVector {
|
|
|
1096
1099
|
}
|
|
1097
1100
|
}
|
|
1098
1101
|
};
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1102
|
+
function getSchemaName(schema) {
|
|
1103
|
+
return schema ? `"${parseSqlIdentifier(schema, "schema name")}"` : void 0;
|
|
1104
|
+
}
|
|
1105
|
+
function getTableName({ indexName, schemaName }) {
|
|
1106
|
+
const parsedIndexName = parseSqlIdentifier(indexName, "index name");
|
|
1107
|
+
const quotedIndexName = `"${parsedIndexName}"`;
|
|
1108
|
+
const quotedSchemaName = schemaName;
|
|
1109
|
+
return quotedSchemaName ? `${quotedSchemaName}.${quotedIndexName}` : quotedIndexName;
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
// src/storage/domains/legacy-evals/index.ts
|
|
1113
|
+
function transformEvalRow(row) {
|
|
1114
|
+
let testInfoValue = null;
|
|
1115
|
+
if (row.test_info) {
|
|
1106
1116
|
try {
|
|
1107
|
-
|
|
1108
|
-
if (!config.connectionString || typeof config.connectionString !== "string" || config.connectionString.trim() === "") {
|
|
1109
|
-
throw new Error(
|
|
1110
|
-
"PostgresStore: connectionString must be provided and cannot be empty. Passing an empty string may cause fallback to local Postgres defaults."
|
|
1111
|
-
);
|
|
1112
|
-
}
|
|
1113
|
-
} else {
|
|
1114
|
-
const required = ["host", "database", "user", "password"];
|
|
1115
|
-
for (const key of required) {
|
|
1116
|
-
if (!(key in config) || typeof config[key] !== "string" || config[key].trim() === "") {
|
|
1117
|
-
throw new Error(
|
|
1118
|
-
`PostgresStore: ${key} must be provided and cannot be empty. Passing an empty string may cause fallback to local Postgres defaults.`
|
|
1119
|
-
);
|
|
1120
|
-
}
|
|
1121
|
-
}
|
|
1122
|
-
}
|
|
1123
|
-
super({ name: "PostgresStore" });
|
|
1124
|
-
this.pgp = pgPromise();
|
|
1125
|
-
this.schema = config.schemaName;
|
|
1126
|
-
this.db = this.pgp(
|
|
1127
|
-
`connectionString` in config ? { connectionString: config.connectionString } : {
|
|
1128
|
-
host: config.host,
|
|
1129
|
-
port: config.port,
|
|
1130
|
-
database: config.database,
|
|
1131
|
-
user: config.user,
|
|
1132
|
-
password: config.password,
|
|
1133
|
-
ssl: config.ssl
|
|
1134
|
-
}
|
|
1135
|
-
);
|
|
1117
|
+
testInfoValue = typeof row.test_info === "string" ? JSON.parse(row.test_info) : row.test_info;
|
|
1136
1118
|
} catch (e) {
|
|
1137
|
-
|
|
1138
|
-
{
|
|
1139
|
-
id: "MASTRA_STORAGE_PG_STORE_INITIALIZATION_FAILED",
|
|
1140
|
-
domain: ErrorDomain.STORAGE,
|
|
1141
|
-
category: ErrorCategory.USER
|
|
1142
|
-
},
|
|
1143
|
-
e
|
|
1144
|
-
);
|
|
1119
|
+
console.warn("Failed to parse test_info:", e);
|
|
1145
1120
|
}
|
|
1146
1121
|
}
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
}
|
|
1159
|
-
|
|
1160
|
-
|
|
1122
|
+
return {
|
|
1123
|
+
agentName: row.agent_name,
|
|
1124
|
+
input: row.input,
|
|
1125
|
+
output: row.output,
|
|
1126
|
+
result: row.result,
|
|
1127
|
+
metricName: row.metric_name,
|
|
1128
|
+
instructions: row.instructions,
|
|
1129
|
+
testInfo: testInfoValue,
|
|
1130
|
+
globalRunId: row.global_run_id,
|
|
1131
|
+
runId: row.run_id,
|
|
1132
|
+
createdAt: row.created_atZ || row.created_at
|
|
1133
|
+
};
|
|
1134
|
+
}
|
|
1135
|
+
var LegacyEvalsPG = class extends LegacyEvalsStorage {
|
|
1136
|
+
client;
|
|
1137
|
+
schema;
|
|
1138
|
+
constructor({ client, schema }) {
|
|
1139
|
+
super();
|
|
1140
|
+
this.client = client;
|
|
1141
|
+
this.schema = schema;
|
|
1161
1142
|
}
|
|
1162
1143
|
/** @deprecated use getEvals instead */
|
|
1163
1144
|
async getEvalsByAgentName(agentName, type) {
|
|
1164
1145
|
try {
|
|
1165
|
-
const baseQuery = `SELECT * FROM ${
|
|
1146
|
+
const baseQuery = `SELECT * FROM ${getTableName({ indexName: TABLE_EVALS, schemaName: getSchemaName(this.schema) })} WHERE agent_name = $1`;
|
|
1166
1147
|
const typeCondition = type === "test" ? " AND test_info IS NOT NULL AND test_info->>'testPath' IS NOT NULL" : type === "live" ? " AND (test_info IS NULL OR test_info->>'testPath' IS NULL)" : "";
|
|
1167
1148
|
const query = `${baseQuery}${typeCondition} ORDER BY created_at DESC`;
|
|
1168
|
-
const rows = await this.
|
|
1169
|
-
return rows?.map((row) =>
|
|
1149
|
+
const rows = await this.client.manyOrNone(query, [agentName]);
|
|
1150
|
+
return rows?.map((row) => transformEvalRow(row)) ?? [];
|
|
1170
1151
|
} catch (error) {
|
|
1171
1152
|
if (error instanceof Error && error.message.includes("relation") && error.message.includes("does not exist")) {
|
|
1172
1153
|
return [];
|
|
@@ -1175,486 +1156,522 @@ var PostgresStore = class extends MastraStorage {
|
|
|
1175
1156
|
throw error;
|
|
1176
1157
|
}
|
|
1177
1158
|
}
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1159
|
+
async getEvals(options = {}) {
|
|
1160
|
+
const tableName = getTableName({ indexName: TABLE_EVALS, schemaName: getSchemaName(this.schema) });
|
|
1161
|
+
const { agentName, type, page = 0, perPage = 100, dateRange } = options;
|
|
1162
|
+
const fromDate = dateRange?.start;
|
|
1163
|
+
const toDate = dateRange?.end;
|
|
1164
|
+
const conditions = [];
|
|
1165
|
+
const queryParams = [];
|
|
1166
|
+
let paramIndex = 1;
|
|
1167
|
+
if (agentName) {
|
|
1168
|
+
conditions.push(`agent_name = $${paramIndex++}`);
|
|
1169
|
+
queryParams.push(agentName);
|
|
1186
1170
|
}
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1171
|
+
if (type === "test") {
|
|
1172
|
+
conditions.push(`(test_info IS NOT NULL AND test_info->>'testPath' IS NOT NULL)`);
|
|
1173
|
+
} else if (type === "live") {
|
|
1174
|
+
conditions.push(`(test_info IS NULL OR test_info->>'testPath' IS NULL)`);
|
|
1175
|
+
}
|
|
1176
|
+
if (fromDate) {
|
|
1177
|
+
conditions.push(`created_at >= $${paramIndex++}`);
|
|
1178
|
+
queryParams.push(fromDate);
|
|
1179
|
+
}
|
|
1180
|
+
if (toDate) {
|
|
1181
|
+
conditions.push(`created_at <= $${paramIndex++}`);
|
|
1182
|
+
queryParams.push(toDate);
|
|
1183
|
+
}
|
|
1184
|
+
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
1185
|
+
const countQuery = `SELECT COUNT(*) FROM ${tableName} ${whereClause}`;
|
|
1201
1186
|
try {
|
|
1202
|
-
await this.
|
|
1203
|
-
|
|
1204
|
-
|
|
1187
|
+
const countResult = await this.client.one(countQuery, queryParams);
|
|
1188
|
+
const total = parseInt(countResult.count, 10);
|
|
1189
|
+
const currentOffset = page * perPage;
|
|
1190
|
+
if (total === 0) {
|
|
1191
|
+
return {
|
|
1192
|
+
evals: [],
|
|
1193
|
+
total: 0,
|
|
1194
|
+
page,
|
|
1195
|
+
perPage,
|
|
1196
|
+
hasMore: false
|
|
1197
|
+
};
|
|
1205
1198
|
}
|
|
1206
|
-
|
|
1199
|
+
const dataQuery = `SELECT * FROM ${tableName} ${whereClause} ORDER BY created_at DESC LIMIT $${paramIndex++} OFFSET $${paramIndex++}`;
|
|
1200
|
+
const rows = await this.client.manyOrNone(dataQuery, [...queryParams, perPage, currentOffset]);
|
|
1201
|
+
return {
|
|
1202
|
+
evals: rows?.map((row) => transformEvalRow(row)) ?? [],
|
|
1203
|
+
total,
|
|
1204
|
+
page,
|
|
1205
|
+
perPage,
|
|
1206
|
+
hasMore: currentOffset + (rows?.length ?? 0) < total
|
|
1207
|
+
};
|
|
1207
1208
|
} catch (error) {
|
|
1208
|
-
|
|
1209
|
-
throw new MastraError(
|
|
1209
|
+
const mastraError = new MastraError(
|
|
1210
1210
|
{
|
|
1211
|
-
id: "
|
|
1211
|
+
id: "MASTRA_STORAGE_PG_STORE_GET_EVALS_FAILED",
|
|
1212
1212
|
domain: ErrorDomain.STORAGE,
|
|
1213
1213
|
category: ErrorCategory.THIRD_PARTY,
|
|
1214
1214
|
details: {
|
|
1215
|
-
|
|
1216
|
-
|
|
1215
|
+
agentName: agentName || "all",
|
|
1216
|
+
type: type || "all",
|
|
1217
|
+
page,
|
|
1218
|
+
perPage
|
|
1217
1219
|
}
|
|
1218
1220
|
},
|
|
1219
1221
|
error
|
|
1220
1222
|
);
|
|
1223
|
+
this.logger?.error?.(mastraError.toString());
|
|
1224
|
+
this.logger?.trackException(mastraError);
|
|
1225
|
+
throw mastraError;
|
|
1221
1226
|
}
|
|
1222
1227
|
}
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1228
|
+
};
|
|
1229
|
+
var MemoryPG = class extends MemoryStorage {
|
|
1230
|
+
client;
|
|
1231
|
+
schema;
|
|
1232
|
+
operations;
|
|
1233
|
+
constructor({
|
|
1234
|
+
client,
|
|
1235
|
+
schema,
|
|
1236
|
+
operations
|
|
1237
|
+
}) {
|
|
1238
|
+
super();
|
|
1239
|
+
this.client = client;
|
|
1240
|
+
this.schema = schema;
|
|
1241
|
+
this.operations = operations;
|
|
1235
1242
|
}
|
|
1236
|
-
async
|
|
1237
|
-
const { name, scope, page = 0, perPage: perPageInput, attributes, filters, dateRange } = args;
|
|
1238
|
-
const fromDate = dateRange?.start;
|
|
1239
|
-
const toDate = dateRange?.end;
|
|
1240
|
-
const perPage = perPageInput !== void 0 ? perPageInput : 100;
|
|
1241
|
-
const currentOffset = page * perPage;
|
|
1242
|
-
const queryParams = [];
|
|
1243
|
-
const conditions = [];
|
|
1244
|
-
let paramIndex = 1;
|
|
1245
|
-
if (name) {
|
|
1246
|
-
conditions.push(`name LIKE $${paramIndex++}`);
|
|
1247
|
-
queryParams.push(`${name}%`);
|
|
1248
|
-
}
|
|
1249
|
-
if (scope) {
|
|
1250
|
-
conditions.push(`scope = $${paramIndex++}`);
|
|
1251
|
-
queryParams.push(scope);
|
|
1252
|
-
}
|
|
1253
|
-
if (attributes) {
|
|
1254
|
-
Object.entries(attributes).forEach(([key, value]) => {
|
|
1255
|
-
const parsedKey = parseFieldKey(key);
|
|
1256
|
-
conditions.push(`attributes->>'${parsedKey}' = $${paramIndex++}`);
|
|
1257
|
-
queryParams.push(value);
|
|
1258
|
-
});
|
|
1259
|
-
}
|
|
1260
|
-
if (filters) {
|
|
1261
|
-
Object.entries(filters).forEach(([key, value]) => {
|
|
1262
|
-
const parsedKey = parseFieldKey(key);
|
|
1263
|
-
conditions.push(`"${parsedKey}" = $${paramIndex++}`);
|
|
1264
|
-
queryParams.push(value);
|
|
1265
|
-
});
|
|
1266
|
-
}
|
|
1267
|
-
if (fromDate) {
|
|
1268
|
-
conditions.push(`"createdAt" >= $${paramIndex++}`);
|
|
1269
|
-
queryParams.push(fromDate);
|
|
1270
|
-
}
|
|
1271
|
-
if (toDate) {
|
|
1272
|
-
conditions.push(`"createdAt" <= $${paramIndex++}`);
|
|
1273
|
-
queryParams.push(toDate);
|
|
1274
|
-
}
|
|
1275
|
-
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
1276
|
-
const countQuery = `SELECT COUNT(*) FROM ${this.getTableName(TABLE_TRACES)} ${whereClause}`;
|
|
1277
|
-
let total = 0;
|
|
1243
|
+
async getThreadById({ threadId }) {
|
|
1278
1244
|
try {
|
|
1279
|
-
const
|
|
1280
|
-
|
|
1245
|
+
const tableName = getTableName({ indexName: TABLE_THREADS, schemaName: getSchemaName(this.schema) });
|
|
1246
|
+
const thread = await this.client.oneOrNone(
|
|
1247
|
+
`SELECT * FROM ${tableName} WHERE id = $1`,
|
|
1248
|
+
[threadId]
|
|
1249
|
+
);
|
|
1250
|
+
if (!thread) {
|
|
1251
|
+
return null;
|
|
1252
|
+
}
|
|
1253
|
+
return {
|
|
1254
|
+
id: thread.id,
|
|
1255
|
+
resourceId: thread.resourceId,
|
|
1256
|
+
title: thread.title,
|
|
1257
|
+
metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
|
|
1258
|
+
createdAt: thread.createdAtZ || thread.createdAt,
|
|
1259
|
+
updatedAt: thread.updatedAtZ || thread.updatedAt
|
|
1260
|
+
};
|
|
1281
1261
|
} catch (error) {
|
|
1282
1262
|
throw new MastraError(
|
|
1283
1263
|
{
|
|
1284
|
-
id: "
|
|
1264
|
+
id: "MASTRA_STORAGE_PG_STORE_GET_THREAD_BY_ID_FAILED",
|
|
1285
1265
|
domain: ErrorDomain.STORAGE,
|
|
1286
1266
|
category: ErrorCategory.THIRD_PARTY,
|
|
1287
1267
|
details: {
|
|
1288
|
-
|
|
1289
|
-
scope: args.scope ?? ""
|
|
1268
|
+
threadId
|
|
1290
1269
|
}
|
|
1291
1270
|
},
|
|
1292
1271
|
error
|
|
1293
1272
|
);
|
|
1294
1273
|
}
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
};
|
|
1274
|
+
}
|
|
1275
|
+
/**
|
|
1276
|
+
* @deprecated use getThreadsByResourceIdPaginated instead
|
|
1277
|
+
*/
|
|
1278
|
+
async getThreadsByResourceId(args) {
|
|
1279
|
+
const { resourceId } = args;
|
|
1280
|
+
try {
|
|
1281
|
+
const tableName = getTableName({ indexName: TABLE_THREADS, schemaName: getSchemaName(this.schema) });
|
|
1282
|
+
const baseQuery = `FROM ${tableName} WHERE "resourceId" = $1`;
|
|
1283
|
+
const queryParams = [resourceId];
|
|
1284
|
+
const dataQuery = `SELECT id, "resourceId", title, metadata, "createdAt", "updatedAt" ${baseQuery} ORDER BY "createdAt" DESC`;
|
|
1285
|
+
const rows = await this.client.manyOrNone(dataQuery, queryParams);
|
|
1286
|
+
return (rows || []).map((thread) => ({
|
|
1287
|
+
...thread,
|
|
1288
|
+
metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
|
|
1289
|
+
createdAt: thread.createdAt,
|
|
1290
|
+
updatedAt: thread.updatedAt
|
|
1291
|
+
}));
|
|
1292
|
+
} catch (error) {
|
|
1293
|
+
this.logger.error(`Error getting threads for resource ${resourceId}:`, error);
|
|
1294
|
+
return [];
|
|
1303
1295
|
}
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
const finalQueryParams = [...queryParams, perPage, currentOffset];
|
|
1296
|
+
}
|
|
1297
|
+
async getThreadsByResourceIdPaginated(args) {
|
|
1298
|
+
const { resourceId, page = 0, perPage: perPageInput } = args;
|
|
1308
1299
|
try {
|
|
1309
|
-
const
|
|
1310
|
-
const
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1300
|
+
const tableName = getTableName({ indexName: TABLE_THREADS, schemaName: getSchemaName(this.schema) });
|
|
1301
|
+
const baseQuery = `FROM ${tableName} WHERE "resourceId" = $1`;
|
|
1302
|
+
const queryParams = [resourceId];
|
|
1303
|
+
const perPage = perPageInput !== void 0 ? perPageInput : 100;
|
|
1304
|
+
const currentOffset = page * perPage;
|
|
1305
|
+
const countQuery = `SELECT COUNT(*) ${baseQuery}`;
|
|
1306
|
+
const countResult = await this.client.one(countQuery, queryParams);
|
|
1307
|
+
const total = parseInt(countResult.count, 10);
|
|
1308
|
+
if (total === 0) {
|
|
1309
|
+
return {
|
|
1310
|
+
threads: [],
|
|
1311
|
+
total: 0,
|
|
1312
|
+
page,
|
|
1313
|
+
perPage,
|
|
1314
|
+
hasMore: false
|
|
1315
|
+
};
|
|
1316
|
+
}
|
|
1317
|
+
const dataQuery = `SELECT id, "resourceId", title, metadata, "createdAt", "updatedAt" ${baseQuery} ORDER BY "createdAt" DESC LIMIT $2 OFFSET $3`;
|
|
1318
|
+
const rows = await this.client.manyOrNone(dataQuery, [...queryParams, perPage, currentOffset]);
|
|
1319
|
+
const threads = (rows || []).map((thread) => ({
|
|
1320
|
+
...thread,
|
|
1321
|
+
metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
|
|
1322
|
+
createdAt: thread.createdAt,
|
|
1323
|
+
// Assuming already Date objects or ISO strings
|
|
1324
|
+
updatedAt: thread.updatedAt
|
|
1325
1325
|
}));
|
|
1326
1326
|
return {
|
|
1327
|
-
|
|
1327
|
+
threads,
|
|
1328
1328
|
total,
|
|
1329
1329
|
page,
|
|
1330
1330
|
perPage,
|
|
1331
|
-
hasMore: currentOffset +
|
|
1331
|
+
hasMore: currentOffset + threads.length < total
|
|
1332
1332
|
};
|
|
1333
1333
|
} catch (error) {
|
|
1334
|
-
|
|
1334
|
+
const mastraError = new MastraError(
|
|
1335
1335
|
{
|
|
1336
|
-
id: "
|
|
1336
|
+
id: "MASTRA_STORAGE_PG_STORE_GET_THREADS_BY_RESOURCE_ID_PAGINATED_FAILED",
|
|
1337
1337
|
domain: ErrorDomain.STORAGE,
|
|
1338
1338
|
category: ErrorCategory.THIRD_PARTY,
|
|
1339
1339
|
details: {
|
|
1340
|
-
|
|
1341
|
-
|
|
1340
|
+
resourceId,
|
|
1341
|
+
page
|
|
1342
1342
|
}
|
|
1343
1343
|
},
|
|
1344
1344
|
error
|
|
1345
1345
|
);
|
|
1346
|
+
this.logger?.error?.(mastraError.toString());
|
|
1347
|
+
this.logger?.trackException(mastraError);
|
|
1348
|
+
return { threads: [], total: 0, page, perPage: perPageInput || 100, hasMore: false };
|
|
1346
1349
|
}
|
|
1347
1350
|
}
|
|
1348
|
-
async
|
|
1349
|
-
if (!this.schema || this.schemaSetupComplete) {
|
|
1350
|
-
return;
|
|
1351
|
-
}
|
|
1352
|
-
if (!this.setupSchemaPromise) {
|
|
1353
|
-
this.setupSchemaPromise = (async () => {
|
|
1354
|
-
try {
|
|
1355
|
-
const schemaExists = await this.db.oneOrNone(
|
|
1356
|
-
`
|
|
1357
|
-
SELECT EXISTS (
|
|
1358
|
-
SELECT 1 FROM information_schema.schemata
|
|
1359
|
-
WHERE schema_name = $1
|
|
1360
|
-
)
|
|
1361
|
-
`,
|
|
1362
|
-
[this.schema]
|
|
1363
|
-
);
|
|
1364
|
-
if (!schemaExists?.exists) {
|
|
1365
|
-
try {
|
|
1366
|
-
await this.db.none(`CREATE SCHEMA IF NOT EXISTS ${this.getSchemaName()}`);
|
|
1367
|
-
this.logger.info(`Schema "${this.schema}" created successfully`);
|
|
1368
|
-
} catch (error) {
|
|
1369
|
-
this.logger.error(`Failed to create schema "${this.schema}"`, { error });
|
|
1370
|
-
throw new Error(
|
|
1371
|
-
`Unable to create schema "${this.schema}". This requires CREATE privilege on the database. Either create the schema manually or grant CREATE privilege to the user.`
|
|
1372
|
-
);
|
|
1373
|
-
}
|
|
1374
|
-
}
|
|
1375
|
-
this.schemaSetupComplete = true;
|
|
1376
|
-
this.logger.debug(`Schema "${this.schema}" is ready for use`);
|
|
1377
|
-
} catch (error) {
|
|
1378
|
-
this.schemaSetupComplete = void 0;
|
|
1379
|
-
this.setupSchemaPromise = null;
|
|
1380
|
-
throw error;
|
|
1381
|
-
} finally {
|
|
1382
|
-
this.setupSchemaPromise = null;
|
|
1383
|
-
}
|
|
1384
|
-
})();
|
|
1385
|
-
}
|
|
1386
|
-
await this.setupSchemaPromise;
|
|
1387
|
-
}
|
|
1388
|
-
async createTable({
|
|
1389
|
-
tableName,
|
|
1390
|
-
schema
|
|
1391
|
-
}) {
|
|
1351
|
+
async saveThread({ thread }) {
|
|
1392
1352
|
try {
|
|
1393
|
-
const
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1353
|
+
const tableName = getTableName({ indexName: TABLE_THREADS, schemaName: getSchemaName(this.schema) });
|
|
1354
|
+
await this.client.none(
|
|
1355
|
+
`INSERT INTO ${tableName} (
|
|
1356
|
+
id,
|
|
1357
|
+
"resourceId",
|
|
1358
|
+
title,
|
|
1359
|
+
metadata,
|
|
1360
|
+
"createdAt",
|
|
1361
|
+
"createdAtZ",
|
|
1362
|
+
"updatedAt",
|
|
1363
|
+
"updatedAtZ"
|
|
1364
|
+
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
|
1365
|
+
ON CONFLICT (id) DO UPDATE SET
|
|
1366
|
+
"resourceId" = EXCLUDED."resourceId",
|
|
1367
|
+
title = EXCLUDED.title,
|
|
1368
|
+
metadata = EXCLUDED.metadata,
|
|
1369
|
+
"createdAt" = EXCLUDED."createdAt",
|
|
1370
|
+
"createdAtZ" = EXCLUDED."createdAtZ",
|
|
1371
|
+
"updatedAt" = EXCLUDED."updatedAt",
|
|
1372
|
+
"updatedAtZ" = EXCLUDED."updatedAtZ"`,
|
|
1373
|
+
[
|
|
1374
|
+
thread.id,
|
|
1375
|
+
thread.resourceId,
|
|
1376
|
+
thread.title,
|
|
1377
|
+
thread.metadata ? JSON.stringify(thread.metadata) : null,
|
|
1378
|
+
thread.createdAt,
|
|
1379
|
+
thread.createdAt,
|
|
1380
|
+
thread.updatedAt,
|
|
1381
|
+
thread.updatedAt
|
|
1382
|
+
]
|
|
1383
|
+
);
|
|
1384
|
+
return thread;
|
|
1420
1385
|
} catch (error) {
|
|
1421
1386
|
throw new MastraError(
|
|
1422
1387
|
{
|
|
1423
|
-
id: "
|
|
1388
|
+
id: "MASTRA_STORAGE_PG_STORE_SAVE_THREAD_FAILED",
|
|
1424
1389
|
domain: ErrorDomain.STORAGE,
|
|
1425
1390
|
category: ErrorCategory.THIRD_PARTY,
|
|
1426
1391
|
details: {
|
|
1427
|
-
|
|
1392
|
+
threadId: thread.id
|
|
1428
1393
|
}
|
|
1429
1394
|
},
|
|
1430
1395
|
error
|
|
1431
1396
|
);
|
|
1432
1397
|
}
|
|
1433
1398
|
}
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
case "jsonb":
|
|
1439
|
-
return "DEFAULT '{}'::jsonb";
|
|
1440
|
-
default:
|
|
1441
|
-
return super.getDefaultValue(type);
|
|
1442
|
-
}
|
|
1443
|
-
}
|
|
1444
|
-
/**
|
|
1445
|
-
* Alters table schema to add columns if they don't exist
|
|
1446
|
-
* @param tableName Name of the table
|
|
1447
|
-
* @param schema Schema of the table
|
|
1448
|
-
* @param ifNotExists Array of column names to add if they don't exist
|
|
1449
|
-
*/
|
|
1450
|
-
async alterTable({
|
|
1451
|
-
tableName,
|
|
1452
|
-
schema,
|
|
1453
|
-
ifNotExists
|
|
1399
|
+
async updateThread({
|
|
1400
|
+
id,
|
|
1401
|
+
title,
|
|
1402
|
+
metadata
|
|
1454
1403
|
}) {
|
|
1455
|
-
const
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
this.logger?.debug?.(`Ensured column ${parsedColumnName} exists in table ${fullTableName}`);
|
|
1404
|
+
const threadTableName = getTableName({ indexName: TABLE_THREADS, schemaName: getSchemaName(this.schema) });
|
|
1405
|
+
const existingThread = await this.getThreadById({ threadId: id });
|
|
1406
|
+
if (!existingThread) {
|
|
1407
|
+
throw new MastraError({
|
|
1408
|
+
id: "MASTRA_STORAGE_PG_STORE_UPDATE_THREAD_FAILED",
|
|
1409
|
+
domain: ErrorDomain.STORAGE,
|
|
1410
|
+
category: ErrorCategory.USER,
|
|
1411
|
+
text: `Thread ${id} not found`,
|
|
1412
|
+
details: {
|
|
1413
|
+
threadId: id,
|
|
1414
|
+
title
|
|
1467
1415
|
}
|
|
1468
|
-
}
|
|
1416
|
+
});
|
|
1417
|
+
}
|
|
1418
|
+
const mergedMetadata = {
|
|
1419
|
+
...existingThread.metadata,
|
|
1420
|
+
...metadata
|
|
1421
|
+
};
|
|
1422
|
+
try {
|
|
1423
|
+
const thread = await this.client.one(
|
|
1424
|
+
`UPDATE ${threadTableName}
|
|
1425
|
+
SET
|
|
1426
|
+
title = $1,
|
|
1427
|
+
metadata = $2,
|
|
1428
|
+
"updatedAt" = $3,
|
|
1429
|
+
"updatedAtZ" = $3
|
|
1430
|
+
WHERE id = $4
|
|
1431
|
+
RETURNING *
|
|
1432
|
+
`,
|
|
1433
|
+
[title, mergedMetadata, (/* @__PURE__ */ new Date()).toISOString(), id]
|
|
1434
|
+
);
|
|
1435
|
+
return {
|
|
1436
|
+
id: thread.id,
|
|
1437
|
+
resourceId: thread.resourceId,
|
|
1438
|
+
title: thread.title,
|
|
1439
|
+
metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
|
|
1440
|
+
createdAt: thread.createdAtZ || thread.createdAt,
|
|
1441
|
+
updatedAt: thread.updatedAtZ || thread.updatedAt
|
|
1442
|
+
};
|
|
1469
1443
|
} catch (error) {
|
|
1470
1444
|
throw new MastraError(
|
|
1471
1445
|
{
|
|
1472
|
-
id: "
|
|
1446
|
+
id: "MASTRA_STORAGE_PG_STORE_UPDATE_THREAD_FAILED",
|
|
1473
1447
|
domain: ErrorDomain.STORAGE,
|
|
1474
1448
|
category: ErrorCategory.THIRD_PARTY,
|
|
1475
1449
|
details: {
|
|
1476
|
-
|
|
1450
|
+
threadId: id,
|
|
1451
|
+
title
|
|
1477
1452
|
}
|
|
1478
1453
|
},
|
|
1479
1454
|
error
|
|
1480
1455
|
);
|
|
1481
1456
|
}
|
|
1482
1457
|
}
|
|
1483
|
-
async
|
|
1458
|
+
async deleteThread({ threadId }) {
|
|
1484
1459
|
try {
|
|
1485
|
-
|
|
1460
|
+
const tableName = getTableName({ indexName: TABLE_MESSAGES, schemaName: getSchemaName(this.schema) });
|
|
1461
|
+
const threadTableName = getTableName({ indexName: TABLE_THREADS, schemaName: getSchemaName(this.schema) });
|
|
1462
|
+
await this.client.tx(async (t) => {
|
|
1463
|
+
await t.none(`DELETE FROM ${tableName} WHERE thread_id = $1`, [threadId]);
|
|
1464
|
+
await t.none(`DELETE FROM ${threadTableName} WHERE id = $1`, [threadId]);
|
|
1465
|
+
});
|
|
1486
1466
|
} catch (error) {
|
|
1487
1467
|
throw new MastraError(
|
|
1488
1468
|
{
|
|
1489
|
-
id: "
|
|
1469
|
+
id: "MASTRA_STORAGE_PG_STORE_DELETE_THREAD_FAILED",
|
|
1490
1470
|
domain: ErrorDomain.STORAGE,
|
|
1491
1471
|
category: ErrorCategory.THIRD_PARTY,
|
|
1492
1472
|
details: {
|
|
1493
|
-
|
|
1473
|
+
threadId
|
|
1494
1474
|
}
|
|
1495
1475
|
},
|
|
1496
1476
|
error
|
|
1497
1477
|
);
|
|
1498
1478
|
}
|
|
1499
1479
|
}
|
|
1500
|
-
async
|
|
1480
|
+
async _getIncludedMessages({
|
|
1481
|
+
threadId,
|
|
1482
|
+
selectBy,
|
|
1483
|
+
orderByStatement
|
|
1484
|
+
}) {
|
|
1485
|
+
const include = selectBy?.include;
|
|
1486
|
+
if (!include) return null;
|
|
1487
|
+
const unionQueries = [];
|
|
1488
|
+
const params = [];
|
|
1489
|
+
let paramIdx = 1;
|
|
1490
|
+
const tableName = getTableName({ indexName: TABLE_MESSAGES, schemaName: getSchemaName(this.schema) });
|
|
1491
|
+
for (const inc of include) {
|
|
1492
|
+
const { id, withPreviousMessages = 0, withNextMessages = 0 } = inc;
|
|
1493
|
+
const searchId = inc.threadId || threadId;
|
|
1494
|
+
unionQueries.push(
|
|
1495
|
+
`
|
|
1496
|
+
SELECT * FROM (
|
|
1497
|
+
WITH ordered_messages AS (
|
|
1498
|
+
SELECT
|
|
1499
|
+
*,
|
|
1500
|
+
ROW_NUMBER() OVER (${orderByStatement}) as row_num
|
|
1501
|
+
FROM ${tableName}
|
|
1502
|
+
WHERE thread_id = $${paramIdx}
|
|
1503
|
+
)
|
|
1504
|
+
SELECT
|
|
1505
|
+
m.id,
|
|
1506
|
+
m.content,
|
|
1507
|
+
m.role,
|
|
1508
|
+
m.type,
|
|
1509
|
+
m."createdAt",
|
|
1510
|
+
m.thread_id AS "threadId",
|
|
1511
|
+
m."resourceId"
|
|
1512
|
+
FROM ordered_messages m
|
|
1513
|
+
WHERE m.id = $${paramIdx + 1}
|
|
1514
|
+
OR EXISTS (
|
|
1515
|
+
SELECT 1 FROM ordered_messages target
|
|
1516
|
+
WHERE target.id = $${paramIdx + 1}
|
|
1517
|
+
AND (
|
|
1518
|
+
-- Get previous messages based on the max withPreviousMessages
|
|
1519
|
+
(m.row_num <= target.row_num + $${paramIdx + 2} AND m.row_num > target.row_num)
|
|
1520
|
+
OR
|
|
1521
|
+
-- Get next messages based on the max withNextMessages
|
|
1522
|
+
(m.row_num >= target.row_num - $${paramIdx + 3} AND m.row_num < target.row_num)
|
|
1523
|
+
)
|
|
1524
|
+
)
|
|
1525
|
+
) AS query_${paramIdx}
|
|
1526
|
+
`
|
|
1527
|
+
// Keep ASC for final sorting after fetching context
|
|
1528
|
+
);
|
|
1529
|
+
params.push(searchId, id, withPreviousMessages, withNextMessages);
|
|
1530
|
+
paramIdx += 4;
|
|
1531
|
+
}
|
|
1532
|
+
const finalQuery = unionQueries.join(" UNION ALL ") + ' ORDER BY "createdAt" ASC';
|
|
1533
|
+
const includedRows = await this.client.manyOrNone(finalQuery, params);
|
|
1534
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1535
|
+
const dedupedRows = includedRows.filter((row) => {
|
|
1536
|
+
if (seen.has(row.id)) return false;
|
|
1537
|
+
seen.add(row.id);
|
|
1538
|
+
return true;
|
|
1539
|
+
});
|
|
1540
|
+
return dedupedRows;
|
|
1541
|
+
}
|
|
1542
|
+
async getMessages(args) {
|
|
1543
|
+
const { threadId, format, selectBy } = args;
|
|
1544
|
+
const selectStatement = `SELECT id, content, role, type, "createdAt", thread_id AS "threadId", "resourceId"`;
|
|
1545
|
+
const orderByStatement = `ORDER BY "createdAt" DESC`;
|
|
1546
|
+
const limit = resolveMessageLimit({ last: selectBy?.last, defaultLimit: 40 });
|
|
1501
1547
|
try {
|
|
1502
|
-
|
|
1503
|
-
const
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1548
|
+
let rows = [];
|
|
1549
|
+
const include = selectBy?.include || [];
|
|
1550
|
+
if (include?.length) {
|
|
1551
|
+
const includeMessages = await this._getIncludedMessages({ threadId, selectBy, orderByStatement });
|
|
1552
|
+
if (includeMessages) {
|
|
1553
|
+
rows.push(...includeMessages);
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
const excludeIds = rows.map((m) => m.id);
|
|
1557
|
+
const tableName = getTableName({ indexName: TABLE_MESSAGES, schemaName: getSchemaName(this.schema) });
|
|
1558
|
+
const excludeIdsParam = excludeIds.map((_, idx) => `$${idx + 2}`).join(", ");
|
|
1559
|
+
let query = `${selectStatement} FROM ${tableName} WHERE thread_id = $1
|
|
1560
|
+
${excludeIds.length ? `AND id NOT IN (${excludeIdsParam})` : ""}
|
|
1561
|
+
${orderByStatement}
|
|
1562
|
+
LIMIT $${excludeIds.length + 2}
|
|
1563
|
+
`;
|
|
1564
|
+
const queryParams = [threadId, ...excludeIds, limit];
|
|
1565
|
+
const remainingRows = await this.client.manyOrNone(query, queryParams);
|
|
1566
|
+
rows.push(...remainingRows);
|
|
1567
|
+
const fetchedMessages = (rows || []).map((message) => {
|
|
1568
|
+
if (typeof message.content === "string") {
|
|
1569
|
+
try {
|
|
1570
|
+
message.content = JSON.parse(message.content);
|
|
1571
|
+
} catch {
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
if (message.type === "v2") delete message.type;
|
|
1575
|
+
return message;
|
|
1576
|
+
});
|
|
1577
|
+
const sortedMessages = fetchedMessages.sort(
|
|
1578
|
+
(a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()
|
|
1508
1579
|
);
|
|
1580
|
+
return format === "v2" ? sortedMessages.map(
|
|
1581
|
+
(m) => ({ ...m, content: m.content || { format: 2, parts: [{ type: "text", text: "" }] } })
|
|
1582
|
+
) : sortedMessages;
|
|
1509
1583
|
} catch (error) {
|
|
1510
|
-
|
|
1584
|
+
const mastraError = new MastraError(
|
|
1511
1585
|
{
|
|
1512
|
-
id: "
|
|
1586
|
+
id: "MASTRA_STORAGE_PG_STORE_GET_MESSAGES_FAILED",
|
|
1513
1587
|
domain: ErrorDomain.STORAGE,
|
|
1514
1588
|
category: ErrorCategory.THIRD_PARTY,
|
|
1515
1589
|
details: {
|
|
1516
|
-
|
|
1517
|
-
}
|
|
1518
|
-
},
|
|
1519
|
-
error
|
|
1520
|
-
);
|
|
1521
|
-
}
|
|
1522
|
-
}
|
|
1523
|
-
async load({ tableName, keys }) {
|
|
1524
|
-
try {
|
|
1525
|
-
const keyEntries = Object.entries(keys).map(([key, value]) => [parseSqlIdentifier(key, "column name"), value]);
|
|
1526
|
-
const conditions = keyEntries.map(([key], index) => `"${key}" = $${index + 1}`).join(" AND ");
|
|
1527
|
-
const values = keyEntries.map(([_, value]) => value);
|
|
1528
|
-
const result = await this.db.oneOrNone(
|
|
1529
|
-
`SELECT * FROM ${this.getTableName(tableName)} WHERE ${conditions}`,
|
|
1530
|
-
values
|
|
1531
|
-
);
|
|
1532
|
-
if (!result) {
|
|
1533
|
-
return null;
|
|
1534
|
-
}
|
|
1535
|
-
if (tableName === TABLE_WORKFLOW_SNAPSHOT) {
|
|
1536
|
-
const snapshot = result;
|
|
1537
|
-
if (typeof snapshot.snapshot === "string") {
|
|
1538
|
-
snapshot.snapshot = JSON.parse(snapshot.snapshot);
|
|
1539
|
-
}
|
|
1540
|
-
return snapshot;
|
|
1541
|
-
}
|
|
1542
|
-
return result;
|
|
1543
|
-
} catch (error) {
|
|
1544
|
-
throw new MastraError(
|
|
1545
|
-
{
|
|
1546
|
-
id: "MASTRA_STORAGE_PG_STORE_LOAD_FAILED",
|
|
1547
|
-
domain: ErrorDomain.STORAGE,
|
|
1548
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
1549
|
-
details: {
|
|
1550
|
-
tableName
|
|
1590
|
+
threadId
|
|
1551
1591
|
}
|
|
1552
1592
|
},
|
|
1553
1593
|
error
|
|
1554
1594
|
);
|
|
1595
|
+
this.logger?.error?.(mastraError.toString());
|
|
1596
|
+
this.logger?.trackException(mastraError);
|
|
1597
|
+
return [];
|
|
1555
1598
|
}
|
|
1556
1599
|
}
|
|
1557
|
-
async
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
[threadId]
|
|
1570
|
-
);
|
|
1571
|
-
if (!thread) {
|
|
1572
|
-
return null;
|
|
1600
|
+
async getMessagesPaginated(args) {
|
|
1601
|
+
const { threadId, format, selectBy } = args;
|
|
1602
|
+
const { page = 0, perPage: perPageInput, dateRange } = selectBy?.pagination || {};
|
|
1603
|
+
const fromDate = dateRange?.start;
|
|
1604
|
+
const toDate = dateRange?.end;
|
|
1605
|
+
const selectStatement = `SELECT id, content, role, type, "createdAt", thread_id AS "threadId", "resourceId"`;
|
|
1606
|
+
const orderByStatement = `ORDER BY "createdAt" DESC`;
|
|
1607
|
+
const messages = [];
|
|
1608
|
+
if (selectBy?.include?.length) {
|
|
1609
|
+
const includeMessages = await this._getIncludedMessages({ threadId, selectBy, orderByStatement });
|
|
1610
|
+
if (includeMessages) {
|
|
1611
|
+
messages.push(...includeMessages);
|
|
1573
1612
|
}
|
|
1574
|
-
return {
|
|
1575
|
-
...thread,
|
|
1576
|
-
metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
|
|
1577
|
-
createdAt: thread.createdAt,
|
|
1578
|
-
updatedAt: thread.updatedAt
|
|
1579
|
-
};
|
|
1580
|
-
} catch (error) {
|
|
1581
|
-
throw new MastraError(
|
|
1582
|
-
{
|
|
1583
|
-
id: "MASTRA_STORAGE_PG_STORE_GET_THREAD_BY_ID_FAILED",
|
|
1584
|
-
domain: ErrorDomain.STORAGE,
|
|
1585
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
1586
|
-
details: {
|
|
1587
|
-
threadId
|
|
1588
|
-
}
|
|
1589
|
-
},
|
|
1590
|
-
error
|
|
1591
|
-
);
|
|
1592
|
-
}
|
|
1593
|
-
}
|
|
1594
|
-
/**
|
|
1595
|
-
* @deprecated use getThreadsByResourceIdPaginated instead
|
|
1596
|
-
*/
|
|
1597
|
-
async getThreadsByResourceId(args) {
|
|
1598
|
-
const { resourceId } = args;
|
|
1599
|
-
try {
|
|
1600
|
-
const baseQuery = `FROM ${this.getTableName(TABLE_THREADS)} WHERE "resourceId" = $1`;
|
|
1601
|
-
const queryParams = [resourceId];
|
|
1602
|
-
const dataQuery = `SELECT id, "resourceId", title, metadata, "createdAt", "updatedAt" ${baseQuery} ORDER BY "createdAt" DESC`;
|
|
1603
|
-
const rows = await this.db.manyOrNone(dataQuery, queryParams);
|
|
1604
|
-
return (rows || []).map((thread) => ({
|
|
1605
|
-
...thread,
|
|
1606
|
-
metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
|
|
1607
|
-
createdAt: thread.createdAt,
|
|
1608
|
-
updatedAt: thread.updatedAt
|
|
1609
|
-
}));
|
|
1610
|
-
} catch (error) {
|
|
1611
|
-
this.logger.error(`Error getting threads for resource ${resourceId}:`, error);
|
|
1612
|
-
return [];
|
|
1613
1613
|
}
|
|
1614
|
-
}
|
|
1615
|
-
async getThreadsByResourceIdPaginated(args) {
|
|
1616
|
-
const { resourceId, page = 0, perPage: perPageInput } = args;
|
|
1617
1614
|
try {
|
|
1618
|
-
const
|
|
1619
|
-
const queryParams = [resourceId];
|
|
1620
|
-
const perPage = perPageInput !== void 0 ? perPageInput : 100;
|
|
1615
|
+
const perPage = perPageInput !== void 0 ? perPageInput : resolveMessageLimit({ last: selectBy?.last, defaultLimit: 40 });
|
|
1621
1616
|
const currentOffset = page * perPage;
|
|
1622
|
-
const
|
|
1623
|
-
const
|
|
1617
|
+
const conditions = [`thread_id = $1`];
|
|
1618
|
+
const queryParams = [threadId];
|
|
1619
|
+
let paramIndex = 2;
|
|
1620
|
+
if (fromDate) {
|
|
1621
|
+
conditions.push(`"createdAt" >= $${paramIndex++}`);
|
|
1622
|
+
queryParams.push(fromDate);
|
|
1623
|
+
}
|
|
1624
|
+
if (toDate) {
|
|
1625
|
+
conditions.push(`"createdAt" <= $${paramIndex++}`);
|
|
1626
|
+
queryParams.push(toDate);
|
|
1627
|
+
}
|
|
1628
|
+
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
1629
|
+
const tableName = getTableName({ indexName: TABLE_MESSAGES, schemaName: getSchemaName(this.schema) });
|
|
1630
|
+
const countQuery = `SELECT COUNT(*) FROM ${tableName} ${whereClause}`;
|
|
1631
|
+
const countResult = await this.client.one(countQuery, queryParams);
|
|
1624
1632
|
const total = parseInt(countResult.count, 10);
|
|
1625
|
-
if (total === 0) {
|
|
1633
|
+
if (total === 0 && messages.length === 0) {
|
|
1626
1634
|
return {
|
|
1627
|
-
|
|
1635
|
+
messages: [],
|
|
1628
1636
|
total: 0,
|
|
1629
1637
|
page,
|
|
1630
1638
|
perPage,
|
|
1631
1639
|
hasMore: false
|
|
1632
1640
|
};
|
|
1633
1641
|
}
|
|
1634
|
-
const
|
|
1635
|
-
const
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1642
|
+
const excludeIds = messages.map((m) => m.id);
|
|
1643
|
+
const excludeIdsParam = excludeIds.map((_, idx) => `$${idx + paramIndex}`).join(", ");
|
|
1644
|
+
paramIndex += excludeIds.length;
|
|
1645
|
+
const dataQuery = `${selectStatement} FROM ${tableName} ${whereClause} ${excludeIds.length ? `AND id NOT IN (${excludeIdsParam})` : ""}${orderByStatement} LIMIT $${paramIndex++} OFFSET $${paramIndex++}`;
|
|
1646
|
+
const rows = await this.client.manyOrNone(dataQuery, [...queryParams, ...excludeIds, perPage, currentOffset]);
|
|
1647
|
+
messages.push(...rows || []);
|
|
1648
|
+
const messagesWithParsedContent = messages.map((message) => {
|
|
1649
|
+
if (typeof message.content === "string") {
|
|
1650
|
+
try {
|
|
1651
|
+
return { ...message, content: JSON.parse(message.content) };
|
|
1652
|
+
} catch {
|
|
1653
|
+
return message;
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
return message;
|
|
1657
|
+
});
|
|
1658
|
+
const list = new MessageList().add(messagesWithParsedContent, "memory");
|
|
1659
|
+
const messagesToReturn = format === `v2` ? list.get.all.v2() : list.get.all.v1();
|
|
1643
1660
|
return {
|
|
1644
|
-
|
|
1661
|
+
messages: messagesToReturn,
|
|
1645
1662
|
total,
|
|
1646
1663
|
page,
|
|
1647
1664
|
perPage,
|
|
1648
|
-
hasMore: currentOffset +
|
|
1665
|
+
hasMore: currentOffset + rows.length < total
|
|
1649
1666
|
};
|
|
1650
1667
|
} catch (error) {
|
|
1651
1668
|
const mastraError = new MastraError(
|
|
1652
1669
|
{
|
|
1653
|
-
id: "
|
|
1670
|
+
id: "MASTRA_STORAGE_PG_STORE_GET_MESSAGES_PAGINATED_FAILED",
|
|
1654
1671
|
domain: ErrorDomain.STORAGE,
|
|
1655
1672
|
category: ErrorCategory.THIRD_PARTY,
|
|
1656
1673
|
details: {
|
|
1657
|
-
|
|
1674
|
+
threadId,
|
|
1658
1675
|
page
|
|
1659
1676
|
}
|
|
1660
1677
|
},
|
|
@@ -1662,872 +1679,1348 @@ var PostgresStore = class extends MastraStorage {
|
|
|
1662
1679
|
);
|
|
1663
1680
|
this.logger?.error?.(mastraError.toString());
|
|
1664
1681
|
this.logger?.trackException(mastraError);
|
|
1665
|
-
return {
|
|
1682
|
+
return { messages: [], total: 0, page, perPage: perPageInput || 40, hasMore: false };
|
|
1666
1683
|
}
|
|
1667
1684
|
}
|
|
1668
|
-
async
|
|
1685
|
+
async saveMessages({
|
|
1686
|
+
messages,
|
|
1687
|
+
format
|
|
1688
|
+
}) {
|
|
1689
|
+
if (messages.length === 0) return messages;
|
|
1690
|
+
const threadId = messages[0]?.threadId;
|
|
1691
|
+
if (!threadId) {
|
|
1692
|
+
throw new MastraError({
|
|
1693
|
+
id: "MASTRA_STORAGE_PG_STORE_SAVE_MESSAGES_FAILED",
|
|
1694
|
+
domain: ErrorDomain.STORAGE,
|
|
1695
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1696
|
+
text: `Thread ID is required`
|
|
1697
|
+
});
|
|
1698
|
+
}
|
|
1699
|
+
const thread = await this.getThreadById({ threadId });
|
|
1700
|
+
if (!thread) {
|
|
1701
|
+
throw new MastraError({
|
|
1702
|
+
id: "MASTRA_STORAGE_PG_STORE_SAVE_MESSAGES_FAILED",
|
|
1703
|
+
domain: ErrorDomain.STORAGE,
|
|
1704
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1705
|
+
text: `Thread ${threadId} not found`,
|
|
1706
|
+
details: {
|
|
1707
|
+
threadId
|
|
1708
|
+
}
|
|
1709
|
+
});
|
|
1710
|
+
}
|
|
1669
1711
|
try {
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1712
|
+
const tableName = getTableName({ indexName: TABLE_MESSAGES, schemaName: getSchemaName(this.schema) });
|
|
1713
|
+
await this.client.tx(async (t) => {
|
|
1714
|
+
const messageInserts = messages.map((message) => {
|
|
1715
|
+
if (!message.threadId) {
|
|
1716
|
+
throw new Error(
|
|
1717
|
+
`Expected to find a threadId for message, but couldn't find one. An unexpected error has occurred.`
|
|
1718
|
+
);
|
|
1719
|
+
}
|
|
1720
|
+
if (!message.resourceId) {
|
|
1721
|
+
throw new Error(
|
|
1722
|
+
`Expected to find a resourceId for message, but couldn't find one. An unexpected error has occurred.`
|
|
1723
|
+
);
|
|
1724
|
+
}
|
|
1725
|
+
return t.none(
|
|
1726
|
+
`INSERT INTO ${tableName} (id, thread_id, content, "createdAt", "createdAtZ", role, type, "resourceId")
|
|
1727
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
|
1728
|
+
ON CONFLICT (id) DO UPDATE SET
|
|
1729
|
+
thread_id = EXCLUDED.thread_id,
|
|
1730
|
+
content = EXCLUDED.content,
|
|
1731
|
+
role = EXCLUDED.role,
|
|
1732
|
+
type = EXCLUDED.type,
|
|
1733
|
+
"resourceId" = EXCLUDED."resourceId"`,
|
|
1734
|
+
[
|
|
1735
|
+
message.id,
|
|
1736
|
+
message.threadId,
|
|
1737
|
+
typeof message.content === "string" ? message.content : JSON.stringify(message.content),
|
|
1738
|
+
message.createdAt || (/* @__PURE__ */ new Date()).toISOString(),
|
|
1739
|
+
message.createdAt || (/* @__PURE__ */ new Date()).toISOString(),
|
|
1740
|
+
message.role,
|
|
1741
|
+
message.type || "v2",
|
|
1742
|
+
message.resourceId
|
|
1743
|
+
]
|
|
1744
|
+
);
|
|
1745
|
+
});
|
|
1746
|
+
const threadTableName = getTableName({ indexName: TABLE_THREADS, schemaName: getSchemaName(this.schema) });
|
|
1747
|
+
const threadUpdate = t.none(
|
|
1748
|
+
`UPDATE ${threadTableName}
|
|
1749
|
+
SET
|
|
1750
|
+
"updatedAt" = $1,
|
|
1751
|
+
"updatedAtZ" = $1
|
|
1752
|
+
WHERE id = $2
|
|
1753
|
+
`,
|
|
1754
|
+
[(/* @__PURE__ */ new Date()).toISOString(), threadId]
|
|
1755
|
+
);
|
|
1756
|
+
await Promise.all([...messageInserts, threadUpdate]);
|
|
1757
|
+
});
|
|
1758
|
+
const messagesWithParsedContent = messages.map((message) => {
|
|
1759
|
+
if (typeof message.content === "string") {
|
|
1760
|
+
try {
|
|
1761
|
+
return { ...message, content: JSON.parse(message.content) };
|
|
1762
|
+
} catch {
|
|
1763
|
+
return message;
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
return message;
|
|
1767
|
+
});
|
|
1768
|
+
const list = new MessageList().add(messagesWithParsedContent, "memory");
|
|
1769
|
+
if (format === `v2`) return list.get.all.v2();
|
|
1770
|
+
return list.get.all.v1();
|
|
1695
1771
|
} catch (error) {
|
|
1696
1772
|
throw new MastraError(
|
|
1697
1773
|
{
|
|
1698
|
-
id: "
|
|
1774
|
+
id: "MASTRA_STORAGE_PG_STORE_SAVE_MESSAGES_FAILED",
|
|
1699
1775
|
domain: ErrorDomain.STORAGE,
|
|
1700
1776
|
category: ErrorCategory.THIRD_PARTY,
|
|
1701
1777
|
details: {
|
|
1702
|
-
threadId
|
|
1778
|
+
threadId
|
|
1703
1779
|
}
|
|
1704
1780
|
},
|
|
1705
1781
|
error
|
|
1706
1782
|
);
|
|
1707
1783
|
}
|
|
1708
1784
|
}
|
|
1709
|
-
async
|
|
1710
|
-
|
|
1711
|
-
title,
|
|
1712
|
-
metadata
|
|
1785
|
+
async updateMessages({
|
|
1786
|
+
messages
|
|
1713
1787
|
}) {
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1788
|
+
if (messages.length === 0) {
|
|
1789
|
+
return [];
|
|
1790
|
+
}
|
|
1791
|
+
const messageIds = messages.map((m) => m.id);
|
|
1792
|
+
const selectQuery = `SELECT id, content, role, type, "createdAt", thread_id AS "threadId", "resourceId" FROM ${getTableName({ indexName: TABLE_MESSAGES, schemaName: getSchemaName(this.schema) })} WHERE id IN ($1:list)`;
|
|
1793
|
+
const existingMessagesDb = await this.client.manyOrNone(selectQuery, [messageIds]);
|
|
1794
|
+
if (existingMessagesDb.length === 0) {
|
|
1795
|
+
return [];
|
|
1796
|
+
}
|
|
1797
|
+
const existingMessages = existingMessagesDb.map((msg) => {
|
|
1798
|
+
if (typeof msg.content === "string") {
|
|
1799
|
+
try {
|
|
1800
|
+
msg.content = JSON.parse(msg.content);
|
|
1801
|
+
} catch {
|
|
1724
1802
|
}
|
|
1725
|
-
}
|
|
1803
|
+
}
|
|
1804
|
+
return msg;
|
|
1805
|
+
});
|
|
1806
|
+
const threadIdsToUpdate = /* @__PURE__ */ new Set();
|
|
1807
|
+
await this.client.tx(async (t) => {
|
|
1808
|
+
const queries = [];
|
|
1809
|
+
const columnMapping = {
|
|
1810
|
+
threadId: "thread_id"
|
|
1811
|
+
};
|
|
1812
|
+
for (const existingMessage of existingMessages) {
|
|
1813
|
+
const updatePayload = messages.find((m) => m.id === existingMessage.id);
|
|
1814
|
+
if (!updatePayload) continue;
|
|
1815
|
+
const { id, ...fieldsToUpdate } = updatePayload;
|
|
1816
|
+
if (Object.keys(fieldsToUpdate).length === 0) continue;
|
|
1817
|
+
threadIdsToUpdate.add(existingMessage.threadId);
|
|
1818
|
+
if (updatePayload.threadId && updatePayload.threadId !== existingMessage.threadId) {
|
|
1819
|
+
threadIdsToUpdate.add(updatePayload.threadId);
|
|
1820
|
+
}
|
|
1821
|
+
const setClauses = [];
|
|
1822
|
+
const values = [];
|
|
1823
|
+
let paramIndex = 1;
|
|
1824
|
+
const updatableFields = { ...fieldsToUpdate };
|
|
1825
|
+
if (updatableFields.content) {
|
|
1826
|
+
const newContent = {
|
|
1827
|
+
...existingMessage.content,
|
|
1828
|
+
...updatableFields.content,
|
|
1829
|
+
// Deep merge metadata if it exists on both
|
|
1830
|
+
...existingMessage.content?.metadata && updatableFields.content.metadata ? {
|
|
1831
|
+
metadata: {
|
|
1832
|
+
...existingMessage.content.metadata,
|
|
1833
|
+
...updatableFields.content.metadata
|
|
1834
|
+
}
|
|
1835
|
+
} : {}
|
|
1836
|
+
};
|
|
1837
|
+
setClauses.push(`content = $${paramIndex++}`);
|
|
1838
|
+
values.push(newContent);
|
|
1839
|
+
delete updatableFields.content;
|
|
1840
|
+
}
|
|
1841
|
+
for (const key in updatableFields) {
|
|
1842
|
+
if (Object.prototype.hasOwnProperty.call(updatableFields, key)) {
|
|
1843
|
+
const dbColumn = columnMapping[key] || key;
|
|
1844
|
+
setClauses.push(`"${dbColumn}" = $${paramIndex++}`);
|
|
1845
|
+
values.push(updatableFields[key]);
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
if (setClauses.length > 0) {
|
|
1849
|
+
values.push(id);
|
|
1850
|
+
const sql = `UPDATE ${getTableName({ indexName: TABLE_MESSAGES, schemaName: getSchemaName(this.schema) })} SET ${setClauses.join(", ")} WHERE id = $${paramIndex}`;
|
|
1851
|
+
queries.push(t.none(sql, values));
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1854
|
+
if (threadIdsToUpdate.size > 0) {
|
|
1855
|
+
queries.push(
|
|
1856
|
+
t.none(
|
|
1857
|
+
`UPDATE ${getTableName({ indexName: TABLE_THREADS, schemaName: getSchemaName(this.schema) })} SET "updatedAt" = NOW(), "updatedAtZ" = NOW() WHERE id IN ($1:list)`,
|
|
1858
|
+
[Array.from(threadIdsToUpdate)]
|
|
1859
|
+
)
|
|
1860
|
+
);
|
|
1861
|
+
}
|
|
1862
|
+
if (queries.length > 0) {
|
|
1863
|
+
await t.batch(queries);
|
|
1864
|
+
}
|
|
1865
|
+
});
|
|
1866
|
+
const updatedMessages = await this.client.manyOrNone(selectQuery, [messageIds]);
|
|
1867
|
+
return (updatedMessages || []).map((message) => {
|
|
1868
|
+
if (typeof message.content === "string") {
|
|
1869
|
+
try {
|
|
1870
|
+
message.content = JSON.parse(message.content);
|
|
1871
|
+
} catch {
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
return message;
|
|
1875
|
+
});
|
|
1876
|
+
}
|
|
1877
|
+
async getResourceById({ resourceId }) {
|
|
1878
|
+
const tableName = getTableName({ indexName: TABLE_RESOURCES, schemaName: getSchemaName(this.schema) });
|
|
1879
|
+
const result = await this.client.oneOrNone(
|
|
1880
|
+
`SELECT * FROM ${tableName} WHERE id = $1`,
|
|
1881
|
+
[resourceId]
|
|
1882
|
+
);
|
|
1883
|
+
if (!result) {
|
|
1884
|
+
return null;
|
|
1726
1885
|
}
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1886
|
+
return {
|
|
1887
|
+
id: result.id,
|
|
1888
|
+
createdAt: result.createdAtZ || result.createdAt,
|
|
1889
|
+
updatedAt: result.updatedAtZ || result.updatedAt,
|
|
1890
|
+
workingMemory: result.workingMemory,
|
|
1891
|
+
metadata: typeof result.metadata === "string" ? JSON.parse(result.metadata) : result.metadata
|
|
1892
|
+
};
|
|
1893
|
+
}
|
|
1894
|
+
async saveResource({ resource }) {
|
|
1895
|
+
await this.operations.insert({
|
|
1896
|
+
tableName: TABLE_RESOURCES,
|
|
1897
|
+
record: {
|
|
1898
|
+
...resource,
|
|
1899
|
+
metadata: JSON.stringify(resource.metadata)
|
|
1900
|
+
}
|
|
1901
|
+
});
|
|
1902
|
+
return resource;
|
|
1903
|
+
}
|
|
1904
|
+
async updateResource({
|
|
1905
|
+
resourceId,
|
|
1906
|
+
workingMemory,
|
|
1907
|
+
metadata
|
|
1908
|
+
}) {
|
|
1909
|
+
const existingResource = await this.getResourceById({ resourceId });
|
|
1910
|
+
if (!existingResource) {
|
|
1911
|
+
const newResource = {
|
|
1912
|
+
id: resourceId,
|
|
1913
|
+
workingMemory,
|
|
1914
|
+
metadata: metadata || {},
|
|
1915
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
1916
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1917
|
+
};
|
|
1918
|
+
return this.saveResource({ resource: newResource });
|
|
1919
|
+
}
|
|
1920
|
+
const updatedResource = {
|
|
1921
|
+
...existingResource,
|
|
1922
|
+
workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
|
|
1923
|
+
metadata: {
|
|
1924
|
+
...existingResource.metadata,
|
|
1925
|
+
...metadata
|
|
1926
|
+
},
|
|
1927
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1730
1928
|
};
|
|
1929
|
+
const tableName = getTableName({ indexName: TABLE_RESOURCES, schemaName: getSchemaName(this.schema) });
|
|
1930
|
+
const updates = [];
|
|
1931
|
+
const values = [];
|
|
1932
|
+
let paramIndex = 1;
|
|
1933
|
+
if (workingMemory !== void 0) {
|
|
1934
|
+
updates.push(`"workingMemory" = $${paramIndex}`);
|
|
1935
|
+
values.push(workingMemory);
|
|
1936
|
+
paramIndex++;
|
|
1937
|
+
}
|
|
1938
|
+
if (metadata) {
|
|
1939
|
+
updates.push(`metadata = $${paramIndex}`);
|
|
1940
|
+
values.push(JSON.stringify(updatedResource.metadata));
|
|
1941
|
+
paramIndex++;
|
|
1942
|
+
}
|
|
1943
|
+
updates.push(`"updatedAt" = $${paramIndex}`);
|
|
1944
|
+
values.push(updatedResource.updatedAt.toISOString());
|
|
1945
|
+
updates.push(`"updatedAtZ" = $${paramIndex++}`);
|
|
1946
|
+
values.push(updatedResource.updatedAt.toISOString());
|
|
1947
|
+
paramIndex++;
|
|
1948
|
+
values.push(resourceId);
|
|
1949
|
+
await this.client.none(`UPDATE ${tableName} SET ${updates.join(", ")} WHERE id = $${paramIndex}`, values);
|
|
1950
|
+
return updatedResource;
|
|
1951
|
+
}
|
|
1952
|
+
};
|
|
1953
|
+
var StoreOperationsPG = class extends StoreOperations {
|
|
1954
|
+
client;
|
|
1955
|
+
schemaName;
|
|
1956
|
+
setupSchemaPromise = null;
|
|
1957
|
+
schemaSetupComplete = void 0;
|
|
1958
|
+
constructor({ client, schemaName }) {
|
|
1959
|
+
super();
|
|
1960
|
+
this.client = client;
|
|
1961
|
+
this.schemaName = schemaName;
|
|
1962
|
+
}
|
|
1963
|
+
async hasColumn(table, column) {
|
|
1964
|
+
const schema = this.schemaName || "public";
|
|
1965
|
+
const result = await this.client.oneOrNone(
|
|
1966
|
+
`SELECT 1 FROM information_schema.columns WHERE table_schema = $1 AND table_name = $2 AND (column_name = $3 OR column_name = $4)`,
|
|
1967
|
+
[schema, table, column, column.toLowerCase()]
|
|
1968
|
+
);
|
|
1969
|
+
return !!result;
|
|
1970
|
+
}
|
|
1971
|
+
async setupSchema() {
|
|
1972
|
+
if (!this.schemaName || this.schemaSetupComplete) {
|
|
1973
|
+
return;
|
|
1974
|
+
}
|
|
1975
|
+
const schemaName = getSchemaName(this.schemaName);
|
|
1976
|
+
if (!this.setupSchemaPromise) {
|
|
1977
|
+
this.setupSchemaPromise = (async () => {
|
|
1978
|
+
try {
|
|
1979
|
+
const schemaExists = await this.client.oneOrNone(
|
|
1980
|
+
`
|
|
1981
|
+
SELECT EXISTS (
|
|
1982
|
+
SELECT 1 FROM information_schema.schemata
|
|
1983
|
+
WHERE schema_name = $1
|
|
1984
|
+
)
|
|
1985
|
+
`,
|
|
1986
|
+
[this.schemaName]
|
|
1987
|
+
);
|
|
1988
|
+
if (!schemaExists?.exists) {
|
|
1989
|
+
try {
|
|
1990
|
+
await this.client.none(`CREATE SCHEMA IF NOT EXISTS ${schemaName}`);
|
|
1991
|
+
this.logger.info(`Schema "${this.schemaName}" created successfully`);
|
|
1992
|
+
} catch (error) {
|
|
1993
|
+
this.logger.error(`Failed to create schema "${this.schemaName}"`, { error });
|
|
1994
|
+
throw new Error(
|
|
1995
|
+
`Unable to create schema "${this.schemaName}". This requires CREATE privilege on the database. Either create the schema manually or grant CREATE privilege to the user.`
|
|
1996
|
+
);
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
this.schemaSetupComplete = true;
|
|
2000
|
+
this.logger.debug(`Schema "${schemaName}" is ready for use`);
|
|
2001
|
+
} catch (error) {
|
|
2002
|
+
this.schemaSetupComplete = void 0;
|
|
2003
|
+
this.setupSchemaPromise = null;
|
|
2004
|
+
throw error;
|
|
2005
|
+
} finally {
|
|
2006
|
+
this.setupSchemaPromise = null;
|
|
2007
|
+
}
|
|
2008
|
+
})();
|
|
2009
|
+
}
|
|
2010
|
+
await this.setupSchemaPromise;
|
|
2011
|
+
}
|
|
2012
|
+
async insert({ tableName, record }) {
|
|
1731
2013
|
try {
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
2014
|
+
if (record.createdAt) {
|
|
2015
|
+
record.createdAtZ = record.createdAt;
|
|
2016
|
+
}
|
|
2017
|
+
if (record.created_at) {
|
|
2018
|
+
record.created_atZ = record.created_at;
|
|
2019
|
+
}
|
|
2020
|
+
if (record.updatedAt) {
|
|
2021
|
+
record.updatedAtZ = record.updatedAt;
|
|
2022
|
+
}
|
|
2023
|
+
const schemaName = getSchemaName(this.schemaName);
|
|
2024
|
+
const columns = Object.keys(record).map((col) => parseSqlIdentifier(col, "column name"));
|
|
2025
|
+
const values = Object.values(record);
|
|
2026
|
+
const placeholders = values.map((_, i) => `$${i + 1}`).join(", ");
|
|
2027
|
+
await this.client.none(
|
|
2028
|
+
`INSERT INTO ${getTableName({ indexName: tableName, schemaName })} (${columns.map((c) => `"${c}"`).join(", ")}) VALUES (${placeholders})`,
|
|
2029
|
+
values
|
|
1740
2030
|
);
|
|
1741
|
-
return {
|
|
1742
|
-
...thread,
|
|
1743
|
-
metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
|
|
1744
|
-
createdAt: thread.createdAt,
|
|
1745
|
-
updatedAt: thread.updatedAt
|
|
1746
|
-
};
|
|
1747
2031
|
} catch (error) {
|
|
1748
2032
|
throw new MastraError(
|
|
1749
2033
|
{
|
|
1750
|
-
id: "
|
|
2034
|
+
id: "MASTRA_STORAGE_PG_STORE_INSERT_FAILED",
|
|
1751
2035
|
domain: ErrorDomain.STORAGE,
|
|
1752
2036
|
category: ErrorCategory.THIRD_PARTY,
|
|
1753
2037
|
details: {
|
|
1754
|
-
|
|
1755
|
-
title
|
|
2038
|
+
tableName
|
|
1756
2039
|
}
|
|
1757
2040
|
},
|
|
1758
2041
|
error
|
|
1759
2042
|
);
|
|
1760
2043
|
}
|
|
1761
2044
|
}
|
|
1762
|
-
async
|
|
2045
|
+
async clearTable({ tableName }) {
|
|
1763
2046
|
try {
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
});
|
|
2047
|
+
const schemaName = getSchemaName(this.schemaName);
|
|
2048
|
+
const tableNameWithSchema = getTableName({ indexName: tableName, schemaName });
|
|
2049
|
+
await this.client.none(`TRUNCATE TABLE ${tableNameWithSchema} CASCADE`);
|
|
1768
2050
|
} catch (error) {
|
|
1769
2051
|
throw new MastraError(
|
|
1770
2052
|
{
|
|
1771
|
-
id: "
|
|
2053
|
+
id: "MASTRA_STORAGE_PG_STORE_CLEAR_TABLE_FAILED",
|
|
1772
2054
|
domain: ErrorDomain.STORAGE,
|
|
1773
2055
|
category: ErrorCategory.THIRD_PARTY,
|
|
1774
2056
|
details: {
|
|
1775
|
-
|
|
2057
|
+
tableName
|
|
1776
2058
|
}
|
|
1777
2059
|
},
|
|
1778
2060
|
error
|
|
1779
2061
|
);
|
|
1780
2062
|
}
|
|
1781
2063
|
}
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
2064
|
+
getDefaultValue(type) {
|
|
2065
|
+
switch (type) {
|
|
2066
|
+
case "timestamp":
|
|
2067
|
+
return "DEFAULT NOW()";
|
|
2068
|
+
case "jsonb":
|
|
2069
|
+
return "DEFAULT '{}'::jsonb";
|
|
2070
|
+
default:
|
|
2071
|
+
return super.getDefaultValue(type);
|
|
2072
|
+
}
|
|
2073
|
+
}
|
|
2074
|
+
async createTable({
|
|
2075
|
+
tableName,
|
|
2076
|
+
schema
|
|
1786
2077
|
}) {
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
const
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
2078
|
+
try {
|
|
2079
|
+
const timeZColumnNames = Object.entries(schema).filter(([_, def]) => def.type === "timestamp").map(([name]) => name);
|
|
2080
|
+
const timeZColumns = Object.entries(schema).filter(([_, def]) => def.type === "timestamp").map(([name]) => {
|
|
2081
|
+
const parsedName = parseSqlIdentifier(name, "column name");
|
|
2082
|
+
return `"${parsedName}Z" TIMESTAMPTZ DEFAULT NOW()`;
|
|
2083
|
+
});
|
|
2084
|
+
const columns = Object.entries(schema).map(([name, def]) => {
|
|
2085
|
+
const parsedName = parseSqlIdentifier(name, "column name");
|
|
2086
|
+
const constraints = [];
|
|
2087
|
+
if (def.primaryKey) constraints.push("PRIMARY KEY");
|
|
2088
|
+
if (!def.nullable) constraints.push("NOT NULL");
|
|
2089
|
+
return `"${parsedName}" ${def.type.toUpperCase()} ${constraints.join(" ")}`;
|
|
2090
|
+
});
|
|
2091
|
+
if (this.schemaName) {
|
|
2092
|
+
await this.setupSchema();
|
|
2093
|
+
}
|
|
2094
|
+
const finalColumns = [...columns, ...timeZColumns].join(",\n");
|
|
2095
|
+
const sql = `
|
|
2096
|
+
CREATE TABLE IF NOT EXISTS ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} (
|
|
2097
|
+
${finalColumns}
|
|
2098
|
+
);
|
|
2099
|
+
${tableName === TABLE_WORKFLOW_SNAPSHOT ? `
|
|
2100
|
+
DO $$ BEGIN
|
|
2101
|
+
IF NOT EXISTS (
|
|
2102
|
+
SELECT 1 FROM pg_constraint WHERE conname = 'mastra_workflow_snapshot_workflow_name_run_id_key'
|
|
2103
|
+
) THEN
|
|
2104
|
+
ALTER TABLE ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })}
|
|
2105
|
+
ADD CONSTRAINT mastra_workflow_snapshot_workflow_name_run_id_key
|
|
2106
|
+
UNIQUE (workflow_name, run_id);
|
|
2107
|
+
END IF;
|
|
2108
|
+
END $$;
|
|
2109
|
+
` : ""}
|
|
2110
|
+
`;
|
|
2111
|
+
await this.client.none(sql);
|
|
2112
|
+
console.log("Alter Table SQL", tableName);
|
|
2113
|
+
console.log("timeZColumnNames", timeZColumnNames);
|
|
2114
|
+
await this.alterTable({
|
|
2115
|
+
tableName,
|
|
2116
|
+
schema,
|
|
2117
|
+
ifNotExists: timeZColumnNames
|
|
2118
|
+
});
|
|
2119
|
+
} catch (error) {
|
|
2120
|
+
throw new MastraError(
|
|
2121
|
+
{
|
|
2122
|
+
id: "MASTRA_STORAGE_PG_STORE_CREATE_TABLE_FAILED",
|
|
2123
|
+
domain: ErrorDomain.STORAGE,
|
|
2124
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2125
|
+
details: {
|
|
2126
|
+
tableName
|
|
2127
|
+
}
|
|
2128
|
+
},
|
|
2129
|
+
error
|
|
1829
2130
|
);
|
|
1830
|
-
params.push(searchId, id, withPreviousMessages, withNextMessages);
|
|
1831
|
-
paramIdx += 4;
|
|
1832
2131
|
}
|
|
1833
|
-
const finalQuery = unionQueries.join(" UNION ALL ") + ' ORDER BY "createdAt" ASC';
|
|
1834
|
-
const includedRows = await this.db.manyOrNone(finalQuery, params);
|
|
1835
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1836
|
-
const dedupedRows = includedRows.filter((row) => {
|
|
1837
|
-
if (seen.has(row.id)) return false;
|
|
1838
|
-
seen.add(row.id);
|
|
1839
|
-
return true;
|
|
1840
|
-
});
|
|
1841
|
-
return dedupedRows;
|
|
1842
2132
|
}
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
2133
|
+
/**
|
|
2134
|
+
* Alters table schema to add columns if they don't exist
|
|
2135
|
+
* @param tableName Name of the table
|
|
2136
|
+
* @param schema Schema of the table
|
|
2137
|
+
* @param ifNotExists Array of column names to add if they don't exist
|
|
2138
|
+
*/
|
|
2139
|
+
async alterTable({
|
|
2140
|
+
tableName,
|
|
2141
|
+
schema,
|
|
2142
|
+
ifNotExists
|
|
2143
|
+
}) {
|
|
2144
|
+
const fullTableName = getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) });
|
|
1848
2145
|
try {
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
${orderByStatement}
|
|
1862
|
-
LIMIT $${excludeIds.length + 2}
|
|
1863
|
-
`;
|
|
1864
|
-
const queryParams = [threadId, ...excludeIds, limit];
|
|
1865
|
-
const remainingRows = await this.db.manyOrNone(query, queryParams);
|
|
1866
|
-
rows.push(...remainingRows);
|
|
1867
|
-
const fetchedMessages = (rows || []).map((message) => {
|
|
1868
|
-
if (typeof message.content === "string") {
|
|
1869
|
-
try {
|
|
1870
|
-
message.content = JSON.parse(message.content);
|
|
1871
|
-
} catch {
|
|
2146
|
+
for (const columnName of ifNotExists) {
|
|
2147
|
+
if (schema[columnName]) {
|
|
2148
|
+
const columnDef = schema[columnName];
|
|
2149
|
+
const sqlType = this.getSqlType(columnDef.type);
|
|
2150
|
+
const nullable = columnDef.nullable === false ? "NOT NULL" : "";
|
|
2151
|
+
const defaultValue = columnDef.nullable === false ? this.getDefaultValue(columnDef.type) : "";
|
|
2152
|
+
const parsedColumnName = parseSqlIdentifier(columnName, "column name");
|
|
2153
|
+
const alterSql = `ALTER TABLE ${fullTableName} ADD COLUMN IF NOT EXISTS "${parsedColumnName}" ${sqlType} ${nullable} ${defaultValue}`.trim();
|
|
2154
|
+
await this.client.none(alterSql);
|
|
2155
|
+
if (sqlType === "TIMESTAMP") {
|
|
2156
|
+
const alterSql2 = `ALTER TABLE ${fullTableName} ADD COLUMN IF NOT EXISTS "${parsedColumnName}Z" TIMESTAMPTZ DEFAULT NOW()`.trim();
|
|
2157
|
+
await this.client.none(alterSql2);
|
|
1872
2158
|
}
|
|
2159
|
+
this.logger?.debug?.(`Ensured column ${parsedColumnName} exists in table ${fullTableName}`);
|
|
1873
2160
|
}
|
|
1874
|
-
|
|
1875
|
-
return message;
|
|
1876
|
-
});
|
|
1877
|
-
const sortedMessages = fetchedMessages.sort(
|
|
1878
|
-
(a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()
|
|
1879
|
-
);
|
|
1880
|
-
return format === "v2" ? sortedMessages.map(
|
|
1881
|
-
(m) => ({ ...m, content: m.content || { format: 2, parts: [{ type: "text", text: "" }] } })
|
|
1882
|
-
) : sortedMessages;
|
|
2161
|
+
}
|
|
1883
2162
|
} catch (error) {
|
|
1884
|
-
|
|
2163
|
+
throw new MastraError(
|
|
1885
2164
|
{
|
|
1886
|
-
id: "
|
|
2165
|
+
id: "MASTRA_STORAGE_PG_STORE_ALTER_TABLE_FAILED",
|
|
1887
2166
|
domain: ErrorDomain.STORAGE,
|
|
1888
2167
|
category: ErrorCategory.THIRD_PARTY,
|
|
1889
2168
|
details: {
|
|
1890
|
-
|
|
2169
|
+
tableName
|
|
1891
2170
|
}
|
|
1892
2171
|
},
|
|
1893
2172
|
error
|
|
1894
2173
|
);
|
|
1895
|
-
this.logger?.error?.(mastraError.toString());
|
|
1896
|
-
this.logger?.trackException(mastraError);
|
|
1897
|
-
return [];
|
|
1898
2174
|
}
|
|
1899
2175
|
}
|
|
1900
|
-
async
|
|
1901
|
-
const { threadId, format, selectBy } = args;
|
|
1902
|
-
const { page = 0, perPage: perPageInput, dateRange } = selectBy?.pagination || {};
|
|
1903
|
-
const fromDate = dateRange?.start;
|
|
1904
|
-
const toDate = dateRange?.end;
|
|
1905
|
-
const selectStatement = `SELECT id, content, role, type, "createdAt", thread_id AS "threadId", "resourceId"`;
|
|
1906
|
-
const orderByStatement = `ORDER BY "createdAt" DESC`;
|
|
1907
|
-
const messages = [];
|
|
1908
|
-
if (selectBy?.include?.length) {
|
|
1909
|
-
const includeMessages = await this._getIncludedMessages({ threadId, selectBy, orderByStatement });
|
|
1910
|
-
if (includeMessages) {
|
|
1911
|
-
messages.push(...includeMessages);
|
|
1912
|
-
}
|
|
1913
|
-
}
|
|
2176
|
+
async load({ tableName, keys }) {
|
|
1914
2177
|
try {
|
|
1915
|
-
const
|
|
1916
|
-
const
|
|
1917
|
-
const
|
|
1918
|
-
const
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
if (toDate) {
|
|
1925
|
-
conditions.push(`"createdAt" <= $${paramIndex++}`);
|
|
1926
|
-
queryParams.push(toDate);
|
|
1927
|
-
}
|
|
1928
|
-
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
1929
|
-
const countQuery = `SELECT COUNT(*) FROM ${this.getTableName(TABLE_MESSAGES)} ${whereClause}`;
|
|
1930
|
-
const countResult = await this.db.one(countQuery, queryParams);
|
|
1931
|
-
const total = parseInt(countResult.count, 10);
|
|
1932
|
-
if (total === 0 && messages.length === 0) {
|
|
1933
|
-
return {
|
|
1934
|
-
messages: [],
|
|
1935
|
-
total: 0,
|
|
1936
|
-
page,
|
|
1937
|
-
perPage,
|
|
1938
|
-
hasMore: false
|
|
1939
|
-
};
|
|
2178
|
+
const keyEntries = Object.entries(keys).map(([key, value]) => [parseSqlIdentifier(key, "column name"), value]);
|
|
2179
|
+
const conditions = keyEntries.map(([key], index) => `"${key}" = $${index + 1}`).join(" AND ");
|
|
2180
|
+
const values = keyEntries.map(([_, value]) => value);
|
|
2181
|
+
const result = await this.client.oneOrNone(
|
|
2182
|
+
`SELECT * FROM ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} WHERE ${conditions}`,
|
|
2183
|
+
values
|
|
2184
|
+
);
|
|
2185
|
+
if (!result) {
|
|
2186
|
+
return null;
|
|
1940
2187
|
}
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
TABLE_MESSAGES
|
|
1946
|
-
)} ${whereClause} ${excludeIds.length ? `AND id NOT IN (${excludeIdsParam})` : ""}${orderByStatement} LIMIT $${paramIndex++} OFFSET $${paramIndex++}`;
|
|
1947
|
-
const rows = await this.db.manyOrNone(dataQuery, [...queryParams, ...excludeIds, perPage, currentOffset]);
|
|
1948
|
-
messages.push(...rows || []);
|
|
1949
|
-
const messagesWithParsedContent = messages.map((message) => {
|
|
1950
|
-
if (typeof message.content === "string") {
|
|
1951
|
-
try {
|
|
1952
|
-
return { ...message, content: JSON.parse(message.content) };
|
|
1953
|
-
} catch {
|
|
1954
|
-
return message;
|
|
1955
|
-
}
|
|
2188
|
+
if (tableName === TABLE_WORKFLOW_SNAPSHOT) {
|
|
2189
|
+
const snapshot = result;
|
|
2190
|
+
if (typeof snapshot.snapshot === "string") {
|
|
2191
|
+
snapshot.snapshot = JSON.parse(snapshot.snapshot);
|
|
1956
2192
|
}
|
|
1957
|
-
return
|
|
1958
|
-
}
|
|
1959
|
-
|
|
1960
|
-
const messagesToReturn = format === `v2` ? list.get.all.v2() : list.get.all.v1();
|
|
1961
|
-
return {
|
|
1962
|
-
messages: messagesToReturn,
|
|
1963
|
-
total,
|
|
1964
|
-
page,
|
|
1965
|
-
perPage,
|
|
1966
|
-
hasMore: currentOffset + rows.length < total
|
|
1967
|
-
};
|
|
2193
|
+
return snapshot;
|
|
2194
|
+
}
|
|
2195
|
+
return result;
|
|
1968
2196
|
} catch (error) {
|
|
1969
|
-
|
|
2197
|
+
throw new MastraError(
|
|
1970
2198
|
{
|
|
1971
|
-
id: "
|
|
2199
|
+
id: "MASTRA_STORAGE_PG_STORE_LOAD_FAILED",
|
|
1972
2200
|
domain: ErrorDomain.STORAGE,
|
|
1973
2201
|
category: ErrorCategory.THIRD_PARTY,
|
|
1974
2202
|
details: {
|
|
1975
|
-
|
|
1976
|
-
page
|
|
2203
|
+
tableName
|
|
1977
2204
|
}
|
|
1978
2205
|
},
|
|
1979
2206
|
error
|
|
1980
2207
|
);
|
|
1981
|
-
this.logger?.error?.(mastraError.toString());
|
|
1982
|
-
this.logger?.trackException(mastraError);
|
|
1983
|
-
return { messages: [], total: 0, page, perPage: perPageInput || 40, hasMore: false };
|
|
1984
2208
|
}
|
|
1985
2209
|
}
|
|
1986
|
-
async
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
details: {
|
|
2008
|
-
threadId
|
|
2009
|
-
}
|
|
2010
|
-
});
|
|
2210
|
+
async batchInsert({ tableName, records }) {
|
|
2211
|
+
try {
|
|
2212
|
+
await this.client.query("BEGIN");
|
|
2213
|
+
for (const record of records) {
|
|
2214
|
+
await this.insert({ tableName, record });
|
|
2215
|
+
}
|
|
2216
|
+
await this.client.query("COMMIT");
|
|
2217
|
+
} catch (error) {
|
|
2218
|
+
await this.client.query("ROLLBACK");
|
|
2219
|
+
throw new MastraError(
|
|
2220
|
+
{
|
|
2221
|
+
id: "MASTRA_STORAGE_PG_STORE_BATCH_INSERT_FAILED",
|
|
2222
|
+
domain: ErrorDomain.STORAGE,
|
|
2223
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2224
|
+
details: {
|
|
2225
|
+
tableName,
|
|
2226
|
+
numberOfRecords: records.length
|
|
2227
|
+
}
|
|
2228
|
+
},
|
|
2229
|
+
error
|
|
2230
|
+
);
|
|
2011
2231
|
}
|
|
2232
|
+
}
|
|
2233
|
+
async dropTable({ tableName }) {
|
|
2012
2234
|
try {
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
}
|
|
2025
|
-
return t.none(
|
|
2026
|
-
`INSERT INTO ${this.getTableName(TABLE_MESSAGES)} (id, thread_id, content, "createdAt", role, type, "resourceId")
|
|
2027
|
-
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
2028
|
-
ON CONFLICT (id) DO UPDATE SET
|
|
2029
|
-
thread_id = EXCLUDED.thread_id,
|
|
2030
|
-
content = EXCLUDED.content,
|
|
2031
|
-
role = EXCLUDED.role,
|
|
2032
|
-
type = EXCLUDED.type,
|
|
2033
|
-
"resourceId" = EXCLUDED."resourceId"`,
|
|
2034
|
-
[
|
|
2035
|
-
message.id,
|
|
2036
|
-
message.threadId,
|
|
2037
|
-
typeof message.content === "string" ? message.content : JSON.stringify(message.content),
|
|
2038
|
-
message.createdAt || (/* @__PURE__ */ new Date()).toISOString(),
|
|
2039
|
-
message.role,
|
|
2040
|
-
message.type || "v2",
|
|
2041
|
-
message.resourceId
|
|
2042
|
-
]
|
|
2043
|
-
);
|
|
2044
|
-
});
|
|
2045
|
-
const threadUpdate = t.none(
|
|
2046
|
-
`UPDATE ${this.getTableName(TABLE_THREADS)}
|
|
2047
|
-
SET "updatedAt" = $1
|
|
2048
|
-
WHERE id = $2`,
|
|
2049
|
-
[(/* @__PURE__ */ new Date()).toISOString(), threadId]
|
|
2050
|
-
);
|
|
2051
|
-
await Promise.all([...messageInserts, threadUpdate]);
|
|
2052
|
-
});
|
|
2053
|
-
const messagesWithParsedContent = messages.map((message) => {
|
|
2054
|
-
if (typeof message.content === "string") {
|
|
2055
|
-
try {
|
|
2056
|
-
return { ...message, content: JSON.parse(message.content) };
|
|
2057
|
-
} catch {
|
|
2058
|
-
return message;
|
|
2059
|
-
}
|
|
2060
|
-
}
|
|
2061
|
-
return message;
|
|
2062
|
-
});
|
|
2063
|
-
const list = new MessageList().add(messagesWithParsedContent, "memory");
|
|
2064
|
-
if (format === `v2`) return list.get.all.v2();
|
|
2065
|
-
return list.get.all.v1();
|
|
2066
|
-
} catch (error) {
|
|
2067
|
-
throw new MastraError(
|
|
2068
|
-
{
|
|
2069
|
-
id: "MASTRA_STORAGE_PG_STORE_SAVE_MESSAGES_FAILED",
|
|
2070
|
-
domain: ErrorDomain.STORAGE,
|
|
2071
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
2072
|
-
details: {
|
|
2073
|
-
threadId
|
|
2235
|
+
const schemaName = getSchemaName(this.schemaName);
|
|
2236
|
+
const tableNameWithSchema = getTableName({ indexName: tableName, schemaName });
|
|
2237
|
+
await this.client.none(`DROP TABLE IF EXISTS ${tableNameWithSchema}`);
|
|
2238
|
+
} catch (error) {
|
|
2239
|
+
throw new MastraError(
|
|
2240
|
+
{
|
|
2241
|
+
id: "MASTRA_STORAGE_PG_STORE_DROP_TABLE_FAILED",
|
|
2242
|
+
domain: ErrorDomain.STORAGE,
|
|
2243
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2244
|
+
details: {
|
|
2245
|
+
tableName
|
|
2074
2246
|
}
|
|
2075
2247
|
},
|
|
2076
2248
|
error
|
|
2077
2249
|
);
|
|
2078
2250
|
}
|
|
2079
2251
|
}
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
}) {
|
|
2252
|
+
};
|
|
2253
|
+
function transformScoreRow(row) {
|
|
2254
|
+
let input = void 0;
|
|
2255
|
+
if (row.input) {
|
|
2085
2256
|
try {
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2257
|
+
input = JSON.parse(row.input);
|
|
2258
|
+
} catch {
|
|
2259
|
+
input = row.input;
|
|
2260
|
+
}
|
|
2261
|
+
}
|
|
2262
|
+
return {
|
|
2263
|
+
...row,
|
|
2264
|
+
input,
|
|
2265
|
+
createdAt: row.createdAtZ || row.createdAt,
|
|
2266
|
+
updatedAt: row.updatedAtZ || row.updatedAt
|
|
2267
|
+
};
|
|
2268
|
+
}
|
|
2269
|
+
var ScoresPG = class extends ScoresStorage {
|
|
2270
|
+
client;
|
|
2271
|
+
operations;
|
|
2272
|
+
constructor({ client, operations }) {
|
|
2273
|
+
super();
|
|
2274
|
+
this.client = client;
|
|
2275
|
+
this.operations = operations;
|
|
2276
|
+
}
|
|
2277
|
+
async getScoreById({ id }) {
|
|
2278
|
+
try {
|
|
2279
|
+
const result = await this.client.oneOrNone(`SELECT * FROM ${TABLE_SCORERS} WHERE id = $1`, [id]);
|
|
2280
|
+
return transformScoreRow(result);
|
|
2100
2281
|
} catch (error) {
|
|
2101
2282
|
throw new MastraError(
|
|
2102
2283
|
{
|
|
2103
|
-
id: "
|
|
2284
|
+
id: "MASTRA_STORAGE_PG_STORE_GET_SCORE_BY_ID_FAILED",
|
|
2104
2285
|
domain: ErrorDomain.STORAGE,
|
|
2105
|
-
category: ErrorCategory.THIRD_PARTY
|
|
2106
|
-
details: {
|
|
2107
|
-
workflowName,
|
|
2108
|
-
runId
|
|
2109
|
-
}
|
|
2286
|
+
category: ErrorCategory.THIRD_PARTY
|
|
2110
2287
|
},
|
|
2111
2288
|
error
|
|
2112
2289
|
);
|
|
2113
2290
|
}
|
|
2114
2291
|
}
|
|
2115
|
-
async
|
|
2116
|
-
|
|
2117
|
-
|
|
2292
|
+
async getScoresByScorerId({
|
|
2293
|
+
scorerId,
|
|
2294
|
+
pagination
|
|
2118
2295
|
}) {
|
|
2119
2296
|
try {
|
|
2120
|
-
const
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2297
|
+
const total = await this.client.oneOrNone(
|
|
2298
|
+
`SELECT COUNT(*) FROM ${TABLE_SCORERS} WHERE "scorerId" = $1`,
|
|
2299
|
+
[scorerId]
|
|
2300
|
+
);
|
|
2301
|
+
if (total?.count === "0" || !total?.count) {
|
|
2302
|
+
return {
|
|
2303
|
+
pagination: {
|
|
2304
|
+
total: 0,
|
|
2305
|
+
page: pagination.page,
|
|
2306
|
+
perPage: pagination.perPage,
|
|
2307
|
+
hasMore: false
|
|
2308
|
+
},
|
|
2309
|
+
scores: []
|
|
2310
|
+
};
|
|
2129
2311
|
}
|
|
2130
|
-
|
|
2312
|
+
const result = await this.client.manyOrNone(
|
|
2313
|
+
`SELECT * FROM ${TABLE_SCORERS} WHERE "scorerId" = $1 LIMIT $2 OFFSET $3`,
|
|
2314
|
+
[scorerId, pagination.perPage, pagination.page * pagination.perPage]
|
|
2315
|
+
);
|
|
2316
|
+
return {
|
|
2317
|
+
pagination: {
|
|
2318
|
+
total: Number(total?.count) || 0,
|
|
2319
|
+
page: pagination.page,
|
|
2320
|
+
perPage: pagination.perPage,
|
|
2321
|
+
hasMore: Number(total?.count) > (pagination.page + 1) * pagination.perPage
|
|
2322
|
+
},
|
|
2323
|
+
scores: result
|
|
2324
|
+
};
|
|
2131
2325
|
} catch (error) {
|
|
2132
2326
|
throw new MastraError(
|
|
2133
2327
|
{
|
|
2134
|
-
id: "
|
|
2328
|
+
id: "MASTRA_STORAGE_PG_STORE_GET_SCORES_BY_SCORER_ID_FAILED",
|
|
2135
2329
|
domain: ErrorDomain.STORAGE,
|
|
2136
|
-
category: ErrorCategory.THIRD_PARTY
|
|
2137
|
-
details: {
|
|
2138
|
-
workflowName,
|
|
2139
|
-
runId
|
|
2140
|
-
}
|
|
2330
|
+
category: ErrorCategory.THIRD_PARTY
|
|
2141
2331
|
},
|
|
2142
2332
|
error
|
|
2143
2333
|
);
|
|
2144
2334
|
}
|
|
2145
2335
|
}
|
|
2146
|
-
async
|
|
2147
|
-
const schema = this.schema || "public";
|
|
2148
|
-
const result = await this.db.oneOrNone(
|
|
2149
|
-
`SELECT 1 FROM information_schema.columns WHERE table_schema = $1 AND table_name = $2 AND (column_name = $3 OR column_name = $4)`,
|
|
2150
|
-
[schema, table, column, column.toLowerCase()]
|
|
2151
|
-
);
|
|
2152
|
-
return !!result;
|
|
2153
|
-
}
|
|
2154
|
-
parseWorkflowRun(row) {
|
|
2155
|
-
let parsedSnapshot = row.snapshot;
|
|
2156
|
-
if (typeof parsedSnapshot === "string") {
|
|
2157
|
-
try {
|
|
2158
|
-
parsedSnapshot = JSON.parse(row.snapshot);
|
|
2159
|
-
} catch (e) {
|
|
2160
|
-
console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
|
|
2161
|
-
}
|
|
2162
|
-
}
|
|
2163
|
-
return {
|
|
2164
|
-
workflowName: row.workflow_name,
|
|
2165
|
-
runId: row.run_id,
|
|
2166
|
-
snapshot: parsedSnapshot,
|
|
2167
|
-
createdAt: row.createdAt,
|
|
2168
|
-
updatedAt: row.updatedAt,
|
|
2169
|
-
resourceId: row.resourceId
|
|
2170
|
-
};
|
|
2171
|
-
}
|
|
2172
|
-
async getWorkflowRuns({
|
|
2173
|
-
workflowName,
|
|
2174
|
-
fromDate,
|
|
2175
|
-
toDate,
|
|
2176
|
-
limit,
|
|
2177
|
-
offset,
|
|
2178
|
-
resourceId
|
|
2179
|
-
} = {}) {
|
|
2336
|
+
async saveScore(score) {
|
|
2180
2337
|
try {
|
|
2181
|
-
const
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
if (resourceId) {
|
|
2190
|
-
const hasResourceId = await this.hasColumn(TABLE_WORKFLOW_SNAPSHOT, "resourceId");
|
|
2191
|
-
if (hasResourceId) {
|
|
2192
|
-
conditions.push(`"resourceId" = $${paramIndex}`);
|
|
2193
|
-
values.push(resourceId);
|
|
2194
|
-
paramIndex++;
|
|
2195
|
-
} else {
|
|
2196
|
-
console.warn(`[${TABLE_WORKFLOW_SNAPSHOT}] resourceId column not found. Skipping resourceId filter.`);
|
|
2338
|
+
const { input, ...rest } = score;
|
|
2339
|
+
await this.operations.insert({
|
|
2340
|
+
tableName: TABLE_SCORERS,
|
|
2341
|
+
record: {
|
|
2342
|
+
...rest,
|
|
2343
|
+
input: JSON.stringify(input),
|
|
2344
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2345
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2197
2346
|
}
|
|
2198
|
-
}
|
|
2199
|
-
if (fromDate) {
|
|
2200
|
-
conditions.push(`"createdAt" >= $${paramIndex}`);
|
|
2201
|
-
values.push(fromDate);
|
|
2202
|
-
paramIndex++;
|
|
2203
|
-
}
|
|
2204
|
-
if (toDate) {
|
|
2205
|
-
conditions.push(`"createdAt" <= $${paramIndex}`);
|
|
2206
|
-
values.push(toDate);
|
|
2207
|
-
paramIndex++;
|
|
2208
|
-
}
|
|
2209
|
-
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
2210
|
-
let total = 0;
|
|
2211
|
-
if (limit !== void 0 && offset !== void 0) {
|
|
2212
|
-
const countResult = await this.db.one(
|
|
2213
|
-
`SELECT COUNT(*) as count FROM ${this.getTableName(TABLE_WORKFLOW_SNAPSHOT)} ${whereClause}`,
|
|
2214
|
-
values
|
|
2215
|
-
);
|
|
2216
|
-
total = Number(countResult.count);
|
|
2217
|
-
}
|
|
2218
|
-
const query = `
|
|
2219
|
-
SELECT * FROM ${this.getTableName(TABLE_WORKFLOW_SNAPSHOT)}
|
|
2220
|
-
${whereClause}
|
|
2221
|
-
ORDER BY "createdAt" DESC
|
|
2222
|
-
${limit !== void 0 && offset !== void 0 ? ` LIMIT $${paramIndex} OFFSET $${paramIndex + 1}` : ""}
|
|
2223
|
-
`;
|
|
2224
|
-
const queryValues = limit !== void 0 && offset !== void 0 ? [...values, limit, offset] : values;
|
|
2225
|
-
const result = await this.db.manyOrNone(query, queryValues);
|
|
2226
|
-
const runs = (result || []).map((row) => {
|
|
2227
|
-
return this.parseWorkflowRun(row);
|
|
2228
2347
|
});
|
|
2229
|
-
|
|
2348
|
+
const scoreFromDb = await this.getScoreById({ id: score.id });
|
|
2349
|
+
return { score: scoreFromDb };
|
|
2230
2350
|
} catch (error) {
|
|
2231
2351
|
throw new MastraError(
|
|
2232
2352
|
{
|
|
2233
|
-
id: "
|
|
2353
|
+
id: "MASTRA_STORAGE_PG_STORE_SAVE_SCORE_FAILED",
|
|
2234
2354
|
domain: ErrorDomain.STORAGE,
|
|
2235
|
-
category: ErrorCategory.THIRD_PARTY
|
|
2236
|
-
details: {
|
|
2237
|
-
workflowName: workflowName || "all"
|
|
2238
|
-
}
|
|
2355
|
+
category: ErrorCategory.THIRD_PARTY
|
|
2239
2356
|
},
|
|
2240
2357
|
error
|
|
2241
2358
|
);
|
|
2242
2359
|
}
|
|
2243
2360
|
}
|
|
2244
|
-
async
|
|
2361
|
+
async getScoresByRunId({
|
|
2245
2362
|
runId,
|
|
2246
|
-
|
|
2363
|
+
pagination
|
|
2247
2364
|
}) {
|
|
2248
2365
|
try {
|
|
2249
|
-
const
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
${whereClause}
|
|
2266
|
-
`;
|
|
2267
|
-
const queryValues = values;
|
|
2268
|
-
const result = await this.db.oneOrNone(query, queryValues);
|
|
2269
|
-
if (!result) {
|
|
2270
|
-
return null;
|
|
2366
|
+
const total = await this.client.oneOrNone(
|
|
2367
|
+
`SELECT COUNT(*) FROM ${TABLE_SCORERS} WHERE "runId" = $1`,
|
|
2368
|
+
[runId]
|
|
2369
|
+
);
|
|
2370
|
+
console.log(`total: ${total?.count}`);
|
|
2371
|
+
console.log(`typeof total: ${typeof total?.count}`);
|
|
2372
|
+
if (total?.count === "0" || !total?.count) {
|
|
2373
|
+
return {
|
|
2374
|
+
pagination: {
|
|
2375
|
+
total: 0,
|
|
2376
|
+
page: pagination.page,
|
|
2377
|
+
perPage: pagination.perPage,
|
|
2378
|
+
hasMore: false
|
|
2379
|
+
},
|
|
2380
|
+
scores: []
|
|
2381
|
+
};
|
|
2271
2382
|
}
|
|
2272
|
-
|
|
2383
|
+
const result = await this.client.manyOrNone(
|
|
2384
|
+
`SELECT * FROM ${TABLE_SCORERS} WHERE "runId" = $1 LIMIT $2 OFFSET $3`,
|
|
2385
|
+
[runId, pagination.perPage, pagination.page * pagination.perPage]
|
|
2386
|
+
);
|
|
2387
|
+
return {
|
|
2388
|
+
pagination: {
|
|
2389
|
+
total: Number(total?.count) || 0,
|
|
2390
|
+
page: pagination.page,
|
|
2391
|
+
perPage: pagination.perPage,
|
|
2392
|
+
hasMore: Number(total?.count) > (pagination.page + 1) * pagination.perPage
|
|
2393
|
+
},
|
|
2394
|
+
scores: result
|
|
2395
|
+
};
|
|
2273
2396
|
} catch (error) {
|
|
2274
2397
|
throw new MastraError(
|
|
2275
2398
|
{
|
|
2276
|
-
id: "
|
|
2399
|
+
id: "MASTRA_STORAGE_PG_STORE_GET_SCORES_BY_RUN_ID_FAILED",
|
|
2277
2400
|
domain: ErrorDomain.STORAGE,
|
|
2278
|
-
category: ErrorCategory.THIRD_PARTY
|
|
2279
|
-
details: {
|
|
2280
|
-
runId,
|
|
2281
|
-
workflowName: workflowName || ""
|
|
2282
|
-
}
|
|
2401
|
+
category: ErrorCategory.THIRD_PARTY
|
|
2283
2402
|
},
|
|
2284
2403
|
error
|
|
2285
2404
|
);
|
|
2286
2405
|
}
|
|
2287
2406
|
}
|
|
2288
|
-
async
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
const fromDate = dateRange?.start;
|
|
2294
|
-
const toDate = dateRange?.end;
|
|
2295
|
-
const conditions = [];
|
|
2296
|
-
const queryParams = [];
|
|
2297
|
-
let paramIndex = 1;
|
|
2298
|
-
if (agentName) {
|
|
2299
|
-
conditions.push(`agent_name = $${paramIndex++}`);
|
|
2300
|
-
queryParams.push(agentName);
|
|
2301
|
-
}
|
|
2302
|
-
if (type === "test") {
|
|
2303
|
-
conditions.push(`(test_info IS NOT NULL AND test_info->>'testPath' IS NOT NULL)`);
|
|
2304
|
-
} else if (type === "live") {
|
|
2305
|
-
conditions.push(`(test_info IS NULL OR test_info->>'testPath' IS NULL)`);
|
|
2306
|
-
}
|
|
2307
|
-
if (fromDate) {
|
|
2308
|
-
conditions.push(`created_at >= $${paramIndex++}`);
|
|
2309
|
-
queryParams.push(fromDate);
|
|
2310
|
-
}
|
|
2311
|
-
if (toDate) {
|
|
2312
|
-
conditions.push(`created_at <= $${paramIndex++}`);
|
|
2313
|
-
queryParams.push(toDate);
|
|
2314
|
-
}
|
|
2315
|
-
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
2316
|
-
const countQuery = `SELECT COUNT(*) FROM ${this.getTableName(TABLE_EVALS)} ${whereClause}`;
|
|
2407
|
+
async getScoresByEntityId({
|
|
2408
|
+
entityId,
|
|
2409
|
+
entityType,
|
|
2410
|
+
pagination
|
|
2411
|
+
}) {
|
|
2317
2412
|
try {
|
|
2318
|
-
const
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2413
|
+
const total = await this.client.oneOrNone(
|
|
2414
|
+
`SELECT COUNT(*) FROM ${TABLE_SCORERS} WHERE "entityId" = $1 AND "entityType" = $2`,
|
|
2415
|
+
[entityId, entityType]
|
|
2416
|
+
);
|
|
2417
|
+
if (total?.count === "0" || !total?.count) {
|
|
2322
2418
|
return {
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2419
|
+
pagination: {
|
|
2420
|
+
total: 0,
|
|
2421
|
+
page: pagination.page,
|
|
2422
|
+
perPage: pagination.perPage,
|
|
2423
|
+
hasMore: false
|
|
2424
|
+
},
|
|
2425
|
+
scores: []
|
|
2328
2426
|
};
|
|
2329
2427
|
}
|
|
2330
|
-
const
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2428
|
+
const result = await this.client.manyOrNone(
|
|
2429
|
+
`SELECT * FROM ${TABLE_SCORERS} WHERE "entityId" = $1 AND "entityType" = $2 LIMIT $3 OFFSET $4`,
|
|
2430
|
+
[entityId, entityType, pagination.perPage, pagination.page * pagination.perPage]
|
|
2431
|
+
);
|
|
2334
2432
|
return {
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2433
|
+
pagination: {
|
|
2434
|
+
total: Number(total?.count) || 0,
|
|
2435
|
+
page: pagination.page,
|
|
2436
|
+
perPage: pagination.perPage,
|
|
2437
|
+
hasMore: Number(total?.count) > (pagination.page + 1) * pagination.perPage
|
|
2438
|
+
},
|
|
2439
|
+
scores: result
|
|
2340
2440
|
};
|
|
2341
2441
|
} catch (error) {
|
|
2342
|
-
|
|
2442
|
+
throw new MastraError(
|
|
2343
2443
|
{
|
|
2344
|
-
id: "
|
|
2444
|
+
id: "MASTRA_STORAGE_PG_STORE_GET_SCORES_BY_ENTITY_ID_FAILED",
|
|
2345
2445
|
domain: ErrorDomain.STORAGE,
|
|
2346
|
-
category: ErrorCategory.THIRD_PARTY
|
|
2347
|
-
details: {
|
|
2348
|
-
agentName: agentName || "all",
|
|
2349
|
-
type: type || "all",
|
|
2350
|
-
page,
|
|
2351
|
-
perPage
|
|
2352
|
-
}
|
|
2446
|
+
category: ErrorCategory.THIRD_PARTY
|
|
2353
2447
|
},
|
|
2354
2448
|
error
|
|
2355
2449
|
);
|
|
2356
|
-
this.logger?.error?.(mastraError.toString());
|
|
2357
|
-
this.logger?.trackException(mastraError);
|
|
2358
|
-
throw mastraError;
|
|
2359
2450
|
}
|
|
2360
2451
|
}
|
|
2361
|
-
|
|
2362
|
-
|
|
2452
|
+
};
|
|
2453
|
+
var TracesPG = class extends TracesStorage {
|
|
2454
|
+
client;
|
|
2455
|
+
operations;
|
|
2456
|
+
schema;
|
|
2457
|
+
constructor({
|
|
2458
|
+
client,
|
|
2459
|
+
operations,
|
|
2460
|
+
schema
|
|
2363
2461
|
}) {
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2462
|
+
super();
|
|
2463
|
+
this.client = client;
|
|
2464
|
+
this.operations = operations;
|
|
2465
|
+
this.schema = schema;
|
|
2466
|
+
}
|
|
2467
|
+
async getTraces(args) {
|
|
2468
|
+
if (args.fromDate || args.toDate) {
|
|
2469
|
+
args.dateRange = {
|
|
2470
|
+
start: args.fromDate,
|
|
2471
|
+
end: args.toDate
|
|
2472
|
+
};
|
|
2374
2473
|
}
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2474
|
+
try {
|
|
2475
|
+
const result = await this.getTracesPaginated(args);
|
|
2476
|
+
return result.traces;
|
|
2477
|
+
} catch (error) {
|
|
2478
|
+
throw new MastraError(
|
|
2479
|
+
{
|
|
2480
|
+
id: "MASTRA_STORAGE_PG_STORE_GET_TRACES_FAILED",
|
|
2481
|
+
domain: ErrorDomain.STORAGE,
|
|
2482
|
+
category: ErrorCategory.THIRD_PARTY
|
|
2483
|
+
},
|
|
2484
|
+
error
|
|
2485
|
+
);
|
|
2486
|
+
}
|
|
2487
|
+
}
|
|
2488
|
+
async getTracesPaginated(args) {
|
|
2489
|
+
const { name, scope, page = 0, perPage = 100, attributes, filters, dateRange } = args;
|
|
2490
|
+
const fromDate = dateRange?.start;
|
|
2491
|
+
const toDate = dateRange?.end;
|
|
2492
|
+
const currentOffset = page * perPage;
|
|
2493
|
+
const queryParams = [];
|
|
2494
|
+
const conditions = [];
|
|
2495
|
+
let paramIndex = 1;
|
|
2496
|
+
if (name) {
|
|
2497
|
+
conditions.push(`name LIKE $${paramIndex++}`);
|
|
2498
|
+
queryParams.push(`${name}%`);
|
|
2499
|
+
}
|
|
2500
|
+
if (scope) {
|
|
2501
|
+
conditions.push(`scope = $${paramIndex++}`);
|
|
2502
|
+
queryParams.push(scope);
|
|
2503
|
+
}
|
|
2504
|
+
if (attributes) {
|
|
2505
|
+
Object.entries(attributes).forEach(([key, value]) => {
|
|
2506
|
+
const parsedKey = parseFieldKey(key);
|
|
2507
|
+
conditions.push(`attributes->>'${parsedKey}' = $${paramIndex++}`);
|
|
2508
|
+
queryParams.push(value);
|
|
2509
|
+
});
|
|
2510
|
+
}
|
|
2511
|
+
if (filters) {
|
|
2512
|
+
Object.entries(filters).forEach(([key, value]) => {
|
|
2513
|
+
const parsedKey = parseFieldKey(key);
|
|
2514
|
+
conditions.push(`"${parsedKey}" = $${paramIndex++}`);
|
|
2515
|
+
queryParams.push(value);
|
|
2516
|
+
});
|
|
2517
|
+
}
|
|
2518
|
+
if (fromDate) {
|
|
2519
|
+
conditions.push(`"createdAt" >= $${paramIndex++}`);
|
|
2520
|
+
queryParams.push(fromDate);
|
|
2521
|
+
}
|
|
2522
|
+
if (toDate) {
|
|
2523
|
+
conditions.push(`"createdAt" <= $${paramIndex++}`);
|
|
2524
|
+
queryParams.push(toDate);
|
|
2525
|
+
}
|
|
2526
|
+
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
2527
|
+
try {
|
|
2528
|
+
const countResult = await this.client.oneOrNone(
|
|
2529
|
+
`SELECT COUNT(*) FROM ${getTableName({ indexName: TABLE_TRACES, schemaName: getSchemaName(this.schema) })} ${whereClause}`,
|
|
2530
|
+
queryParams
|
|
2531
|
+
);
|
|
2532
|
+
const total = Number(countResult?.count ?? 0);
|
|
2533
|
+
if (total === 0) {
|
|
2534
|
+
return {
|
|
2535
|
+
traces: [],
|
|
2536
|
+
total: 0,
|
|
2537
|
+
page,
|
|
2538
|
+
perPage,
|
|
2539
|
+
hasMore: false
|
|
2540
|
+
};
|
|
2381
2541
|
}
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
const
|
|
2387
|
-
|
|
2388
|
-
|
|
2542
|
+
const dataResult = await this.client.manyOrNone(
|
|
2543
|
+
`SELECT * FROM ${getTableName({ indexName: TABLE_TRACES, schemaName: getSchemaName(this.schema) })} ${whereClause} ORDER BY "startTime" DESC LIMIT $${paramIndex++} OFFSET $${paramIndex++}`,
|
|
2544
|
+
[...queryParams, perPage, currentOffset]
|
|
2545
|
+
);
|
|
2546
|
+
const traces = dataResult.map((row) => ({
|
|
2547
|
+
id: row.id,
|
|
2548
|
+
parentSpanId: row.parentSpanId,
|
|
2549
|
+
traceId: row.traceId,
|
|
2550
|
+
name: row.name,
|
|
2551
|
+
scope: row.scope,
|
|
2552
|
+
kind: row.kind,
|
|
2553
|
+
status: safelyParseJSON(row.status),
|
|
2554
|
+
events: safelyParseJSON(row.events),
|
|
2555
|
+
links: safelyParseJSON(row.links),
|
|
2556
|
+
attributes: safelyParseJSON(row.attributes),
|
|
2557
|
+
startTime: row.startTime,
|
|
2558
|
+
endTime: row.endTime,
|
|
2559
|
+
other: safelyParseJSON(row.other),
|
|
2560
|
+
createdAt: row.createdAtZ || row.createdAt
|
|
2561
|
+
}));
|
|
2562
|
+
return {
|
|
2563
|
+
traces,
|
|
2564
|
+
total,
|
|
2565
|
+
page,
|
|
2566
|
+
perPage,
|
|
2567
|
+
hasMore: currentOffset + traces.length < total
|
|
2389
2568
|
};
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2569
|
+
} catch (error) {
|
|
2570
|
+
throw new MastraError(
|
|
2571
|
+
{
|
|
2572
|
+
id: "MASTRA_STORAGE_PG_STORE_GET_TRACES_PAGINATED_FAILED",
|
|
2573
|
+
domain: ErrorDomain.STORAGE,
|
|
2574
|
+
category: ErrorCategory.THIRD_PARTY
|
|
2575
|
+
},
|
|
2576
|
+
error
|
|
2577
|
+
);
|
|
2578
|
+
}
|
|
2579
|
+
}
|
|
2580
|
+
async batchTraceInsert({ records }) {
|
|
2581
|
+
this.logger.debug("Batch inserting traces", { count: records.length });
|
|
2582
|
+
await this.operations.batchInsert({
|
|
2583
|
+
tableName: TABLE_TRACES,
|
|
2584
|
+
records
|
|
2585
|
+
});
|
|
2586
|
+
}
|
|
2587
|
+
};
|
|
2588
|
+
function parseWorkflowRun(row) {
|
|
2589
|
+
let parsedSnapshot = row.snapshot;
|
|
2590
|
+
if (typeof parsedSnapshot === "string") {
|
|
2591
|
+
try {
|
|
2592
|
+
parsedSnapshot = JSON.parse(row.snapshot);
|
|
2593
|
+
} catch (e) {
|
|
2594
|
+
console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
|
|
2595
|
+
}
|
|
2596
|
+
}
|
|
2597
|
+
return {
|
|
2598
|
+
workflowName: row.workflow_name,
|
|
2599
|
+
runId: row.run_id,
|
|
2600
|
+
snapshot: parsedSnapshot,
|
|
2601
|
+
resourceId: row.resourceId,
|
|
2602
|
+
createdAt: new Date(row.createdAtZ || row.createdAt),
|
|
2603
|
+
updatedAt: new Date(row.updatedAtZ || row.updatedAt)
|
|
2604
|
+
};
|
|
2605
|
+
}
|
|
2606
|
+
var WorkflowsPG = class extends WorkflowsStorage {
|
|
2607
|
+
client;
|
|
2608
|
+
operations;
|
|
2609
|
+
schema;
|
|
2610
|
+
constructor({
|
|
2611
|
+
client,
|
|
2612
|
+
operations,
|
|
2613
|
+
schema
|
|
2614
|
+
}) {
|
|
2615
|
+
super();
|
|
2616
|
+
this.client = client;
|
|
2617
|
+
this.operations = operations;
|
|
2618
|
+
this.schema = schema;
|
|
2619
|
+
}
|
|
2620
|
+
async persistWorkflowSnapshot({
|
|
2621
|
+
workflowName,
|
|
2622
|
+
runId,
|
|
2623
|
+
snapshot
|
|
2624
|
+
}) {
|
|
2625
|
+
try {
|
|
2626
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2627
|
+
await this.client.none(
|
|
2628
|
+
`INSERT INTO ${TABLE_WORKFLOW_SNAPSHOT} (workflow_name, run_id, snapshot, "createdAt", "updatedAt")
|
|
2629
|
+
VALUES ($1, $2, $3, $4, $5)
|
|
2630
|
+
ON CONFLICT (workflow_name, run_id) DO UPDATE
|
|
2631
|
+
SET snapshot = $3, "updatedAt" = $5`,
|
|
2632
|
+
[workflowName, runId, JSON.stringify(snapshot), now, now]
|
|
2633
|
+
);
|
|
2634
|
+
} catch (error) {
|
|
2635
|
+
throw new MastraError(
|
|
2636
|
+
{
|
|
2637
|
+
id: "MASTRA_STORAGE_PG_STORE_PERSIST_WORKFLOW_SNAPSHOT_FAILED",
|
|
2638
|
+
domain: ErrorDomain.STORAGE,
|
|
2639
|
+
category: ErrorCategory.THIRD_PARTY
|
|
2640
|
+
},
|
|
2641
|
+
error
|
|
2642
|
+
);
|
|
2643
|
+
}
|
|
2644
|
+
}
|
|
2645
|
+
async loadWorkflowSnapshot({
|
|
2646
|
+
workflowName,
|
|
2647
|
+
runId
|
|
2648
|
+
}) {
|
|
2649
|
+
try {
|
|
2650
|
+
const result = await this.operations.load({
|
|
2651
|
+
tableName: TABLE_WORKFLOW_SNAPSHOT,
|
|
2652
|
+
keys: { workflow_name: workflowName, run_id: runId }
|
|
2653
|
+
});
|
|
2654
|
+
return result ? result.snapshot : null;
|
|
2655
|
+
} catch (error) {
|
|
2656
|
+
throw new MastraError(
|
|
2657
|
+
{
|
|
2658
|
+
id: "MASTRA_STORAGE_PG_STORE_LOAD_WORKFLOW_SNAPSHOT_FAILED",
|
|
2659
|
+
domain: ErrorDomain.STORAGE,
|
|
2660
|
+
category: ErrorCategory.THIRD_PARTY
|
|
2661
|
+
},
|
|
2662
|
+
error
|
|
2663
|
+
);
|
|
2664
|
+
}
|
|
2665
|
+
}
|
|
2666
|
+
async getWorkflowRunById({
|
|
2667
|
+
runId,
|
|
2668
|
+
workflowName
|
|
2669
|
+
}) {
|
|
2670
|
+
try {
|
|
2671
|
+
const conditions = [];
|
|
2672
|
+
const values = [];
|
|
2673
|
+
let paramIndex = 1;
|
|
2674
|
+
if (runId) {
|
|
2675
|
+
conditions.push(`run_id = $${paramIndex}`);
|
|
2676
|
+
values.push(runId);
|
|
2677
|
+
paramIndex++;
|
|
2678
|
+
}
|
|
2679
|
+
if (workflowName) {
|
|
2680
|
+
conditions.push(`workflow_name = $${paramIndex}`);
|
|
2681
|
+
values.push(workflowName);
|
|
2682
|
+
paramIndex++;
|
|
2683
|
+
}
|
|
2684
|
+
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
2685
|
+
const query = `
|
|
2686
|
+
SELECT * FROM ${getTableName({ indexName: TABLE_WORKFLOW_SNAPSHOT, schemaName: this.schema })}
|
|
2687
|
+
${whereClause}
|
|
2688
|
+
`;
|
|
2689
|
+
const queryValues = values;
|
|
2690
|
+
const result = await this.client.oneOrNone(query, queryValues);
|
|
2691
|
+
if (!result) {
|
|
2692
|
+
return null;
|
|
2693
|
+
}
|
|
2694
|
+
return parseWorkflowRun(result);
|
|
2695
|
+
} catch (error) {
|
|
2696
|
+
throw new MastraError(
|
|
2697
|
+
{
|
|
2698
|
+
id: "MASTRA_STORAGE_PG_STORE_GET_WORKFLOW_RUN_BY_ID_FAILED",
|
|
2699
|
+
domain: ErrorDomain.STORAGE,
|
|
2700
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2701
|
+
details: {
|
|
2702
|
+
runId,
|
|
2703
|
+
workflowName: workflowName || ""
|
|
2424
2704
|
}
|
|
2425
|
-
}
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2705
|
+
},
|
|
2706
|
+
error
|
|
2707
|
+
);
|
|
2708
|
+
}
|
|
2709
|
+
}
|
|
2710
|
+
async getWorkflowRuns({
|
|
2711
|
+
workflowName,
|
|
2712
|
+
fromDate,
|
|
2713
|
+
toDate,
|
|
2714
|
+
limit,
|
|
2715
|
+
offset,
|
|
2716
|
+
resourceId
|
|
2717
|
+
} = {}) {
|
|
2718
|
+
try {
|
|
2719
|
+
const conditions = [];
|
|
2720
|
+
const values = [];
|
|
2721
|
+
let paramIndex = 1;
|
|
2722
|
+
if (workflowName) {
|
|
2723
|
+
conditions.push(`workflow_name = $${paramIndex}`);
|
|
2724
|
+
values.push(workflowName);
|
|
2725
|
+
paramIndex++;
|
|
2726
|
+
}
|
|
2727
|
+
if (resourceId) {
|
|
2728
|
+
const hasResourceId = await this.operations.hasColumn(TABLE_WORKFLOW_SNAPSHOT, "resourceId");
|
|
2729
|
+
if (hasResourceId) {
|
|
2730
|
+
conditions.push(`"resourceId" = $${paramIndex}`);
|
|
2731
|
+
values.push(resourceId);
|
|
2732
|
+
paramIndex++;
|
|
2733
|
+
} else {
|
|
2734
|
+
console.warn(`[${TABLE_WORKFLOW_SNAPSHOT}] resourceId column not found. Skipping resourceId filter.`);
|
|
2432
2735
|
}
|
|
2433
2736
|
}
|
|
2434
|
-
if (
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
])
|
|
2439
|
-
);
|
|
2737
|
+
if (fromDate) {
|
|
2738
|
+
conditions.push(`"createdAt" >= $${paramIndex}`);
|
|
2739
|
+
values.push(fromDate);
|
|
2740
|
+
paramIndex++;
|
|
2440
2741
|
}
|
|
2441
|
-
if (
|
|
2442
|
-
|
|
2742
|
+
if (toDate) {
|
|
2743
|
+
conditions.push(`"createdAt" <= $${paramIndex}`);
|
|
2744
|
+
values.push(toDate);
|
|
2745
|
+
paramIndex++;
|
|
2443
2746
|
}
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2747
|
+
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
2748
|
+
let total = 0;
|
|
2749
|
+
if (limit !== void 0 && offset !== void 0) {
|
|
2750
|
+
const countResult = await this.client.one(
|
|
2751
|
+
`SELECT COUNT(*) as count FROM ${getTableName({ indexName: TABLE_WORKFLOW_SNAPSHOT, schemaName: this.schema })} ${whereClause}`,
|
|
2752
|
+
values
|
|
2753
|
+
);
|
|
2754
|
+
total = Number(countResult.count);
|
|
2452
2755
|
}
|
|
2453
|
-
|
|
2454
|
-
|
|
2756
|
+
const query = `
|
|
2757
|
+
SELECT * FROM ${getTableName({ indexName: TABLE_WORKFLOW_SNAPSHOT, schemaName: this.schema })}
|
|
2758
|
+
${whereClause}
|
|
2759
|
+
ORDER BY "createdAt" DESC
|
|
2760
|
+
${limit !== void 0 && offset !== void 0 ? ` LIMIT $${paramIndex} OFFSET $${paramIndex + 1}` : ""}
|
|
2761
|
+
`;
|
|
2762
|
+
const queryValues = limit !== void 0 && offset !== void 0 ? [...values, limit, offset] : values;
|
|
2763
|
+
const result = await this.client.manyOrNone(query, queryValues);
|
|
2764
|
+
const runs = (result || []).map((row) => {
|
|
2765
|
+
return parseWorkflowRun(row);
|
|
2766
|
+
});
|
|
2767
|
+
return { runs, total: total || runs.length };
|
|
2768
|
+
} catch (error) {
|
|
2769
|
+
throw new MastraError(
|
|
2770
|
+
{
|
|
2771
|
+
id: "MASTRA_STORAGE_PG_STORE_GET_WORKFLOW_RUNS_FAILED",
|
|
2772
|
+
domain: ErrorDomain.STORAGE,
|
|
2773
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2774
|
+
details: {
|
|
2775
|
+
workflowName: workflowName || "all"
|
|
2776
|
+
}
|
|
2777
|
+
},
|
|
2778
|
+
error
|
|
2779
|
+
);
|
|
2780
|
+
}
|
|
2455
2781
|
}
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2782
|
+
};
|
|
2783
|
+
|
|
2784
|
+
// src/storage/index.ts
|
|
2785
|
+
var PostgresStore = class extends MastraStorage {
|
|
2786
|
+
db;
|
|
2787
|
+
pgp;
|
|
2788
|
+
client;
|
|
2789
|
+
schema;
|
|
2790
|
+
stores;
|
|
2791
|
+
constructor(config) {
|
|
2792
|
+
try {
|
|
2793
|
+
if ("connectionString" in config) {
|
|
2794
|
+
if (!config.connectionString || typeof config.connectionString !== "string" || config.connectionString.trim() === "") {
|
|
2795
|
+
throw new Error(
|
|
2796
|
+
"PostgresStore: connectionString must be provided and cannot be empty. Passing an empty string may cause fallback to local Postgres defaults."
|
|
2797
|
+
);
|
|
2798
|
+
}
|
|
2799
|
+
} else {
|
|
2800
|
+
const required = ["host", "database", "user", "password"];
|
|
2801
|
+
for (const key of required) {
|
|
2802
|
+
if (!(key in config) || typeof config[key] !== "string" || config[key].trim() === "") {
|
|
2803
|
+
throw new Error(
|
|
2804
|
+
`PostgresStore: ${key} must be provided and cannot be empty. Passing an empty string may cause fallback to local Postgres defaults.`
|
|
2805
|
+
);
|
|
2806
|
+
}
|
|
2807
|
+
}
|
|
2808
|
+
}
|
|
2809
|
+
super({ name: "PostgresStore" });
|
|
2810
|
+
this.pgp = pgPromise();
|
|
2811
|
+
this.schema = config.schemaName || "public";
|
|
2812
|
+
this.db = this.pgp(
|
|
2813
|
+
`connectionString` in config ? { connectionString: config.connectionString } : {
|
|
2814
|
+
host: config.host,
|
|
2815
|
+
port: config.port,
|
|
2816
|
+
database: config.database,
|
|
2817
|
+
user: config.user,
|
|
2818
|
+
password: config.password,
|
|
2819
|
+
ssl: config.ssl
|
|
2820
|
+
}
|
|
2821
|
+
);
|
|
2822
|
+
this.client = this.db;
|
|
2823
|
+
const operations = new StoreOperationsPG({ client: this.client, schemaName: this.schema });
|
|
2824
|
+
const scores = new ScoresPG({ client: this.client, operations });
|
|
2825
|
+
const traces = new TracesPG({ client: this.client, operations, schema: this.schema });
|
|
2826
|
+
const workflows = new WorkflowsPG({ client: this.client, operations, schema: this.schema });
|
|
2827
|
+
const legacyEvals = new LegacyEvalsPG({ client: this.client, schema: this.schema });
|
|
2828
|
+
const memory = new MemoryPG({ client: this.client, schema: this.schema, operations });
|
|
2829
|
+
this.stores = {
|
|
2830
|
+
operations,
|
|
2831
|
+
scores,
|
|
2832
|
+
traces,
|
|
2833
|
+
workflows,
|
|
2834
|
+
legacyEvals,
|
|
2835
|
+
memory
|
|
2836
|
+
};
|
|
2837
|
+
} catch (e) {
|
|
2838
|
+
throw new MastraError(
|
|
2839
|
+
{
|
|
2840
|
+
id: "MASTRA_STORAGE_PG_STORE_INITIALIZATION_FAILED",
|
|
2841
|
+
domain: ErrorDomain.STORAGE,
|
|
2842
|
+
category: ErrorCategory.USER
|
|
2843
|
+
},
|
|
2844
|
+
e
|
|
2845
|
+
);
|
|
2463
2846
|
}
|
|
2847
|
+
}
|
|
2848
|
+
get supports() {
|
|
2464
2849
|
return {
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2850
|
+
selectByIncludeResourceScope: true,
|
|
2851
|
+
resourceWorkingMemory: true,
|
|
2852
|
+
hasColumn: true,
|
|
2853
|
+
createTable: true
|
|
2469
2854
|
};
|
|
2470
2855
|
}
|
|
2856
|
+
/** @deprecated use getEvals instead */
|
|
2857
|
+
async getEvalsByAgentName(agentName, type) {
|
|
2858
|
+
return this.stores.legacyEvals.getEvalsByAgentName(agentName, type);
|
|
2859
|
+
}
|
|
2860
|
+
async getEvals(options = {}) {
|
|
2861
|
+
return this.stores.legacyEvals.getEvals(options);
|
|
2862
|
+
}
|
|
2863
|
+
/**
|
|
2864
|
+
* @deprecated use getTracesPaginated instead
|
|
2865
|
+
*/
|
|
2866
|
+
async getTraces(args) {
|
|
2867
|
+
return this.stores.traces.getTraces(args);
|
|
2868
|
+
}
|
|
2869
|
+
async getTracesPaginated(args) {
|
|
2870
|
+
return this.stores.traces.getTracesPaginated(args);
|
|
2871
|
+
}
|
|
2872
|
+
async batchTraceInsert({ records }) {
|
|
2873
|
+
return this.stores.traces.batchTraceInsert({ records });
|
|
2874
|
+
}
|
|
2875
|
+
async createTable({
|
|
2876
|
+
tableName,
|
|
2877
|
+
schema
|
|
2878
|
+
}) {
|
|
2879
|
+
return this.stores.operations.createTable({ tableName, schema });
|
|
2880
|
+
}
|
|
2881
|
+
async alterTable({
|
|
2882
|
+
tableName,
|
|
2883
|
+
schema,
|
|
2884
|
+
ifNotExists
|
|
2885
|
+
}) {
|
|
2886
|
+
return this.stores.operations.alterTable({ tableName, schema, ifNotExists });
|
|
2887
|
+
}
|
|
2888
|
+
async clearTable({ tableName }) {
|
|
2889
|
+
return this.stores.operations.clearTable({ tableName });
|
|
2890
|
+
}
|
|
2891
|
+
async dropTable({ tableName }) {
|
|
2892
|
+
return this.stores.operations.dropTable({ tableName });
|
|
2893
|
+
}
|
|
2894
|
+
async insert({ tableName, record }) {
|
|
2895
|
+
return this.stores.operations.insert({ tableName, record });
|
|
2896
|
+
}
|
|
2897
|
+
async batchInsert({ tableName, records }) {
|
|
2898
|
+
return this.stores.operations.batchInsert({ tableName, records });
|
|
2899
|
+
}
|
|
2900
|
+
async load({ tableName, keys }) {
|
|
2901
|
+
return this.stores.operations.load({ tableName, keys });
|
|
2902
|
+
}
|
|
2903
|
+
/**
|
|
2904
|
+
* Memory
|
|
2905
|
+
*/
|
|
2906
|
+
async getThreadById({ threadId }) {
|
|
2907
|
+
return this.stores.memory.getThreadById({ threadId });
|
|
2908
|
+
}
|
|
2909
|
+
/**
|
|
2910
|
+
* @deprecated use getThreadsByResourceIdPaginated instead
|
|
2911
|
+
*/
|
|
2912
|
+
async getThreadsByResourceId(args) {
|
|
2913
|
+
return this.stores.memory.getThreadsByResourceId(args);
|
|
2914
|
+
}
|
|
2915
|
+
async getThreadsByResourceIdPaginated(args) {
|
|
2916
|
+
return this.stores.memory.getThreadsByResourceIdPaginated(args);
|
|
2917
|
+
}
|
|
2918
|
+
async saveThread({ thread }) {
|
|
2919
|
+
return this.stores.memory.saveThread({ thread });
|
|
2920
|
+
}
|
|
2921
|
+
async updateThread({
|
|
2922
|
+
id,
|
|
2923
|
+
title,
|
|
2924
|
+
metadata
|
|
2925
|
+
}) {
|
|
2926
|
+
return this.stores.memory.updateThread({ id, title, metadata });
|
|
2927
|
+
}
|
|
2928
|
+
async deleteThread({ threadId }) {
|
|
2929
|
+
return this.stores.memory.deleteThread({ threadId });
|
|
2930
|
+
}
|
|
2931
|
+
async getMessages(args) {
|
|
2932
|
+
return this.stores.memory.getMessages(args);
|
|
2933
|
+
}
|
|
2934
|
+
async getMessagesPaginated(args) {
|
|
2935
|
+
return this.stores.memory.getMessagesPaginated(args);
|
|
2936
|
+
}
|
|
2937
|
+
async saveMessages(args) {
|
|
2938
|
+
return this.stores.memory.saveMessages(args);
|
|
2939
|
+
}
|
|
2940
|
+
async updateMessages({
|
|
2941
|
+
messages
|
|
2942
|
+
}) {
|
|
2943
|
+
return this.stores.memory.updateMessages({ messages });
|
|
2944
|
+
}
|
|
2945
|
+
async getResourceById({ resourceId }) {
|
|
2946
|
+
return this.stores.memory.getResourceById({ resourceId });
|
|
2947
|
+
}
|
|
2471
2948
|
async saveResource({ resource }) {
|
|
2472
|
-
|
|
2473
|
-
await this.db.none(
|
|
2474
|
-
`INSERT INTO ${tableName} (id, "workingMemory", metadata, "createdAt", "updatedAt")
|
|
2475
|
-
VALUES ($1, $2, $3, $4, $5)`,
|
|
2476
|
-
[
|
|
2477
|
-
resource.id,
|
|
2478
|
-
resource.workingMemory,
|
|
2479
|
-
JSON.stringify(resource.metadata),
|
|
2480
|
-
resource.createdAt.toISOString(),
|
|
2481
|
-
resource.updatedAt.toISOString()
|
|
2482
|
-
]
|
|
2483
|
-
);
|
|
2484
|
-
return resource;
|
|
2949
|
+
return this.stores.memory.saveResource({ resource });
|
|
2485
2950
|
}
|
|
2486
2951
|
async updateResource({
|
|
2487
2952
|
resourceId,
|
|
2488
2953
|
workingMemory,
|
|
2489
2954
|
metadata
|
|
2490
2955
|
}) {
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
}
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2956
|
+
return this.stores.memory.updateResource({ resourceId, workingMemory, metadata });
|
|
2957
|
+
}
|
|
2958
|
+
/**
|
|
2959
|
+
* Workflows
|
|
2960
|
+
*/
|
|
2961
|
+
async persistWorkflowSnapshot({
|
|
2962
|
+
workflowName,
|
|
2963
|
+
runId,
|
|
2964
|
+
snapshot
|
|
2965
|
+
}) {
|
|
2966
|
+
return this.stores.workflows.persistWorkflowSnapshot({ workflowName, runId, snapshot });
|
|
2967
|
+
}
|
|
2968
|
+
async loadWorkflowSnapshot({
|
|
2969
|
+
workflowName,
|
|
2970
|
+
runId
|
|
2971
|
+
}) {
|
|
2972
|
+
return this.stores.workflows.loadWorkflowSnapshot({ workflowName, runId });
|
|
2973
|
+
}
|
|
2974
|
+
async getWorkflowRuns({
|
|
2975
|
+
workflowName,
|
|
2976
|
+
fromDate,
|
|
2977
|
+
toDate,
|
|
2978
|
+
limit,
|
|
2979
|
+
offset,
|
|
2980
|
+
resourceId
|
|
2981
|
+
} = {}) {
|
|
2982
|
+
return this.stores.workflows.getWorkflowRuns({ workflowName, fromDate, toDate, limit, offset, resourceId });
|
|
2983
|
+
}
|
|
2984
|
+
async getWorkflowRunById({
|
|
2985
|
+
runId,
|
|
2986
|
+
workflowName
|
|
2987
|
+
}) {
|
|
2988
|
+
return this.stores.workflows.getWorkflowRunById({ runId, workflowName });
|
|
2989
|
+
}
|
|
2990
|
+
async close() {
|
|
2991
|
+
this.pgp.end();
|
|
2992
|
+
}
|
|
2993
|
+
/**
|
|
2994
|
+
* Scorers
|
|
2995
|
+
*/
|
|
2996
|
+
async getScoreById({ id: _id }) {
|
|
2997
|
+
return this.stores.scores.getScoreById({ id: _id });
|
|
2998
|
+
}
|
|
2999
|
+
async getScoresByScorerId({
|
|
3000
|
+
scorerId: _scorerId,
|
|
3001
|
+
pagination: _pagination
|
|
3002
|
+
}) {
|
|
3003
|
+
return this.stores.scores.getScoresByScorerId({ scorerId: _scorerId, pagination: _pagination });
|
|
3004
|
+
}
|
|
3005
|
+
async saveScore(_score) {
|
|
3006
|
+
return this.stores.scores.saveScore(_score);
|
|
3007
|
+
}
|
|
3008
|
+
async getScoresByRunId({
|
|
3009
|
+
runId: _runId,
|
|
3010
|
+
pagination: _pagination
|
|
3011
|
+
}) {
|
|
3012
|
+
return this.stores.scores.getScoresByRunId({ runId: _runId, pagination: _pagination });
|
|
3013
|
+
}
|
|
3014
|
+
async getScoresByEntityId({
|
|
3015
|
+
entityId: _entityId,
|
|
3016
|
+
entityType: _entityType,
|
|
3017
|
+
pagination: _pagination
|
|
3018
|
+
}) {
|
|
3019
|
+
return this.stores.scores.getScoresByEntityId({
|
|
3020
|
+
entityId: _entityId,
|
|
3021
|
+
entityType: _entityType,
|
|
3022
|
+
pagination: _pagination
|
|
3023
|
+
});
|
|
2531
3024
|
}
|
|
2532
3025
|
};
|
|
2533
3026
|
|