@mastra/mysql 0.8.0 → 0.8.1-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/CHANGELOG.md +55 -0
- package/dist/index.cjs +92 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +93 -15
- package/dist/index.js.map +1 -1
- package/dist/storage/domains/experiments/index.d.ts +2 -1
- package/dist/storage/domains/experiments/index.d.ts.map +1 -1
- package/dist/storage/domains/scores/index.d.ts.map +1 -1
- package/dist/storage/domains/workflows/index.d.ts.map +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,60 @@
|
|
|
1
1
|
# @mastra/mysql
|
|
2
2
|
|
|
3
|
+
## 0.8.1-alpha.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Added HTTP endpoints for caller-driven experiments: `POST /datasets/:datasetId/experiments` now accepts `start: false` to create an experiment without spawning the runner (with an optional target and run-level `scorerIds`), and new routes `POST /datasets/:datasetId/experiments/:experimentId/items/:itemId/run`, `POST /datasets/:datasetId/experiments/:experimentId/results`, and `POST /datasets/:datasetId/experiments/:experimentId/finalize` let external orchestrators run one item server-side, submit externally computed per-item results (idempotent upsert on retries), and finalize the run with server-computed counts. ([#21888](https://github.com/mastra-ai/mastra/pull/21888))
|
|
8
|
+
|
|
9
|
+
- Added `createDatasetExperiment()`, `runExperimentItem()`, `submitExperimentResult()`, and `finalizeExperiment()` methods so a caller-owned orchestrator (for example Temporal) can drive an experiment loop while Mastra either executes each item server-side or ingests externally computed results. ([#21888](https://github.com/mastra-ai/mastra/pull/21888))
|
|
10
|
+
|
|
11
|
+
- Added `upsertExperimentResult()` to the experiments storage domain plus an `attempt` column on experiment results and a nullable target with an optional `scorerIds` column on experiments, enabling retry-safe result writes for caller-driven experiments (retried submissions with the same `(experimentId, itemId, attempt)` key converge on a single row). `saveScore()` now accepts an optional caller-supplied `id` and upserts on it, so retried experiment submissions replace their previous score rows (latest wins) instead of accumulating duplicates. ([#21888](https://github.com/mastra-ai/mastra/pull/21888))
|
|
12
|
+
|
|
13
|
+
- Added caller-driven experiments so an external orchestrator (for example Temporal workers) can own the experiment loop while Mastra stays the system of record. ([#21888](https://github.com/mastra-ai/mastra/pull/21888))
|
|
14
|
+
|
|
15
|
+
Create an experiment with `dataset.createExperiment()` (idempotent when you pass your own id). With a target, Mastra runs each item for you: call `dataset.runExperimentItem()` per item and Mastra executes the registered agent or workflow, resolves scorers (experiment `scorers`, falling back to item `scorerIds`, then dataset `scorerIds`), and upserts the result. Without a target, run everything yourself and report per-item results with `dataset.submitExperimentResult()` (upsert semantics on `(experimentId, itemId, attempt)` so retried workers converge on a single row). Either way, close the run with `dataset.finalizeExperiment()` and Mastra computes per-item succeeded/failed/skipped counts from the persisted rows. Results go into the same storage as native runs, so Studio views, comparisons, and review summaries work unchanged.
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
// Caller drives the loop, Mastra runs each item
|
|
19
|
+
const { experimentId } = await dataset.createExperiment({
|
|
20
|
+
id: workflowRunId,
|
|
21
|
+
targetType: 'agent',
|
|
22
|
+
targetId: 'support-agent',
|
|
23
|
+
scorers: ['accuracy'],
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
await dataset.runExperimentItem({ experimentId, itemId });
|
|
27
|
+
|
|
28
|
+
// Or: caller runs everything, Mastra ingests results
|
|
29
|
+
const ingest = await dataset.createExperiment({ id: workflowRunId });
|
|
30
|
+
await dataset.submitExperimentResult({
|
|
31
|
+
experimentId: ingest.experimentId,
|
|
32
|
+
itemId,
|
|
33
|
+
output,
|
|
34
|
+
scores: [{ scorerId: 'accuracy', score: 0.92 }],
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const experiment = await dataset.finalizeExperiment({ experimentId });
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
- Fixed versioned dataset item lookups to return the item visible in the requested dataset snapshot. ([#21979](https://github.com/mastra-ai/mastra/pull/21979))
|
|
41
|
+
|
|
42
|
+
- Updated dependencies [[`9267e9b`](https://github.com/mastra-ai/mastra/commit/9267e9b3d9c2fcf16936050495a787054c2431ab), [`acc3471`](https://github.com/mastra-ai/mastra/commit/acc3471de5f3fde8027ee4e355af292b2bc1bc30), [`b6a771e`](https://github.com/mastra-ai/mastra/commit/b6a771ef23d203ddb348efca8065eff65def8191), [`9267e9b`](https://github.com/mastra-ai/mastra/commit/9267e9b3d9c2fcf16936050495a787054c2431ab), [`26d4016`](https://github.com/mastra-ai/mastra/commit/26d40160ff7f7d8bf95fee2039a52cbc83863533), [`9267e9b`](https://github.com/mastra-ai/mastra/commit/9267e9b3d9c2fcf16936050495a787054c2431ab), [`9267e9b`](https://github.com/mastra-ai/mastra/commit/9267e9b3d9c2fcf16936050495a787054c2431ab), [`57c5103`](https://github.com/mastra-ai/mastra/commit/57c51035a2a36e3df3c4f32f46bb789a66ed5946)]:
|
|
43
|
+
- @mastra/core@1.61.0-alpha.3
|
|
44
|
+
|
|
45
|
+
## 0.8.1-alpha.0
|
|
46
|
+
|
|
47
|
+
### Patch Changes
|
|
48
|
+
|
|
49
|
+
- Fixed concurrent resume() calls on the same suspended workflow run executing downstream steps more than once. A resume now atomically claims the run before executing anything, so only one caller continues a given suspension. Losing callers throw WORKFLOW_RESUME_ALREADY_CLAIMED without running any steps. Fixes #20443 ([#21725](https://github.com/mastra-ai/mastra/pull/21725))
|
|
50
|
+
|
|
51
|
+
- Workflow state updates now support an optional expectedStatus guard, so a status change is only applied when the stored run is in an expected state. This is what makes concurrent workflow resumes safe. ([#21725](https://github.com/mastra-ai/mastra/pull/21725))
|
|
52
|
+
|
|
53
|
+
- Resume conflicts now return 409 Conflict. When a suspended workflow run has already been resumed by another caller, the resume endpoints respond with 409 instead of a generic error. ([#21725](https://github.com/mastra-ai/mastra/pull/21725))
|
|
54
|
+
|
|
55
|
+
- Updated dependencies [[`88d14ca`](https://github.com/mastra-ai/mastra/commit/88d14cac008582a618fecc3d5c7fd3bdf4f6ddc3), [`84a5b69`](https://github.com/mastra-ai/mastra/commit/84a5b699f84d6bae0a34efe5a970d891090b9f41), [`84a5b69`](https://github.com/mastra-ai/mastra/commit/84a5b699f84d6bae0a34efe5a970d891090b9f41), [`84a5b69`](https://github.com/mastra-ai/mastra/commit/84a5b699f84d6bae0a34efe5a970d891090b9f41), [`038b7b4`](https://github.com/mastra-ai/mastra/commit/038b7b405cb4ac25ab3f3031334111b1f87ac112), [`4132d61`](https://github.com/mastra-ai/mastra/commit/4132d61f8367077120ee9e6420d3224dffd93c93)]:
|
|
56
|
+
- @mastra/core@1.60.1-alpha.0
|
|
57
|
+
|
|
3
58
|
## 0.8.0
|
|
4
59
|
|
|
5
60
|
### Minor Changes
|
package/dist/index.cjs
CHANGED
|
@@ -2577,7 +2577,11 @@ var DatasetsMySQL = class DatasetsMySQL extends _mastra_core_storage.DatasetsSto
|
|
|
2577
2577
|
try {
|
|
2578
2578
|
const tableItemsName = formatTableName(_mastra_core_storage.TABLE_DATASET_ITEMS);
|
|
2579
2579
|
let rows;
|
|
2580
|
-
if (args.datasetVersion !== void 0) [rows] = await this.pool.execute(`SELECT * FROM ${tableItemsName} WHERE \`id\` = ? AND \`datasetVersion\`
|
|
2580
|
+
if (args.datasetVersion !== void 0) [rows] = await this.pool.execute(`SELECT * FROM ${tableItemsName} WHERE \`id\` = ? AND \`datasetVersion\` <= ? AND (\`validTo\` IS NULL OR \`validTo\` > ?) AND \`isDeleted\` = 0 ORDER BY \`datasetVersion\` DESC LIMIT 1`, [
|
|
2581
|
+
args.id,
|
|
2582
|
+
args.datasetVersion,
|
|
2583
|
+
args.datasetVersion
|
|
2584
|
+
]);
|
|
2581
2585
|
else [rows] = await this.pool.execute(`SELECT * FROM ${tableItemsName} WHERE \`id\` = ? AND \`validTo\` IS NULL AND \`isDeleted\` = 0`, [args.id]);
|
|
2582
2586
|
return rows.length > 0 ? this.mapItem(rows[0]) : null;
|
|
2583
2587
|
} catch (error) {
|
|
@@ -3014,7 +3018,8 @@ var ExperimentsMySQL = class ExperimentsMySQL extends _mastra_core_storage.Exper
|
|
|
3014
3018
|
"experimentSetId",
|
|
3015
3019
|
"comparisonId",
|
|
3016
3020
|
"variantId",
|
|
3017
|
-
"trialIndex"
|
|
3021
|
+
"trialIndex",
|
|
3022
|
+
"scorerIds"
|
|
3018
3023
|
]
|
|
3019
3024
|
});
|
|
3020
3025
|
await this.operations.alterTable({
|
|
@@ -3023,7 +3028,8 @@ var ExperimentsMySQL = class ExperimentsMySQL extends _mastra_core_storage.Exper
|
|
|
3023
3028
|
ifNotExists: [
|
|
3024
3029
|
"comment",
|
|
3025
3030
|
"organizationId",
|
|
3026
|
-
"projectId"
|
|
3031
|
+
"projectId",
|
|
3032
|
+
"attempt"
|
|
3027
3033
|
]
|
|
3028
3034
|
});
|
|
3029
3035
|
await this.createDefaultIndexes();
|
|
@@ -3041,8 +3047,9 @@ var ExperimentsMySQL = class ExperimentsMySQL extends _mastra_core_storage.Exper
|
|
|
3041
3047
|
agentVersion: row.agentVersion ?? null,
|
|
3042
3048
|
organizationId: row.organizationId ?? null,
|
|
3043
3049
|
projectId: row.projectId ?? null,
|
|
3044
|
-
targetType: row.targetType,
|
|
3045
|
-
targetId: row.targetId,
|
|
3050
|
+
targetType: row.targetType ?? null,
|
|
3051
|
+
targetId: row.targetId ?? null,
|
|
3052
|
+
scorerIds: parseJSON$2(row.scorerIds) ?? null,
|
|
3046
3053
|
name: row.name ?? void 0,
|
|
3047
3054
|
description: row.description ?? void 0,
|
|
3048
3055
|
metadata: parseJSON$2(row.metadata),
|
|
@@ -3078,6 +3085,7 @@ var ExperimentsMySQL = class ExperimentsMySQL extends _mastra_core_storage.Exper
|
|
|
3078
3085
|
startedAt: parseDateTime(row.startedAt) ?? /* @__PURE__ */ new Date(),
|
|
3079
3086
|
completedAt: parseDateTime(row.completedAt) ?? /* @__PURE__ */ new Date(),
|
|
3080
3087
|
retryCount: row.retryCount,
|
|
3088
|
+
attempt: row.attempt != null ? Number(row.attempt) : 0,
|
|
3081
3089
|
traceId: row.traceId ?? null,
|
|
3082
3090
|
status: row.status ?? null,
|
|
3083
3091
|
tags: row.tags ? parseJSON$2(row.tags) ?? null : null,
|
|
@@ -3098,8 +3106,9 @@ var ExperimentsMySQL = class ExperimentsMySQL extends _mastra_core_storage.Exper
|
|
|
3098
3106
|
agentVersion: input.agentVersion ?? null,
|
|
3099
3107
|
organizationId: input.organizationId ?? null,
|
|
3100
3108
|
projectId: input.projectId ?? null,
|
|
3101
|
-
targetType: input.targetType,
|
|
3102
|
-
targetId: input.targetId,
|
|
3109
|
+
targetType: input.targetType ?? null,
|
|
3110
|
+
targetId: input.targetId ?? null,
|
|
3111
|
+
scorerIds: input.scorerIds ? JSON.stringify(input.scorerIds) : null,
|
|
3103
3112
|
name: input.name ?? null,
|
|
3104
3113
|
description: input.description ?? null,
|
|
3105
3114
|
metadata: input.metadata ? JSON.stringify(input.metadata) : null,
|
|
@@ -3127,8 +3136,9 @@ var ExperimentsMySQL = class ExperimentsMySQL extends _mastra_core_storage.Exper
|
|
|
3127
3136
|
agentVersion: input.agentVersion ?? null,
|
|
3128
3137
|
organizationId: input.organizationId ?? null,
|
|
3129
3138
|
projectId: input.projectId ?? null,
|
|
3130
|
-
targetType: input.targetType,
|
|
3131
|
-
targetId: input.targetId,
|
|
3139
|
+
targetType: input.targetType ?? null,
|
|
3140
|
+
targetId: input.targetId ?? null,
|
|
3141
|
+
scorerIds: input.scorerIds ?? null,
|
|
3132
3142
|
name: input.name,
|
|
3133
3143
|
description: input.description,
|
|
3134
3144
|
metadata: input.metadata,
|
|
@@ -3372,12 +3382,13 @@ var ExperimentsMySQL = class ExperimentsMySQL extends _mastra_core_storage.Exper
|
|
|
3372
3382
|
organizationId: input.organizationId ?? null,
|
|
3373
3383
|
projectId: input.projectId ?? null,
|
|
3374
3384
|
input: JSON.stringify(input.input),
|
|
3375
|
-
output: input.output ? JSON.stringify(input.output) : null,
|
|
3376
|
-
groundTruth: input.groundTruth ? JSON.stringify(input.groundTruth) : null,
|
|
3385
|
+
output: input.output != null ? JSON.stringify(input.output) : null,
|
|
3386
|
+
groundTruth: input.groundTruth != null ? JSON.stringify(input.groundTruth) : null,
|
|
3377
3387
|
error: input.error ? JSON.stringify(input.error) : null,
|
|
3378
3388
|
startedAt: input.startedAt,
|
|
3379
3389
|
completedAt: input.completedAt,
|
|
3380
3390
|
retryCount: input.retryCount,
|
|
3391
|
+
attempt: input.attempt ?? 0,
|
|
3381
3392
|
traceId: input.traceId ?? null,
|
|
3382
3393
|
status: input.status ?? null,
|
|
3383
3394
|
tags: input.tags ? JSON.stringify(input.tags) : null,
|
|
@@ -3398,6 +3409,7 @@ var ExperimentsMySQL = class ExperimentsMySQL extends _mastra_core_storage.Exper
|
|
|
3398
3409
|
startedAt: input.startedAt,
|
|
3399
3410
|
completedAt: input.completedAt,
|
|
3400
3411
|
retryCount: input.retryCount,
|
|
3412
|
+
attempt: input.attempt ?? 0,
|
|
3401
3413
|
traceId: input.traceId ?? null,
|
|
3402
3414
|
status: input.status ?? null,
|
|
3403
3415
|
tags: input.tags ?? null,
|
|
@@ -3430,6 +3442,66 @@ var ExperimentsMySQL = class ExperimentsMySQL extends _mastra_core_storage.Exper
|
|
|
3430
3442
|
}, error);
|
|
3431
3443
|
}
|
|
3432
3444
|
}
|
|
3445
|
+
async upsertExperimentResult(input) {
|
|
3446
|
+
if (input.toolMockReport) throw new _mastra_core_error.MastraError({
|
|
3447
|
+
id: "MYSQL_EXPERIMENT_TOOL_MOCK_REPORT_UNSUPPORTED",
|
|
3448
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
3449
|
+
category: _mastra_core_error.ErrorCategory.USER,
|
|
3450
|
+
text: "Tool mock reports are not supported on the MySQL storage adapter. Use a supported adapter (LibSQL, PostgreSQL, MongoDB, or Spanner) to persist experiment tool mock reports."
|
|
3451
|
+
});
|
|
3452
|
+
try {
|
|
3453
|
+
const attempt = input.attempt ?? 0;
|
|
3454
|
+
const existingId = (await this.operations.query(`SELECT ${quoteIdentifier("id", "column name")} FROM ${formatTableName(_mastra_core_storage.TABLE_EXPERIMENT_RESULTS)} WHERE ${quoteIdentifier("experimentId", "column name")} = ? AND ${quoteIdentifier("itemId", "column name")} = ? AND COALESCE(${quoteIdentifier("attempt", "column name")}, 0) = ?`, [
|
|
3455
|
+
input.experimentId,
|
|
3456
|
+
input.itemId,
|
|
3457
|
+
attempt
|
|
3458
|
+
]))[0]?.id;
|
|
3459
|
+
if (!existingId) return await this.addExperimentResult({
|
|
3460
|
+
...input,
|
|
3461
|
+
attempt
|
|
3462
|
+
});
|
|
3463
|
+
await this.operations.update({
|
|
3464
|
+
tableName: _mastra_core_storage.TABLE_EXPERIMENT_RESULTS,
|
|
3465
|
+
keys: { id: existingId },
|
|
3466
|
+
data: {
|
|
3467
|
+
itemDatasetVersion: input.itemDatasetVersion ?? null,
|
|
3468
|
+
organizationId: input.organizationId ?? null,
|
|
3469
|
+
projectId: input.projectId ?? null,
|
|
3470
|
+
input: JSON.stringify(input.input),
|
|
3471
|
+
output: input.output != null ? JSON.stringify(input.output) : null,
|
|
3472
|
+
groundTruth: input.groundTruth != null ? JSON.stringify(input.groundTruth) : null,
|
|
3473
|
+
error: input.error ? JSON.stringify(input.error) : null,
|
|
3474
|
+
startedAt: input.startedAt,
|
|
3475
|
+
completedAt: input.completedAt,
|
|
3476
|
+
retryCount: input.retryCount,
|
|
3477
|
+
attempt,
|
|
3478
|
+
traceId: input.traceId ?? null,
|
|
3479
|
+
status: input.status ?? null,
|
|
3480
|
+
tags: input.tags ? JSON.stringify(input.tags) : null
|
|
3481
|
+
}
|
|
3482
|
+
});
|
|
3483
|
+
const updated = await this.getExperimentResultById({ id: existingId });
|
|
3484
|
+
if (!updated) throw new _mastra_core_error.MastraError({
|
|
3485
|
+
id: "MYSQL_UPSERT_EXPERIMENT_RESULT_NOT_FOUND",
|
|
3486
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
3487
|
+
category: _mastra_core_error.ErrorCategory.USER,
|
|
3488
|
+
text: `Experiment result ${existingId} not found after upsert`,
|
|
3489
|
+
details: {
|
|
3490
|
+
experimentId: input.experimentId,
|
|
3491
|
+
itemId: input.itemId,
|
|
3492
|
+
attempt
|
|
3493
|
+
}
|
|
3494
|
+
});
|
|
3495
|
+
return updated;
|
|
3496
|
+
} catch (error) {
|
|
3497
|
+
if (error instanceof _mastra_core_error.MastraError) throw error;
|
|
3498
|
+
throw new _mastra_core_error.MastraError({
|
|
3499
|
+
id: "MYSQL_UPSERT_EXPERIMENT_RESULT_FAILED",
|
|
3500
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
3501
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY
|
|
3502
|
+
}, error);
|
|
3503
|
+
}
|
|
3504
|
+
}
|
|
3433
3505
|
async updateExperimentResult(input) {
|
|
3434
3506
|
try {
|
|
3435
3507
|
const existing = await this.operations.load({
|
|
@@ -9315,7 +9387,7 @@ var ScoresMySQL = class ScoresMySQL extends _mastra_core_storage.ScoresStorage {
|
|
|
9315
9387
|
}
|
|
9316
9388
|
async saveScore(score) {
|
|
9317
9389
|
try {
|
|
9318
|
-
const id = crypto.randomUUID();
|
|
9390
|
+
const id = score.id ?? crypto.randomUUID();
|
|
9319
9391
|
const now = /* @__PURE__ */ new Date();
|
|
9320
9392
|
await this.operations.insert({
|
|
9321
9393
|
tableName: _mastra_core_storage.TABLE_SCORERS,
|
|
@@ -10529,9 +10601,15 @@ var WorkflowsMySQL = class WorkflowsMySQL extends _mastra_core_storage.Workflows
|
|
|
10529
10601
|
await connection.rollback();
|
|
10530
10602
|
return;
|
|
10531
10603
|
}
|
|
10604
|
+
const existing = parseSnapshot(rows[0].snapshot);
|
|
10605
|
+
const { expectedStatus, ...state } = opts;
|
|
10606
|
+
if (!(0, _mastra_core_storage.matchesExpectedWorkflowStatus)(existing.status, expectedStatus)) {
|
|
10607
|
+
await connection.rollback();
|
|
10608
|
+
return;
|
|
10609
|
+
}
|
|
10532
10610
|
const updatedSnapshot = {
|
|
10533
|
-
...
|
|
10534
|
-
...
|
|
10611
|
+
...existing,
|
|
10612
|
+
...state
|
|
10535
10613
|
};
|
|
10536
10614
|
await connection.execute(`UPDATE ${formatTableName(_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT)} SET ${quoteIdentifier("snapshot", "column name")} = ?, ${quoteIdentifier("updatedAt", "column name")} = ? WHERE ${quoteIdentifier("workflow_name", "column name")} = ? AND ${quoteIdentifier("run_id", "column name")} = ?`, [
|
|
10537
10615
|
JSON.stringify(updatedSnapshot),
|