@mastra/pg 1.23.0-alpha.5 → 1.23.0-alpha.7

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.cjs CHANGED
@@ -584,6 +584,8 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
584
584
  indexMetadataCache = /* @__PURE__ */ new Map();
585
585
  createdIndexes = /* @__PURE__ */ new Map();
586
586
  namespaceReadyIndexes = /* @__PURE__ */ new Set();
587
+ /** In-flight lazy namespace migrations, keyed by index name (see {@link ensureNamespaceReady}). */
588
+ namespaceReadyCache = /* @__PURE__ */ new Map();
587
589
  indexVectorTypes = /* @__PURE__ */ new Map();
588
590
  mutexesByName = /* @__PURE__ */ new Map();
589
591
  schema;
@@ -828,8 +830,10 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
828
830
  AND attnum > 0
829
831
  AND NOT attisdropped`, [tableName])).rowCount === 0) return;
830
832
  await client.query(`ALTER TABLE ${tableName} ADD COLUMN IF NOT EXISTS namespace VARCHAR(255) NOT NULL DEFAULT '${DEFAULT_NAMESPACE}'`);
831
- const namespaceIndexName = this.getNamespaceIndexName(parsedIndexName);
832
- await client.query(`CREATE UNIQUE INDEX IF NOT EXISTS "${namespaceIndexName}" ON ${tableName} (namespace, vector_id)`);
833
+ if (!(await this.getNamespaceSchemaState(tableName, client)).composite_index) {
834
+ const namespaceIndexName = this.getNamespaceIndexName(parsedIndexName);
835
+ await client.query(`CREATE UNIQUE INDEX IF NOT EXISTS "${namespaceIndexName}" ON ${tableName} (namespace, vector_id)`);
836
+ }
833
837
  const legacyConstraints = await client.query(`SELECT c.conname
834
838
  FROM pg_constraint c
835
839
  WHERE c.conrelid = to_regclass($1)
@@ -840,21 +844,84 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
840
844
  await client.query(`ALTER TABLE ${tableName} DROP CONSTRAINT "${parsedConstraintName}"`);
841
845
  }
842
846
  }
843
- /**
844
- * Name of the unique (namespace, vector_id) index for a table.
845
- *
846
- * The full `<index>_namespace_vector_id_idx` name is kept whenever it fits so tables that
847
- * were already migrated keep matching `IF NOT EXISTS`. Longer index names would exceed
848
- * Postgres' 63-char identifier limit, so those fall back to a truncated prefix plus a hash
849
- * of the index name to distinguish tables that
850
- * share a long prefix.
851
- */
852
847
  getNamespaceIndexName(parsedIndexName) {
853
848
  const fullName = `${parsedIndexName}_namespace_vector_id_idx`;
854
849
  if (fullName.length <= 63) return fullName;
855
850
  const suffix = `_ns_${(0, crypto$1.createHash)("sha256").update(parsedIndexName).digest("hex").slice(0, 32)}_idx`;
856
851
  return `${parsedIndexName.slice(0, 63 - suffix.length)}${suffix}`;
857
852
  }
853
+ async getNamespaceSchemaState(tableName, client) {
854
+ return (await client.query(`SELECT
855
+ EXISTS (SELECT 1 FROM pg_attribute WHERE attrelid = to_regclass($1)
856
+ AND attname = 'vector_id' AND attnum > 0 AND NOT attisdropped) AS vector_id,
857
+ EXISTS (SELECT 1 FROM pg_attribute WHERE attrelid = to_regclass($1)
858
+ AND attname = 'namespace' AND attnum > 0 AND NOT attisdropped) AS namespace,
859
+ EXISTS (
860
+ SELECT 1 FROM pg_index i
861
+ WHERE i.indrelid = to_regclass($1)
862
+ AND i.indisunique AND i.indisvalid AND i.indisready AND i.indimmediate
863
+ AND i.indpred IS NULL AND i.indexprs IS NULL AND i.indnkeyatts = 2
864
+ AND (SELECT array_agg(a.attname::text ORDER BY a.attname)
865
+ FROM unnest(i.indkey) WITH ORDINALITY AS k(attnum, position)
866
+ JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = k.attnum
867
+ WHERE k.position <= i.indnkeyatts) = ARRAY['namespace', 'vector_id']
868
+ ) AS composite_index,
869
+ EXISTS (SELECT 1 FROM pg_constraint WHERE conrelid = to_regclass($1)
870
+ AND contype = 'u' AND pg_get_constraintdef(oid) = 'UNIQUE (vector_id)') AS legacy_constraint`, [tableName])).rows[0];
871
+ }
872
+ async reconcileNamespace(indexName, client) {
873
+ const { tableName } = this.getTableName(indexName);
874
+ await client.query("BEGIN");
875
+ try {
876
+ await client.query("SELECT pg_advisory_xact_lock((1936876916::bigint << 32) | to_regclass($1)::oid::bigint)", [tableName]);
877
+ const state = await this.getNamespaceSchemaState(tableName, client);
878
+ if (!state.vector_id) {
879
+ await client.query("ROLLBACK");
880
+ return false;
881
+ }
882
+ if (!state.namespace || !state.composite_index || state.legacy_constraint) {
883
+ if (this.disableInit || process.env.MASTRA_DISABLE_STORAGE_INIT === "true") throw new _mastra_core_error.MastraError({
884
+ id: (0, _mastra_core_storage.createVectorErrorId)("PG", "ENSURE_NAMESPACE", "MIGRATION_REQUIRED"),
885
+ domain: _mastra_core_error.ErrorDomain.MASTRA_VECTOR,
886
+ category: _mastra_core_error.ErrorCategory.USER,
887
+ text: `Vector index "${indexName}" requires namespace migration and schema changes are disabled. Call createIndex({ indexName: "${indexName}", dimension }) with init enabled, or apply the namespace migration SQL from the @mastra/pg changelog.`,
888
+ details: { indexName }
889
+ });
890
+ await this.ensureNamespaceSchema(indexName, client);
891
+ const migrated = await this.getNamespaceSchemaState(tableName, client);
892
+ if (!migrated.namespace || !migrated.composite_index || migrated.legacy_constraint) throw new _mastra_core_error.MastraError({
893
+ id: (0, _mastra_core_storage.createVectorErrorId)("PG", "ENSURE_NAMESPACE", "MIGRATION_REQUIRED"),
894
+ domain: _mastra_core_error.ErrorDomain.MASTRA_VECTOR,
895
+ category: _mastra_core_error.ErrorCategory.USER,
896
+ text: `Vector index "${indexName}" requires a valid unique (namespace, vector_id) index. Resolve conflicting index names and retry the namespace migration.`,
897
+ details: { indexName }
898
+ });
899
+ }
900
+ await client.query("COMMIT");
901
+ return true;
902
+ } catch (error) {
903
+ await client.query("ROLLBACK");
904
+ throw error;
905
+ }
906
+ }
907
+ /**
908
+ * Every data path filters on `namespace`, but tables created before that column existed are
909
+ * only migrated by `createIndex()`, which callers are not required to invoke before reading.
910
+ * Run the migration lazily instead, once per index per process.
911
+ */
912
+ ensureNamespaceReady(indexName) {
913
+ if (this.namespaceReadyIndexes.has(indexName)) return Promise.resolve();
914
+ return this.memoize(this.namespaceReadyCache, indexName, () => this.getMutexByName(`create-${indexName}`).runExclusive(async () => {
915
+ if (this.namespaceReadyIndexes.has(indexName)) return;
916
+ const client = await this.pool.connect();
917
+ try {
918
+ if (await this.reconcileNamespace(indexName, client)) this.namespaceReadyIndexes.add(indexName);
919
+ else this.namespaceReadyCache.delete(indexName);
920
+ } finally {
921
+ client.release();
922
+ }
923
+ }));
924
+ }
858
925
  transformFilter(filter) {
859
926
  return new PGFilterTranslator().translate(filter);
860
927
  }
@@ -912,8 +979,10 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
912
979
  throw mastraError;
913
980
  }
914
981
  if (queryVector === void 0) {
915
- const client = await this.pool.connect();
982
+ let client;
916
983
  try {
984
+ await this.ensureNamespaceReady(indexName);
985
+ client = await this.pool.connect();
917
986
  const { sql: filterQuery, values: filterValues } = buildDeleteFilterQuery(this.transformFilter(filter));
918
987
  const { tableName } = this.getTableName(indexName);
919
988
  const filterClause = filterQuery.trim().replace(/^WHERE\s+/i, "");
@@ -939,6 +1008,10 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
939
1008
  ...includeVector && embedding && { vector: JSON.parse(embedding) }
940
1009
  }));
941
1010
  } catch (error) {
1011
+ if (error instanceof _mastra_core_error.MastraError) {
1012
+ this.logger?.trackException(error);
1013
+ throw error;
1014
+ }
942
1015
  const mastraError = new _mastra_core_error.MastraError({
943
1016
  id: (0, _mastra_core_storage.createVectorErrorId)("PG", "QUERY", "FAILED"),
944
1017
  domain: _mastra_core_error.ErrorDomain.MASTRA_VECTOR,
@@ -948,12 +1021,14 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
948
1021
  this.logger?.trackException(mastraError);
949
1022
  throw mastraError;
950
1023
  } finally {
951
- client.release();
1024
+ client?.release();
952
1025
  }
953
1026
  }
954
- const indexInfo = await this.getIndexMetadata({ indexName });
955
- const client = await this.pool.connect();
1027
+ let client;
956
1028
  try {
1029
+ await this.ensureNamespaceReady(indexName);
1030
+ const indexInfo = await this.getIndexMetadata({ indexName });
1031
+ client = await this.pool.connect();
957
1032
  await this.ensureSearchPath(client);
958
1033
  await client.query("BEGIN");
959
1034
  const { sql: filterQuery, values: filterValues } = buildFilterQuery(this.transformFilter(filter), minScore, topK);
@@ -1015,7 +1090,11 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
1015
1090
  ...includeVector && embedding && { vector: ops.parseEmbedding(embedding) }
1016
1091
  }));
1017
1092
  } catch (error) {
1018
- await client.query("ROLLBACK");
1093
+ await client?.query("ROLLBACK");
1094
+ if (error instanceof _mastra_core_error.MastraError) {
1095
+ this.logger?.trackException(error);
1096
+ throw error;
1097
+ }
1019
1098
  const mastraError = new _mastra_core_error.MastraError({
1020
1099
  id: (0, _mastra_core_storage.createVectorErrorId)("PG", "QUERY", "FAILED"),
1021
1100
  domain: _mastra_core_error.ErrorDomain.MASTRA_VECTOR,
@@ -1025,15 +1104,17 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
1025
1104
  this.logger?.trackException(mastraError);
1026
1105
  throw mastraError;
1027
1106
  } finally {
1028
- client.release();
1107
+ client?.release();
1029
1108
  }
1030
1109
  }
1031
1110
  async upsert({ indexName, vectors, metadata, ids, deleteFilter, namespace = DEFAULT_NAMESPACE }) {
1032
1111
  (0, _mastra_core_vector.validateUpsertInput)("PG", vectors, metadata, ids);
1033
1112
  const { tableName } = this.getTableName(indexName);
1034
- const indexInfo = await this.getIndexMetadata({ indexName });
1035
- const client = await this.pool.connect();
1113
+ let client;
1036
1114
  try {
1115
+ const indexInfo = await this.getIndexMetadata({ indexName });
1116
+ await this.ensureNamespaceReady(indexName);
1117
+ client = await this.pool.connect();
1037
1118
  await this.ensureSearchPath(client);
1038
1119
  await client.query("BEGIN");
1039
1120
  if (deleteFilter) {
@@ -1099,7 +1180,11 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
1099
1180
  });
1100
1181
  return vectorIds;
1101
1182
  } catch (error) {
1102
- await client.query("ROLLBACK");
1183
+ await client?.query("ROLLBACK");
1184
+ if (error instanceof _mastra_core_error.MastraError) {
1185
+ this.logger?.trackException(error);
1186
+ throw error;
1187
+ }
1103
1188
  if (error instanceof Error && error.message?.includes("expected") && error.message?.includes("dimensions")) {
1104
1189
  const match = error.message.match(/expected (\d+) dimensions, not (\d+)/);
1105
1190
  if (match) {
@@ -1128,7 +1213,7 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
1128
1213
  this.logger?.trackException(mastraError);
1129
1214
  throw mastraError;
1130
1215
  } finally {
1131
- client.release();
1216
+ client?.release();
1132
1217
  }
1133
1218
  }
1134
1219
  hasher = (0, xxhash_wasm.default)();
@@ -1256,9 +1341,8 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
1256
1341
  namespace VARCHAR(255) NOT NULL DEFAULT '${DEFAULT_NAMESPACE}'
1257
1342
  );
1258
1343
  `);
1259
- await this.ensureNamespaceSchema(indexName, client);
1344
+ if (await this.reconcileNamespace(indexName, client)) this.namespaceReadyIndexes.add(indexName);
1260
1345
  this.createdIndexes.set(indexName, indexCacheKey);
1261
- this.namespaceReadyIndexes.add(indexName);
1262
1346
  this.indexVectorTypes.set(indexName, vectorType);
1263
1347
  if (buildIndex) await this.setupIndex({
1264
1348
  indexName,
@@ -1270,6 +1354,7 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
1270
1354
  } catch (error) {
1271
1355
  this.createdIndexes.delete(indexName);
1272
1356
  this.namespaceReadyIndexes.delete(indexName);
1357
+ this.namespaceReadyCache.delete(indexName);
1273
1358
  this.indexVectorTypes.delete(indexName);
1274
1359
  throw error;
1275
1360
  } finally {
@@ -1644,27 +1729,30 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
1644
1729
  }
1645
1730
  }
1646
1731
  async deleteIndex({ indexName }) {
1647
- const client = await this.pool.connect();
1648
- try {
1649
- const { tableName } = this.getTableName(indexName);
1650
- await client.query(`DROP TABLE IF EXISTS ${tableName} CASCADE`);
1651
- this.createdIndexes.delete(indexName);
1652
- this.namespaceReadyIndexes.delete(indexName);
1653
- this.indexVectorTypes.delete(indexName);
1654
- this.invalidateIndexCaches(indexName);
1655
- } catch (error) {
1656
- await client.query("ROLLBACK");
1657
- const mastraError = new _mastra_core_error.MastraError({
1658
- id: (0, _mastra_core_storage.createVectorErrorId)("PG", "DELETE_INDEX", "FAILED"),
1659
- domain: _mastra_core_error.ErrorDomain.MASTRA_VECTOR,
1660
- category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
1661
- details: { indexName }
1662
- }, error);
1663
- this.logger?.trackException(mastraError);
1664
- throw mastraError;
1665
- } finally {
1666
- client.release();
1667
- }
1732
+ await this.getMutexByName(`create-${indexName}`).runExclusive(async () => {
1733
+ const client = await this.pool.connect();
1734
+ try {
1735
+ const { tableName } = this.getTableName(indexName);
1736
+ await client.query(`DROP TABLE IF EXISTS ${tableName} CASCADE`);
1737
+ this.createdIndexes.delete(indexName);
1738
+ this.namespaceReadyIndexes.delete(indexName);
1739
+ this.namespaceReadyCache.delete(indexName);
1740
+ this.indexVectorTypes.delete(indexName);
1741
+ this.invalidateIndexCaches(indexName);
1742
+ } catch (error) {
1743
+ await client.query("ROLLBACK");
1744
+ const mastraError = new _mastra_core_error.MastraError({
1745
+ id: (0, _mastra_core_storage.createVectorErrorId)("PG", "DELETE_INDEX", "FAILED"),
1746
+ domain: _mastra_core_error.ErrorDomain.MASTRA_VECTOR,
1747
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
1748
+ details: { indexName }
1749
+ }, error);
1750
+ this.logger?.trackException(mastraError);
1751
+ throw mastraError;
1752
+ } finally {
1753
+ client.release();
1754
+ }
1755
+ });
1668
1756
  }
1669
1757
  async truncateIndex({ indexName }) {
1670
1758
  const client = await this.pool.connect();
@@ -1720,6 +1808,7 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
1720
1808
  details: { indexName }
1721
1809
  });
1722
1810
  const indexInfo = await this.getIndexMetadata({ indexName });
1811
+ await this.ensureNamespaceReady(indexName);
1723
1812
  client = await this.pool.connect();
1724
1813
  await this.ensureSearchPath(client);
1725
1814
  const { tableName } = this.getTableName(indexName);
@@ -1813,6 +1902,7 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
1813
1902
  async deleteVector({ indexName, id, namespace = DEFAULT_NAMESPACE }) {
1814
1903
  let client;
1815
1904
  try {
1905
+ await this.ensureNamespaceReady(indexName);
1816
1906
  client = await this.pool.connect();
1817
1907
  const { tableName } = this.getTableName(indexName);
1818
1908
  const query = `
@@ -1821,6 +1911,7 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
1821
1911
  `;
1822
1912
  await client.query(query, [id, namespace]);
1823
1913
  } catch (error) {
1914
+ if (error instanceof _mastra_core_error.MastraError) throw error;
1824
1915
  const mastraError = new _mastra_core_error.MastraError({
1825
1916
  id: (0, _mastra_core_storage.createVectorErrorId)("PG", "DELETE_VECTOR", "FAILED"),
1826
1917
  domain: _mastra_core_error.ErrorDomain.MASTRA_VECTOR,
@@ -1847,6 +1938,7 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
1847
1938
  let client;
1848
1939
  const effectiveNamespace = namespace ?? DEFAULT_NAMESPACE;
1849
1940
  try {
1941
+ await this.ensureNamespaceReady(indexName);
1850
1942
  client = await this.pool.connect();
1851
1943
  const { tableName } = this.getTableName(indexName);
1852
1944
  if (!filter && !ids && namespace === void 0) throw new _mastra_core_error.MastraError({
@@ -5577,6 +5669,17 @@ var ChannelsPG = class ChannelsPG extends _mastra_core_storage.ChannelsStorage {
5577
5669
  function jsonbArg(value) {
5578
5670
  return value === void 0 || value === null ? null : JSON.stringify(value);
5579
5671
  }
5672
+ function parseStoredJSON(value) {
5673
+ if (typeof value === "string") try {
5674
+ return JSON.parse(value);
5675
+ } catch {
5676
+ return value;
5677
+ }
5678
+ return value;
5679
+ }
5680
+ function parseOptionalJSON(value, emptyValue) {
5681
+ return value === null || value === void 0 ? emptyValue : parseStoredJSON(value);
5682
+ }
5580
5683
  var DatasetsPG = class DatasetsPG extends _mastra_core_storage.DatasetsStorage {
5581
5684
  #db;
5582
5685
  #schema;
@@ -5753,6 +5856,8 @@ var DatasetsPG = class DatasetsPG extends _mastra_core_storage.DatasetsStorage {
5753
5856
  };
5754
5857
  }
5755
5858
  transformItemRow(row) {
5859
+ const metadata = parseOptionalJSON(row.metadata, void 0);
5860
+ const emptyValue = metadata?.__purged === true ? null : void 0;
5756
5861
  return {
5757
5862
  id: row.id,
5758
5863
  datasetId: row.datasetId,
@@ -5760,20 +5865,22 @@ var DatasetsPG = class DatasetsPG extends _mastra_core_storage.DatasetsStorage {
5760
5865
  externalId: row.externalId ?? null,
5761
5866
  organizationId: row.organizationId ?? null,
5762
5867
  projectId: row.projectId ?? null,
5763
- input: (0, _mastra_core_storage.safelyParseJSON)(row.input),
5764
- groundTruth: row.groundTruth ? (0, _mastra_core_storage.safelyParseJSON)(row.groundTruth) : void 0,
5765
- expectedTrajectory: row.expectedTrajectory ? (0, _mastra_core_storage.safelyParseJSON)(row.expectedTrajectory) : void 0,
5766
- toolMocks: row.toolMocks ? (0, _mastra_core_storage.safelyParseJSON)(row.toolMocks) : void 0,
5767
- unmockedToolPolicy: row.unmockedToolPolicy ?? void 0,
5768
- scorerIds: row.scorerIds ? (0, _mastra_core_storage.safelyParseJSON)(row.scorerIds) : void 0,
5769
- requestContext: row.requestContext ? (0, _mastra_core_storage.safelyParseJSON)(row.requestContext) : void 0,
5770
- metadata: row.metadata ? (0, _mastra_core_storage.safelyParseJSON)(row.metadata) : void 0,
5771
- source: row.source ? (0, _mastra_core_storage.safelyParseJSON)(row.source) : void 0,
5868
+ input: row.input === null ? null : parseStoredJSON(row.input),
5869
+ groundTruth: parseOptionalJSON(row.groundTruth, emptyValue),
5870
+ expectedTrajectory: parseOptionalJSON(row.expectedTrajectory, emptyValue),
5871
+ toolMocks: parseOptionalJSON(row.toolMocks, emptyValue),
5872
+ unmockedToolPolicy: row.unmockedToolPolicy ?? emptyValue,
5873
+ scorerIds: parseOptionalJSON(row.scorerIds, emptyValue),
5874
+ requestContext: parseOptionalJSON(row.requestContext, emptyValue),
5875
+ metadata,
5876
+ source: parseOptionalJSON(row.source, emptyValue),
5772
5877
  createdAt: (0, _mastra_core_storage.ensureDate)(row.createdAtZ || row.createdAt),
5773
5878
  updatedAt: (0, _mastra_core_storage.ensureDate)(row.updatedAtZ || row.updatedAt)
5774
5879
  };
5775
5880
  }
5776
5881
  transformItemRowFull(row) {
5882
+ const metadata = parseOptionalJSON(row.metadata, void 0);
5883
+ const emptyValue = metadata?.__purged === true ? null : void 0;
5777
5884
  return {
5778
5885
  id: row.id,
5779
5886
  datasetId: row.datasetId,
@@ -5783,15 +5890,15 @@ var DatasetsPG = class DatasetsPG extends _mastra_core_storage.DatasetsStorage {
5783
5890
  projectId: row.projectId ?? null,
5784
5891
  validTo: row.validTo,
5785
5892
  isDeleted: Boolean(row.isDeleted),
5786
- input: (0, _mastra_core_storage.safelyParseJSON)(row.input),
5787
- groundTruth: row.groundTruth ? (0, _mastra_core_storage.safelyParseJSON)(row.groundTruth) : void 0,
5788
- expectedTrajectory: row.expectedTrajectory ? (0, _mastra_core_storage.safelyParseJSON)(row.expectedTrajectory) : void 0,
5789
- toolMocks: row.toolMocks ? (0, _mastra_core_storage.safelyParseJSON)(row.toolMocks) : void 0,
5790
- unmockedToolPolicy: row.unmockedToolPolicy ?? void 0,
5791
- scorerIds: row.scorerIds ? (0, _mastra_core_storage.safelyParseJSON)(row.scorerIds) : void 0,
5792
- requestContext: row.requestContext ? (0, _mastra_core_storage.safelyParseJSON)(row.requestContext) : void 0,
5793
- metadata: row.metadata ? (0, _mastra_core_storage.safelyParseJSON)(row.metadata) : void 0,
5794
- source: row.source ? (0, _mastra_core_storage.safelyParseJSON)(row.source) : void 0,
5893
+ input: row.input === null ? null : parseStoredJSON(row.input),
5894
+ groundTruth: parseOptionalJSON(row.groundTruth, emptyValue),
5895
+ expectedTrajectory: parseOptionalJSON(row.expectedTrajectory, emptyValue),
5896
+ toolMocks: parseOptionalJSON(row.toolMocks, emptyValue),
5897
+ unmockedToolPolicy: row.unmockedToolPolicy ?? emptyValue,
5898
+ scorerIds: parseOptionalJSON(row.scorerIds, emptyValue),
5899
+ requestContext: parseOptionalJSON(row.requestContext, emptyValue),
5900
+ metadata,
5901
+ source: parseOptionalJSON(row.source, emptyValue),
5795
5902
  createdAt: (0, _mastra_core_storage.ensureDate)(row.createdAtZ || row.createdAt),
5796
5903
  updatedAt: (0, _mastra_core_storage.ensureDate)(row.updatedAtZ || row.updatedAt)
5797
5904
  };
@@ -6367,6 +6474,52 @@ var DatasetsPG = class DatasetsPG extends _mastra_core_storage.DatasetsStorage {
6367
6474
  }, error);
6368
6475
  }
6369
6476
  }
6477
+ async _doPurgeItem({ id, datasetId }) {
6478
+ try {
6479
+ const datasetsTable = getTableName$5({
6480
+ indexName: _mastra_core_storage.TABLE_DATASETS,
6481
+ schemaName: getSchemaName$5(this.#schema)
6482
+ });
6483
+ const itemsTable = getTableName$5({
6484
+ indexName: _mastra_core_storage.TABLE_DATASET_ITEMS,
6485
+ schemaName: getSchemaName$5(this.#schema)
6486
+ });
6487
+ const experimentsTable = getTableName$5({
6488
+ indexName: _mastra_core_storage.TABLE_EXPERIMENTS,
6489
+ schemaName: getSchemaName$5(this.#schema)
6490
+ });
6491
+ const experimentResultsTable = getTableName$5({
6492
+ indexName: _mastra_core_storage.TABLE_EXPERIMENT_RESULTS,
6493
+ schemaName: getSchemaName$5(this.#schema)
6494
+ });
6495
+ const purgedAt = (/* @__PURE__ */ new Date()).toISOString();
6496
+ const purgedMetadata = JSON.stringify({
6497
+ __purged: true,
6498
+ purgedAt
6499
+ });
6500
+ await this.#db.client.tx(async (t) => {
6501
+ if (!await t.oneOrNone(`SELECT "id" FROM ${datasetsTable} WHERE "id" = $1 FOR UPDATE`, [datasetId])) return;
6502
+ if (!await t.oneOrNone(`SELECT "id" FROM ${itemsTable} WHERE "id" = $1 AND "datasetId" = $2 LIMIT 1 FOR UPDATE`, [id, datasetId])) return;
6503
+ await t.none(`UPDATE ${itemsTable} SET "input" = 'null'::jsonb, "groundTruth" = NULL, "expectedTrajectory" = NULL, "toolMocks" = NULL, "unmockedToolPolicy" = NULL, "scorerIds" = NULL, "requestContext" = NULL, "metadata" = $2::jsonb, "source" = NULL WHERE "id" = $1 AND "datasetId" = $3`, [
6504
+ id,
6505
+ purgedMetadata,
6506
+ datasetId
6507
+ ]);
6508
+ if ((await t.one(`SELECT to_regclass($1) IS NOT NULL AND to_regclass($2) IS NOT NULL AS exists`, [experimentResultsTable, experimentsTable])).exists) await t.none(`UPDATE ${experimentResultsTable} SET "input" = 'null'::jsonb, "output" = NULL, "groundTruth" = NULL, "error" = NULL, "toolMockReport" = NULL, "tags" = NULL, "comment" = NULL, "metadata" = $2::jsonb WHERE "itemId" = $1 AND "experimentId" IN (SELECT "id" FROM ${experimentsTable} WHERE "datasetId" = $3)`, [
6509
+ id,
6510
+ purgedMetadata,
6511
+ datasetId
6512
+ ]);
6513
+ });
6514
+ } catch (error) {
6515
+ if (error instanceof _mastra_core_error.MastraError) throw error;
6516
+ throw new _mastra_core_error.MastraError({
6517
+ id: (0, _mastra_core_storage.createStorageErrorId)("PG", "PURGE_ITEM", "FAILED"),
6518
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
6519
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY
6520
+ }, error);
6521
+ }
6522
+ }
6370
6523
  async _doBatchInsertItems(input) {
6371
6524
  try {
6372
6525
  if (input.items.length === 0) return [];
@@ -7018,7 +7171,7 @@ var ExperimentsPG = class ExperimentsPG extends _mastra_core_storage.Experiments
7018
7171
  itemDatasetVersion: row.itemDatasetVersion != null ? row.itemDatasetVersion : null,
7019
7172
  organizationId: row.organizationId ?? null,
7020
7173
  projectId: row.projectId ?? null,
7021
- input: (0, _mastra_core_storage.safelyParseJSON)(row.input),
7174
+ input: row.input === null ? null : (0, _mastra_core_storage.safelyParseJSON)(row.input),
7022
7175
  output: row.output ? (0, _mastra_core_storage.safelyParseJSON)(row.output) : null,
7023
7176
  groundTruth: row.groundTruth ? (0, _mastra_core_storage.safelyParseJSON)(row.groundTruth) : null,
7024
7177
  metadata: row.metadata ? (0, _mastra_core_storage.safelyParseJSON)(row.metadata) : null,
@@ -7320,58 +7473,65 @@ var ExperimentsPG = class ExperimentsPG extends _mastra_core_storage.Experiments
7320
7473
  }, error);
7321
7474
  }
7322
7475
  }
7476
+ async #resolvePurgeMetadata(t, datasetId, itemId) {
7477
+ if (!datasetId) return null;
7478
+ const datasetsTable = getTableName$5({
7479
+ indexName: _mastra_core_storage.TABLE_DATASETS,
7480
+ schemaName: getSchemaName$5(this.#schema)
7481
+ });
7482
+ const itemsTable = getTableName$5({
7483
+ indexName: _mastra_core_storage.TABLE_DATASET_ITEMS,
7484
+ schemaName: getSchemaName$5(this.#schema)
7485
+ });
7486
+ if (!await t.oneOrNone(`SELECT "id" FROM ${datasetsTable} WHERE "id" = $1 FOR UPDATE`, [datasetId])) return null;
7487
+ return (await t.oneOrNone(`SELECT "metadata" FROM ${itemsTable}
7488
+ WHERE "id" = $1 AND "datasetId" = $2 AND "metadata"->>'__purged' = 'true'
7489
+ LIMIT 1`, [itemId, datasetId]))?.metadata ?? null;
7490
+ }
7323
7491
  async addExperimentResult(input) {
7324
7492
  try {
7325
7493
  const id = input.id ?? crypto.randomUUID();
7326
- const now = /* @__PURE__ */ new Date();
7327
- const nowIso = now.toISOString();
7328
- await this.#db.insert({
7329
- tableName: _mastra_core_storage.TABLE_EXPERIMENT_RESULTS,
7330
- record: {
7494
+ const resultsTable = getTableName$5({
7495
+ indexName: _mastra_core_storage.TABLE_EXPERIMENT_RESULTS,
7496
+ schemaName: getSchemaName$5(this.#schema)
7497
+ });
7498
+ const experimentsTable = getTableName$5({
7499
+ indexName: _mastra_core_storage.TABLE_EXPERIMENTS,
7500
+ schemaName: getSchemaName$5(this.#schema)
7501
+ });
7502
+ const row = await this.#db.client.tx(async (t) => {
7503
+ const owner = await t.oneOrNone(`SELECT "datasetId" FROM ${experimentsTable} WHERE "id" = $1`, [input.experimentId]);
7504
+ const purgeMetadata = await this.#resolvePurgeMetadata(t, owner?.datasetId, input.itemId);
7505
+ const nowIso = (/* @__PURE__ */ new Date()).toISOString();
7506
+ return t.one(`INSERT INTO ${resultsTable} (
7507
+ "id", "experimentId", "itemId", "itemDatasetVersion", "organizationId", "projectId",
7508
+ "input", "output", "groundTruth", "metadata", "error", "startedAt", "completedAt",
7509
+ "retryCount", "attempt", "traceId", "status", "tags", "toolMockReport", "createdAt"
7510
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20)
7511
+ RETURNING *`, [
7331
7512
  id,
7332
- experimentId: input.experimentId,
7333
- itemId: input.itemId,
7334
- itemDatasetVersion: input.itemDatasetVersion ?? null,
7335
- organizationId: input.organizationId ?? null,
7336
- projectId: input.projectId ?? null,
7337
- input: input.input,
7338
- output: input.output ?? null,
7339
- groundTruth: input.groundTruth ?? null,
7340
- metadata: input.metadata ?? null,
7341
- error: input.error ?? null,
7342
- startedAt: input.startedAt.toISOString(),
7343
- completedAt: input.completedAt.toISOString(),
7344
- retryCount: input.retryCount,
7345
- attempt: input.attempt ?? 0,
7346
- traceId: input.traceId ?? null,
7347
- status: input.status ?? null,
7348
- tags: input.tags ?? null,
7349
- toolMockReport: input.toolMockReport ?? null,
7350
- createdAt: nowIso
7351
- }
7513
+ input.experimentId,
7514
+ input.itemId,
7515
+ input.itemDatasetVersion ?? null,
7516
+ input.organizationId ?? null,
7517
+ input.projectId ?? null,
7518
+ JSON.stringify(purgeMetadata ? null : input.input),
7519
+ purgeMetadata || input.output == null ? null : JSON.stringify(input.output),
7520
+ purgeMetadata || input.groundTruth == null ? null : JSON.stringify(input.groundTruth),
7521
+ JSON.stringify(purgeMetadata ?? input.metadata ?? null),
7522
+ purgeMetadata || input.error == null ? null : JSON.stringify(input.error),
7523
+ input.startedAt.toISOString(),
7524
+ input.completedAt.toISOString(),
7525
+ input.retryCount,
7526
+ input.attempt ?? 0,
7527
+ input.traceId ?? null,
7528
+ input.status ?? null,
7529
+ purgeMetadata || input.tags == null ? null : JSON.stringify(input.tags),
7530
+ purgeMetadata || input.toolMockReport == null ? null : JSON.stringify(input.toolMockReport),
7531
+ nowIso
7532
+ ]);
7352
7533
  });
7353
- return {
7354
- id,
7355
- experimentId: input.experimentId,
7356
- itemId: input.itemId,
7357
- itemDatasetVersion: input.itemDatasetVersion ?? null,
7358
- organizationId: input.organizationId ?? null,
7359
- projectId: input.projectId ?? null,
7360
- input: input.input,
7361
- output: input.output ?? null,
7362
- groundTruth: input.groundTruth ?? null,
7363
- metadata: input.metadata ?? null,
7364
- error: input.error ?? null,
7365
- startedAt: input.startedAt,
7366
- completedAt: input.completedAt,
7367
- retryCount: input.retryCount,
7368
- attempt: input.attempt ?? 0,
7369
- traceId: input.traceId ?? null,
7370
- status: input.status ?? null,
7371
- tags: input.tags ?? null,
7372
- toolMockReport: input.toolMockReport ?? null,
7373
- createdAt: now
7374
- };
7534
+ return this.transformExperimentResultRow(row);
7375
7535
  } catch (error) {
7376
7536
  throw new _mastra_core_error.MastraError({
7377
7537
  id: (0, _mastra_core_storage.createStorageErrorId)("PG", "ADD_EXPERIMENT_RESULT", "FAILED"),
@@ -7386,8 +7546,14 @@ var ExperimentsPG = class ExperimentsPG extends _mastra_core_storage.Experiments
7386
7546
  indexName: _mastra_core_storage.TABLE_EXPERIMENT_RESULTS,
7387
7547
  schemaName: getSchemaName$5(this.#schema)
7388
7548
  });
7549
+ const experimentsTable = getTableName$5({
7550
+ indexName: _mastra_core_storage.TABLE_EXPERIMENTS,
7551
+ schemaName: getSchemaName$5(this.#schema)
7552
+ });
7389
7553
  const attempt = input.attempt ?? 0;
7390
7554
  const row = await this.#db.client.tx(async (t) => {
7555
+ const owner = await t.oneOrNone(`SELECT "datasetId" FROM ${experimentsTable} WHERE "id" = $1`, [input.experimentId]);
7556
+ const purgeMetadata = await this.#resolvePurgeMetadata(t, owner?.datasetId, input.itemId);
7391
7557
  const existing = await t.oneOrNone(`SELECT "id" FROM ${tableName} WHERE "experimentId" = $1 AND "itemId" = $2 AND COALESCE("attempt", 0) = $3 FOR UPDATE`, [
7392
7558
  input.experimentId,
7393
7559
  input.itemId,
@@ -7407,19 +7573,19 @@ var ExperimentsPG = class ExperimentsPG extends _mastra_core_storage.Experiments
7407
7573
  input.itemDatasetVersion ?? null,
7408
7574
  input.organizationId ?? null,
7409
7575
  input.projectId ?? null,
7410
- JSON.stringify(input.input),
7411
- input.output != null ? JSON.stringify(input.output) : null,
7412
- input.groundTruth != null ? JSON.stringify(input.groundTruth) : null,
7413
- input.metadata != null ? JSON.stringify(input.metadata) : null,
7414
- input.error != null ? JSON.stringify(input.error) : null,
7576
+ JSON.stringify(purgeMetadata ? null : input.input),
7577
+ purgeMetadata || input.output == null ? null : JSON.stringify(input.output),
7578
+ purgeMetadata || input.groundTruth == null ? null : JSON.stringify(input.groundTruth),
7579
+ JSON.stringify(purgeMetadata ?? input.metadata ?? null),
7580
+ purgeMetadata || input.error == null ? null : JSON.stringify(input.error),
7415
7581
  input.startedAt.toISOString(),
7416
7582
  input.completedAt.toISOString(),
7417
7583
  input.retryCount,
7418
7584
  attempt,
7419
7585
  input.traceId ?? null,
7420
7586
  input.status ?? null,
7421
- input.tags != null ? JSON.stringify(input.tags) : null,
7422
- input.toolMockReport != null ? JSON.stringify(input.toolMockReport) : null,
7587
+ purgeMetadata || input.tags == null ? null : JSON.stringify(input.tags),
7588
+ purgeMetadata || input.toolMockReport == null ? null : JSON.stringify(input.toolMockReport),
7423
7589
  (/* @__PURE__ */ new Date()).toISOString()
7424
7590
  ]);
7425
7591
  }
@@ -7433,19 +7599,19 @@ var ExperimentsPG = class ExperimentsPG extends _mastra_core_storage.Experiments
7433
7599
  input.itemDatasetVersion ?? null,
7434
7600
  input.organizationId ?? null,
7435
7601
  input.projectId ?? null,
7436
- JSON.stringify(input.input),
7437
- input.output != null ? JSON.stringify(input.output) : null,
7438
- input.groundTruth != null ? JSON.stringify(input.groundTruth) : null,
7439
- input.metadata != null ? JSON.stringify(input.metadata) : null,
7440
- input.error != null ? JSON.stringify(input.error) : null,
7602
+ JSON.stringify(purgeMetadata ? null : input.input),
7603
+ purgeMetadata || input.output == null ? null : JSON.stringify(input.output),
7604
+ purgeMetadata || input.groundTruth == null ? null : JSON.stringify(input.groundTruth),
7605
+ JSON.stringify(purgeMetadata ?? input.metadata ?? null),
7606
+ purgeMetadata || input.error == null ? null : JSON.stringify(input.error),
7441
7607
  input.startedAt.toISOString(),
7442
7608
  input.completedAt.toISOString(),
7443
7609
  input.retryCount,
7444
7610
  attempt,
7445
7611
  input.traceId ?? null,
7446
7612
  input.status ?? null,
7447
- input.tags != null ? JSON.stringify(input.tags) : null,
7448
- input.toolMockReport != null ? JSON.stringify(input.toolMockReport) : null
7613
+ purgeMetadata || input.tags == null ? null : JSON.stringify(input.tags),
7614
+ purgeMetadata || input.toolMockReport == null ? null : JSON.stringify(input.toolMockReport)
7449
7615
  ]);
7450
7616
  });
7451
7617
  return this.transformExperimentResultRow(row);
@@ -7463,39 +7629,34 @@ var ExperimentsPG = class ExperimentsPG extends _mastra_core_storage.Experiments
7463
7629
  indexName: _mastra_core_storage.TABLE_EXPERIMENT_RESULTS,
7464
7630
  schemaName: getSchemaName$5(this.#schema)
7465
7631
  });
7466
- const setClauses = [];
7467
- const values = [];
7468
- let paramIndex = 1;
7469
- if (input.status !== void 0) {
7470
- setClauses.push(`"status" = $${paramIndex++}`);
7471
- values.push(input.status);
7472
- }
7473
- if (input.tags !== void 0) {
7474
- setClauses.push(`"tags" = $${paramIndex++}`);
7475
- values.push(JSON.stringify(input.tags));
7476
- }
7477
- if (input.comment !== void 0) {
7478
- setClauses.push(`"comment" = $${paramIndex++}`);
7479
- values.push(input.comment);
7480
- }
7481
- if (setClauses.length === 0) {
7482
- const existing = await this.#getExperimentResultById(this.#db.client, { id: input.id });
7483
- if (!existing) throw new _mastra_core_error.MastraError({
7484
- id: (0, _mastra_core_storage.createStorageErrorId)("PG", "UPDATE_EXPERIMENT_RESULT", "NOT_FOUND"),
7485
- domain: _mastra_core_error.ErrorDomain.STORAGE,
7486
- category: _mastra_core_error.ErrorCategory.USER,
7487
- details: { resultId: input.id }
7488
- });
7489
- return existing;
7490
- }
7491
- values.push(input.id);
7492
- let whereClause = `"id" = $${paramIndex}`;
7493
- if (input.experimentId) {
7494
- paramIndex++;
7495
- values.push(input.experimentId);
7496
- whereClause += ` AND "experimentId" = $${paramIndex}`;
7497
- }
7498
- const row = await this.#db.client.oneOrNone(`UPDATE ${tableName} SET ${setClauses.join(", ")} WHERE ${whereClause} RETURNING *`, values);
7632
+ const experimentsTable = getTableName$5({
7633
+ indexName: _mastra_core_storage.TABLE_EXPERIMENTS,
7634
+ schemaName: getSchemaName$5(this.#schema)
7635
+ });
7636
+ const row = await this.#db.client.tx(async (t) => {
7637
+ const owner = await t.oneOrNone(`SELECT e."datasetId", r."itemId"
7638
+ FROM ${tableName} r
7639
+ JOIN ${experimentsTable} e ON e."id" = r."experimentId"
7640
+ WHERE r."id" = $1${input.experimentId !== void 0 ? " AND r.\"experimentId\" = $2" : ""}`, input.experimentId !== void 0 ? [input.id, input.experimentId] : [input.id]);
7641
+ if (!owner) return null;
7642
+ const purgeMetadata = await this.#resolvePurgeMetadata(t, owner.datasetId, owner.itemId);
7643
+ return t.oneOrNone(`UPDATE ${tableName}
7644
+ SET "status" = CASE WHEN $2 THEN $3 ELSE "status" END,
7645
+ "tags" = CASE WHEN $4 THEN NULL WHEN $5 THEN $6::jsonb ELSE "tags" END,
7646
+ "comment" = CASE WHEN $4 THEN NULL WHEN $7 THEN $8 ELSE "comment" END
7647
+ WHERE "id" = $1${input.experimentId !== void 0 ? " AND \"experimentId\" = $9" : ""}
7648
+ RETURNING *`, [
7649
+ input.id,
7650
+ input.status !== void 0,
7651
+ input.status ?? null,
7652
+ Boolean(purgeMetadata),
7653
+ input.tags !== void 0,
7654
+ input.tags === void 0 ? null : JSON.stringify(input.tags),
7655
+ input.comment !== void 0,
7656
+ input.comment ?? null,
7657
+ ...input.experimentId !== void 0 ? [input.experimentId] : []
7658
+ ]);
7659
+ });
7499
7660
  if (!row) throw new _mastra_core_error.MastraError({
7500
7661
  id: (0, _mastra_core_storage.createStorageErrorId)("PG", "UPDATE_EXPERIMENT_RESULT", "NOT_FOUND"),
7501
7662
  domain: _mastra_core_error.ErrorDomain.STORAGE,
@@ -10895,12 +11056,13 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
10895
11056
  /**
10896
11057
  * Reads one page of messages together with the total row count.
10897
11058
  *
10898
- * `COUNT(*) OVER ()` reports the count over the whole WHERE result on the same
10899
- * statement as the page, so the page costs one database round-trip instead of
10900
- * two. The page and the count also come from one snapshot, so the count always
10901
- * describes the returned rows. A separate `COUNT(*)` runs only when the page is
10902
- * empty and the caller asked for a page after the last row, because a window
10903
- * function has no row to carry the count on.
11059
+ * Every page query carries a skinny scalar `(SELECT COUNT(*) ...)` subquery that
11060
+ * reports the total over the whole WHERE result on the same statement as the page,
11061
+ * so the page costs one database round-trip instead of two. The page and the count
11062
+ * also come from one snapshot, so the count always describes the returned rows. A
11063
+ * separate `COUNT(*)` runs only as a fallback when the page is empty and the caller
11064
+ * asked for a page after the last row, because there is then no row to carry the
11065
+ * count on.
10904
11066
  */
10905
11067
  async #fetchMessagePage({ selectStatement, tableName, whereClause, orderByStatement, queryParams, perPageInput, perPage, offset }) {
10906
11068
  const limitClause = perPageInput === false ? "" : ` LIMIT $${queryParams.length + 1} OFFSET $${queryParams.length + 2}`;
@@ -10909,7 +11071,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
10909
11071
  perPage,
10910
11072
  offset
10911
11073
  ];
10912
- const rows = await this.#db.readClient.manyOrNone(`${selectStatement}, COUNT(*) OVER () AS "__total" FROM ${tableName} ${whereClause} ${orderByStatement}${limitClause}`, dataParams) || [];
11074
+ const rows = await this.#db.readClient.manyOrNone(`${selectStatement}, (SELECT COUNT(*) FROM ${tableName} ${whereClause}) AS "__total" FROM ${tableName} ${whereClause} ${orderByStatement}${limitClause}`, dataParams) || [];
10913
11075
  if (rows.length > 0) return {
10914
11076
  total: Number(rows[0].__total),
10915
11077
  messages: rows
@@ -10948,7 +11110,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
10948
11110
  const metadataFilter = (0, _mastra_core_storage.validateStorageMetadataFilter)(filter?.metadata);
10949
11111
  try {
10950
11112
  const { field, direction } = this.parseOrderBy(orderBy, "ASC");
10951
- const orderByStatement = `ORDER BY COALESCE("${field}Z", "${field}") ${direction}`;
11113
+ const orderByStatement = `ORDER BY "${field}" ${direction}`;
10952
11114
  const selectStatement = `SELECT id, content, role, type, "createdAt", "createdAtZ", thread_id AS "threadId", "resourceId"`;
10953
11115
  const tableName = getTableName$3({
10954
11116
  indexName: _mastra_core_storage.TABLE_MESSAGES,
@@ -11096,7 +11258,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
11096
11258
  const metadataFilter = (0, _mastra_core_storage.validateStorageMetadataFilter)(filter?.metadata);
11097
11259
  try {
11098
11260
  const { field, direction } = this.parseOrderBy(orderBy, "ASC");
11099
- const orderByStatement = `ORDER BY COALESCE("${field}Z", "${field}") ${direction}`;
11261
+ const orderByStatement = `ORDER BY "${field}" ${direction}`;
11100
11262
  const selectStatement = `SELECT id, content, role, type, "createdAt", "createdAtZ", thread_id AS "threadId", "resourceId"`;
11101
11263
  const tableName = getTableName$3({
11102
11264
  indexName: _mastra_core_storage.TABLE_MESSAGES,