@mastra/mysql 0.3.2 → 0.3.3-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 +152 -0
- package/dist/index.cjs +228 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +229 -26
- package/dist/index.js.map +1 -1
- package/dist/storage/domains/datasets/index.d.ts +7 -7
- package/dist/storage/domains/datasets/index.d.ts.map +1 -1
- package/dist/storage/domains/experiments/index.d.ts +5 -1
- package/dist/storage/domains/experiments/index.d.ts.map +1 -1
- package/dist/storage/domains/scores/index.d.ts +13 -5
- package/dist/storage/domains/scores/index.d.ts.map +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,157 @@
|
|
|
1
1
|
# @mastra/mysql
|
|
2
2
|
|
|
3
|
+
## 0.3.3-alpha.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Added optional tenancy arguments to `getDataset`, `updateDataset`, and `deleteDataset`. ([#18750](https://github.com/mastra-ai/mastra/pull/18750))
|
|
8
|
+
|
|
9
|
+
You can now pass `organizationId` and `projectId` to scope dataset reads, updates, and deletes to a specific tenant. Reads and updates against a dataset in a different tenant throw `DATASET_NOT_FOUND` (surfaced as a 404 over HTTP). Deletes silently no-op on a tenancy mismatch — matching the existing "delete non-existent id is a no-op" semantics so cross-tenant existence is never leaked via error timing or status.
|
|
10
|
+
|
|
11
|
+
**Example**
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
// Before
|
|
15
|
+
await client.getDataset('abc123');
|
|
16
|
+
await client.deleteDataset('abc123');
|
|
17
|
+
await client.updateDataset({ id: 'abc123', name: 'renamed' });
|
|
18
|
+
|
|
19
|
+
// After — scope to a tenant
|
|
20
|
+
await client.getDataset('abc123', { organizationId: 'org_a', projectId: 'proj_1' });
|
|
21
|
+
await client.deleteDataset('abc123', { organizationId: 'org_a' });
|
|
22
|
+
await client.updateDataset({ id: 'abc123', name: 'renamed', organizationId: 'org_a' });
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
- Pushed remaining dataset read filters and pagination down to storage. ([#18710](https://github.com/mastra-ai/mastra/pull/18710))
|
|
26
|
+
|
|
27
|
+
`DatasetsManager.list({ filters })` now accepts `targetType`, `targetIds` (overlap/union semantics), and `name` (substring, case-insensitive) in addition to the existing tenancy and candidate filters. Filtering is pushed down to the storage layer so callers no longer have to post-filter results.
|
|
28
|
+
|
|
29
|
+
Storage adapters must also be upgraded to the versions listed below to honor the new filters. If a caller is on this version of `@mastra/core` but on an older storage adapter, the new `targetType`/`targetIds`/`name` filter keys are silently ignored by the adapter — no runtime error, but the filter has no effect and every dataset in the tenancy is returned.
|
|
30
|
+
|
|
31
|
+
`Dataset.listItems({ version, search, page, perPage })` now applies `search` and pagination at the storage layer when `version` is provided alongside any of those. Previously they were silently dropped whenever `version` was set. The return shape is unchanged: passing only `version` still returns a bare `DatasetItem[]` snapshot; passing `search`, `page`, or `perPage` (with or without `version`) returns the paginated `{ items, pagination }` shape. The bare-array branch is marked `@deprecated`; prefer passing `page` / `perPage` to always receive the paginated shape.
|
|
32
|
+
|
|
33
|
+
- Tenancy-scope experiments `getById` and `delete*` on `ExperimentsStorage`. ([#18770](https://github.com/mastra-ai/mastra/pull/18770))
|
|
34
|
+
|
|
35
|
+
`ExperimentsStorage.getExperimentById`, `getExperimentResultById`, `deleteExperiment`, and `deleteExperimentResults` used to key on the primary id alone, so any caller who knew the id could read or delete the row regardless of tenant. All four now accept an optional `filters: { organizationId?, projectId? }` argument that is enforced on every adapter (inmemory, libsql, pg, mysql, mongodb, spanner):
|
|
36
|
+
- On tenancy mismatch, `get*` returns `null` at the storage layer.
|
|
37
|
+
- On tenancy mismatch, `delete*` is a silent no-op.
|
|
38
|
+
- The tenancy predicate is folded into the destructive DML itself (scoped `WHERE` on the DELETE, an atomic gate + delete inside a transaction, or a scoped subquery for the results cascade). A concurrent tenant swap of the same id between a pre-check and the DELETE cannot let a scoped delete hit another tenant's row.
|
|
39
|
+
|
|
40
|
+
Both behaviors match how a missing id already responds, so existence does not leak through error timing or messages.
|
|
41
|
+
|
|
42
|
+
The same atomic-DML pattern is also applied to `DatasetsStorage.deleteDataset` across all 5 store adapters, closing a TOCTOU window between the pre-check and the parent DELETE that was introduced when tenancy filters were originally added.
|
|
43
|
+
|
|
44
|
+
`Dataset.getExperiment` and the shared experiment-ownership gate on `Dataset` now forward the dataset's tenancy scope to storage, so experiment reads and downstream mutations (list results, update result, delete experiment) reached through a dataset handle are automatically scoped to the owning tenant.
|
|
45
|
+
|
|
46
|
+
Legacy calls that omit `filters` are unchanged, so this is fully backwards-compatible.
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
// Before: any caller who knew the id could read/delete across tenants.
|
|
50
|
+
await store.experiments.getExperimentById({ id: experimentId });
|
|
51
|
+
await store.experiments.deleteExperiment({ id: experimentId });
|
|
52
|
+
|
|
53
|
+
// After: pass the caller's scope; wrong tenant gets null / silent no-op.
|
|
54
|
+
await store.experiments.getExperimentById({
|
|
55
|
+
id: experimentId,
|
|
56
|
+
filters: { organizationId, projectId },
|
|
57
|
+
});
|
|
58
|
+
await store.experiments.deleteExperiment({
|
|
59
|
+
id: experimentId,
|
|
60
|
+
filters: { organizationId, projectId },
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
- Filled a pre-existing CRUD gap so the new dataset filter API works end-to-end on MySQL. ([#18710](https://github.com/mastra-ai/mastra/pull/18710))
|
|
65
|
+
|
|
66
|
+
`createDataset`, `updateDataset`, and `mapDataset` now persist and hydrate `targetType`, `targetIds`, `scorerIds`, `tags`, and `requestContextSchema`. The columns were already declared by the shared schema but were never written or read, so `listDatasets({ filters: { targetType, targetIds, name } })` would have matched nothing on MySQL before this fix. `alterTable.ifNotExists` was widened so in-place upgrades pick up the columns for older databases.
|
|
67
|
+
|
|
68
|
+
Also fixed a `mapItem` row deserialization bug: when the stored input/groundTruth/metadata was a JSON string scalar, the mysql2 driver auto-parses the JSON column to a JS string and the previous `parseJSON` helper then tried to `JSON.parse` it again and silently returned `undefined`. It now falls back to the raw string when re-parsing fails, so versioned `listItems({ search })` results round-trip the original input.
|
|
69
|
+
|
|
70
|
+
- Fixed a cross-tenant data-access issue on datasets by scoping `DatasetsManager.get` and `DatasetsManager.delete` to tenancy filters. ([#18750](https://github.com/mastra-ai/mastra/pull/18750))
|
|
71
|
+
|
|
72
|
+
Previously `get({ id })` and `delete({ id })` looked up a dataset by its primary key alone. Any caller who knew a dataset id could read or delete it regardless of which `organizationId` / `projectId` it belonged to. This is now closed at the storage layer via a scoped SQL predicate (option (a) — no fetch-then-assert).
|
|
73
|
+
|
|
74
|
+
**What changed**
|
|
75
|
+
- `DatasetsManager.get` and `DatasetsManager.delete` accept optional `organizationId` and `projectId`.
|
|
76
|
+
- The tenancy is stashed on the returned `Dataset` handle and forwarded to every downstream storage call (`getDetails`, `update`, `addItem`, item batch ops, `startExperimentAsync`).
|
|
77
|
+
- The abstract storage contract (`getDatasetById`, `deleteDataset`) gained an optional `filters?: DatasetTenancyFilters` arg.
|
|
78
|
+
- Item-mutation inputs (`AddDatasetItemInput`, `UpdateDatasetItemInput`, `BatchInsertItemsInput`, `BatchDeleteItemsInput`) and `UpdateDatasetInput` accept optional `filters` for the internal existence check.
|
|
79
|
+
|
|
80
|
+
**Behavior**
|
|
81
|
+
- Omitting tenancy preserves the existing behavior (no predicate added) — fully backwards compatible.
|
|
82
|
+
- On tenancy mismatch, `get` throws NOT_FOUND (returns null at the storage layer) and `delete` is a silent no-op — matching how a missing id already behaves, so existence does not leak through error timing or messages.
|
|
83
|
+
|
|
84
|
+
**Example**
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
// Before
|
|
88
|
+
const ds = await mastra.datasets.get({ id });
|
|
89
|
+
await mastra.datasets.delete({ id });
|
|
90
|
+
|
|
91
|
+
// After — scope to a tenant
|
|
92
|
+
const ds = await mastra.datasets.get({ id, organizationId, projectId });
|
|
93
|
+
await mastra.datasets.delete({ id, organizationId, projectId });
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
- Add optional `batchId`, `datasetId`, and `datasetItemId` fields to persisted scores so saved baseline scores can be grouped as one scoring pass and joined back to the dataset items they came from. ([#18331](https://github.com/mastra-ai/mastra/pull/18331))
|
|
97
|
+
- `scoreTrace()` accepts top-level `batchId`, `datasetId`, and `datasetItemId` when persisting a score for a stored trace.
|
|
98
|
+
- `ScoreRowData` and score save payloads now include nullable `batchId`, `datasetId`, and `datasetItemId`.
|
|
99
|
+
- Built-in stores with explicit score schema or attribute mappings now persist these provenance fields on saved scores.
|
|
100
|
+
- D1, DSQL, MSSQL, and Upstash score stores now apply additive provenance migrations or deterministic score ordering for persisted score reads.
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
await scoreTrace({
|
|
104
|
+
storage,
|
|
105
|
+
scorer,
|
|
106
|
+
target: { traceId },
|
|
107
|
+
batchId: 'baseline-batch-1',
|
|
108
|
+
datasetId,
|
|
109
|
+
datasetItemId,
|
|
110
|
+
});
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
- Added optional `organizationId` and `projectId` fields to scores for multi-tenant isolation. Scores can now be saved with tenancy metadata and the `listScoresBy*` methods accept a `filters` option to scope results by organization and project. ([#18331](https://github.com/mastra-ai/mastra/pull/18331))
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
await storage.saveScore({ ...score, organizationId: 'org-a', projectId: 'proj-1' });
|
|
117
|
+
|
|
118
|
+
const result = await storage.listScoresByScorerId({
|
|
119
|
+
scorerId,
|
|
120
|
+
filters: { organizationId: 'org-a', projectId: 'proj-1' },
|
|
121
|
+
});
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
`projectId` identifies the project scope, separate from `resourceId` which continues to mean the agent memory resource.
|
|
125
|
+
|
|
126
|
+
- Scoped `getDatasetById` and `deleteDataset` to tenancy filters when the caller passes `organizationId` / `projectId`. ([#18750](https://github.com/mastra-ai/mastra/pull/18750))
|
|
127
|
+
|
|
128
|
+
The adapters now push the tenancy predicate into the SQL/query when the new optional `filters` argument is present. Legacy calls that omit tenancy are unchanged. On mismatch, `getDatasetById` returns `null` and `deleteDataset` is a silent no-op — the cascade delete (dataset items and versions) is gated by a scoped parent pre-check, so cross-tenant data is never touched.
|
|
129
|
+
|
|
130
|
+
- Added optional `organizationId` and `projectId` query parameters to the dataset routes. ([#18750](https://github.com/mastra-ai/mastra/pull/18750))
|
|
131
|
+
|
|
132
|
+
`GET /datasets/:datasetId`, `PATCH /datasets/:datasetId`, and `DELETE /datasets/:datasetId` now accept optional tenancy query parameters. When provided, they are forwarded to `mastra.datasets.get` / `.delete` and the operation returns 404 if the dataset does not belong to the requested tenant. Requests that omit the query parameters keep their existing behavior.
|
|
133
|
+
|
|
134
|
+
**Example**
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
GET /datasets/abc123?organizationId=org_a&projectId=proj_1
|
|
138
|
+
DELETE /datasets/abc123?organizationId=org_a
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
- Updated dependencies [[`9250acd`](https://github.com/mastra-ai/mastra/commit/9250acd1357f0f1f33d0dcca16f9655084c58eca), [`215f9b0`](https://github.com/mastra-ai/mastra/commit/215f9b0f3f3f6fc165edad360582dd4d3d7ea748), [`c64c2a8`](https://github.com/mastra-ai/mastra/commit/c64c2a8503a50252f9ca6b8e8c54cadee31b92a2), [`06e2680`](https://github.com/mastra-ai/mastra/commit/06e26806b51d2cbd858afdc66daa2b86ff3ba64a), [`1240f05`](https://github.com/mastra-ai/mastra/commit/1240f051c8e5371f1c014448bf37b1a1b9a05e47), [`215f9b0`](https://github.com/mastra-ai/mastra/commit/215f9b0f3f3f6fc165edad360582dd4d3d7ea748), [`24c10d3`](https://github.com/mastra-ai/mastra/commit/24c10d333e6649ac06075903aeeee13a933db3b3), [`24c10d3`](https://github.com/mastra-ai/mastra/commit/24c10d333e6649ac06075903aeeee13a933db3b3), [`24c10d3`](https://github.com/mastra-ai/mastra/commit/24c10d333e6649ac06075903aeeee13a933db3b3), [`24c10d3`](https://github.com/mastra-ai/mastra/commit/24c10d333e6649ac06075903aeeee13a933db3b3), [`215f9b0`](https://github.com/mastra-ai/mastra/commit/215f9b0f3f3f6fc165edad360582dd4d3d7ea748), [`215f9b0`](https://github.com/mastra-ai/mastra/commit/215f9b0f3f3f6fc165edad360582dd4d3d7ea748)]:
|
|
142
|
+
- @mastra/core@1.49.0-alpha.5
|
|
143
|
+
|
|
144
|
+
## 0.3.3-alpha.0
|
|
145
|
+
|
|
146
|
+
### Patch Changes
|
|
147
|
+
|
|
148
|
+
- Fixed `listExperiments` in the MySQL store ignoring `targetType`, `targetId`, `agentVersion`, and `status` filters. Queries now correctly narrow on these fields, matching the behavior of the other stores (Postgres, LibSQL, Spanner, in-memory). ([#18769](https://github.com/mastra-ai/mastra/pull/18769))
|
|
149
|
+
|
|
150
|
+
Also persisted `agentVersion` on experiment rows in the MySQL store. The column existed in the schema but `createExperiment` never wrote it and `getExperimentById`/`listExperiments` never returned it, so filtering by `agentVersion` would have matched nothing on rows created by this backend. New experiments now round-trip `agentVersion` end-to-end. Existing tables gain the column via the `init()` backfill.
|
|
151
|
+
|
|
152
|
+
- Updated dependencies [[`6a61846`](https://github.com/mastra-ai/mastra/commit/6a61846eeda29fb714549b70f1bee2bf6b141c44)]:
|
|
153
|
+
- @mastra/core@1.49.0-alpha.4
|
|
154
|
+
|
|
3
155
|
## 0.3.2
|
|
4
156
|
|
|
5
157
|
### Patch Changes
|
package/dist/index.cjs
CHANGED
|
@@ -2052,11 +2052,11 @@ function parseJSON(value) {
|
|
|
2052
2052
|
try {
|
|
2053
2053
|
return JSON.parse(value);
|
|
2054
2054
|
} catch {
|
|
2055
|
-
return
|
|
2055
|
+
return value;
|
|
2056
2056
|
}
|
|
2057
2057
|
}
|
|
2058
2058
|
if (typeof value === "object") return value;
|
|
2059
|
-
return
|
|
2059
|
+
return value;
|
|
2060
2060
|
}
|
|
2061
2061
|
function jsonArg(value) {
|
|
2062
2062
|
return value === void 0 || value === null ? null : JSON.stringify(value);
|
|
@@ -2145,7 +2145,17 @@ var DatasetsMySQL = class _DatasetsMySQL extends storage.DatasetsStorage {
|
|
|
2145
2145
|
await this.operations.alterTable({
|
|
2146
2146
|
tableName: storage.TABLE_DATASETS,
|
|
2147
2147
|
schema: storage.DATASETS_SCHEMA,
|
|
2148
|
-
ifNotExists: [
|
|
2148
|
+
ifNotExists: [
|
|
2149
|
+
"organizationId",
|
|
2150
|
+
"projectId",
|
|
2151
|
+
"candidateKey",
|
|
2152
|
+
"candidateId",
|
|
2153
|
+
"requestContextSchema",
|
|
2154
|
+
"tags",
|
|
2155
|
+
"targetType",
|
|
2156
|
+
"targetIds",
|
|
2157
|
+
"scorerIds"
|
|
2158
|
+
]
|
|
2149
2159
|
});
|
|
2150
2160
|
await this.operations.alterTable({
|
|
2151
2161
|
tableName: storage.TABLE_DATASET_ITEMS,
|
|
@@ -2160,6 +2170,19 @@ var DatasetsMySQL = class _DatasetsMySQL extends storage.DatasetsStorage {
|
|
|
2160
2170
|
await this.pool.execute(`DELETE FROM ${formatTableName(storage.TABLE_DATASET_ITEMS)}`);
|
|
2161
2171
|
await this.pool.execute(`DELETE FROM ${formatTableName(storage.TABLE_DATASETS)}`);
|
|
2162
2172
|
}
|
|
2173
|
+
async experimentTablesExist() {
|
|
2174
|
+
try {
|
|
2175
|
+
const [rows] = await this.pool.execute(
|
|
2176
|
+
`SELECT COUNT(*) AS c FROM information_schema.tables
|
|
2177
|
+
WHERE table_schema = DATABASE() AND table_name IN (?, ?)`,
|
|
2178
|
+
[storage.TABLE_EXPERIMENTS, storage.TABLE_EXPERIMENT_RESULTS]
|
|
2179
|
+
);
|
|
2180
|
+
const row = Array.isArray(rows) ? rows[0] : void 0;
|
|
2181
|
+
return Number(row?.c ?? 0) === 2;
|
|
2182
|
+
} catch {
|
|
2183
|
+
return false;
|
|
2184
|
+
}
|
|
2185
|
+
}
|
|
2163
2186
|
// --- Row transformers ---
|
|
2164
2187
|
mapDataset(row) {
|
|
2165
2188
|
return {
|
|
@@ -2169,6 +2192,11 @@ var DatasetsMySQL = class _DatasetsMySQL extends storage.DatasetsStorage {
|
|
|
2169
2192
|
metadata: parseJSON(row.metadata),
|
|
2170
2193
|
inputSchema: parseJSON(row.inputSchema),
|
|
2171
2194
|
groundTruthSchema: parseJSON(row.groundTruthSchema),
|
|
2195
|
+
requestContextSchema: parseJSON(row.requestContextSchema),
|
|
2196
|
+
tags: parseJSON(row.tags) ?? null,
|
|
2197
|
+
targetType: row.targetType ?? null,
|
|
2198
|
+
targetIds: parseJSON(row.targetIds) ?? null,
|
|
2199
|
+
scorerIds: parseJSON(row.scorerIds) ?? null,
|
|
2172
2200
|
version: row.version,
|
|
2173
2201
|
organizationId: row.organizationId ?? null,
|
|
2174
2202
|
projectId: row.projectId ?? null,
|
|
@@ -2230,6 +2258,10 @@ var DatasetsMySQL = class _DatasetsMySQL extends storage.DatasetsStorage {
|
|
|
2230
2258
|
metadata: jsonArg(input.metadata),
|
|
2231
2259
|
inputSchema: jsonArg(input.inputSchema),
|
|
2232
2260
|
groundTruthSchema: jsonArg(input.groundTruthSchema),
|
|
2261
|
+
requestContextSchema: jsonArg(input.requestContextSchema),
|
|
2262
|
+
targetType: input.targetType ?? null,
|
|
2263
|
+
targetIds: jsonArg(input.targetIds),
|
|
2264
|
+
scorerIds: jsonArg(input.scorerIds),
|
|
2233
2265
|
version: 0,
|
|
2234
2266
|
organizationId: input.organizationId ?? null,
|
|
2235
2267
|
projectId: input.projectId ?? null,
|
|
@@ -2246,6 +2278,10 @@ var DatasetsMySQL = class _DatasetsMySQL extends storage.DatasetsStorage {
|
|
|
2246
2278
|
metadata: input.metadata,
|
|
2247
2279
|
inputSchema: input.inputSchema ?? void 0,
|
|
2248
2280
|
groundTruthSchema: input.groundTruthSchema ?? void 0,
|
|
2281
|
+
requestContextSchema: input.requestContextSchema ?? void 0,
|
|
2282
|
+
targetType: input.targetType ?? null,
|
|
2283
|
+
targetIds: input.targetIds ?? null,
|
|
2284
|
+
scorerIds: input.scorerIds ?? null,
|
|
2249
2285
|
version: 0,
|
|
2250
2286
|
organizationId: input.organizationId ?? null,
|
|
2251
2287
|
projectId: input.projectId ?? null,
|
|
@@ -2265,11 +2301,18 @@ var DatasetsMySQL = class _DatasetsMySQL extends storage.DatasetsStorage {
|
|
|
2265
2301
|
);
|
|
2266
2302
|
}
|
|
2267
2303
|
}
|
|
2268
|
-
async getDatasetById({
|
|
2304
|
+
async getDatasetById({
|
|
2305
|
+
id,
|
|
2306
|
+
filters
|
|
2307
|
+
}) {
|
|
2269
2308
|
try {
|
|
2270
2309
|
const row = await this.operations.load({
|
|
2271
2310
|
tableName: storage.TABLE_DATASETS,
|
|
2272
|
-
keys: {
|
|
2311
|
+
keys: {
|
|
2312
|
+
id,
|
|
2313
|
+
organizationId: filters?.organizationId,
|
|
2314
|
+
projectId: filters?.projectId
|
|
2315
|
+
}
|
|
2273
2316
|
});
|
|
2274
2317
|
return row ? this.mapDataset(row) : null;
|
|
2275
2318
|
} catch (error$1) {
|
|
@@ -2285,7 +2328,7 @@ var DatasetsMySQL = class _DatasetsMySQL extends storage.DatasetsStorage {
|
|
|
2285
2328
|
}
|
|
2286
2329
|
async _doUpdateDataset(args) {
|
|
2287
2330
|
try {
|
|
2288
|
-
const existing = await this.getDatasetById({ id: args.id });
|
|
2331
|
+
const existing = await this.getDatasetById({ id: args.id, filters: args.filters });
|
|
2289
2332
|
if (!existing) {
|
|
2290
2333
|
throw new error.MastraError({
|
|
2291
2334
|
id: "MYSQL_UPDATE_DATASET_NOT_FOUND",
|
|
@@ -2302,6 +2345,14 @@ var DatasetsMySQL = class _DatasetsMySQL extends storage.DatasetsStorage {
|
|
|
2302
2345
|
data.inputSchema = args.inputSchema === null ? null : JSON.stringify(args.inputSchema);
|
|
2303
2346
|
if (args.groundTruthSchema !== void 0)
|
|
2304
2347
|
data.groundTruthSchema = args.groundTruthSchema === null ? null : JSON.stringify(args.groundTruthSchema);
|
|
2348
|
+
if (args.requestContextSchema !== void 0)
|
|
2349
|
+
data.requestContextSchema = args.requestContextSchema === null ? null : JSON.stringify(args.requestContextSchema);
|
|
2350
|
+
if (args.tags !== void 0) data.tags = args.tags === null ? null : JSON.stringify(args.tags);
|
|
2351
|
+
if (args.targetType !== void 0) data.targetType = args.targetType;
|
|
2352
|
+
if (args.targetIds !== void 0)
|
|
2353
|
+
data.targetIds = args.targetIds === null ? null : JSON.stringify(args.targetIds);
|
|
2354
|
+
if (args.scorerIds !== void 0)
|
|
2355
|
+
data.scorerIds = args.scorerIds === null ? null : JSON.stringify(args.scorerIds);
|
|
2305
2356
|
await this.operations.update({
|
|
2306
2357
|
tableName: storage.TABLE_DATASETS,
|
|
2307
2358
|
keys: { id: args.id },
|
|
@@ -2314,6 +2365,11 @@ var DatasetsMySQL = class _DatasetsMySQL extends storage.DatasetsStorage {
|
|
|
2314
2365
|
metadata: args.metadata ?? existing.metadata,
|
|
2315
2366
|
inputSchema: (args.inputSchema !== void 0 ? args.inputSchema : existing.inputSchema) ?? void 0,
|
|
2316
2367
|
groundTruthSchema: (args.groundTruthSchema !== void 0 ? args.groundTruthSchema : existing.groundTruthSchema) ?? void 0,
|
|
2368
|
+
requestContextSchema: (args.requestContextSchema !== void 0 ? args.requestContextSchema : existing.requestContextSchema) ?? void 0,
|
|
2369
|
+
tags: (args.tags !== void 0 ? args.tags : existing.tags) ?? null,
|
|
2370
|
+
targetType: (args.targetType !== void 0 ? args.targetType : existing.targetType) ?? null,
|
|
2371
|
+
targetIds: (args.targetIds !== void 0 ? args.targetIds : existing.targetIds) ?? null,
|
|
2372
|
+
scorerIds: (args.scorerIds !== void 0 ? args.scorerIds : existing.scorerIds) ?? null,
|
|
2317
2373
|
updatedAt: data.updatedAt
|
|
2318
2374
|
};
|
|
2319
2375
|
} catch (error$1) {
|
|
@@ -2328,23 +2384,39 @@ var DatasetsMySQL = class _DatasetsMySQL extends storage.DatasetsStorage {
|
|
|
2328
2384
|
);
|
|
2329
2385
|
}
|
|
2330
2386
|
}
|
|
2331
|
-
async deleteDataset({ id }) {
|
|
2387
|
+
async deleteDataset({ id, filters }) {
|
|
2388
|
+
const filterCols = [];
|
|
2389
|
+
const filterVals = [];
|
|
2390
|
+
if (filters?.organizationId !== void 0) {
|
|
2391
|
+
filterCols.push(`${quoteIdentifier("organizationId", "column name")} = ?`);
|
|
2392
|
+
filterVals.push(filters.organizationId);
|
|
2393
|
+
}
|
|
2394
|
+
if (filters?.projectId !== void 0) {
|
|
2395
|
+
filterCols.push(`${quoteIdentifier("projectId", "column name")} = ?`);
|
|
2396
|
+
filterVals.push(filters.projectId);
|
|
2397
|
+
}
|
|
2398
|
+
const scopedWhere = ["id = ?", ...filterCols].join(" AND ");
|
|
2399
|
+
const experimentTablesExist = await this.experimentTablesExist();
|
|
2332
2400
|
const connection = await this.pool.getConnection();
|
|
2333
2401
|
try {
|
|
2334
2402
|
await connection.beginTransaction();
|
|
2335
|
-
|
|
2403
|
+
const [rows] = await connection.execute(
|
|
2404
|
+
`SELECT id FROM ${formatTableName(storage.TABLE_DATASETS)} WHERE ${scopedWhere} FOR UPDATE`,
|
|
2405
|
+
[id, ...filterVals]
|
|
2406
|
+
);
|
|
2407
|
+
if (!Array.isArray(rows) || rows.length === 0) {
|
|
2408
|
+
await connection.commit();
|
|
2409
|
+
return;
|
|
2410
|
+
}
|
|
2411
|
+
if (experimentTablesExist) {
|
|
2336
2412
|
await connection.execute(
|
|
2337
2413
|
`DELETE FROM ${formatTableName(storage.TABLE_EXPERIMENT_RESULTS)} WHERE ${quoteIdentifier("experimentId", "column name")} IN (SELECT id FROM ${formatTableName(storage.TABLE_EXPERIMENTS)} WHERE ${quoteIdentifier("datasetId", "column name")} = ?)`,
|
|
2338
2414
|
[id]
|
|
2339
2415
|
);
|
|
2340
|
-
} catch {
|
|
2341
|
-
}
|
|
2342
|
-
try {
|
|
2343
2416
|
await connection.execute(
|
|
2344
2417
|
`UPDATE ${formatTableName(storage.TABLE_EXPERIMENTS)} SET ${quoteIdentifier("datasetId", "column name")} = NULL, ${quoteIdentifier("datasetVersion", "column name")} = NULL WHERE ${quoteIdentifier("datasetId", "column name")} = ?`,
|
|
2345
2418
|
[id]
|
|
2346
2419
|
);
|
|
2347
|
-
} catch {
|
|
2348
2420
|
}
|
|
2349
2421
|
await connection.execute(
|
|
2350
2422
|
`DELETE FROM ${formatTableName(storage.TABLE_DATASET_VERSIONS)} WHERE ${quoteIdentifier("datasetId", "column name")} = ?`,
|
|
@@ -2354,7 +2426,10 @@ var DatasetsMySQL = class _DatasetsMySQL extends storage.DatasetsStorage {
|
|
|
2354
2426
|
`DELETE FROM ${formatTableName(storage.TABLE_DATASET_ITEMS)} WHERE ${quoteIdentifier("datasetId", "column name")} = ?`,
|
|
2355
2427
|
[id]
|
|
2356
2428
|
);
|
|
2357
|
-
await connection.execute(`DELETE FROM ${formatTableName(storage.TABLE_DATASETS)} WHERE
|
|
2429
|
+
await connection.execute(`DELETE FROM ${formatTableName(storage.TABLE_DATASETS)} WHERE ${scopedWhere}`, [
|
|
2430
|
+
id,
|
|
2431
|
+
...filterVals
|
|
2432
|
+
]);
|
|
2358
2433
|
await connection.commit();
|
|
2359
2434
|
} catch (error$1) {
|
|
2360
2435
|
await connection.rollback();
|
|
@@ -2391,6 +2466,19 @@ var DatasetsMySQL = class _DatasetsMySQL extends storage.DatasetsStorage {
|
|
|
2391
2466
|
filterParts.push(`${quoteIdentifier("candidateId", "column name")} = ?`);
|
|
2392
2467
|
filterArgs.push(args.filters.candidateId);
|
|
2393
2468
|
}
|
|
2469
|
+
if (args.filters?.targetType !== void 0) {
|
|
2470
|
+
filterParts.push(`${quoteIdentifier("targetType", "column name")} = ?`);
|
|
2471
|
+
filterArgs.push(args.filters.targetType);
|
|
2472
|
+
}
|
|
2473
|
+
if (args.filters?.targetIds !== void 0 && args.filters.targetIds.length > 0) {
|
|
2474
|
+
const placeholders = args.filters.targetIds.map(() => "?").join(",");
|
|
2475
|
+
filterParts.push(`JSON_OVERLAPS(${quoteIdentifier("targetIds", "column name")}, JSON_ARRAY(${placeholders}))`);
|
|
2476
|
+
filterArgs.push(...args.filters.targetIds);
|
|
2477
|
+
}
|
|
2478
|
+
if (args.filters?.name !== void 0 && args.filters.name.length > 0) {
|
|
2479
|
+
filterParts.push(`LOWER(${quoteIdentifier("name", "column name")}) LIKE LOWER(?)`);
|
|
2480
|
+
filterArgs.push(`%${args.filters.name}%`);
|
|
2481
|
+
}
|
|
2394
2482
|
const whereClause = {
|
|
2395
2483
|
sql: filterParts.length > 0 ? `WHERE ${filterParts.join(" AND ")}` : "",
|
|
2396
2484
|
args: filterArgs
|
|
@@ -3116,7 +3204,7 @@ var ExperimentsMySQL = class _ExperimentsMySQL extends storage.ExperimentsStorag
|
|
|
3116
3204
|
await this.operations.alterTable({
|
|
3117
3205
|
tableName: storage.TABLE_EXPERIMENTS,
|
|
3118
3206
|
schema: storage.EXPERIMENTS_SCHEMA,
|
|
3119
|
-
ifNotExists: ["organizationId", "projectId"]
|
|
3207
|
+
ifNotExists: ["agentVersion", "organizationId", "projectId"]
|
|
3120
3208
|
});
|
|
3121
3209
|
await this.operations.alterTable({
|
|
3122
3210
|
tableName: storage.TABLE_EXPERIMENT_RESULTS,
|
|
@@ -3135,6 +3223,7 @@ var ExperimentsMySQL = class _ExperimentsMySQL extends storage.ExperimentsStorag
|
|
|
3135
3223
|
id: row.id,
|
|
3136
3224
|
datasetId: row.datasetId ?? null,
|
|
3137
3225
|
datasetVersion: row.datasetVersion ?? null,
|
|
3226
|
+
agentVersion: row.agentVersion ?? null,
|
|
3138
3227
|
organizationId: row.organizationId ?? null,
|
|
3139
3228
|
projectId: row.projectId ?? null,
|
|
3140
3229
|
targetType: row.targetType,
|
|
@@ -3184,6 +3273,7 @@ var ExperimentsMySQL = class _ExperimentsMySQL extends storage.ExperimentsStorag
|
|
|
3184
3273
|
id,
|
|
3185
3274
|
datasetId: input.datasetId ?? null,
|
|
3186
3275
|
datasetVersion: input.datasetVersion ?? null,
|
|
3276
|
+
agentVersion: input.agentVersion ?? null,
|
|
3187
3277
|
organizationId: input.organizationId ?? null,
|
|
3188
3278
|
projectId: input.projectId ?? null,
|
|
3189
3279
|
targetType: input.targetType,
|
|
@@ -3206,6 +3296,7 @@ var ExperimentsMySQL = class _ExperimentsMySQL extends storage.ExperimentsStorag
|
|
|
3206
3296
|
id,
|
|
3207
3297
|
datasetId: input.datasetId,
|
|
3208
3298
|
datasetVersion: input.datasetVersion,
|
|
3299
|
+
agentVersion: input.agentVersion ?? null,
|
|
3209
3300
|
organizationId: input.organizationId ?? null,
|
|
3210
3301
|
projectId: input.projectId ?? null,
|
|
3211
3302
|
targetType: input.targetType,
|
|
@@ -3279,7 +3370,11 @@ var ExperimentsMySQL = class _ExperimentsMySQL extends storage.ExperimentsStorag
|
|
|
3279
3370
|
try {
|
|
3280
3371
|
const row = await this.operations.load({
|
|
3281
3372
|
tableName: storage.TABLE_EXPERIMENTS,
|
|
3282
|
-
keys: {
|
|
3373
|
+
keys: {
|
|
3374
|
+
id: args.id,
|
|
3375
|
+
organizationId: args.filters?.organizationId,
|
|
3376
|
+
projectId: args.filters?.projectId
|
|
3377
|
+
}
|
|
3283
3378
|
});
|
|
3284
3379
|
return row ? this.mapExperiment(row) : null;
|
|
3285
3380
|
} catch (error$1) {
|
|
@@ -3302,6 +3397,22 @@ var ExperimentsMySQL = class _ExperimentsMySQL extends storage.ExperimentsStorag
|
|
|
3302
3397
|
conditions.push(`${quoteIdentifier("datasetId", "column name")} = ?`);
|
|
3303
3398
|
params.push(args.datasetId);
|
|
3304
3399
|
}
|
|
3400
|
+
if (args.targetType) {
|
|
3401
|
+
conditions.push(`${quoteIdentifier("targetType", "column name")} = ?`);
|
|
3402
|
+
params.push(args.targetType);
|
|
3403
|
+
}
|
|
3404
|
+
if (args.targetId) {
|
|
3405
|
+
conditions.push(`${quoteIdentifier("targetId", "column name")} = ?`);
|
|
3406
|
+
params.push(args.targetId);
|
|
3407
|
+
}
|
|
3408
|
+
if (args.agentVersion) {
|
|
3409
|
+
conditions.push(`${quoteIdentifier("agentVersion", "column name")} = ?`);
|
|
3410
|
+
params.push(args.agentVersion);
|
|
3411
|
+
}
|
|
3412
|
+
if (args.status) {
|
|
3413
|
+
conditions.push(`${quoteIdentifier("status", "column name")} = ?`);
|
|
3414
|
+
params.push(args.status);
|
|
3415
|
+
}
|
|
3305
3416
|
if (args.filters) {
|
|
3306
3417
|
const { organizationId, projectId } = args.filters;
|
|
3307
3418
|
if (organizationId !== void 0) {
|
|
@@ -3356,9 +3467,28 @@ var ExperimentsMySQL = class _ExperimentsMySQL extends storage.ExperimentsStorag
|
|
|
3356
3467
|
}
|
|
3357
3468
|
async deleteExperiment(args) {
|
|
3358
3469
|
try {
|
|
3470
|
+
const tenancyConditions = [];
|
|
3471
|
+
const tenancyParams = [];
|
|
3472
|
+
if (args.filters?.organizationId !== void 0) {
|
|
3473
|
+
tenancyConditions.push(`${quoteIdentifier("organizationId", "column name")} = ?`);
|
|
3474
|
+
tenancyParams.push(args.filters.organizationId);
|
|
3475
|
+
}
|
|
3476
|
+
if (args.filters?.projectId !== void 0) {
|
|
3477
|
+
tenancyConditions.push(`${quoteIdentifier("projectId", "column name")} = ?`);
|
|
3478
|
+
tenancyParams.push(args.filters.projectId);
|
|
3479
|
+
}
|
|
3480
|
+
const gateWhere = ["id = ?", ...tenancyConditions].join(" AND ");
|
|
3359
3481
|
const connection = await this.pool.getConnection();
|
|
3360
3482
|
try {
|
|
3361
3483
|
await connection.beginTransaction();
|
|
3484
|
+
const [gateRows] = await connection.execute(
|
|
3485
|
+
`SELECT id FROM ${formatTableName(storage.TABLE_EXPERIMENTS)} WHERE ${gateWhere} FOR UPDATE`,
|
|
3486
|
+
[args.id, ...tenancyParams]
|
|
3487
|
+
);
|
|
3488
|
+
if (!Array.isArray(gateRows) || gateRows.length === 0) {
|
|
3489
|
+
await connection.commit();
|
|
3490
|
+
return;
|
|
3491
|
+
}
|
|
3362
3492
|
await connection.execute(
|
|
3363
3493
|
`DELETE FROM ${formatTableName(storage.TABLE_EXPERIMENT_RESULTS)} WHERE ${quoteIdentifier("experimentId", "column name")} = ?`,
|
|
3364
3494
|
[args.id]
|
|
@@ -3465,7 +3595,11 @@ var ExperimentsMySQL = class _ExperimentsMySQL extends storage.ExperimentsStorag
|
|
|
3465
3595
|
try {
|
|
3466
3596
|
const row = await this.operations.load({
|
|
3467
3597
|
tableName: storage.TABLE_EXPERIMENT_RESULTS,
|
|
3468
|
-
keys: {
|
|
3598
|
+
keys: {
|
|
3599
|
+
id: args.id,
|
|
3600
|
+
organizationId: args.filters?.organizationId,
|
|
3601
|
+
projectId: args.filters?.projectId
|
|
3602
|
+
}
|
|
3469
3603
|
});
|
|
3470
3604
|
return row ? this.mapExperimentResult(row) : null;
|
|
3471
3605
|
} catch (error$1) {
|
|
@@ -3619,6 +3753,24 @@ var ExperimentsMySQL = class _ExperimentsMySQL extends storage.ExperimentsStorag
|
|
|
3619
3753
|
}
|
|
3620
3754
|
async deleteExperimentResults(args) {
|
|
3621
3755
|
try {
|
|
3756
|
+
if (args.filters?.organizationId !== void 0 || args.filters?.projectId !== void 0) {
|
|
3757
|
+
const tenancyConditions = [];
|
|
3758
|
+
const tenancyParams = [];
|
|
3759
|
+
if (args.filters?.organizationId !== void 0) {
|
|
3760
|
+
tenancyConditions.push(`${quoteIdentifier("organizationId", "column name")} = ?`);
|
|
3761
|
+
tenancyParams.push(args.filters.organizationId);
|
|
3762
|
+
}
|
|
3763
|
+
if (args.filters?.projectId !== void 0) {
|
|
3764
|
+
tenancyConditions.push(`${quoteIdentifier("projectId", "column name")} = ?`);
|
|
3765
|
+
tenancyParams.push(args.filters.projectId);
|
|
3766
|
+
}
|
|
3767
|
+
const parentWhere = ["id = ?", ...tenancyConditions].join(" AND ");
|
|
3768
|
+
await this.pool.execute(
|
|
3769
|
+
`DELETE FROM ${formatTableName(storage.TABLE_EXPERIMENT_RESULTS)} WHERE ${quoteIdentifier("experimentId", "column name")} IN (SELECT id FROM ${formatTableName(storage.TABLE_EXPERIMENTS)} WHERE ${parentWhere})`,
|
|
3770
|
+
[args.experimentId, ...tenancyParams]
|
|
3771
|
+
);
|
|
3772
|
+
return;
|
|
3773
|
+
}
|
|
3622
3774
|
await this.pool.execute(
|
|
3623
3775
|
`DELETE FROM ${formatTableName(storage.TABLE_EXPERIMENT_RESULTS)} WHERE ${quoteIdentifier("experimentId", "column name")} = ?`,
|
|
3624
3776
|
[args.experimentId]
|
|
@@ -8768,7 +8920,7 @@ var ScoresMySQL = class _ScoresMySQL extends storage.ScoresStorage {
|
|
|
8768
8920
|
await this.operations.alterTable({
|
|
8769
8921
|
tableName: storage.TABLE_SCORERS,
|
|
8770
8922
|
schema: storage.SCORERS_SCHEMA,
|
|
8771
|
-
ifNotExists: ["spanId", "requestContext"]
|
|
8923
|
+
ifNotExists: ["spanId", "requestContext", "organizationId", "projectId", "batchId", "datasetId", "datasetItemId"]
|
|
8772
8924
|
});
|
|
8773
8925
|
await this.createDefaultIndexes();
|
|
8774
8926
|
await this.createCustomIndexes();
|
|
@@ -8843,6 +8995,11 @@ var ScoresMySQL = class _ScoresMySQL extends storage.ScoresStorage {
|
|
|
8843
8995
|
entityId: row.entityId ?? void 0,
|
|
8844
8996
|
source: row.source ?? void 0,
|
|
8845
8997
|
resourceId: row.resourceId ?? void 0,
|
|
8998
|
+
organizationId: row.organizationId ?? void 0,
|
|
8999
|
+
projectId: row.projectId ?? void 0,
|
|
9000
|
+
batchId: row.batchId ?? void 0,
|
|
9001
|
+
datasetId: row.datasetId ?? void 0,
|
|
9002
|
+
datasetItemId: row.datasetItemId ?? void 0,
|
|
8846
9003
|
threadId: row.threadId ?? void 0,
|
|
8847
9004
|
createdAt: parseDateTime(row.createdAt) ?? /* @__PURE__ */ new Date(),
|
|
8848
9005
|
updatedAt: parseDateTime(row.updatedAt) ?? /* @__PURE__ */ new Date()
|
|
@@ -8878,6 +9035,11 @@ var ScoresMySQL = class _ScoresMySQL extends storage.ScoresStorage {
|
|
|
8878
9035
|
entity: toJson(score.entity),
|
|
8879
9036
|
entityId: score.entityId ?? null,
|
|
8880
9037
|
resourceId: score.resourceId ?? null,
|
|
9038
|
+
organizationId: score.organizationId ?? null,
|
|
9039
|
+
projectId: score.projectId ?? null,
|
|
9040
|
+
batchId: score.batchId ?? null,
|
|
9041
|
+
datasetId: score.datasetId ?? null,
|
|
9042
|
+
datasetItemId: score.datasetItemId ?? null,
|
|
8881
9043
|
threadId: score.threadId ?? null,
|
|
8882
9044
|
source: score.source ?? null,
|
|
8883
9045
|
createdAt,
|
|
@@ -8981,33 +9143,74 @@ var ScoresMySQL = class _ScoresMySQL extends storage.ScoresStorage {
|
|
|
8981
9143
|
pagination,
|
|
8982
9144
|
entityId,
|
|
8983
9145
|
entityType,
|
|
8984
|
-
source
|
|
9146
|
+
source,
|
|
9147
|
+
filters
|
|
8985
9148
|
}) {
|
|
8986
|
-
return this.fetchScores(
|
|
9149
|
+
return this.fetchScores(
|
|
9150
|
+
this.buildWhereClause({
|
|
9151
|
+
scorerId,
|
|
9152
|
+
entityId,
|
|
9153
|
+
entityType,
|
|
9154
|
+
source,
|
|
9155
|
+
organizationId: filters?.organizationId,
|
|
9156
|
+
projectId: filters?.projectId
|
|
9157
|
+
}),
|
|
9158
|
+
pagination
|
|
9159
|
+
);
|
|
8987
9160
|
}
|
|
8988
9161
|
async listScoresByRunId({
|
|
8989
9162
|
runId,
|
|
8990
9163
|
pagination,
|
|
8991
9164
|
entityId,
|
|
8992
9165
|
entityType,
|
|
8993
|
-
source
|
|
9166
|
+
source,
|
|
9167
|
+
filters
|
|
8994
9168
|
}) {
|
|
8995
|
-
return this.fetchScores(
|
|
9169
|
+
return this.fetchScores(
|
|
9170
|
+
this.buildWhereClause({
|
|
9171
|
+
runId,
|
|
9172
|
+
entityId,
|
|
9173
|
+
entityType,
|
|
9174
|
+
source,
|
|
9175
|
+
organizationId: filters?.organizationId,
|
|
9176
|
+
projectId: filters?.projectId
|
|
9177
|
+
}),
|
|
9178
|
+
pagination
|
|
9179
|
+
);
|
|
8996
9180
|
}
|
|
8997
9181
|
async listScoresBySpan({
|
|
8998
9182
|
traceId,
|
|
8999
9183
|
spanId,
|
|
9000
|
-
pagination
|
|
9184
|
+
pagination,
|
|
9185
|
+
filters
|
|
9001
9186
|
}) {
|
|
9002
|
-
return this.fetchScores(
|
|
9187
|
+
return this.fetchScores(
|
|
9188
|
+
this.buildWhereClause({
|
|
9189
|
+
traceId,
|
|
9190
|
+
spanId,
|
|
9191
|
+
organizationId: filters?.organizationId,
|
|
9192
|
+
projectId: filters?.projectId
|
|
9193
|
+
}),
|
|
9194
|
+
pagination
|
|
9195
|
+
);
|
|
9003
9196
|
}
|
|
9004
9197
|
async listScoresByEntityId({
|
|
9005
9198
|
entityId,
|
|
9006
9199
|
pagination,
|
|
9007
9200
|
entityType,
|
|
9008
|
-
source
|
|
9201
|
+
source,
|
|
9202
|
+
filters
|
|
9009
9203
|
}) {
|
|
9010
|
-
return this.fetchScores(
|
|
9204
|
+
return this.fetchScores(
|
|
9205
|
+
this.buildWhereClause({
|
|
9206
|
+
entityId,
|
|
9207
|
+
entityType,
|
|
9208
|
+
source,
|
|
9209
|
+
organizationId: filters?.organizationId,
|
|
9210
|
+
projectId: filters?.projectId
|
|
9211
|
+
}),
|
|
9212
|
+
pagination
|
|
9213
|
+
);
|
|
9011
9214
|
}
|
|
9012
9215
|
};
|
|
9013
9216
|
var SNAPSHOT_FIELDS = [
|