@mastra/pg 1.26.0-alpha.2 → 1.26.0-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -10758,7 +10758,7 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
10758
10758
  hasMore: false
10759
10759
  };
10760
10760
  const limitValue = perPageInput === false ? total : perPage;
10761
- const dataQuery = `SELECT id, "resourceId", title, metadata, "createdAt", "createdAtZ", "updatedAt", "updatedAtZ" ${baseQuery} ORDER BY COALESCE("${field}Z", "${field}") ${direction} LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`;
10761
+ const dataQuery = `SELECT id, "resourceId", title, metadata, "createdAt", "createdAtZ", "updatedAt", "updatedAtZ" ${baseQuery} ORDER BY COALESCE("${field}Z", "${field}") ${direction}, "id" ${direction} LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`;
10762
10762
  return {
10763
10763
  threads: (await this.#db.readClient.manyOrNone(dataQuery, [
10764
10764
  ...queryParams,
@@ -10951,10 +10951,11 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
10951
10951
  return messages.sort((a, b) => {
10952
10952
  const aValue = field === "createdAt" ? new Date(a.createdAt).getTime() : a[field];
10953
10953
  const bValue = field === "createdAt" ? new Date(b.createdAt).getTime() : b[field];
10954
- if (aValue == null && bValue == null) return a.id.localeCompare(b.id);
10954
+ const idOrder = direction === "ASC" ? a.id.localeCompare(b.id) : b.id.localeCompare(a.id);
10955
+ if (aValue == null && bValue == null) return idOrder;
10955
10956
  if (aValue == null) return 1;
10956
10957
  if (bValue == null) return -1;
10957
- if (aValue === bValue) return a.id.localeCompare(b.id);
10958
+ if (aValue === bValue) return idOrder;
10958
10959
  if (typeof aValue === "number" && typeof bValue === "number") return direction === "ASC" ? aValue - bValue : bValue - aValue;
10959
10960
  return direction === "ASC" ? String(aValue).localeCompare(String(bValue)) : String(bValue).localeCompare(String(aValue));
10960
10961
  });
@@ -11157,7 +11158,7 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
11157
11158
  const metadataFilter = validateStorageMetadataFilter(filter?.metadata);
11158
11159
  try {
11159
11160
  const { field, direction } = this.parseOrderBy(orderBy, "ASC");
11160
- const orderByStatement = `ORDER BY "${field}" ${direction}`;
11161
+ const orderByStatement = `ORDER BY "${field}" ${direction}, "id" ${direction}`;
11161
11162
  const selectStatement = `SELECT id, content, role, type, "createdAt", "createdAtZ", thread_id AS "threadId", "resourceId"`;
11162
11163
  const tableName = getTableName$3({
11163
11164
  indexName: TABLE_MESSAGES,
@@ -11312,7 +11313,7 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
11312
11313
  const metadataFilter = validateStorageMetadataFilter(filter?.metadata);
11313
11314
  try {
11314
11315
  const { field, direction } = this.parseOrderBy(orderBy, "ASC");
11315
- const orderByStatement = `ORDER BY "${field}" ${direction}`;
11316
+ const orderByStatement = `ORDER BY "${field}" ${direction}, "id" ${direction}`;
11316
11317
  const selectStatement = `SELECT id, content, role, type, "createdAt", "createdAtZ", thread_id AS "threadId", "resourceId"`;
11317
11318
  const tableName = getTableName$3({
11318
11319
  indexName: TABLE_MESSAGES,
@@ -14807,6 +14808,11 @@ function tableIndexes() {
14807
14808
  columns: "(\"tags\")",
14808
14809
  using: "gin"
14809
14810
  },
14811
+ {
14812
+ name: "mastra_score_events_scoreid_cursor_idx",
14813
+ table: TABLE_SCORE_EVENTS,
14814
+ columns: "(\"scoreId\", \"cursorId\" DESC)"
14815
+ },
14810
14816
  {
14811
14817
  name: "mastra_score_events_cursor_idx",
14812
14818
  table: TABLE_SCORE_EVENTS,
@@ -16939,13 +16945,32 @@ function pushScoreIdentity(acc, scorerId, scoreSource) {
16939
16945
  acc.params.push(scoreSource);
16940
16946
  }
16941
16947
  }
16948
+ function scoreRewriteConflict(row) {
16949
+ return `ON CONFLICT ("scoreId", "timestamp") DO UPDATE SET ${[
16950
+ ...Object.keys(row).filter((column) => column !== "scoreId" && column !== "timestamp").map((column) => `"${column}" = EXCLUDED."${column}"`),
16951
+ "\"cursorId\" = EXCLUDED.\"cursorId\"",
16952
+ "\"xactId\" = EXCLUDED.\"xactId\""
16953
+ ].join(", ")}`;
16954
+ }
16955
+ function collapseExactScoreConflicts(rows) {
16956
+ const records = /* @__PURE__ */ new Map();
16957
+ for (const row of rows) {
16958
+ const timestamp = new Date(row.timestamp).toISOString();
16959
+ const key = `${String(row.scoreId)}\u0000${timestamp}`;
16960
+ records.delete(key);
16961
+ records.set(key, row);
16962
+ }
16963
+ return [...records.values()];
16964
+ }
16942
16965
  async function createScore(client, schema, args) {
16943
- const insert = buildInsert(schema, TABLE_SCORE_EVENTS, [scoreRecordToRow(args.score)]);
16966
+ const row = scoreRecordToRow(args.score);
16967
+ const insert = buildInsert(schema, TABLE_SCORE_EVENTS, [row], scoreRewriteConflict(row));
16944
16968
  if (insert) await client.query(insert.text, insert.values);
16945
16969
  }
16946
16970
  async function batchCreateScores(client, schema, args) {
16947
16971
  if (args.scores.length === 0) return;
16948
- const insert = buildInsert(schema, TABLE_SCORE_EVENTS, args.scores.map(scoreRecordToRow));
16972
+ const rows = collapseExactScoreConflicts(args.scores.map(scoreRecordToRow));
16973
+ const insert = buildInsert(schema, TABLE_SCORE_EVENTS, rows, scoreRewriteConflict(rows[0]));
16949
16974
  if (insert) await client.query(insert.text, insert.values);
16950
16975
  }
16951
16976
  /**
@@ -16968,6 +16993,16 @@ async function deleteScores(client, schema, args) {
16968
16993
  }
16969
16994
  await client.query(`DELETE FROM ${table} WHERE ${conditions.join(" AND ")}`, values);
16970
16995
  }
16996
+ function latestScorePredicate(table, alias = "s") {
16997
+ return `NOT EXISTS (
16998
+ SELECT 1 FROM ${table} newer
16999
+ WHERE newer."scoreId" = ${alias}."scoreId"
17000
+ AND newer."cursorId" > ${alias}."cursorId"
17001
+ )`;
17002
+ }
17003
+ function applyLatestScorePredicate(acc, table) {
17004
+ acc.conditions.push(latestScorePredicate(table));
17005
+ }
16971
17006
  async function listScores(client, schema, args) {
16972
17007
  const { mode, filters, pagination, orderBy, after, limit } = listScoresArgsSchema.parse(args);
16973
17008
  const table = qualifiedTable(schema, TABLE_SCORE_EVENTS);
@@ -16978,28 +17013,50 @@ async function listScores(client, schema, args) {
16978
17013
  return listScoresPage(client, table, filters, pagination.page, pagination.perPage, orderBy.field, orderBy.direction);
16979
17014
  }
16980
17015
  async function getScoreById(client, schema, scoreId) {
17016
+ const table = qualifiedTable(schema, TABLE_SCORE_EVENTS);
16981
17017
  const row = await client.oneOrNone(`SELECT ${SCORE_SELECT_COLUMNS}
16982
- FROM ${qualifiedTable(schema, TABLE_SCORE_EVENTS)}
17018
+ FROM ${table}
16983
17019
  WHERE "scoreId" = $1
16984
- ORDER BY "timestamp" DESC
17020
+ ORDER BY "cursorId" DESC
16985
17021
  LIMIT 1`, [scoreId]);
16986
17022
  return row ? rowToScoreRecord(row) : null;
16987
17023
  }
16988
17024
  async function listScoresPage(client, table, filters, page, perPage, orderField, orderDir) {
16989
- return listSignalPage({
17025
+ const acc = newFilterAccumulator();
17026
+ applyScoreFilters(acc, filters);
17027
+ applyLatestScorePredicate(acc, table);
17028
+ const whereClause = whereOrEmpty(acc);
17029
+ const countRow = await client.oneOrNone(`SELECT COUNT(*)::text AS count FROM ${table} s ${whereClause}`, acc.params);
17030
+ const total = Number(countRow?.count ?? 0);
17031
+ let scores = [];
17032
+ if (total > 0) {
17033
+ const safeOrderField = parseSqlIdentifier(orderField, "order field");
17034
+ scores = (await client.manyOrNone(`SELECT ${SCORE_SELECT_COLUMNS}
17035
+ FROM ${table} s
17036
+ ${whereClause}
17037
+ ORDER BY "${safeOrderField}" ${orderDir}, "cursorId" ${orderDir}
17038
+ LIMIT $${acc.next++} OFFSET $${acc.next++}`, [
17039
+ ...acc.params,
17040
+ perPage,
17041
+ page * perPage
17042
+ ])).map(rowToScoreRecord);
17043
+ }
17044
+ const deltaCursor = deltaPollingFeatureEnabled() ? await readSignalStreamHeadCursor({
16990
17045
  client,
16991
17046
  table,
16992
17047
  filters,
16993
- page,
16994
- perPage,
16995
- orderField,
16996
- orderDir,
16997
- includeDeltaCursor: deltaPollingFeatureEnabled(),
16998
- selectColumns: SCORE_SELECT_COLUMNS,
16999
- responseKey: "scores",
17000
- applyFilters: applyScoreFilters,
17001
- mapRow: rowToScoreRecord
17002
- });
17048
+ applyFilters: applyScoreFilters
17049
+ }) : void 0;
17050
+ return {
17051
+ scores,
17052
+ pagination: {
17053
+ total,
17054
+ page,
17055
+ perPage,
17056
+ hasMore: (page + 1) * perPage < total
17057
+ },
17058
+ ...deltaCursor !== void 0 ? { deltaCursor } : {}
17059
+ };
17003
17060
  }
17004
17061
  async function listScoresDelta(client, table, filters, after, limit) {
17005
17062
  return listSignalDelta({
@@ -17015,12 +17072,14 @@ async function listScoresDelta(client, table, filters, after, limit) {
17015
17072
  });
17016
17073
  }
17017
17074
  async function runScoreAggregateQuery(client, schema, args, filters) {
17075
+ const table = qualifiedTable(schema, TABLE_SCORE_EVENTS);
17018
17076
  const acc = newFilterAccumulator();
17019
17077
  pushScoreIdentity(acc, args.scorerId, args.scoreSource);
17020
17078
  applyScoreFilters(acc, filters);
17079
+ applyLatestScorePredicate(acc, table);
17021
17080
  const sql = `
17022
17081
  SELECT ${aggregationSql(args.aggregation, "\"score\"")} AS "value"
17023
- FROM ${qualifiedTable(schema, TABLE_SCORE_EVENTS)}
17082
+ FROM ${table} s
17024
17083
  ${whereOrEmpty(acc)}
17025
17084
  `;
17026
17085
  const row = await client.oneOrNone(sql, acc.params);
@@ -17052,10 +17111,12 @@ async function getScoreBreakdown(client, schema, args) {
17052
17111
  });
17053
17112
  pushScoreIdentity(acc, args.scorerId, args.scoreSource);
17054
17113
  applyScoreFilters(acc, args.filters);
17114
+ const table = qualifiedTable(schema, TABLE_SCORE_EVENTS);
17115
+ applyLatestScorePredicate(acc, table);
17055
17116
  const sql = `
17056
17117
  SELECT ${resolved.map((e) => e.selectSql).join(", ")},
17057
17118
  ${aggregationSql(args.aggregation, "\"score\"")} AS "value"
17058
- FROM ${qualifiedTable(schema, TABLE_SCORE_EVENTS)}
17119
+ FROM ${table} s
17059
17120
  ${whereOrEmpty(acc)}
17060
17121
  GROUP BY ${resolved.map((e) => e.alias).join(", ")}
17061
17122
  ORDER BY "value" DESC NULLS LAST
@@ -17075,11 +17136,13 @@ async function getScoreTimeSeries(client, schema, args) {
17075
17136
  });
17076
17137
  pushScoreIdentity(acc, args.scorerId, args.scoreSource);
17077
17138
  applyScoreFilters(acc, args.filters);
17139
+ const table = qualifiedTable(schema, TABLE_SCORE_EVENTS);
17140
+ applyLatestScorePredicate(acc, table);
17078
17141
  const sql = `
17079
17142
  SELECT ${bucket} AS bucket,
17080
17143
  ${resolved.map((e) => e.selectSql).join(", ")},
17081
17144
  ${aggregationSql(args.aggregation, "\"score\"")} AS "value"
17082
- FROM ${qualifiedTable(schema, TABLE_SCORE_EVENTS)}
17145
+ FROM ${table} s
17083
17146
  ${whereOrEmpty(acc)}
17084
17147
  GROUP BY bucket, ${resolved.map((e) => e.alias).join(", ")}
17085
17148
  ORDER BY bucket
@@ -17097,10 +17160,12 @@ async function getScoreTimeSeries(client, schema, args) {
17097
17160
  const acc = newFilterAccumulator();
17098
17161
  pushScoreIdentity(acc, args.scorerId, args.scoreSource);
17099
17162
  applyScoreFilters(acc, args.filters);
17163
+ const table = qualifiedTable(schema, TABLE_SCORE_EVENTS);
17164
+ applyLatestScorePredicate(acc, table);
17100
17165
  const sql = `
17101
17166
  SELECT ${bucket} AS bucket,
17102
17167
  ${aggregationSql(args.aggregation, "\"score\"")} AS "value"
17103
- FROM ${qualifiedTable(schema, TABLE_SCORE_EVENTS)}
17168
+ FROM ${table} s
17104
17169
  ${whereOrEmpty(acc)}
17105
17170
  GROUP BY bucket
17106
17171
  ORDER BY bucket
@@ -17120,9 +17185,11 @@ async function getScorePercentiles(client, schema, args) {
17120
17185
  const acc = newFilterAccumulator();
17121
17186
  pushScoreIdentity(acc, args.scorerId, args.scoreSource);
17122
17187
  applyScoreFilters(acc, args.filters);
17188
+ const table = qualifiedTable(schema, TABLE_SCORE_EVENTS);
17189
+ applyLatestScorePredicate(acc, table);
17123
17190
  const sql = `
17124
17191
  SELECT ${bucket} AS bucket, ${percentileSelectSql(args.percentiles, "\"score\"")}
17125
- FROM ${qualifiedTable(schema, TABLE_SCORE_EVENTS)}
17192
+ FROM ${table} s
17126
17193
  ${whereOrEmpty(acc)}
17127
17194
  GROUP BY bucket
17128
17195
  ORDER BY bucket
@@ -17321,13 +17388,6 @@ function latestSpanPredicate$1(spanTable) {
17321
17388
  AND (newer."isPending" < s."isPending" OR (newer."isPending" = s."isPending" AND newer."cursorId" > s."cursorId"))
17322
17389
  )`;
17323
17390
  }
17324
- function latestScorePredicate(scoreTable) {
17325
- return `NOT EXISTS (
17326
- SELECT 1 FROM ${scoreTable} newer
17327
- WHERE newer."scoreId" = s."scoreId"
17328
- AND newer."cursorId" > s."cursorId"
17329
- )`;
17330
- }
17331
17391
  function latestFeedbackPredicate(feedbackTable) {
17332
17392
  return `NOT EXISTS (
17333
17393
  SELECT 1 FROM ${feedbackTable} newer
@@ -17497,7 +17557,7 @@ function compilePostgresTraceScope(schema, selection, relationCollections) {
17497
17557
  values
17498
17558
  };
17499
17559
  }
17500
- function compilePostgresTraceQuery(schema, plan) {
17560
+ function compilePostgresTraceQuery(schema, plan, mode = "data") {
17501
17561
  const { ctes, values } = compilePostgresTraceScope(schema, plan, collectRelationCollections(plan.where));
17502
17562
  let predicateSql = "TRUE";
17503
17563
  if (plan.where) {
@@ -17511,6 +17571,12 @@ function compilePostgresTraceQuery(schema, plan) {
17511
17571
  WHERE ${predicateSql}
17512
17572
  )`);
17513
17573
  const candidates = `WITH ${ctes.join(",\n")}`;
17574
+ if (plan.paginationMode === "page" && mode === "count") return {
17575
+ text: `${candidates}
17576
+ SELECT COUNT(*)::text AS count
17577
+ FROM candidates`,
17578
+ values
17579
+ };
17514
17580
  if (plan.result === "groups") {
17515
17581
  const pageCondition = plan.cursor ? `AND "threadId" > $${values.length + 1}` : "";
17516
17582
  if (plan.cursor) values.push(plan.cursor.threadId);
@@ -17528,6 +17594,17 @@ LIMIT $${values.length}`,
17528
17594
  }
17529
17595
  const orderField = plan.orderBy.field === "startedAt" ? "\"startedAt\"" : "\"endedAt\"";
17530
17596
  const direction = plan.orderBy.direction === "asc" ? "ASC" : "DESC";
17597
+ if (plan.paginationMode === "page") {
17598
+ values.push(plan.perPage, plan.page * plan.perPage);
17599
+ return {
17600
+ text: `${candidates}
17601
+ SELECT *
17602
+ FROM candidates
17603
+ ORDER BY ${orderField} ${direction}, "traceId" ASC
17604
+ LIMIT $${values.length - 1} OFFSET $${values.length}`,
17605
+ values
17606
+ };
17607
+ }
17531
17608
  let pageCondition = "";
17532
17609
  if (plan.cursor) {
17533
17610
  const comparison = plan.orderBy.direction === "asc" ? ">" : "<";
@@ -17674,10 +17751,11 @@ function isPostgresResourceLimit(error) {
17674
17751
  const candidate = error;
17675
17752
  return candidate.code === "53200" || candidate.code === "53400";
17676
17753
  }
17677
- async function runWithPostgresTraceQueryTimeout(client, timeoutMs, execute) {
17754
+ async function runWithPostgresTraceQueryTimeout(client, timeoutMs, execute, options = {}) {
17678
17755
  const resolvedTimeoutMs = coreStorage.resolveTraceQueryTimeoutMs(timeoutMs);
17679
17756
  try {
17680
17757
  return await client.tx(async (transaction) => {
17758
+ if (options.repeatableRead) await transaction.query("SET TRANSACTION ISOLATION LEVEL REPEATABLE READ");
17681
17759
  await transaction.query(`SELECT set_config('statement_timeout', $1, true)`, [`${resolvedTimeoutMs}ms`]);
17682
17760
  return execute(transaction);
17683
17761
  });
@@ -17711,6 +17789,50 @@ async function getTraceQueryValues(client, schema, plan, timeoutMs) {
17711
17789
  });
17712
17790
  }
17713
17791
  async function queryTraces(client, schema, plan, timeoutMs) {
17792
+ if (plan.paginationMode === "page") {
17793
+ const resolvedTimeoutMs = coreStorage.resolveTraceQueryTimeoutMs(timeoutMs);
17794
+ const deadline = performance.now() + resolvedTimeoutMs;
17795
+ const countQuery = compilePostgresTraceQuery(schema, plan, "count");
17796
+ const dataQuery = compilePostgresTraceQuery(schema, plan);
17797
+ const { total, rows } = await runWithPostgresTraceQueryTimeout(client, resolvedTimeoutMs, async (transaction) => {
17798
+ const countRows = await transaction.any(countQuery.text, countQuery.values);
17799
+ const remainingTimeoutMs = Math.floor(deadline - performance.now());
17800
+ if (remainingTimeoutMs <= 0) throw new coreStorage.TraceQueryExecutionError();
17801
+ await transaction.query(`SELECT set_config('statement_timeout', $1, true)`, [`${remainingTimeoutMs}ms`]);
17802
+ const rows = await transaction.any(dataQuery.text, dataQuery.values);
17803
+ return {
17804
+ total: Number(countRows[0]?.count ?? 0),
17805
+ rows
17806
+ };
17807
+ }, { repeatableRead: true });
17808
+ const traces = rows.map((row) => ({
17809
+ traceId: String(row.traceId),
17810
+ rootSpanId: String(row.rootSpanId),
17811
+ name: row.name,
17812
+ entityId: row.entityId ?? null,
17813
+ parentSpanId: row.parentSpanId ?? null,
17814
+ createdAt: asIsoTimestamp$1(row.startedAt),
17815
+ metadata: row.metadata ?? null,
17816
+ inputPreview: coreStorage.buildInputPreview(row.input) ?? null,
17817
+ threadId: row.threadId == null ? null : String(row.threadId),
17818
+ resourceId: row.resourceId == null ? null : String(row.resourceId),
17819
+ startedAt: asIsoTimestamp$1(row.startedAt),
17820
+ endedAt: asIsoTimestamp$1(row.endedAt),
17821
+ entityName: row.entityName == null ? null : String(row.entityName),
17822
+ entityType: row.entityType == null ? null : String(row.entityType),
17823
+ environment: row.environment == null ? null : String(row.environment),
17824
+ status: row.status
17825
+ }));
17826
+ return coreStorage.traceQueryResponseSchema.parse({
17827
+ traces,
17828
+ pagination: {
17829
+ total,
17830
+ page: plan.page,
17831
+ perPage: plan.perPage,
17832
+ hasMore: (plan.page + 1) * plan.perPage < total
17833
+ }
17834
+ });
17835
+ }
17714
17836
  const query = compilePostgresTraceQuery(schema, plan);
17715
17837
  const rows = await runWithPostgresTraceQueryTimeout(client, timeoutMs, (transaction) => transaction.any(query.text, query.values));
17716
17838
  const visibleRows = rows.slice(0, plan.limit);