@mastra/spanner 1.6.4 → 1.6.5-alpha.1

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
@@ -2786,6 +2786,7 @@ function rowToItem(row) {
2786
2786
  tableName: _mastra_core_storage.TABLE_DATASET_ITEMS,
2787
2787
  row
2788
2788
  });
2789
+ const emptyValue = t.metadata?.__purged === true ? null : void 0;
2789
2790
  return {
2790
2791
  id: String(t.id),
2791
2792
  datasetId: String(t.datasetId),
@@ -2794,14 +2795,14 @@ function rowToItem(row) {
2794
2795
  organizationId: t.organizationId ?? null,
2795
2796
  projectId: t.projectId ?? null,
2796
2797
  input: t.input,
2797
- groundTruth: t.groundTruth ?? void 0,
2798
- expectedTrajectory: t.expectedTrajectory ?? void 0,
2799
- toolMocks: t.toolMocks ?? void 0,
2800
- unmockedToolPolicy: t.unmockedToolPolicy ?? void 0,
2801
- scorerIds: t.scorerIds ?? void 0,
2802
- requestContext: t.requestContext ?? void 0,
2798
+ groundTruth: t.groundTruth ?? emptyValue,
2799
+ expectedTrajectory: t.expectedTrajectory ?? emptyValue,
2800
+ toolMocks: t.toolMocks ?? emptyValue,
2801
+ unmockedToolPolicy: t.unmockedToolPolicy ?? emptyValue,
2802
+ scorerIds: t.scorerIds ?? emptyValue,
2803
+ requestContext: t.requestContext ?? emptyValue,
2803
2804
  metadata: t.metadata ?? void 0,
2804
- source: t.source ?? void 0,
2805
+ source: t.source ?? emptyValue,
2805
2806
  createdAt: toDate$1(t.createdAt),
2806
2807
  updatedAt: toDate$1(t.updatedAt)
2807
2808
  };
@@ -2947,21 +2948,17 @@ var DatasetsSpanner = class DatasetsSpanner extends _mastra_core_storage.Dataset
2947
2948
  await this.db.clearTable({ tableName: _mastra_core_storage.TABLE_DATASETS });
2948
2949
  }
2949
2950
  async experimentTablesExist() {
2950
- try {
2951
- const [rows] = await this.database.run({
2952
- sql: `SELECT COUNT(*) AS c FROM INFORMATION_SCHEMA.TABLES
2953
- WHERE TABLE_SCHEMA = "" AND TABLE_NAME IN (@a, @b)`,
2954
- params: {
2955
- a: _mastra_core_storage.TABLE_EXPERIMENTS,
2956
- b: _mastra_core_storage.TABLE_EXPERIMENT_RESULTS
2957
- },
2958
- json: true
2959
- });
2960
- const row = rows?.[0];
2961
- return Number(row?.c ?? 0) === 2;
2962
- } catch {
2963
- return false;
2964
- }
2951
+ const [rows] = await this.database.run({
2952
+ sql: `SELECT COUNT(*) AS c FROM INFORMATION_SCHEMA.TABLES
2953
+ WHERE TABLE_SCHEMA = "" AND TABLE_NAME IN (@a, @b)`,
2954
+ params: {
2955
+ a: _mastra_core_storage.TABLE_EXPERIMENTS,
2956
+ b: _mastra_core_storage.TABLE_EXPERIMENT_RESULTS
2957
+ },
2958
+ json: true
2959
+ });
2960
+ const row = rows?.[0];
2961
+ return Number(row?.c ?? 0) === 2;
2965
2962
  }
2966
2963
  async createDataset(input) {
2967
2964
  try {
@@ -3575,6 +3572,91 @@ var DatasetsSpanner = class DatasetsSpanner extends _mastra_core_storage.Dataset
3575
3572
  }, error);
3576
3573
  }
3577
3574
  }
3575
+ async _doPurgeItem({ id, datasetId }) {
3576
+ try {
3577
+ const experimentTablesExist = await this.experimentTablesExist();
3578
+ const purgedAt = (/* @__PURE__ */ new Date()).toISOString();
3579
+ await this.db.runWithAbortRetry(() => this.database.runTransactionAsync(async (tx) => {
3580
+ try {
3581
+ const [datasetRowCount] = await tx.runUpdate({
3582
+ sql: `UPDATE ${quoteIdent(_mastra_core_storage.TABLE_DATASETS, "table name")}
3583
+ SET ${quoteIdent("version", "column name")} = ${quoteIdent("version", "column name")}
3584
+ WHERE ${quoteIdent("id", "column name")} = @datasetId`,
3585
+ params: { datasetId }
3586
+ });
3587
+ if (Number(datasetRowCount) === 0) {
3588
+ await tx.commit();
3589
+ return;
3590
+ }
3591
+ const [rows] = await tx.run({
3592
+ sql: `SELECT ${quoteIdent("id", "column name")} FROM ${quoteIdent(_mastra_core_storage.TABLE_DATASET_ITEMS, "table name")} WHERE ${quoteIdent("id", "column name")} = @id AND ${quoteIdent("datasetId", "column name")} = @datasetId LIMIT 1`,
3593
+ params: {
3594
+ id,
3595
+ datasetId
3596
+ },
3597
+ json: true
3598
+ });
3599
+ if (!rows?.[0]) {
3600
+ await tx.commit();
3601
+ return;
3602
+ }
3603
+ await tx.runUpdate({
3604
+ sql: `UPDATE ${quoteIdent(_mastra_core_storage.TABLE_DATASET_ITEMS, "table name")} SET
3605
+ ${quoteIdent("input", "column name")} = JSON 'null',
3606
+ ${quoteIdent("groundTruth", "column name")} = NULL,
3607
+ ${quoteIdent("expectedTrajectory", "column name")} = NULL,
3608
+ ${quoteIdent("toolMocks", "column name")} = NULL,
3609
+ ${quoteIdent("unmockedToolPolicy", "column name")} = NULL,
3610
+ ${quoteIdent("scorerIds", "column name")} = NULL,
3611
+ ${quoteIdent("requestContext", "column name")} = NULL,
3612
+ ${quoteIdent("metadata", "column name")} = JSON_OBJECT('__purged', TRUE, 'purgedAt', @purgedAt),
3613
+ ${quoteIdent("source", "column name")} = NULL
3614
+ WHERE ${quoteIdent("id", "column name")} = @id AND ${quoteIdent("datasetId", "column name")} = @datasetId`,
3615
+ params: {
3616
+ id,
3617
+ datasetId,
3618
+ purgedAt
3619
+ }
3620
+ });
3621
+ if (experimentTablesExist) await tx.runUpdate({
3622
+ sql: `UPDATE ${quoteIdent(_mastra_core_storage.TABLE_EXPERIMENT_RESULTS, "table name")} SET
3623
+ ${quoteIdent("input", "column name")} = JSON 'null',
3624
+ ${quoteIdent("output", "column name")} = NULL,
3625
+ ${quoteIdent("groundTruth", "column name")} = NULL,
3626
+ ${quoteIdent("error", "column name")} = NULL,
3627
+ ${quoteIdent("toolMockReport", "column name")} = NULL,
3628
+ ${quoteIdent("tags", "column name")} = NULL,
3629
+ ${quoteIdent("comment", "column name")} = NULL,
3630
+ ${quoteIdent("metadata", "column name")} = JSON_OBJECT('__purged', TRUE, 'purgedAt', @purgedAt)
3631
+ WHERE ${quoteIdent("itemId", "column name")} = @id
3632
+ AND ${quoteIdent("experimentId", "column name")} IN (
3633
+ SELECT ${quoteIdent("id", "column name")} FROM ${quoteIdent(_mastra_core_storage.TABLE_EXPERIMENTS, "table name")}
3634
+ WHERE ${quoteIdent("datasetId", "column name")} = @datasetId
3635
+ )`,
3636
+ params: {
3637
+ id,
3638
+ datasetId,
3639
+ purgedAt
3640
+ }
3641
+ });
3642
+ await tx.commit();
3643
+ } catch (err) {
3644
+ await tx.rollback().catch((rollbackErr) => {
3645
+ throw new AggregateError([err, rollbackErr], "Transaction and rollback both failed");
3646
+ });
3647
+ throw err;
3648
+ }
3649
+ }));
3650
+ } catch (error) {
3651
+ if (error instanceof _mastra_core_error.MastraError) throw error;
3652
+ throw new _mastra_core_error.MastraError({
3653
+ id: (0, _mastra_core_storage.createStorageErrorId)("SPANNER", "PURGE_ITEM", "FAILED"),
3654
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
3655
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
3656
+ details: { id }
3657
+ }, error);
3658
+ }
3659
+ }
3578
3660
  async listItems(args) {
3579
3661
  const { page = 0, perPage: perPageInput } = args.pagination;
3580
3662
  const perPage = (0, _mastra_core_storage.normalizePerPage)(perPageInput, 100);
@@ -4060,6 +4142,48 @@ var ExperimentsSpanner = class ExperimentsSpanner extends _mastra_core_storage.E
4060
4142
  this.skipDefaultIndexes = skipDefaultIndexes;
4061
4143
  this.indexes = indexes?.filter((idx) => ExperimentsSpanner.MANAGED_TABLES.includes(idx.table));
4062
4144
  }
4145
+ async #withPurgeBarrier(experimentId, itemId, fn) {
4146
+ return this.db.runWithAbortRetry(() => this.database.runTransactionAsync(async (tx) => {
4147
+ try {
4148
+ const [experimentRows] = await tx.run({
4149
+ sql: `SELECT ${quoteIdent("datasetId", "column name")} FROM ${quoteIdent(_mastra_core_storage.TABLE_EXPERIMENTS, "table name")}
4150
+ WHERE ${quoteIdent("id", "column name")} = @experimentId`,
4151
+ params: { experimentId },
4152
+ json: true
4153
+ });
4154
+ const datasetId = (experimentRows[0]?.toJSON?.() ?? experimentRows[0])?.datasetId;
4155
+ let purgeMetadata = null;
4156
+ if (datasetId) {
4157
+ await tx.runUpdate({
4158
+ sql: `UPDATE ${quoteIdent(_mastra_core_storage.TABLE_DATASETS, "table name")}
4159
+ SET ${quoteIdent("version", "column name")} = ${quoteIdent("version", "column name")}
4160
+ WHERE ${quoteIdent("id", "column name")} = @datasetId`,
4161
+ params: { datasetId }
4162
+ });
4163
+ const [itemRows] = await tx.run({
4164
+ sql: `SELECT ${quoteIdent("metadata", "column name")} FROM ${quoteIdent(_mastra_core_storage.TABLE_DATASET_ITEMS, "table name")}
4165
+ WHERE ${quoteIdent("id", "column name")} = @itemId AND ${quoteIdent("datasetId", "column name")} = @datasetId
4166
+ AND JSON_VALUE(${quoteIdent("metadata", "column name")}, '$.__purged') = 'true'
4167
+ LIMIT 1`,
4168
+ params: {
4169
+ itemId,
4170
+ datasetId
4171
+ },
4172
+ json: true
4173
+ });
4174
+ purgeMetadata = (itemRows[0]?.toJSON?.() ?? itemRows[0])?.metadata ?? null;
4175
+ }
4176
+ const result = await fn(tx, purgeMetadata);
4177
+ await tx.commit();
4178
+ return result;
4179
+ } catch (error) {
4180
+ await tx.rollback().catch((rollbackError) => {
4181
+ throw new AggregateError([error, rollbackError], "Transaction and rollback both failed");
4182
+ });
4183
+ throw error;
4184
+ }
4185
+ }));
4186
+ }
4063
4187
  async init() {
4064
4188
  await this.db.createTable({
4065
4189
  tableName: _mastra_core_storage.TABLE_EXPERIMENTS,
@@ -4474,54 +4598,83 @@ var ExperimentsSpanner = class ExperimentsSpanner extends _mastra_core_storage.E
4474
4598
  try {
4475
4599
  const now = /* @__PURE__ */ new Date();
4476
4600
  const id = input.id ?? (0, crypto.randomUUID)();
4477
- const result = {
4478
- id,
4479
- experimentId: input.experimentId,
4480
- itemId: input.itemId,
4481
- itemDatasetVersion: input.itemDatasetVersion ?? null,
4482
- organizationId: input.organizationId ?? null,
4483
- projectId: input.projectId ?? null,
4484
- input: input.input ?? null,
4485
- output: input.output ?? null,
4486
- groundTruth: input.groundTruth ?? null,
4487
- metadata: input.metadata ?? null,
4488
- error: input.error ?? null,
4489
- startedAt: input.startedAt,
4490
- completedAt: input.completedAt,
4491
- retryCount: input.retryCount,
4492
- attempt: input.attempt ?? 0,
4493
- traceId: input.traceId ?? null,
4494
- status: input.status ?? null,
4495
- tags: input.tags ?? null,
4496
- toolMockReport: input.toolMockReport ?? null,
4497
- createdAt: now
4498
- };
4499
- await this.db.insert({
4500
- tableName: _mastra_core_storage.TABLE_EXPERIMENT_RESULTS,
4501
- record: {
4601
+ return await this.#withPurgeBarrier(input.experimentId, input.itemId, async (tx, marker) => {
4602
+ const result = {
4502
4603
  id,
4503
- experimentId: result.experimentId,
4504
- itemId: result.itemId,
4505
- itemDatasetVersion: result.itemDatasetVersion,
4506
- organizationId: result.organizationId,
4507
- projectId: result.projectId,
4508
- input: result.input,
4509
- output: result.output,
4510
- groundTruth: result.groundTruth,
4511
- metadata: result.metadata,
4512
- error: result.error,
4513
- startedAt: result.startedAt,
4514
- completedAt: result.completedAt,
4515
- retryCount: result.retryCount,
4516
- attempt: result.attempt,
4517
- traceId: result.traceId,
4518
- status: result.status,
4519
- tags: result.tags,
4520
- toolMockReport: result.toolMockReport,
4604
+ experimentId: input.experimentId,
4605
+ itemId: input.itemId,
4606
+ itemDatasetVersion: input.itemDatasetVersion ?? null,
4607
+ organizationId: input.organizationId ?? null,
4608
+ projectId: input.projectId ?? null,
4609
+ input: marker ? null : input.input ?? null,
4610
+ output: marker ? null : input.output ?? null,
4611
+ groundTruth: marker ? null : input.groundTruth ?? null,
4612
+ metadata: marker ?? input.metadata ?? null,
4613
+ error: marker ? null : input.error ?? null,
4614
+ startedAt: input.startedAt,
4615
+ completedAt: input.completedAt,
4616
+ retryCount: input.retryCount,
4617
+ attempt: input.attempt ?? 0,
4618
+ traceId: input.traceId ?? null,
4619
+ status: input.status ?? null,
4620
+ tags: marker ? null : input.tags ?? null,
4621
+ toolMockReport: marker ? null : input.toolMockReport ?? null,
4521
4622
  createdAt: now
4522
- }
4623
+ };
4624
+ await tx.runUpdate({
4625
+ sql: `INSERT INTO ${quoteIdent(_mastra_core_storage.TABLE_EXPERIMENT_RESULTS, "table name")} (
4626
+ ${[
4627
+ "id",
4628
+ "experimentId",
4629
+ "itemId",
4630
+ "itemDatasetVersion",
4631
+ "organizationId",
4632
+ "projectId",
4633
+ "input",
4634
+ "output",
4635
+ "groundTruth",
4636
+ "metadata",
4637
+ "error",
4638
+ "startedAt",
4639
+ "completedAt",
4640
+ "retryCount",
4641
+ "attempt",
4642
+ "traceId",
4643
+ "status",
4644
+ "tags",
4645
+ "toolMockReport",
4646
+ "createdAt"
4647
+ ].map((column) => quoteIdent(column, "column name")).join(", ")}
4648
+ ) VALUES (@id, @experimentId, @itemId, @itemDatasetVersion, @organizationId, @projectId,
4649
+ @input, @output, @groundTruth, @metadata, @error, @startedAt, @completedAt, @retryCount,
4650
+ @attempt, @traceId, @status, @tags, @toolMockReport, @createdAt)`,
4651
+ params: {
4652
+ ...result,
4653
+ input: JSON.stringify(result.input),
4654
+ output: JSON.stringify(result.output),
4655
+ groundTruth: JSON.stringify(result.groundTruth),
4656
+ metadata: JSON.stringify(result.metadata),
4657
+ error: JSON.stringify(result.error),
4658
+ tags: JSON.stringify(result.tags),
4659
+ toolMockReport: JSON.stringify(result.toolMockReport)
4660
+ },
4661
+ types: {
4662
+ itemDatasetVersion: "int64",
4663
+ organizationId: "string",
4664
+ projectId: "string",
4665
+ input: "json",
4666
+ output: "json",
4667
+ groundTruth: "json",
4668
+ metadata: "json",
4669
+ error: "json",
4670
+ traceId: "string",
4671
+ status: "string",
4672
+ tags: "json",
4673
+ toolMockReport: "json"
4674
+ }
4675
+ });
4676
+ return result;
4523
4677
  });
4524
- return result;
4525
4678
  } catch (error) {
4526
4679
  throw new _mastra_core_error.MastraError({
4527
4680
  id: (0, _mastra_core_storage.createStorageErrorId)("SPANNER", "ADD_EXPERIMENT_RESULT", "FAILED"),
@@ -4537,31 +4690,12 @@ var ExperimentsSpanner = class ExperimentsSpanner extends _mastra_core_storage.E
4537
4690
  async upsertExperimentResult(input) {
4538
4691
  try {
4539
4692
  const attempt = input.attempt ?? 0;
4540
- const [rows] = await this.database.run({
4541
- sql: `SELECT ${quoteIdent("id", "column name")} FROM ${quoteIdent(_mastra_core_storage.TABLE_EXPERIMENT_RESULTS, "table name")}
4542
- WHERE ${quoteIdent("experimentId", "column name")} = @experimentId
4543
- AND ${quoteIdent("itemId", "column name")} = @itemId
4544
- AND COALESCE(${quoteIdent("attempt", "column name")}, 0) = @attempt`,
4545
- params: {
4546
- experimentId: input.experimentId,
4547
- itemId: input.itemId,
4548
- attempt
4549
- },
4550
- json: true
4551
- });
4552
- let existingId = rows[0]?.id;
4553
- if (!existingId) try {
4554
- return await this.addExperimentResult({
4555
- ...input,
4556
- attempt
4557
- });
4558
- } catch (insertError) {
4559
- if (!(0, _mastra_core_storage.hasErrorCode)(insertError, /* @__PURE__ */ new Set([6]))) throw insertError;
4560
- const [winnerRows] = await this.database.run({
4561
- sql: `SELECT ${quoteIdent("id", "column name")} FROM ${quoteIdent(_mastra_core_storage.TABLE_EXPERIMENT_RESULTS, "table name")}
4562
- WHERE ${quoteIdent("experimentId", "column name")} = @experimentId
4563
- AND ${quoteIdent("itemId", "column name")} = @itemId
4564
- AND COALESCE(${quoteIdent("attempt", "column name")}, 0) = @attempt`,
4693
+ return await this.#withPurgeBarrier(input.experimentId, input.itemId, async (tx, marker) => {
4694
+ const [rows] = await tx.run({
4695
+ sql: `SELECT * FROM ${quoteIdent(_mastra_core_storage.TABLE_EXPERIMENT_RESULTS, "table name")}
4696
+ WHERE ${quoteIdent("experimentId", "column name")} = @experimentId
4697
+ AND ${quoteIdent("itemId", "column name")} = @itemId
4698
+ AND COALESCE(${quoteIdent("attempt", "column name")}, 0) = @attempt`,
4565
4699
  params: {
4566
4700
  experimentId: input.experimentId,
4567
4701
  itemId: input.itemId,
@@ -4569,71 +4703,97 @@ var ExperimentsSpanner = class ExperimentsSpanner extends _mastra_core_storage.E
4569
4703
  },
4570
4704
  json: true
4571
4705
  });
4572
- existingId = winnerRows[0]?.id;
4573
- if (!existingId) throw insertError;
4574
- }
4575
- await this.db.runDml({
4576
- sql: `UPDATE ${quoteIdent(_mastra_core_storage.TABLE_EXPERIMENT_RESULTS, "table name")} SET
4577
- ${quoteIdent("itemDatasetVersion", "column name")} = @itemDatasetVersion,
4578
- ${quoteIdent("organizationId", "column name")} = @organizationId,
4579
- ${quoteIdent("projectId", "column name")} = @projectId,
4580
- ${quoteIdent("input", "column name")} = @input,
4581
- ${quoteIdent("output", "column name")} = @output,
4582
- ${quoteIdent("groundTruth", "column name")} = @groundTruth,
4583
- ${quoteIdent("metadata", "column name")} = @metadata,
4584
- ${quoteIdent("error", "column name")} = @error,
4585
- ${quoteIdent("startedAt", "column name")} = @startedAt,
4586
- ${quoteIdent("completedAt", "column name")} = @completedAt,
4587
- ${quoteIdent("retryCount", "column name")} = @retryCount,
4588
- ${quoteIdent("attempt", "column name")} = @attempt,
4589
- ${quoteIdent("traceId", "column name")} = @traceId,
4590
- ${quoteIdent("status", "column name")} = @status,
4591
- ${quoteIdent("tags", "column name")} = @tags,
4592
- ${quoteIdent("toolMockReport", "column name")} = @toolMockReport
4593
- WHERE ${quoteIdent("id", "column name")} = @id`,
4594
- params: {
4595
- id: existingId,
4706
+ const existingRow = rows[0]?.toJSON?.() ?? rows[0];
4707
+ const values = {
4596
4708
  itemDatasetVersion: input.itemDatasetVersion ?? null,
4597
4709
  organizationId: input.organizationId ?? null,
4598
4710
  projectId: input.projectId ?? null,
4599
- input: JSON.stringify(input.input),
4600
- output: input.output == null ? null : JSON.stringify(input.output),
4601
- groundTruth: input.groundTruth == null ? null : JSON.stringify(input.groundTruth),
4602
- metadata: input.metadata == null ? null : JSON.stringify(input.metadata),
4603
- error: input.error == null ? null : JSON.stringify(input.error),
4711
+ input: marker ? null : input.input,
4712
+ output: marker ? null : input.output ?? null,
4713
+ groundTruth: marker ? null : input.groundTruth ?? null,
4714
+ metadata: marker ?? input.metadata ?? null,
4715
+ error: marker ? null : input.error ?? null,
4604
4716
  startedAt: input.startedAt,
4605
4717
  completedAt: input.completedAt,
4606
4718
  retryCount: input.retryCount,
4607
4719
  attempt,
4608
4720
  traceId: input.traceId ?? null,
4609
4721
  status: input.status ?? null,
4610
- tags: input.tags == null ? null : JSON.stringify(input.tags),
4611
- toolMockReport: input.toolMockReport == null ? null : JSON.stringify(input.toolMockReport)
4612
- },
4613
- types: {
4614
- itemDatasetVersion: "int64",
4615
- organizationId: "string",
4616
- projectId: "string",
4617
- input: "json",
4618
- output: "json",
4619
- groundTruth: "json",
4620
- metadata: "json",
4621
- error: "json",
4622
- traceId: "string",
4623
- status: "string",
4624
- tags: "json",
4625
- toolMockReport: "json"
4626
- }
4627
- });
4628
- const updated = await this.getExperimentResultById({ id: existingId });
4629
- if (!updated) throw new _mastra_core_error.MastraError({
4630
- id: (0, _mastra_core_storage.createStorageErrorId)("SPANNER", "UPSERT_EXPERIMENT_RESULT", "NOT_FOUND"),
4631
- domain: _mastra_core_error.ErrorDomain.STORAGE,
4632
- category: _mastra_core_error.ErrorCategory.USER,
4633
- text: `Experiment result ${existingId} not found after upsert`,
4634
- details: { id: existingId }
4722
+ tags: marker ? null : input.tags ?? null,
4723
+ toolMockReport: marker ? null : input.toolMockReport ?? null
4724
+ };
4725
+ if (!existingRow) {
4726
+ const result = {
4727
+ id: (0, crypto.randomUUID)(),
4728
+ experimentId: input.experimentId,
4729
+ itemId: input.itemId,
4730
+ ...values,
4731
+ createdAt: /* @__PURE__ */ new Date()
4732
+ };
4733
+ await this.db.insert({
4734
+ tableName: _mastra_core_storage.TABLE_EXPERIMENT_RESULTS,
4735
+ record: result,
4736
+ transaction: tx
4737
+ });
4738
+ return result;
4739
+ }
4740
+ const existing = rowToExperimentResult(existingRow);
4741
+ await this.db.runDml({
4742
+ sql: `UPDATE ${quoteIdent(_mastra_core_storage.TABLE_EXPERIMENT_RESULTS, "table name")} SET
4743
+ ${quoteIdent("itemDatasetVersion", "column name")} = @itemDatasetVersion,
4744
+ ${quoteIdent("organizationId", "column name")} = @organizationId,
4745
+ ${quoteIdent("projectId", "column name")} = @projectId,
4746
+ ${quoteIdent("input", "column name")} = @input,
4747
+ ${quoteIdent("output", "column name")} = @output,
4748
+ ${quoteIdent("groundTruth", "column name")} = @groundTruth,
4749
+ ${quoteIdent("metadata", "column name")} = @metadata,
4750
+ ${quoteIdent("error", "column name")} = @error,
4751
+ ${quoteIdent("startedAt", "column name")} = @startedAt,
4752
+ ${quoteIdent("completedAt", "column name")} = @completedAt,
4753
+ ${quoteIdent("retryCount", "column name")} = @retryCount,
4754
+ ${quoteIdent("attempt", "column name")} = @attempt,
4755
+ ${quoteIdent("traceId", "column name")} = @traceId,
4756
+ ${quoteIdent("status", "column name")} = @status,
4757
+ ${quoteIdent("tags", "column name")} = @tags,
4758
+ ${quoteIdent("toolMockReport", "column name")} = @toolMockReport,
4759
+ ${quoteIdent("comment", "column name")} = @comment
4760
+ WHERE ${quoteIdent("id", "column name")} = @id`,
4761
+ params: {
4762
+ id: existing.id,
4763
+ ...values,
4764
+ input: JSON.stringify(values.input),
4765
+ output: JSON.stringify(values.output),
4766
+ groundTruth: JSON.stringify(values.groundTruth),
4767
+ metadata: JSON.stringify(values.metadata),
4768
+ error: JSON.stringify(values.error),
4769
+ tags: JSON.stringify(values.tags),
4770
+ toolMockReport: JSON.stringify(values.toolMockReport),
4771
+ comment: marker ? null : existing.comment
4772
+ },
4773
+ types: {
4774
+ itemDatasetVersion: "int64",
4775
+ retryCount: "int64",
4776
+ attempt: "int64",
4777
+ organizationId: "string",
4778
+ projectId: "string",
4779
+ input: "json",
4780
+ output: "json",
4781
+ groundTruth: "json",
4782
+ metadata: "json",
4783
+ error: "json",
4784
+ traceId: "string",
4785
+ status: "string",
4786
+ tags: "json",
4787
+ toolMockReport: "json",
4788
+ comment: "string"
4789
+ }
4790
+ }, tx);
4791
+ return {
4792
+ ...existing,
4793
+ ...values,
4794
+ comment: marker ? null : existing.comment
4795
+ };
4635
4796
  });
4636
- return updated;
4637
4797
  } catch (error) {
4638
4798
  if (error instanceof _mastra_core_error.MastraError) throw error;
4639
4799
  throw new _mastra_core_error.MastraError({
@@ -4649,61 +4809,47 @@ var ExperimentsSpanner = class ExperimentsSpanner extends _mastra_core_storage.E
4649
4809
  }
4650
4810
  async updateExperimentResult(input) {
4651
4811
  try {
4652
- if (input.status === void 0 && input.tags === void 0 && input.comment === void 0) {
4653
- const existing = await this.getExperimentResultById({ id: input.id });
4654
- if (!existing || input.experimentId !== void 0 && existing.experimentId !== input.experimentId) throw new _mastra_core_error.MastraError({
4655
- id: (0, _mastra_core_storage.createStorageErrorId)("SPANNER", "UPDATE_EXPERIMENT_RESULT", "NOT_FOUND"),
4656
- domain: _mastra_core_error.ErrorDomain.STORAGE,
4657
- category: _mastra_core_error.ErrorCategory.USER,
4658
- text: `Experiment result ${input.id} not found`,
4659
- details: { id: input.id }
4660
- });
4661
- return existing;
4662
- }
4663
- const setClauses = [];
4664
- const params = { id: input.id };
4665
- const types = {};
4666
- if (input.status !== void 0) {
4667
- setClauses.push(`${quoteIdent("status", "column name")} = @status`);
4668
- params.status = input.status;
4669
- if (input.status === null) types.status = "string";
4670
- }
4671
- if (input.tags !== void 0) {
4672
- setClauses.push(`${quoteIdent("tags", "column name")} = @tags`);
4673
- params.tags = input.tags === null ? null : JSON.stringify(input.tags);
4674
- types.tags = "json";
4675
- }
4676
- if (input.comment !== void 0) {
4677
- setClauses.push(`${quoteIdent("comment", "column name")} = @comment`);
4678
- params.comment = input.comment;
4679
- if (input.comment === null) types.comment = "string";
4680
- }
4681
- const whereClauses = [`${quoteIdent("id", "column name")} = @id`];
4682
- if (input.experimentId !== void 0) {
4683
- whereClauses.push(`${quoteIdent("experimentId", "column name")} = @experimentId`);
4684
- params.experimentId = input.experimentId;
4685
- }
4686
- if (await this.db.runDml({
4687
- sql: `UPDATE ${quoteIdent(_mastra_core_storage.TABLE_EXPERIMENT_RESULTS, "table name")}
4688
- SET ${setClauses.join(", ")} WHERE ${whereClauses.join(" AND ")}`,
4689
- params,
4690
- types
4691
- }) === 0) throw new _mastra_core_error.MastraError({
4812
+ const existing = await this.getExperimentResultById({ id: input.id });
4813
+ if (!existing || input.experimentId !== void 0 && existing.experimentId !== input.experimentId) throw new _mastra_core_error.MastraError({
4692
4814
  id: (0, _mastra_core_storage.createStorageErrorId)("SPANNER", "UPDATE_EXPERIMENT_RESULT", "NOT_FOUND"),
4693
4815
  domain: _mastra_core_error.ErrorDomain.STORAGE,
4694
4816
  category: _mastra_core_error.ErrorCategory.USER,
4695
4817
  text: `Experiment result ${input.id} not found`,
4696
4818
  details: { id: input.id }
4697
4819
  });
4698
- const updated = await this.getExperimentResultById({ id: input.id });
4699
- if (!updated) throw new _mastra_core_error.MastraError({
4700
- id: (0, _mastra_core_storage.createStorageErrorId)("SPANNER", "UPDATE_EXPERIMENT_RESULT", "NOT_FOUND"),
4701
- domain: _mastra_core_error.ErrorDomain.STORAGE,
4702
- category: _mastra_core_error.ErrorCategory.USER,
4703
- text: `Experiment result ${input.id} not found`,
4704
- details: { id: input.id }
4820
+ return await this.#withPurgeBarrier(existing.experimentId, existing.itemId, async (tx, marker) => {
4821
+ const updated = {
4822
+ ...existing,
4823
+ status: input.status !== void 0 ? input.status : existing.status,
4824
+ tags: marker ? null : input.tags !== void 0 ? input.tags : existing.tags,
4825
+ comment: marker ? null : input.comment !== void 0 ? input.comment : existing.comment
4826
+ };
4827
+ if (await this.db.runDml({
4828
+ sql: `UPDATE ${quoteIdent(_mastra_core_storage.TABLE_EXPERIMENT_RESULTS, "table name")}
4829
+ SET ${quoteIdent("status", "column name")} = @status,
4830
+ ${quoteIdent("tags", "column name")} = @tags,
4831
+ ${quoteIdent("comment", "column name")} = @comment
4832
+ WHERE ${quoteIdent("id", "column name")} = @id${input.experimentId ? ` AND ${quoteIdent("experimentId", "column name")} = @experimentId` : ""}`,
4833
+ params: {
4834
+ id: input.id,
4835
+ ...input.experimentId ? { experimentId: input.experimentId } : {},
4836
+ status: updated.status,
4837
+ tags: updated.tags === null ? null : JSON.stringify(updated.tags),
4838
+ comment: updated.comment
4839
+ },
4840
+ types: {
4841
+ status: "string",
4842
+ tags: "json",
4843
+ comment: "string"
4844
+ }
4845
+ }, tx) === 0) throw new _mastra_core_error.MastraError({
4846
+ id: (0, _mastra_core_storage.createStorageErrorId)("SPANNER", "UPDATE_EXPERIMENT_RESULT", "NOT_FOUND"),
4847
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
4848
+ category: _mastra_core_error.ErrorCategory.USER,
4849
+ details: { id: input.id }
4850
+ });
4851
+ return updated;
4705
4852
  });
4706
- return updated;
4707
4853
  } catch (error) {
4708
4854
  if (error instanceof _mastra_core_error.MastraError) throw error;
4709
4855
  throw new _mastra_core_error.MastraError({
@@ -9079,19 +9225,34 @@ var ObservabilitySpanner = class ObservabilitySpanner extends _mastra_core_stora
9079
9225
  }
9080
9226
  }
9081
9227
  async batchDeleteTraces(args) {
9228
+ this.assertUnscopedBatchDeleteTraces(args);
9082
9229
  try {
9083
9230
  if (args.traceIds.length === 0) return;
9084
- const tableName = quoteIdent(_mastra_core_storage.TABLE_SPANS, "table name");
9085
9231
  const params = {};
9086
9232
  const placeholders = args.traceIds.map((id, i) => {
9087
9233
  const name = `t${i}`;
9088
9234
  params[name] = id;
9089
9235
  return `@${name}`;
9090
9236
  });
9091
- await this.db.runDml({
9092
- sql: `DELETE FROM ${tableName} WHERE ${quoteIdent("traceId", "column name")} IN (${placeholders.join(", ")})`,
9093
- params
9094
- });
9237
+ const traceIdFilter = `${quoteIdent("traceId", "column name")} IN (${placeholders.join(", ")})`;
9238
+ await this.db.runWithAbortRetry(() => this.database.runTransactionAsync(async (tx) => {
9239
+ try {
9240
+ await tx.runUpdate({
9241
+ sql: `DELETE FROM ${quoteIdent(_mastra_core_storage.TABLE_SPANS, "table name")} WHERE ${traceIdFilter}`,
9242
+ params
9243
+ });
9244
+ if (!this.disableMetrics) await tx.runUpdate({
9245
+ sql: `DELETE FROM ${quoteIdent(TABLE_AI_METRICS, "table name")} WHERE ${traceIdFilter}`,
9246
+ params
9247
+ });
9248
+ await tx.commit();
9249
+ } catch (error) {
9250
+ await tx.rollback().catch((rollbackError) => {
9251
+ throw new AggregateError([error, rollbackError], "Transaction and rollback both failed");
9252
+ });
9253
+ throw error;
9254
+ }
9255
+ }));
9095
9256
  } catch (error) {
9096
9257
  throw new _mastra_core_error.MastraError({
9097
9258
  id: (0, _mastra_core_storage.createStorageErrorId)("SPANNER", "BATCH_DELETE_TRACES", "FAILED"),