@mastra/libsql 1.15.0-alpha.0 → 1.15.0-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 +139 -0
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/index.cjs +148 -51
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +149 -52
- 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 +9 -5
- package/dist/storage/domains/scores/index.d.ts.map +1 -1
- package/dist/storage/domains/utils.d.ts +34 -0
- package/dist/storage/domains/utils.d.ts.map +1 -0
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,144 @@
|
|
|
1
1
|
# @mastra/libsql
|
|
2
2
|
|
|
3
|
+
## 1.15.0-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
|
+
- Fixed a double-encoding bug where `createDataset` stored `targetIds` and `scorerIds` as JSON-encoded strings instead of arrays. This caused the new `listDatasets({ filters: { targetIds } })` overlap query to never match. ([#18710](https://github.com/mastra-ai/mastra/pull/18710))
|
|
34
|
+
|
|
35
|
+
Existing rows written before this fix are still double-encoded and will not be matched by the new `targetIds` filter. They self-heal on the next `updateDataset` call. Deployments with a long tail of pre-existing datasets should run a one-time backfill (re-encoding `targetIds` and `scorerIds` on affected rows) rather than rely on incidental writes; this can be tracked as a follow-up if needed.
|
|
36
|
+
|
|
37
|
+
- Tenancy-scope experiments `getById` and `delete*` on `ExperimentsStorage`. ([#18770](https://github.com/mastra-ai/mastra/pull/18770))
|
|
38
|
+
|
|
39
|
+
`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):
|
|
40
|
+
- On tenancy mismatch, `get*` returns `null` at the storage layer.
|
|
41
|
+
- On tenancy mismatch, `delete*` is a silent no-op.
|
|
42
|
+
- 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.
|
|
43
|
+
|
|
44
|
+
Both behaviors match how a missing id already responds, so existence does not leak through error timing or messages.
|
|
45
|
+
|
|
46
|
+
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.
|
|
47
|
+
|
|
48
|
+
`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.
|
|
49
|
+
|
|
50
|
+
Legacy calls that omit `filters` are unchanged, so this is fully backwards-compatible.
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
// Before: any caller who knew the id could read/delete across tenants.
|
|
54
|
+
await store.experiments.getExperimentById({ id: experimentId });
|
|
55
|
+
await store.experiments.deleteExperiment({ id: experimentId });
|
|
56
|
+
|
|
57
|
+
// After: pass the caller's scope; wrong tenant gets null / silent no-op.
|
|
58
|
+
await store.experiments.getExperimentById({
|
|
59
|
+
id: experimentId,
|
|
60
|
+
filters: { organizationId, projectId },
|
|
61
|
+
});
|
|
62
|
+
await store.experiments.deleteExperiment({
|
|
63
|
+
id: experimentId,
|
|
64
|
+
filters: { organizationId, projectId },
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
- 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))
|
|
69
|
+
|
|
70
|
+
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).
|
|
71
|
+
|
|
72
|
+
**What changed**
|
|
73
|
+
- `DatasetsManager.get` and `DatasetsManager.delete` accept optional `organizationId` and `projectId`.
|
|
74
|
+
- The tenancy is stashed on the returned `Dataset` handle and forwarded to every downstream storage call (`getDetails`, `update`, `addItem`, item batch ops, `startExperimentAsync`).
|
|
75
|
+
- The abstract storage contract (`getDatasetById`, `deleteDataset`) gained an optional `filters?: DatasetTenancyFilters` arg.
|
|
76
|
+
- Item-mutation inputs (`AddDatasetItemInput`, `UpdateDatasetItemInput`, `BatchInsertItemsInput`, `BatchDeleteItemsInput`) and `UpdateDatasetInput` accept optional `filters` for the internal existence check.
|
|
77
|
+
|
|
78
|
+
**Behavior**
|
|
79
|
+
- Omitting tenancy preserves the existing behavior (no predicate added) — fully backwards compatible.
|
|
80
|
+
- 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.
|
|
81
|
+
|
|
82
|
+
**Example**
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
// Before
|
|
86
|
+
const ds = await mastra.datasets.get({ id });
|
|
87
|
+
await mastra.datasets.delete({ id });
|
|
88
|
+
|
|
89
|
+
// After — scope to a tenant
|
|
90
|
+
const ds = await mastra.datasets.get({ id, organizationId, projectId });
|
|
91
|
+
await mastra.datasets.delete({ id, organizationId, projectId });
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
- 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))
|
|
95
|
+
- `scoreTrace()` accepts top-level `batchId`, `datasetId`, and `datasetItemId` when persisting a score for a stored trace.
|
|
96
|
+
- `ScoreRowData` and score save payloads now include nullable `batchId`, `datasetId`, and `datasetItemId`.
|
|
97
|
+
- Built-in stores with explicit score schema or attribute mappings now persist these provenance fields on saved scores.
|
|
98
|
+
- D1, DSQL, MSSQL, and Upstash score stores now apply additive provenance migrations or deterministic score ordering for persisted score reads.
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
await scoreTrace({
|
|
102
|
+
storage,
|
|
103
|
+
scorer,
|
|
104
|
+
target: { traceId },
|
|
105
|
+
batchId: 'baseline-batch-1',
|
|
106
|
+
datasetId,
|
|
107
|
+
datasetItemId,
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
- 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))
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
await storage.saveScore({ ...score, organizationId: 'org-a', projectId: 'proj-1' });
|
|
115
|
+
|
|
116
|
+
const result = await storage.listScoresByScorerId({
|
|
117
|
+
scorerId,
|
|
118
|
+
filters: { organizationId: 'org-a', projectId: 'proj-1' },
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
`projectId` identifies the project scope, separate from `resourceId` which continues to mean the agent memory resource.
|
|
123
|
+
|
|
124
|
+
- Scoped `getDatasetById` and `deleteDataset` to tenancy filters when the caller passes `organizationId` / `projectId`. ([#18750](https://github.com/mastra-ai/mastra/pull/18750))
|
|
125
|
+
|
|
126
|
+
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.
|
|
127
|
+
|
|
128
|
+
- Added optional `organizationId` and `projectId` query parameters to the dataset routes. ([#18750](https://github.com/mastra-ai/mastra/pull/18750))
|
|
129
|
+
|
|
130
|
+
`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.
|
|
131
|
+
|
|
132
|
+
**Example**
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
GET /datasets/abc123?organizationId=org_a&projectId=proj_1
|
|
136
|
+
DELETE /datasets/abc123?organizationId=org_a
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
- 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)]:
|
|
140
|
+
- @mastra/core@1.49.0-alpha.5
|
|
141
|
+
|
|
3
142
|
## 1.15.0-alpha.0
|
|
4
143
|
|
|
5
144
|
### Minor Changes
|
package/dist/docs/SKILL.md
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -3826,6 +3826,30 @@ var ChannelsLibSQL = class extends storage.ChannelsStorage {
|
|
|
3826
3826
|
};
|
|
3827
3827
|
}
|
|
3828
3828
|
};
|
|
3829
|
+
|
|
3830
|
+
// src/storage/domains/utils.ts
|
|
3831
|
+
function tenancyWhere(filters) {
|
|
3832
|
+
const conditions = [];
|
|
3833
|
+
const params = [];
|
|
3834
|
+
if (filters?.organizationId !== void 0) {
|
|
3835
|
+
conditions.push("organizationId = ?");
|
|
3836
|
+
params.push(filters.organizationId);
|
|
3837
|
+
}
|
|
3838
|
+
if (filters?.projectId !== void 0) {
|
|
3839
|
+
conditions.push("projectId = ?");
|
|
3840
|
+
params.push(filters.projectId);
|
|
3841
|
+
}
|
|
3842
|
+
return { conditions, params };
|
|
3843
|
+
}
|
|
3844
|
+
function buildScopedWhere(idColumn, idValue, filters) {
|
|
3845
|
+
const { conditions, params } = tenancyWhere(filters);
|
|
3846
|
+
return {
|
|
3847
|
+
sql: [`${idColumn} = ?`, ...conditions].join(" AND "),
|
|
3848
|
+
args: [idValue, ...params]
|
|
3849
|
+
};
|
|
3850
|
+
}
|
|
3851
|
+
|
|
3852
|
+
// src/storage/domains/datasets/index.ts
|
|
3829
3853
|
function jsonbArg(value) {
|
|
3830
3854
|
return value === void 0 || value === null ? null : JSON.stringify(value);
|
|
3831
3855
|
}
|
|
@@ -3906,6 +3930,18 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
|
|
|
3906
3930
|
await this.#db.deleteData({ tableName: storage.TABLE_DATASET_ITEMS });
|
|
3907
3931
|
await this.#db.deleteData({ tableName: storage.TABLE_DATASETS });
|
|
3908
3932
|
}
|
|
3933
|
+
async experimentTablesExist() {
|
|
3934
|
+
try {
|
|
3935
|
+
const result = await this.#client.execute({
|
|
3936
|
+
sql: `SELECT COUNT(*) AS c FROM sqlite_master WHERE type = 'table' AND name IN (?, ?)`,
|
|
3937
|
+
args: [storage.TABLE_EXPERIMENTS, storage.TABLE_EXPERIMENT_RESULTS]
|
|
3938
|
+
});
|
|
3939
|
+
const row = result.rows?.[0];
|
|
3940
|
+
return Number(row?.c ?? 0) === 2;
|
|
3941
|
+
} catch {
|
|
3942
|
+
return false;
|
|
3943
|
+
}
|
|
3944
|
+
}
|
|
3909
3945
|
// --- Row transformers ---
|
|
3910
3946
|
transformDatasetRow(row) {
|
|
3911
3947
|
return {
|
|
@@ -3992,8 +4028,8 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
|
|
|
3992
4028
|
groundTruthSchema: input.groundTruthSchema ?? null,
|
|
3993
4029
|
requestContextSchema: input.requestContextSchema ?? null,
|
|
3994
4030
|
targetType: input.targetType ?? null,
|
|
3995
|
-
targetIds: input.targetIds
|
|
3996
|
-
scorerIds: input.scorerIds
|
|
4031
|
+
targetIds: input.targetIds ?? null,
|
|
4032
|
+
scorerIds: input.scorerIds ?? null,
|
|
3997
4033
|
version: 0,
|
|
3998
4034
|
organizationId: input.organizationId ?? null,
|
|
3999
4035
|
projectId: input.projectId ?? null,
|
|
@@ -4033,11 +4069,16 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
|
|
|
4033
4069
|
);
|
|
4034
4070
|
}
|
|
4035
4071
|
}
|
|
4036
|
-
async getDatasetById({
|
|
4072
|
+
async getDatasetById({
|
|
4073
|
+
id,
|
|
4074
|
+
filters
|
|
4075
|
+
}) {
|
|
4037
4076
|
try {
|
|
4077
|
+
const { conditions, params } = tenancyWhere(filters);
|
|
4078
|
+
const whereSql = ["id = ?", ...conditions].join(" AND ");
|
|
4038
4079
|
const result = await this.#client.execute({
|
|
4039
|
-
sql: `SELECT ${buildSelectColumns(storage.TABLE_DATASETS)} FROM ${storage.TABLE_DATASETS} WHERE
|
|
4040
|
-
args: [id]
|
|
4080
|
+
sql: `SELECT ${buildSelectColumns(storage.TABLE_DATASETS)} FROM ${storage.TABLE_DATASETS} WHERE ${whereSql}`,
|
|
4081
|
+
args: [id, ...params]
|
|
4041
4082
|
});
|
|
4042
4083
|
return result.rows?.[0] ? this.transformDatasetRow(result.rows[0]) : null;
|
|
4043
4084
|
} catch (error$1) {
|
|
@@ -4053,7 +4094,7 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
|
|
|
4053
4094
|
}
|
|
4054
4095
|
async _doUpdateDataset(args) {
|
|
4055
4096
|
try {
|
|
4056
|
-
const existing = await this.getDatasetById({ id: args.id });
|
|
4097
|
+
const existing = await this.getDatasetById({ id: args.id, filters: args.filters });
|
|
4057
4098
|
if (!existing) {
|
|
4058
4099
|
throw new error.MastraError({
|
|
4059
4100
|
id: storage.createStorageErrorId("LIBSQL", "UPDATE_DATASET", "NOT_FOUND"),
|
|
@@ -4136,30 +4177,31 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
|
|
|
4136
4177
|
);
|
|
4137
4178
|
}
|
|
4138
4179
|
}
|
|
4139
|
-
async deleteDataset({ id }) {
|
|
4180
|
+
async deleteDataset({ id, filters }) {
|
|
4140
4181
|
try {
|
|
4141
|
-
|
|
4142
|
-
|
|
4182
|
+
const { conditions, params } = tenancyWhere(filters);
|
|
4183
|
+
const scopedWhere = ["id = ?", ...conditions].join(" AND ");
|
|
4184
|
+
const exists = await this.#client.execute({
|
|
4185
|
+
sql: `SELECT id FROM ${storage.TABLE_DATASETS} WHERE ${scopedWhere}`,
|
|
4186
|
+
args: [id, ...params]
|
|
4187
|
+
});
|
|
4188
|
+
if (!exists.rows?.[0]) return;
|
|
4189
|
+
const experimentTablesExist = await this.experimentTablesExist();
|
|
4190
|
+
const statements = [];
|
|
4191
|
+
if (experimentTablesExist) {
|
|
4192
|
+
statements.push({
|
|
4143
4193
|
sql: `DELETE FROM ${storage.TABLE_EXPERIMENT_RESULTS} WHERE experimentId IN (SELECT id FROM ${storage.TABLE_EXPERIMENTS} WHERE datasetId = ?)`,
|
|
4144
4194
|
args: [id]
|
|
4145
4195
|
});
|
|
4146
|
-
|
|
4147
|
-
}
|
|
4148
|
-
try {
|
|
4149
|
-
await this.#client.execute({
|
|
4196
|
+
statements.push({
|
|
4150
4197
|
sql: `UPDATE ${storage.TABLE_EXPERIMENTS} SET datasetId = NULL, datasetVersion = NULL WHERE datasetId = ?`,
|
|
4151
4198
|
args: [id]
|
|
4152
4199
|
});
|
|
4153
|
-
} catch {
|
|
4154
4200
|
}
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
{ sql: `DELETE FROM ${storage.TABLE_DATASETS} WHERE id = ?`, args: [id] }
|
|
4160
|
-
],
|
|
4161
|
-
"write"
|
|
4162
|
-
);
|
|
4201
|
+
statements.push({ sql: `DELETE FROM ${storage.TABLE_DATASET_VERSIONS} WHERE datasetId = ?`, args: [id] });
|
|
4202
|
+
statements.push({ sql: `DELETE FROM ${storage.TABLE_DATASET_ITEMS} WHERE datasetId = ?`, args: [id] });
|
|
4203
|
+
statements.push({ sql: `DELETE FROM ${storage.TABLE_DATASETS} WHERE ${scopedWhere}`, args: [id, ...params] });
|
|
4204
|
+
await this.#client.batch(statements, "write");
|
|
4163
4205
|
} catch (error$1) {
|
|
4164
4206
|
throw new error.MastraError(
|
|
4165
4207
|
{
|
|
@@ -4192,6 +4234,21 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
|
|
|
4192
4234
|
filterConditions.push("candidateId = ?");
|
|
4193
4235
|
filterParams.push(args.filters.candidateId);
|
|
4194
4236
|
}
|
|
4237
|
+
if (args.filters?.targetType !== void 0) {
|
|
4238
|
+
filterConditions.push("targetType = ?");
|
|
4239
|
+
filterParams.push(args.filters.targetType);
|
|
4240
|
+
}
|
|
4241
|
+
if (args.filters?.targetIds !== void 0 && args.filters.targetIds.length > 0) {
|
|
4242
|
+
const placeholders = args.filters.targetIds.map(() => "?").join(",");
|
|
4243
|
+
filterConditions.push(
|
|
4244
|
+
`EXISTS (SELECT 1 FROM json_each(${storage.TABLE_DATASETS}.targetIds) WHERE value IN (${placeholders}))`
|
|
4245
|
+
);
|
|
4246
|
+
for (const id of args.filters.targetIds) filterParams.push(id);
|
|
4247
|
+
}
|
|
4248
|
+
if (args.filters?.name !== void 0 && args.filters.name.length > 0) {
|
|
4249
|
+
filterConditions.push("LOWER(name) LIKE ?");
|
|
4250
|
+
filterParams.push(`%${args.filters.name.toLowerCase()}%`);
|
|
4251
|
+
}
|
|
4195
4252
|
const whereClause = filterConditions.length > 0 ? `WHERE ${filterConditions.join(" AND ")}` : "";
|
|
4196
4253
|
const countResult = await this.#client.execute({
|
|
4197
4254
|
sql: `SELECT COUNT(*) as count FROM ${storage.TABLE_DATASETS} ${whereClause}`,
|
|
@@ -5157,9 +5214,10 @@ var ExperimentsLibSQL = class extends storage.ExperimentsStorage {
|
|
|
5157
5214
|
}
|
|
5158
5215
|
async getExperimentById(args) {
|
|
5159
5216
|
try {
|
|
5217
|
+
const scoped = buildScopedWhere("id", args.id, args.filters);
|
|
5160
5218
|
const result = await this.#client.execute({
|
|
5161
|
-
sql: `SELECT ${buildSelectColumns(storage.TABLE_EXPERIMENTS)} FROM ${storage.TABLE_EXPERIMENTS} WHERE
|
|
5162
|
-
args:
|
|
5219
|
+
sql: `SELECT ${buildSelectColumns(storage.TABLE_EXPERIMENTS)} FROM ${storage.TABLE_EXPERIMENTS} WHERE ${scoped.sql}`,
|
|
5220
|
+
args: scoped.args
|
|
5163
5221
|
});
|
|
5164
5222
|
return result.rows?.[0] ? this.transformExperimentRow(result.rows[0]) : null;
|
|
5165
5223
|
} catch (error$1) {
|
|
@@ -5251,14 +5309,17 @@ var ExperimentsLibSQL = class extends storage.ExperimentsStorage {
|
|
|
5251
5309
|
}
|
|
5252
5310
|
async deleteExperiment(args) {
|
|
5253
5311
|
try {
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
await this.#client.
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
|
|
5312
|
+
const parentScoped = buildScopedWhere("id", args.id, args.filters);
|
|
5313
|
+
const { conditions, params } = tenancyWhere(args.filters);
|
|
5314
|
+
const cascadeWhere = conditions.length ? `experimentId IN (SELECT id FROM ${storage.TABLE_EXPERIMENTS} WHERE ${["id = ?", ...conditions].join(" AND ")})` : `experimentId = ?`;
|
|
5315
|
+
const cascadeArgs = conditions.length ? [args.id, ...params] : [args.id];
|
|
5316
|
+
await this.#client.batch(
|
|
5317
|
+
[
|
|
5318
|
+
{ sql: `DELETE FROM ${storage.TABLE_EXPERIMENT_RESULTS} WHERE ${cascadeWhere}`, args: cascadeArgs },
|
|
5319
|
+
{ sql: `DELETE FROM ${storage.TABLE_EXPERIMENTS} WHERE ${parentScoped.sql}`, args: parentScoped.args }
|
|
5320
|
+
],
|
|
5321
|
+
"write"
|
|
5322
|
+
);
|
|
5262
5323
|
} catch (error$1) {
|
|
5263
5324
|
throw new error.MastraError(
|
|
5264
5325
|
{
|
|
@@ -5396,9 +5457,10 @@ var ExperimentsLibSQL = class extends storage.ExperimentsStorage {
|
|
|
5396
5457
|
}
|
|
5397
5458
|
async getExperimentResultById(args) {
|
|
5398
5459
|
try {
|
|
5460
|
+
const scoped = buildScopedWhere("id", args.id, args.filters);
|
|
5399
5461
|
const result = await this.#client.execute({
|
|
5400
|
-
sql: `SELECT ${buildSelectColumns(storage.TABLE_EXPERIMENT_RESULTS)} FROM ${storage.TABLE_EXPERIMENT_RESULTS} WHERE
|
|
5401
|
-
args:
|
|
5462
|
+
sql: `SELECT ${buildSelectColumns(storage.TABLE_EXPERIMENT_RESULTS)} FROM ${storage.TABLE_EXPERIMENT_RESULTS} WHERE ${scoped.sql}`,
|
|
5463
|
+
args: scoped.args
|
|
5402
5464
|
});
|
|
5403
5465
|
return result.rows?.[0] ? this.transformExperimentResultRow(result.rows[0]) : null;
|
|
5404
5466
|
} catch (error$1) {
|
|
@@ -5478,6 +5540,14 @@ var ExperimentsLibSQL = class extends storage.ExperimentsStorage {
|
|
|
5478
5540
|
}
|
|
5479
5541
|
async deleteExperimentResults(args) {
|
|
5480
5542
|
try {
|
|
5543
|
+
const { conditions, params } = tenancyWhere(args.filters);
|
|
5544
|
+
if (conditions.length) {
|
|
5545
|
+
await this.#client.execute({
|
|
5546
|
+
sql: `DELETE FROM ${storage.TABLE_EXPERIMENT_RESULTS} WHERE experimentId IN (SELECT id FROM ${storage.TABLE_EXPERIMENTS} WHERE ${["id = ?", ...conditions].join(" AND ")})`,
|
|
5547
|
+
args: [args.experimentId, ...params]
|
|
5548
|
+
});
|
|
5549
|
+
return;
|
|
5550
|
+
}
|
|
5481
5551
|
await this.#client.execute({
|
|
5482
5552
|
sql: `DELETE FROM ${storage.TABLE_EXPERIMENT_RESULTS} WHERE experimentId = ?`,
|
|
5483
5553
|
args: [args.experimentId]
|
|
@@ -11044,6 +11114,16 @@ var ScorerDefinitionsLibSQL = class extends storage.ScorerDefinitionsStorage {
|
|
|
11044
11114
|
};
|
|
11045
11115
|
}
|
|
11046
11116
|
};
|
|
11117
|
+
function appendTenancyConditions(conditions, args, filters) {
|
|
11118
|
+
if (filters?.organizationId !== void 0) {
|
|
11119
|
+
conditions.push(`organizationId = ?`);
|
|
11120
|
+
args.push(filters.organizationId);
|
|
11121
|
+
}
|
|
11122
|
+
if (filters?.projectId !== void 0) {
|
|
11123
|
+
conditions.push(`projectId = ?`);
|
|
11124
|
+
args.push(filters.projectId);
|
|
11125
|
+
}
|
|
11126
|
+
}
|
|
11047
11127
|
var ScoresLibSQL = class _ScoresLibSQL extends storage.ScoresStorage {
|
|
11048
11128
|
/**
|
|
11049
11129
|
* Scorer results accumulate as evals run. Single table, anchored on `createdAt`.
|
|
@@ -11064,7 +11144,7 @@ var ScoresLibSQL = class _ScoresLibSQL extends storage.ScoresStorage {
|
|
|
11064
11144
|
await this.#db.alterTable({
|
|
11065
11145
|
tableName: storage.TABLE_SCORERS,
|
|
11066
11146
|
schema: storage.SCORERS_SCHEMA,
|
|
11067
|
-
ifNotExists: ["spanId", "requestContext"]
|
|
11147
|
+
ifNotExists: ["spanId", "requestContext", "organizationId", "projectId", "batchId", "datasetId", "datasetItemId"]
|
|
11068
11148
|
});
|
|
11069
11149
|
}
|
|
11070
11150
|
async dangerouslyClearAll() {
|
|
@@ -11081,13 +11161,18 @@ var ScoresLibSQL = class _ScoresLibSQL extends storage.ScoresStorage {
|
|
|
11081
11161
|
}
|
|
11082
11162
|
async listScoresByRunId({
|
|
11083
11163
|
runId,
|
|
11084
|
-
pagination
|
|
11164
|
+
pagination,
|
|
11165
|
+
filters
|
|
11085
11166
|
}) {
|
|
11086
11167
|
try {
|
|
11087
11168
|
const { page, perPage: perPageInput } = pagination;
|
|
11169
|
+
const conditions = [`runId = ?`];
|
|
11170
|
+
const queryParams = [runId];
|
|
11171
|
+
appendTenancyConditions(conditions, queryParams, filters);
|
|
11172
|
+
const whereClause = `WHERE ${conditions.join(" AND ")}`;
|
|
11088
11173
|
const countResult = await this.#client.execute({
|
|
11089
|
-
sql: `SELECT COUNT(*) as count FROM ${storage.TABLE_SCORERS}
|
|
11090
|
-
args:
|
|
11174
|
+
sql: `SELECT COUNT(*) as count FROM ${storage.TABLE_SCORERS} ${whereClause}`,
|
|
11175
|
+
args: queryParams
|
|
11091
11176
|
});
|
|
11092
11177
|
const total = Number(countResult.rows?.[0]?.count ?? 0);
|
|
11093
11178
|
if (total === 0) {
|
|
@@ -11106,8 +11191,8 @@ var ScoresLibSQL = class _ScoresLibSQL extends storage.ScoresStorage {
|
|
|
11106
11191
|
const limitValue = perPageInput === false ? total : perPage;
|
|
11107
11192
|
const end = perPageInput === false ? total : start + perPage;
|
|
11108
11193
|
const result = await this.#client.execute({
|
|
11109
|
-
sql: `SELECT ${buildSelectColumns(storage.TABLE_SCORERS)} FROM ${storage.TABLE_SCORERS}
|
|
11110
|
-
args: [
|
|
11194
|
+
sql: `SELECT ${buildSelectColumns(storage.TABLE_SCORERS)} FROM ${storage.TABLE_SCORERS} ${whereClause} ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
|
|
11195
|
+
args: [...queryParams, limitValue, start]
|
|
11111
11196
|
});
|
|
11112
11197
|
const scores = result.rows?.map((row) => this.transformScoreRow(row)) ?? [];
|
|
11113
11198
|
return {
|
|
@@ -11135,7 +11220,8 @@ var ScoresLibSQL = class _ScoresLibSQL extends storage.ScoresStorage {
|
|
|
11135
11220
|
entityId,
|
|
11136
11221
|
entityType,
|
|
11137
11222
|
source,
|
|
11138
|
-
pagination
|
|
11223
|
+
pagination,
|
|
11224
|
+
filters
|
|
11139
11225
|
}) {
|
|
11140
11226
|
try {
|
|
11141
11227
|
const { page, perPage: perPageInput } = pagination;
|
|
@@ -11157,6 +11243,7 @@ var ScoresLibSQL = class _ScoresLibSQL extends storage.ScoresStorage {
|
|
|
11157
11243
|
conditions.push(`source = ?`);
|
|
11158
11244
|
queryParams.push(source);
|
|
11159
11245
|
}
|
|
11246
|
+
appendTenancyConditions(conditions, queryParams, filters);
|
|
11160
11247
|
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
11161
11248
|
const countResult = await this.#client.execute({
|
|
11162
11249
|
sql: `SELECT COUNT(*) as count FROM ${storage.TABLE_SCORERS} ${whereClause}`,
|
|
@@ -11264,13 +11351,18 @@ var ScoresLibSQL = class _ScoresLibSQL extends storage.ScoresStorage {
|
|
|
11264
11351
|
async listScoresByEntityId({
|
|
11265
11352
|
entityId,
|
|
11266
11353
|
entityType,
|
|
11267
|
-
pagination
|
|
11354
|
+
pagination,
|
|
11355
|
+
filters
|
|
11268
11356
|
}) {
|
|
11269
11357
|
try {
|
|
11270
11358
|
const { page, perPage: perPageInput } = pagination;
|
|
11359
|
+
const conditions = [`entityId = ?`, `entityType = ?`];
|
|
11360
|
+
const queryParams = [entityId, entityType];
|
|
11361
|
+
appendTenancyConditions(conditions, queryParams, filters);
|
|
11362
|
+
const whereClause = `WHERE ${conditions.join(" AND ")}`;
|
|
11271
11363
|
const countResult = await this.#client.execute({
|
|
11272
|
-
sql: `SELECT COUNT(*) as count FROM ${storage.TABLE_SCORERS}
|
|
11273
|
-
args:
|
|
11364
|
+
sql: `SELECT COUNT(*) as count FROM ${storage.TABLE_SCORERS} ${whereClause}`,
|
|
11365
|
+
args: queryParams
|
|
11274
11366
|
});
|
|
11275
11367
|
const total = Number(countResult.rows?.[0]?.count ?? 0);
|
|
11276
11368
|
if (total === 0) {
|
|
@@ -11289,8 +11381,8 @@ var ScoresLibSQL = class _ScoresLibSQL extends storage.ScoresStorage {
|
|
|
11289
11381
|
const limitValue = perPageInput === false ? total : perPage;
|
|
11290
11382
|
const end = perPageInput === false ? total : start + perPage;
|
|
11291
11383
|
const result = await this.#client.execute({
|
|
11292
|
-
sql: `SELECT ${buildSelectColumns(storage.TABLE_SCORERS)} FROM ${storage.TABLE_SCORERS}
|
|
11293
|
-
args: [
|
|
11384
|
+
sql: `SELECT ${buildSelectColumns(storage.TABLE_SCORERS)} FROM ${storage.TABLE_SCORERS} ${whereClause} ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
|
|
11385
|
+
args: [...queryParams, limitValue, start]
|
|
11294
11386
|
});
|
|
11295
11387
|
const scores = result.rows?.map((row) => this.transformScoreRow(row)) ?? [];
|
|
11296
11388
|
return {
|
|
@@ -11316,22 +11408,27 @@ var ScoresLibSQL = class _ScoresLibSQL extends storage.ScoresStorage {
|
|
|
11316
11408
|
async listScoresBySpan({
|
|
11317
11409
|
traceId,
|
|
11318
11410
|
spanId,
|
|
11319
|
-
pagination
|
|
11411
|
+
pagination,
|
|
11412
|
+
filters
|
|
11320
11413
|
}) {
|
|
11321
11414
|
try {
|
|
11322
11415
|
const { page, perPage: perPageInput } = pagination;
|
|
11323
11416
|
const perPage = storage.normalizePerPage(perPageInput, 100);
|
|
11324
11417
|
const { offset: start, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
|
|
11418
|
+
const conditions = [`traceId = ?`, `spanId = ?`];
|
|
11419
|
+
const queryParams = [traceId, spanId];
|
|
11420
|
+
appendTenancyConditions(conditions, queryParams, filters);
|
|
11421
|
+
const whereClause = `WHERE ${conditions.join(" AND ")}`;
|
|
11325
11422
|
const countSQLResult = await this.#client.execute({
|
|
11326
|
-
sql: `SELECT COUNT(*) as count FROM ${storage.TABLE_SCORERS}
|
|
11327
|
-
args:
|
|
11423
|
+
sql: `SELECT COUNT(*) as count FROM ${storage.TABLE_SCORERS} ${whereClause}`,
|
|
11424
|
+
args: queryParams
|
|
11328
11425
|
});
|
|
11329
11426
|
const total = Number(countSQLResult.rows?.[0]?.count ?? 0);
|
|
11330
11427
|
const limitValue = perPageInput === false ? total : perPage;
|
|
11331
11428
|
const end = perPageInput === false ? total : start + perPage;
|
|
11332
11429
|
const result = await this.#client.execute({
|
|
11333
|
-
sql: `SELECT ${buildSelectColumns(storage.TABLE_SCORERS)} FROM ${storage.TABLE_SCORERS}
|
|
11334
|
-
args: [
|
|
11430
|
+
sql: `SELECT ${buildSelectColumns(storage.TABLE_SCORERS)} FROM ${storage.TABLE_SCORERS} ${whereClause} ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
|
|
11431
|
+
args: [...queryParams, limitValue, start]
|
|
11335
11432
|
});
|
|
11336
11433
|
const scores = result.rows?.map((row) => this.transformScoreRow(row)) ?? [];
|
|
11337
11434
|
return {
|