@mastra/mssql 0.0.0-rag-chunk-extract-llm-option-20250926183645 → 0.0.0-remove-unused-model-providers-api-20251030210744
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 +128 -3
- package/README.md +315 -36
- package/dist/index.cjs +1541 -568
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1555 -582
- package/dist/index.js.map +1 -1
- package/dist/storage/domains/memory/index.d.ts +4 -7
- package/dist/storage/domains/memory/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/index.d.ts +44 -0
- package/dist/storage/domains/observability/index.d.ts.map +1 -0
- package/dist/storage/domains/operations/index.d.ts +67 -4
- package/dist/storage/domains/operations/index.d.ts.map +1 -1
- package/dist/storage/domains/scores/index.d.ts +3 -2
- package/dist/storage/domains/scores/index.d.ts.map +1 -1
- package/dist/storage/domains/utils.d.ts +19 -0
- package/dist/storage/domains/utils.d.ts.map +1 -1
- package/dist/storage/domains/workflows/index.d.ts +6 -11
- package/dist/storage/domains/workflows/index.d.ts.map +1 -1
- package/dist/storage/index.d.ts +46 -40
- package/dist/storage/index.d.ts.map +1 -1
- package/package.json +7 -7
- package/dist/storage/domains/legacy-evals/index.d.ts +0 -20
- package/dist/storage/domains/legacy-evals/index.d.ts.map +0 -1
- package/dist/storage/domains/traces/index.d.ts +0 -37
- package/dist/storage/domains/traces/index.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
|
|
2
|
-
import { MastraStorage,
|
|
2
|
+
import { MastraStorage, StoreOperations, TABLE_WORKFLOW_SNAPSHOT, TABLE_SCHEMAS, TABLE_THREADS, TABLE_MESSAGES, TABLE_TRACES, TABLE_SCORERS, TABLE_AI_SPANS, ScoresStorage, WorkflowsStorage, MemoryStorage, resolveMessageLimit, TABLE_RESOURCES, ObservabilityStorage, safelyParseJSON } from '@mastra/core/storage';
|
|
3
3
|
import sql2 from 'mssql';
|
|
4
|
-
import { parseSqlIdentifier, parseFieldKey } from '@mastra/core/utils';
|
|
5
4
|
import { MessageList } from '@mastra/core/agent';
|
|
5
|
+
import { parseSqlIdentifier } from '@mastra/core/utils';
|
|
6
|
+
import { randomUUID } from 'crypto';
|
|
6
7
|
import { saveScorePayloadSchema } from '@mastra/core/scores';
|
|
7
8
|
|
|
8
9
|
// src/storage/index.ts
|
|
@@ -15,154 +16,71 @@ function getTableName({ indexName, schemaName }) {
|
|
|
15
16
|
const quotedSchemaName = schemaName;
|
|
16
17
|
return quotedSchemaName ? `${quotedSchemaName}.${quotedIndexName}` : quotedIndexName;
|
|
17
18
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if (row.test_info) {
|
|
23
|
-
try {
|
|
24
|
-
testInfoValue = typeof row.test_info === "string" ? JSON.parse(row.test_info) : row.test_info;
|
|
25
|
-
} catch {
|
|
26
|
-
}
|
|
19
|
+
function buildDateRangeFilter(dateRange, fieldName) {
|
|
20
|
+
const filters = {};
|
|
21
|
+
if (dateRange?.start) {
|
|
22
|
+
filters[`${fieldName}_gte`] = dateRange.start;
|
|
27
23
|
}
|
|
28
|
-
if (
|
|
29
|
-
|
|
30
|
-
resultValue = typeof row.result === "string" ? JSON.parse(row.result) : row.result;
|
|
31
|
-
} catch {
|
|
32
|
-
}
|
|
24
|
+
if (dateRange?.end) {
|
|
25
|
+
filters[`${fieldName}_lte`] = dateRange.end;
|
|
33
26
|
}
|
|
27
|
+
return filters;
|
|
28
|
+
}
|
|
29
|
+
function prepareWhereClause(filters, _schema) {
|
|
30
|
+
const conditions = [];
|
|
31
|
+
const params = {};
|
|
32
|
+
let paramIndex = 1;
|
|
33
|
+
Object.entries(filters).forEach(([key, value]) => {
|
|
34
|
+
if (value === void 0) return;
|
|
35
|
+
const paramName = `p${paramIndex++}`;
|
|
36
|
+
if (key.endsWith("_gte")) {
|
|
37
|
+
const fieldName = key.slice(0, -4);
|
|
38
|
+
conditions.push(`[${parseSqlIdentifier(fieldName, "field name")}] >= @${paramName}`);
|
|
39
|
+
params[paramName] = value instanceof Date ? value.toISOString() : value;
|
|
40
|
+
} else if (key.endsWith("_lte")) {
|
|
41
|
+
const fieldName = key.slice(0, -4);
|
|
42
|
+
conditions.push(`[${parseSqlIdentifier(fieldName, "field name")}] <= @${paramName}`);
|
|
43
|
+
params[paramName] = value instanceof Date ? value.toISOString() : value;
|
|
44
|
+
} else if (value === null) {
|
|
45
|
+
conditions.push(`[${parseSqlIdentifier(key, "field name")}] IS NULL`);
|
|
46
|
+
} else {
|
|
47
|
+
conditions.push(`[${parseSqlIdentifier(key, "field name")}] = @${paramName}`);
|
|
48
|
+
params[paramName] = value instanceof Date ? value.toISOString() : value;
|
|
49
|
+
}
|
|
50
|
+
});
|
|
34
51
|
return {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
output: row.output,
|
|
38
|
-
result: resultValue,
|
|
39
|
-
metricName: row.metric_name,
|
|
40
|
-
instructions: row.instructions,
|
|
41
|
-
testInfo: testInfoValue,
|
|
42
|
-
globalRunId: row.global_run_id,
|
|
43
|
-
runId: row.run_id,
|
|
44
|
-
createdAt: row.created_at
|
|
52
|
+
sql: conditions.length > 0 ? ` WHERE ${conditions.join(" AND ")}` : "",
|
|
53
|
+
params
|
|
45
54
|
};
|
|
46
55
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
query += " AND test_info IS NOT NULL AND JSON_VALUE(test_info, '$.testPath') IS NOT NULL";
|
|
61
|
-
} else if (type === "live") {
|
|
62
|
-
query += " AND (test_info IS NULL OR JSON_VALUE(test_info, '$.testPath') IS NULL)";
|
|
63
|
-
}
|
|
64
|
-
query += " ORDER BY created_at DESC";
|
|
65
|
-
const request = this.pool.request();
|
|
66
|
-
request.input("p1", agentName);
|
|
67
|
-
const result = await request.query(query);
|
|
68
|
-
const rows = result.recordset;
|
|
69
|
-
return typeof transformEvalRow === "function" ? rows?.map((row) => transformEvalRow(row)) ?? [] : rows ?? [];
|
|
70
|
-
} catch (error) {
|
|
71
|
-
if (error && error.number === 208 && error.message && error.message.includes("Invalid object name")) {
|
|
72
|
-
return [];
|
|
73
|
-
}
|
|
74
|
-
console.error("Failed to get evals for the specified agent: " + error?.message);
|
|
75
|
-
throw error;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
async getEvals(options = {}) {
|
|
79
|
-
const { agentName, type, page = 0, perPage = 100, dateRange } = options;
|
|
80
|
-
const fromDate = dateRange?.start;
|
|
81
|
-
const toDate = dateRange?.end;
|
|
82
|
-
const where = [];
|
|
83
|
-
const params = {};
|
|
84
|
-
if (agentName) {
|
|
85
|
-
where.push("agent_name = @agentName");
|
|
86
|
-
params["agentName"] = agentName;
|
|
87
|
-
}
|
|
88
|
-
if (type === "test") {
|
|
89
|
-
where.push("test_info IS NOT NULL AND JSON_VALUE(test_info, '$.testPath') IS NOT NULL");
|
|
90
|
-
} else if (type === "live") {
|
|
91
|
-
where.push("(test_info IS NULL OR JSON_VALUE(test_info, '$.testPath') IS NULL)");
|
|
92
|
-
}
|
|
93
|
-
if (fromDate instanceof Date && !isNaN(fromDate.getTime())) {
|
|
94
|
-
where.push(`[created_at] >= @fromDate`);
|
|
95
|
-
params[`fromDate`] = fromDate.toISOString();
|
|
96
|
-
}
|
|
97
|
-
if (toDate instanceof Date && !isNaN(toDate.getTime())) {
|
|
98
|
-
where.push(`[created_at] <= @toDate`);
|
|
99
|
-
params[`toDate`] = toDate.toISOString();
|
|
100
|
-
}
|
|
101
|
-
const whereClause = where.length > 0 ? `WHERE ${where.join(" AND ")}` : "";
|
|
102
|
-
const tableName = getTableName({ indexName: TABLE_EVALS, schemaName: getSchemaName(this.schema) });
|
|
103
|
-
const offset = page * perPage;
|
|
104
|
-
const countQuery = `SELECT COUNT(*) as total FROM ${tableName} ${whereClause}`;
|
|
105
|
-
const dataQuery = `SELECT * FROM ${tableName} ${whereClause} ORDER BY seq_id DESC OFFSET @offset ROWS FETCH NEXT @perPage ROWS ONLY`;
|
|
106
|
-
try {
|
|
107
|
-
const countReq = this.pool.request();
|
|
108
|
-
Object.entries(params).forEach(([key, value]) => {
|
|
109
|
-
if (value instanceof Date) {
|
|
110
|
-
countReq.input(key, sql2.DateTime, value);
|
|
111
|
-
} else {
|
|
112
|
-
countReq.input(key, value);
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
const countResult = await countReq.query(countQuery);
|
|
116
|
-
const total = countResult.recordset[0]?.total || 0;
|
|
117
|
-
if (total === 0) {
|
|
118
|
-
return {
|
|
119
|
-
evals: [],
|
|
120
|
-
total: 0,
|
|
121
|
-
page,
|
|
122
|
-
perPage,
|
|
123
|
-
hasMore: false
|
|
124
|
-
};
|
|
56
|
+
function transformFromSqlRow({
|
|
57
|
+
tableName,
|
|
58
|
+
sqlRow
|
|
59
|
+
}) {
|
|
60
|
+
const schema = TABLE_SCHEMAS[tableName];
|
|
61
|
+
const result = {};
|
|
62
|
+
Object.entries(sqlRow).forEach(([key, value]) => {
|
|
63
|
+
const columnSchema = schema?.[key];
|
|
64
|
+
if (columnSchema?.type === "jsonb" && typeof value === "string") {
|
|
65
|
+
try {
|
|
66
|
+
result[key] = JSON.parse(value);
|
|
67
|
+
} catch {
|
|
68
|
+
result[key] = value;
|
|
125
69
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
req.input("offset", offset);
|
|
135
|
-
req.input("perPage", perPage);
|
|
136
|
-
const result = await req.query(dataQuery);
|
|
137
|
-
const rows = result.recordset;
|
|
138
|
-
return {
|
|
139
|
-
evals: rows?.map((row) => transformEvalRow(row)) ?? [],
|
|
140
|
-
total,
|
|
141
|
-
page,
|
|
142
|
-
perPage,
|
|
143
|
-
hasMore: offset + (rows?.length ?? 0) < total
|
|
144
|
-
};
|
|
145
|
-
} catch (error) {
|
|
146
|
-
const mastraError = new MastraError(
|
|
147
|
-
{
|
|
148
|
-
id: "MASTRA_STORAGE_MSSQL_STORE_GET_EVALS_FAILED",
|
|
149
|
-
domain: ErrorDomain.STORAGE,
|
|
150
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
151
|
-
details: {
|
|
152
|
-
agentName: agentName || "all",
|
|
153
|
-
type: type || "all",
|
|
154
|
-
page,
|
|
155
|
-
perPage
|
|
156
|
-
}
|
|
157
|
-
},
|
|
158
|
-
error
|
|
159
|
-
);
|
|
160
|
-
this.logger?.error?.(mastraError.toString());
|
|
161
|
-
this.logger?.trackException(mastraError);
|
|
162
|
-
throw mastraError;
|
|
70
|
+
} else if (columnSchema?.type === "timestamp" && value && typeof value === "string") {
|
|
71
|
+
result[key] = new Date(value);
|
|
72
|
+
} else if (columnSchema?.type === "timestamp" && value instanceof Date) {
|
|
73
|
+
result[key] = value;
|
|
74
|
+
} else if (columnSchema?.type === "boolean") {
|
|
75
|
+
result[key] = Boolean(value);
|
|
76
|
+
} else {
|
|
77
|
+
result[key] = value;
|
|
163
78
|
}
|
|
164
|
-
}
|
|
165
|
-
|
|
79
|
+
});
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// src/storage/domains/memory/index.ts
|
|
166
84
|
var MemoryMSSQL = class extends MemoryStorage {
|
|
167
85
|
pool;
|
|
168
86
|
schema;
|
|
@@ -194,7 +112,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
194
112
|
}
|
|
195
113
|
async getThreadById({ threadId }) {
|
|
196
114
|
try {
|
|
197
|
-
const
|
|
115
|
+
const sql5 = `SELECT
|
|
198
116
|
id,
|
|
199
117
|
[resourceId],
|
|
200
118
|
title,
|
|
@@ -205,7 +123,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
205
123
|
WHERE id = @threadId`;
|
|
206
124
|
const request = this.pool.request();
|
|
207
125
|
request.input("threadId", threadId);
|
|
208
|
-
const resultSet = await request.query(
|
|
126
|
+
const resultSet = await request.query(sql5);
|
|
209
127
|
const thread = resultSet.recordset[0] || null;
|
|
210
128
|
if (!thread) {
|
|
211
129
|
return null;
|
|
@@ -251,7 +169,8 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
251
169
|
};
|
|
252
170
|
}
|
|
253
171
|
const orderByField = orderBy === "createdAt" ? "[createdAt]" : "[updatedAt]";
|
|
254
|
-
const
|
|
172
|
+
const dir = (sortDirection || "DESC").toUpperCase() === "ASC" ? "ASC" : "DESC";
|
|
173
|
+
const dataQuery = `SELECT id, [resourceId], title, metadata, [createdAt], [updatedAt] ${baseQuery} ORDER BY ${orderByField} ${dir} OFFSET @offset ROWS FETCH NEXT @perPage ROWS ONLY`;
|
|
255
174
|
const dataRequest = this.pool.request();
|
|
256
175
|
dataRequest.input("resourceId", resourceId);
|
|
257
176
|
dataRequest.input("perPage", perPage);
|
|
@@ -308,7 +227,12 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
308
227
|
req.input("id", thread.id);
|
|
309
228
|
req.input("resourceId", thread.resourceId);
|
|
310
229
|
req.input("title", thread.title);
|
|
311
|
-
|
|
230
|
+
const metadata = thread.metadata ? JSON.stringify(thread.metadata) : null;
|
|
231
|
+
if (metadata === null) {
|
|
232
|
+
req.input("metadata", sql2.NVarChar, null);
|
|
233
|
+
} else {
|
|
234
|
+
req.input("metadata", metadata);
|
|
235
|
+
}
|
|
312
236
|
req.input("createdAt", sql2.DateTime2, thread.createdAt);
|
|
313
237
|
req.input("updatedAt", sql2.DateTime2, thread.updatedAt);
|
|
314
238
|
await req.query(mergeSql);
|
|
@@ -335,7 +259,8 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
335
259
|
try {
|
|
336
260
|
const baseQuery = `FROM ${getTableName({ indexName: TABLE_THREADS, schemaName: getSchemaName(this.schema) })} WHERE [resourceId] = @resourceId`;
|
|
337
261
|
const orderByField = orderBy === "createdAt" ? "[createdAt]" : "[updatedAt]";
|
|
338
|
-
const
|
|
262
|
+
const dir = (sortDirection || "DESC").toUpperCase() === "ASC" ? "ASC" : "DESC";
|
|
263
|
+
const dataQuery = `SELECT id, [resourceId], title, metadata, [createdAt], [updatedAt] ${baseQuery} ORDER BY ${orderByField} ${dir}`;
|
|
339
264
|
const request = this.pool.request();
|
|
340
265
|
request.input("resourceId", resourceId);
|
|
341
266
|
const resultSet = await request.query(dataQuery);
|
|
@@ -378,7 +303,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
378
303
|
};
|
|
379
304
|
try {
|
|
380
305
|
const table = getTableName({ indexName: TABLE_THREADS, schemaName: getSchemaName(this.schema) });
|
|
381
|
-
const
|
|
306
|
+
const sql5 = `UPDATE ${table}
|
|
382
307
|
SET title = @title,
|
|
383
308
|
metadata = @metadata,
|
|
384
309
|
[updatedAt] = @updatedAt
|
|
@@ -389,7 +314,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
389
314
|
req.input("title", title);
|
|
390
315
|
req.input("metadata", JSON.stringify(mergedMetadata));
|
|
391
316
|
req.input("updatedAt", /* @__PURE__ */ new Date());
|
|
392
|
-
const result = await req.query(
|
|
317
|
+
const result = await req.query(sql5);
|
|
393
318
|
let thread = result.recordset && result.recordset[0];
|
|
394
319
|
if (thread && "seq_id" in thread) {
|
|
395
320
|
const { seq_id, ...rest } = thread;
|
|
@@ -459,8 +384,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
459
384
|
}
|
|
460
385
|
async _getIncludedMessages({
|
|
461
386
|
threadId,
|
|
462
|
-
selectBy
|
|
463
|
-
orderByStatement
|
|
387
|
+
selectBy
|
|
464
388
|
}) {
|
|
465
389
|
if (!threadId.trim()) throw new Error("threadId must be a non-empty string");
|
|
466
390
|
const include = selectBy?.include;
|
|
@@ -488,7 +412,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
488
412
|
m.[resourceId],
|
|
489
413
|
m.seq_id
|
|
490
414
|
FROM (
|
|
491
|
-
SELECT *, ROW_NUMBER() OVER (
|
|
415
|
+
SELECT *, ROW_NUMBER() OVER (ORDER BY [createdAt] ASC) as row_num
|
|
492
416
|
FROM ${getTableName({ indexName: TABLE_MESSAGES, schemaName: getSchemaName(this.schema) })}
|
|
493
417
|
WHERE [thread_id] = ${pThreadId}
|
|
494
418
|
) AS m
|
|
@@ -496,15 +420,17 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
496
420
|
OR EXISTS (
|
|
497
421
|
SELECT 1
|
|
498
422
|
FROM (
|
|
499
|
-
SELECT *, ROW_NUMBER() OVER (
|
|
423
|
+
SELECT *, ROW_NUMBER() OVER (ORDER BY [createdAt] ASC) as row_num
|
|
500
424
|
FROM ${getTableName({ indexName: TABLE_MESSAGES, schemaName: getSchemaName(this.schema) })}
|
|
501
425
|
WHERE [thread_id] = ${pThreadId}
|
|
502
426
|
) AS target
|
|
503
427
|
WHERE target.id = ${pId}
|
|
504
428
|
AND (
|
|
505
|
-
|
|
429
|
+
-- Get previous messages (messages that come BEFORE the target)
|
|
430
|
+
(m.row_num < target.row_num AND m.row_num >= target.row_num - ${pPrev})
|
|
506
431
|
OR
|
|
507
|
-
|
|
432
|
+
-- Get next messages (messages that come AFTER the target)
|
|
433
|
+
(m.row_num > target.row_num AND m.row_num <= target.row_num + ${pNext})
|
|
508
434
|
)
|
|
509
435
|
)
|
|
510
436
|
`
|
|
@@ -543,7 +469,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
543
469
|
let rows = [];
|
|
544
470
|
const include = selectBy?.include || [];
|
|
545
471
|
if (include?.length) {
|
|
546
|
-
const includeMessages = await this._getIncludedMessages({ threadId, selectBy
|
|
472
|
+
const includeMessages = await this._getIncludedMessages({ threadId, selectBy });
|
|
547
473
|
if (includeMessages) {
|
|
548
474
|
rows.push(...includeMessages);
|
|
549
475
|
}
|
|
@@ -584,14 +510,11 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
584
510
|
error
|
|
585
511
|
);
|
|
586
512
|
this.logger?.error?.(mastraError.toString());
|
|
587
|
-
this.logger?.trackException(mastraError);
|
|
513
|
+
this.logger?.trackException?.(mastraError);
|
|
588
514
|
return [];
|
|
589
515
|
}
|
|
590
516
|
}
|
|
591
|
-
async
|
|
592
|
-
messageIds,
|
|
593
|
-
format
|
|
594
|
-
}) {
|
|
517
|
+
async listMessagesById({ messageIds }) {
|
|
595
518
|
if (messageIds.length === 0) return [];
|
|
596
519
|
const selectStatement = `SELECT seq_id, id, content, role, type, [createdAt], thread_id AS threadId, resourceId`;
|
|
597
520
|
const orderByStatement = `ORDER BY [seq_id] DESC`;
|
|
@@ -609,8 +532,8 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
609
532
|
return timeDiff;
|
|
610
533
|
});
|
|
611
534
|
rows = rows.map(({ seq_id, ...rest }) => rest);
|
|
612
|
-
|
|
613
|
-
return
|
|
535
|
+
const messages = this._parseAndFormatMessages(rows, `v2`);
|
|
536
|
+
return messages;
|
|
614
537
|
} catch (error) {
|
|
615
538
|
const mastraError = new MastraError(
|
|
616
539
|
{
|
|
@@ -624,10 +547,139 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
624
547
|
error
|
|
625
548
|
);
|
|
626
549
|
this.logger?.error?.(mastraError.toString());
|
|
627
|
-
this.logger?.trackException(mastraError);
|
|
550
|
+
this.logger?.trackException?.(mastraError);
|
|
628
551
|
return [];
|
|
629
552
|
}
|
|
630
553
|
}
|
|
554
|
+
async listMessages(args) {
|
|
555
|
+
const { threadId, resourceId, include, filter, limit, offset = 0, orderBy } = args;
|
|
556
|
+
if (!threadId.trim()) {
|
|
557
|
+
throw new MastraError(
|
|
558
|
+
{
|
|
559
|
+
id: "STORAGE_MSSQL_LIST_MESSAGES_INVALID_THREAD_ID",
|
|
560
|
+
domain: ErrorDomain.STORAGE,
|
|
561
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
562
|
+
details: { threadId }
|
|
563
|
+
},
|
|
564
|
+
new Error("threadId must be a non-empty string")
|
|
565
|
+
);
|
|
566
|
+
}
|
|
567
|
+
try {
|
|
568
|
+
let perPage = 40;
|
|
569
|
+
if (limit !== void 0) {
|
|
570
|
+
if (limit === false) {
|
|
571
|
+
perPage = Number.MAX_SAFE_INTEGER;
|
|
572
|
+
} else if (limit === 0) {
|
|
573
|
+
perPage = 0;
|
|
574
|
+
} else if (typeof limit === "number" && limit > 0) {
|
|
575
|
+
perPage = limit;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
const page = perPage === 0 ? 0 : Math.floor(offset / perPage);
|
|
579
|
+
const sortField = orderBy?.field || "createdAt";
|
|
580
|
+
const sortDirection = orderBy?.direction || "DESC";
|
|
581
|
+
const orderByStatement = `ORDER BY [${sortField}] ${sortDirection}`;
|
|
582
|
+
const selectStatement = `SELECT seq_id, id, content, role, type, [createdAt], thread_id AS threadId, resourceId`;
|
|
583
|
+
const tableName = getTableName({ indexName: TABLE_MESSAGES, schemaName: getSchemaName(this.schema) });
|
|
584
|
+
const conditions = ["[thread_id] = @threadId"];
|
|
585
|
+
const request = this.pool.request();
|
|
586
|
+
request.input("threadId", threadId);
|
|
587
|
+
if (resourceId) {
|
|
588
|
+
conditions.push("[resourceId] = @resourceId");
|
|
589
|
+
request.input("resourceId", resourceId);
|
|
590
|
+
}
|
|
591
|
+
if (filter?.dateRange?.start) {
|
|
592
|
+
conditions.push("[createdAt] >= @fromDate");
|
|
593
|
+
request.input("fromDate", filter.dateRange.start);
|
|
594
|
+
}
|
|
595
|
+
if (filter?.dateRange?.end) {
|
|
596
|
+
conditions.push("[createdAt] <= @toDate");
|
|
597
|
+
request.input("toDate", filter.dateRange.end);
|
|
598
|
+
}
|
|
599
|
+
const whereClause = `WHERE ${conditions.join(" AND ")}`;
|
|
600
|
+
const countQuery = `SELECT COUNT(*) as total FROM ${tableName} ${whereClause}`;
|
|
601
|
+
const countResult = await request.query(countQuery);
|
|
602
|
+
const total = parseInt(countResult.recordset[0]?.total, 10) || 0;
|
|
603
|
+
const dataQuery = `${selectStatement} FROM ${tableName} ${whereClause} ${orderByStatement} OFFSET @offset ROWS FETCH NEXT @limit ROWS ONLY`;
|
|
604
|
+
request.input("offset", offset);
|
|
605
|
+
if (perPage > 2147483647) {
|
|
606
|
+
request.input("limit", sql2.BigInt, perPage);
|
|
607
|
+
} else {
|
|
608
|
+
request.input("limit", perPage);
|
|
609
|
+
}
|
|
610
|
+
const rowsResult = await request.query(dataQuery);
|
|
611
|
+
const rows = rowsResult.recordset || [];
|
|
612
|
+
const messages = [...rows];
|
|
613
|
+
if (total === 0 && messages.length === 0) {
|
|
614
|
+
return {
|
|
615
|
+
messages: [],
|
|
616
|
+
total: 0,
|
|
617
|
+
page,
|
|
618
|
+
perPage,
|
|
619
|
+
hasMore: false
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
const messageIds = new Set(messages.map((m) => m.id));
|
|
623
|
+
if (include && include.length > 0) {
|
|
624
|
+
const selectBy = { include };
|
|
625
|
+
const includeMessages = await this._getIncludedMessages({ threadId, selectBy });
|
|
626
|
+
if (includeMessages) {
|
|
627
|
+
for (const includeMsg of includeMessages) {
|
|
628
|
+
if (!messageIds.has(includeMsg.id)) {
|
|
629
|
+
messages.push(includeMsg);
|
|
630
|
+
messageIds.add(includeMsg.id);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
const parsed = this._parseAndFormatMessages(messages, "v2");
|
|
636
|
+
let finalMessages = parsed;
|
|
637
|
+
finalMessages = finalMessages.sort((a, b) => {
|
|
638
|
+
const aValue = sortField === "createdAt" ? new Date(a.createdAt).getTime() : a[sortField];
|
|
639
|
+
const bValue = sortField === "createdAt" ? new Date(b.createdAt).getTime() : b[sortField];
|
|
640
|
+
return sortDirection === "ASC" ? aValue - bValue : bValue - aValue;
|
|
641
|
+
});
|
|
642
|
+
const returnedThreadMessageIds = new Set(finalMessages.filter((m) => m.threadId === threadId).map((m) => m.id));
|
|
643
|
+
const allThreadMessagesReturned = returnedThreadMessageIds.size >= total;
|
|
644
|
+
const hasMore = limit === false ? false : allThreadMessagesReturned ? false : offset + rows.length < total;
|
|
645
|
+
return {
|
|
646
|
+
messages: finalMessages,
|
|
647
|
+
total,
|
|
648
|
+
page,
|
|
649
|
+
perPage,
|
|
650
|
+
hasMore
|
|
651
|
+
};
|
|
652
|
+
} catch (error) {
|
|
653
|
+
const errorPerPage = limit === false ? Number.MAX_SAFE_INTEGER : limit === 0 ? 0 : limit || 40;
|
|
654
|
+
const mastraError = new MastraError(
|
|
655
|
+
{
|
|
656
|
+
id: "MASTRA_STORAGE_MSSQL_STORE_LIST_MESSAGES_FAILED",
|
|
657
|
+
domain: ErrorDomain.STORAGE,
|
|
658
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
659
|
+
details: {
|
|
660
|
+
threadId,
|
|
661
|
+
resourceId: resourceId ?? ""
|
|
662
|
+
}
|
|
663
|
+
},
|
|
664
|
+
error
|
|
665
|
+
);
|
|
666
|
+
this.logger?.error?.(mastraError.toString());
|
|
667
|
+
this.logger?.trackException?.(mastraError);
|
|
668
|
+
return {
|
|
669
|
+
messages: [],
|
|
670
|
+
total: 0,
|
|
671
|
+
page: errorPerPage === 0 ? 0 : Math.floor(offset / errorPerPage),
|
|
672
|
+
perPage: errorPerPage,
|
|
673
|
+
hasMore: false
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
async listThreadsByResourceId(args) {
|
|
678
|
+
const { resourceId, limit, offset, orderBy, sortDirection } = args;
|
|
679
|
+
const page = Math.floor(offset / limit);
|
|
680
|
+
const perPage = limit;
|
|
681
|
+
return this.getThreadsByResourceIdPaginated({ resourceId, page, perPage, orderBy, sortDirection });
|
|
682
|
+
}
|
|
631
683
|
async getMessagesPaginated(args) {
|
|
632
684
|
const { threadId, resourceId, format, selectBy } = args;
|
|
633
685
|
const { page = 0, perPage: perPageInput, dateRange } = selectBy?.pagination || {};
|
|
@@ -639,7 +691,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
639
691
|
const orderByStatement = `ORDER BY [seq_id] DESC`;
|
|
640
692
|
let messages = [];
|
|
641
693
|
if (selectBy?.include?.length) {
|
|
642
|
-
const includeMessages = await this._getIncludedMessages({ threadId, selectBy
|
|
694
|
+
const includeMessages = await this._getIncludedMessages({ threadId, selectBy });
|
|
643
695
|
if (includeMessages) messages.push(...includeMessages);
|
|
644
696
|
}
|
|
645
697
|
const perPage = perPageInput !== void 0 ? perPageInput : resolveMessageLimit({ last: selectBy?.last, defaultLimit: 40 });
|
|
@@ -686,7 +738,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
686
738
|
const parsed = this._parseAndFormatMessages(messages, format);
|
|
687
739
|
return {
|
|
688
740
|
messages: parsed,
|
|
689
|
-
total
|
|
741
|
+
total,
|
|
690
742
|
page,
|
|
691
743
|
perPage,
|
|
692
744
|
hasMore: currentOffset + rows.length < total
|
|
@@ -706,7 +758,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
706
758
|
error
|
|
707
759
|
);
|
|
708
760
|
this.logger?.error?.(mastraError.toString());
|
|
709
|
-
this.logger?.trackException(mastraError);
|
|
761
|
+
this.logger?.trackException?.(mastraError);
|
|
710
762
|
return { messages: [], total: 0, page, perPage: perPageInput || 40, hasMore: false };
|
|
711
763
|
}
|
|
712
764
|
}
|
|
@@ -973,8 +1025,10 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
973
1025
|
return null;
|
|
974
1026
|
}
|
|
975
1027
|
return {
|
|
976
|
-
|
|
977
|
-
|
|
1028
|
+
id: result.id,
|
|
1029
|
+
createdAt: result.createdAt,
|
|
1030
|
+
updatedAt: result.updatedAt,
|
|
1031
|
+
workingMemory: result.workingMemory,
|
|
978
1032
|
metadata: typeof result.metadata === "string" ? JSON.parse(result.metadata) : result.metadata
|
|
979
1033
|
};
|
|
980
1034
|
} catch (error) {
|
|
@@ -988,7 +1042,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
988
1042
|
error
|
|
989
1043
|
);
|
|
990
1044
|
this.logger?.error?.(mastraError.toString());
|
|
991
|
-
this.logger?.trackException(mastraError);
|
|
1045
|
+
this.logger?.trackException?.(mastraError);
|
|
992
1046
|
throw mastraError;
|
|
993
1047
|
}
|
|
994
1048
|
}
|
|
@@ -997,7 +1051,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
997
1051
|
tableName: TABLE_RESOURCES,
|
|
998
1052
|
record: {
|
|
999
1053
|
...resource,
|
|
1000
|
-
metadata:
|
|
1054
|
+
metadata: resource.metadata
|
|
1001
1055
|
}
|
|
1002
1056
|
});
|
|
1003
1057
|
return resource;
|
|
@@ -1055,141 +1109,466 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
1055
1109
|
error
|
|
1056
1110
|
);
|
|
1057
1111
|
this.logger?.error?.(mastraError.toString());
|
|
1058
|
-
this.logger?.trackException(mastraError);
|
|
1112
|
+
this.logger?.trackException?.(mastraError);
|
|
1059
1113
|
throw mastraError;
|
|
1060
1114
|
}
|
|
1061
1115
|
}
|
|
1062
1116
|
};
|
|
1063
|
-
var
|
|
1117
|
+
var ObservabilityMSSQL = class extends ObservabilityStorage {
|
|
1064
1118
|
pool;
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
case "timestamp":
|
|
1073
|
-
return "DATETIME2(7)";
|
|
1074
|
-
case "uuid":
|
|
1075
|
-
return "UNIQUEIDENTIFIER";
|
|
1076
|
-
case "jsonb":
|
|
1077
|
-
return "NVARCHAR(MAX)";
|
|
1078
|
-
case "integer":
|
|
1079
|
-
return "INT";
|
|
1080
|
-
case "bigint":
|
|
1081
|
-
return "BIGINT";
|
|
1082
|
-
case "float":
|
|
1083
|
-
return "FLOAT";
|
|
1084
|
-
default:
|
|
1085
|
-
throw new MastraError({
|
|
1086
|
-
id: "MASTRA_STORAGE_MSSQL_STORE_TYPE_NOT_SUPPORTED",
|
|
1087
|
-
domain: ErrorDomain.STORAGE,
|
|
1088
|
-
category: ErrorCategory.THIRD_PARTY
|
|
1089
|
-
});
|
|
1090
|
-
}
|
|
1091
|
-
}
|
|
1092
|
-
constructor({ pool, schemaName }) {
|
|
1119
|
+
operations;
|
|
1120
|
+
schema;
|
|
1121
|
+
constructor({
|
|
1122
|
+
pool,
|
|
1123
|
+
operations,
|
|
1124
|
+
schema
|
|
1125
|
+
}) {
|
|
1093
1126
|
super();
|
|
1094
1127
|
this.pool = pool;
|
|
1095
|
-
this.
|
|
1096
|
-
|
|
1097
|
-
async hasColumn(table, column) {
|
|
1098
|
-
const schema = this.schemaName || "dbo";
|
|
1099
|
-
const request = this.pool.request();
|
|
1100
|
-
request.input("schema", schema);
|
|
1101
|
-
request.input("table", table);
|
|
1102
|
-
request.input("column", column);
|
|
1103
|
-
request.input("columnLower", column.toLowerCase());
|
|
1104
|
-
const result = await request.query(
|
|
1105
|
-
`SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = @table AND (COLUMN_NAME = @column OR COLUMN_NAME = @columnLower)`
|
|
1106
|
-
);
|
|
1107
|
-
return result.recordset.length > 0;
|
|
1128
|
+
this.operations = operations;
|
|
1129
|
+
this.schema = schema;
|
|
1108
1130
|
}
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
this.setupSchemaPromise = (async () => {
|
|
1115
|
-
try {
|
|
1116
|
-
const checkRequest = this.pool.request();
|
|
1117
|
-
checkRequest.input("schemaName", this.schemaName);
|
|
1118
|
-
const checkResult = await checkRequest.query(`
|
|
1119
|
-
SELECT 1 AS found FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = @schemaName
|
|
1120
|
-
`);
|
|
1121
|
-
const schemaExists = Array.isArray(checkResult.recordset) && checkResult.recordset.length > 0;
|
|
1122
|
-
if (!schemaExists) {
|
|
1123
|
-
try {
|
|
1124
|
-
await this.pool.request().query(`CREATE SCHEMA [${this.schemaName}]`);
|
|
1125
|
-
this.logger?.info?.(`Schema "${this.schemaName}" created successfully`);
|
|
1126
|
-
} catch (error) {
|
|
1127
|
-
this.logger?.error?.(`Failed to create schema "${this.schemaName}"`, { error });
|
|
1128
|
-
throw new Error(
|
|
1129
|
-
`Unable to create schema "${this.schemaName}". This requires CREATE privilege on the database. Either create the schema manually or grant CREATE privilege to the user.`
|
|
1130
|
-
);
|
|
1131
|
-
}
|
|
1132
|
-
}
|
|
1133
|
-
this.schemaSetupComplete = true;
|
|
1134
|
-
this.logger?.debug?.(`Schema "${this.schemaName}" is ready for use`);
|
|
1135
|
-
} catch (error) {
|
|
1136
|
-
this.schemaSetupComplete = void 0;
|
|
1137
|
-
this.setupSchemaPromise = null;
|
|
1138
|
-
throw error;
|
|
1139
|
-
} finally {
|
|
1140
|
-
this.setupSchemaPromise = null;
|
|
1141
|
-
}
|
|
1142
|
-
})();
|
|
1143
|
-
}
|
|
1144
|
-
await this.setupSchemaPromise;
|
|
1131
|
+
get aiTracingStrategy() {
|
|
1132
|
+
return {
|
|
1133
|
+
preferred: "batch-with-updates",
|
|
1134
|
+
supported: ["batch-with-updates", "insert-only"]
|
|
1135
|
+
};
|
|
1145
1136
|
}
|
|
1146
|
-
async
|
|
1137
|
+
async createAISpan(span) {
|
|
1147
1138
|
try {
|
|
1148
|
-
const
|
|
1149
|
-
const
|
|
1150
|
-
const
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
request.input(`param${i}`, JSON.stringify(value));
|
|
1158
|
-
} else {
|
|
1159
|
-
request.input(`param${i}`, value);
|
|
1160
|
-
}
|
|
1161
|
-
});
|
|
1162
|
-
await request.query(insertSql);
|
|
1139
|
+
const startedAt = span.startedAt instanceof Date ? span.startedAt.toISOString() : span.startedAt;
|
|
1140
|
+
const endedAt = span.endedAt instanceof Date ? span.endedAt.toISOString() : span.endedAt;
|
|
1141
|
+
const record = {
|
|
1142
|
+
...span,
|
|
1143
|
+
startedAt,
|
|
1144
|
+
endedAt
|
|
1145
|
+
// Note: createdAt/updatedAt will be set by default values
|
|
1146
|
+
};
|
|
1147
|
+
return this.operations.insert({ tableName: TABLE_AI_SPANS, record });
|
|
1163
1148
|
} catch (error) {
|
|
1164
1149
|
throw new MastraError(
|
|
1165
1150
|
{
|
|
1166
|
-
id: "
|
|
1151
|
+
id: "MSSQL_STORE_CREATE_AI_SPAN_FAILED",
|
|
1167
1152
|
domain: ErrorDomain.STORAGE,
|
|
1168
|
-
category: ErrorCategory.
|
|
1153
|
+
category: ErrorCategory.USER,
|
|
1169
1154
|
details: {
|
|
1170
|
-
|
|
1155
|
+
spanId: span.spanId,
|
|
1156
|
+
traceId: span.traceId,
|
|
1157
|
+
spanType: span.spanType,
|
|
1158
|
+
spanName: span.name
|
|
1171
1159
|
}
|
|
1172
1160
|
},
|
|
1173
1161
|
error
|
|
1174
1162
|
);
|
|
1175
1163
|
}
|
|
1176
1164
|
}
|
|
1177
|
-
async
|
|
1178
|
-
const fullTableName = getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) });
|
|
1165
|
+
async getAITrace(traceId) {
|
|
1179
1166
|
try {
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1167
|
+
const tableName = getTableName({
|
|
1168
|
+
indexName: TABLE_AI_SPANS,
|
|
1169
|
+
schemaName: getSchemaName(this.schema)
|
|
1170
|
+
});
|
|
1171
|
+
const request = this.pool.request();
|
|
1172
|
+
request.input("traceId", traceId);
|
|
1173
|
+
const result = await request.query(
|
|
1174
|
+
`SELECT
|
|
1175
|
+
[traceId], [spanId], [parentSpanId], [name], [scope], [spanType],
|
|
1176
|
+
[attributes], [metadata], [links], [input], [output], [error], [isEvent],
|
|
1177
|
+
[startedAt], [endedAt], [createdAt], [updatedAt]
|
|
1178
|
+
FROM ${tableName}
|
|
1179
|
+
WHERE [traceId] = @traceId
|
|
1180
|
+
ORDER BY [startedAt] DESC`
|
|
1181
|
+
);
|
|
1182
|
+
if (!result.recordset || result.recordset.length === 0) {
|
|
1183
|
+
return null;
|
|
1188
1184
|
}
|
|
1185
|
+
return {
|
|
1186
|
+
traceId,
|
|
1187
|
+
spans: result.recordset.map(
|
|
1188
|
+
(span) => transformFromSqlRow({
|
|
1189
|
+
tableName: TABLE_AI_SPANS,
|
|
1190
|
+
sqlRow: span
|
|
1191
|
+
})
|
|
1192
|
+
)
|
|
1193
|
+
};
|
|
1189
1194
|
} catch (error) {
|
|
1190
1195
|
throw new MastraError(
|
|
1191
1196
|
{
|
|
1192
|
-
id: "
|
|
1197
|
+
id: "MSSQL_STORE_GET_AI_TRACE_FAILED",
|
|
1198
|
+
domain: ErrorDomain.STORAGE,
|
|
1199
|
+
category: ErrorCategory.USER,
|
|
1200
|
+
details: {
|
|
1201
|
+
traceId
|
|
1202
|
+
}
|
|
1203
|
+
},
|
|
1204
|
+
error
|
|
1205
|
+
);
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
async updateAISpan({
|
|
1209
|
+
spanId,
|
|
1210
|
+
traceId,
|
|
1211
|
+
updates
|
|
1212
|
+
}) {
|
|
1213
|
+
try {
|
|
1214
|
+
const data = { ...updates };
|
|
1215
|
+
if (data.endedAt instanceof Date) {
|
|
1216
|
+
data.endedAt = data.endedAt.toISOString();
|
|
1217
|
+
}
|
|
1218
|
+
if (data.startedAt instanceof Date) {
|
|
1219
|
+
data.startedAt = data.startedAt.toISOString();
|
|
1220
|
+
}
|
|
1221
|
+
await this.operations.update({
|
|
1222
|
+
tableName: TABLE_AI_SPANS,
|
|
1223
|
+
keys: { spanId, traceId },
|
|
1224
|
+
data
|
|
1225
|
+
});
|
|
1226
|
+
} catch (error) {
|
|
1227
|
+
throw new MastraError(
|
|
1228
|
+
{
|
|
1229
|
+
id: "MSSQL_STORE_UPDATE_AI_SPAN_FAILED",
|
|
1230
|
+
domain: ErrorDomain.STORAGE,
|
|
1231
|
+
category: ErrorCategory.USER,
|
|
1232
|
+
details: {
|
|
1233
|
+
spanId,
|
|
1234
|
+
traceId
|
|
1235
|
+
}
|
|
1236
|
+
},
|
|
1237
|
+
error
|
|
1238
|
+
);
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
async getAITracesPaginated({
|
|
1242
|
+
filters,
|
|
1243
|
+
pagination
|
|
1244
|
+
}) {
|
|
1245
|
+
const page = pagination?.page ?? 0;
|
|
1246
|
+
const perPage = pagination?.perPage ?? 10;
|
|
1247
|
+
const { entityId, entityType, ...actualFilters } = filters || {};
|
|
1248
|
+
const filtersWithDateRange = {
|
|
1249
|
+
...actualFilters,
|
|
1250
|
+
...buildDateRangeFilter(pagination?.dateRange, "startedAt"),
|
|
1251
|
+
parentSpanId: null
|
|
1252
|
+
// Only get root spans for traces
|
|
1253
|
+
};
|
|
1254
|
+
const whereClause = prepareWhereClause(filtersWithDateRange);
|
|
1255
|
+
let actualWhereClause = whereClause.sql;
|
|
1256
|
+
const params = { ...whereClause.params };
|
|
1257
|
+
let currentParamIndex = Object.keys(params).length + 1;
|
|
1258
|
+
if (entityId && entityType) {
|
|
1259
|
+
let name = "";
|
|
1260
|
+
if (entityType === "workflow") {
|
|
1261
|
+
name = `workflow run: '${entityId}'`;
|
|
1262
|
+
} else if (entityType === "agent") {
|
|
1263
|
+
name = `agent run: '${entityId}'`;
|
|
1264
|
+
} else {
|
|
1265
|
+
const error = new MastraError({
|
|
1266
|
+
id: "MSSQL_STORE_GET_AI_TRACES_PAGINATED_FAILED",
|
|
1267
|
+
domain: ErrorDomain.STORAGE,
|
|
1268
|
+
category: ErrorCategory.USER,
|
|
1269
|
+
details: {
|
|
1270
|
+
entityType
|
|
1271
|
+
},
|
|
1272
|
+
text: `Cannot filter by entity type: ${entityType}`
|
|
1273
|
+
});
|
|
1274
|
+
throw error;
|
|
1275
|
+
}
|
|
1276
|
+
const entityParam = `p${currentParamIndex++}`;
|
|
1277
|
+
if (actualWhereClause) {
|
|
1278
|
+
actualWhereClause += ` AND [name] = @${entityParam}`;
|
|
1279
|
+
} else {
|
|
1280
|
+
actualWhereClause = ` WHERE [name] = @${entityParam}`;
|
|
1281
|
+
}
|
|
1282
|
+
params[entityParam] = name;
|
|
1283
|
+
}
|
|
1284
|
+
const tableName = getTableName({
|
|
1285
|
+
indexName: TABLE_AI_SPANS,
|
|
1286
|
+
schemaName: getSchemaName(this.schema)
|
|
1287
|
+
});
|
|
1288
|
+
try {
|
|
1289
|
+
const countRequest = this.pool.request();
|
|
1290
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
1291
|
+
countRequest.input(key, value);
|
|
1292
|
+
});
|
|
1293
|
+
const countResult = await countRequest.query(
|
|
1294
|
+
`SELECT COUNT(*) as count FROM ${tableName}${actualWhereClause}`
|
|
1295
|
+
);
|
|
1296
|
+
const total = countResult.recordset[0]?.count ?? 0;
|
|
1297
|
+
if (total === 0) {
|
|
1298
|
+
return {
|
|
1299
|
+
pagination: {
|
|
1300
|
+
total: 0,
|
|
1301
|
+
page,
|
|
1302
|
+
perPage,
|
|
1303
|
+
hasMore: false
|
|
1304
|
+
},
|
|
1305
|
+
spans: []
|
|
1306
|
+
};
|
|
1307
|
+
}
|
|
1308
|
+
const dataRequest = this.pool.request();
|
|
1309
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
1310
|
+
dataRequest.input(key, value);
|
|
1311
|
+
});
|
|
1312
|
+
dataRequest.input("offset", page * perPage);
|
|
1313
|
+
dataRequest.input("limit", perPage);
|
|
1314
|
+
const dataResult = await dataRequest.query(
|
|
1315
|
+
`SELECT * FROM ${tableName}${actualWhereClause} ORDER BY [startedAt] DESC OFFSET @offset ROWS FETCH NEXT @limit ROWS ONLY`
|
|
1316
|
+
);
|
|
1317
|
+
const spans = dataResult.recordset.map(
|
|
1318
|
+
(row) => transformFromSqlRow({
|
|
1319
|
+
tableName: TABLE_AI_SPANS,
|
|
1320
|
+
sqlRow: row
|
|
1321
|
+
})
|
|
1322
|
+
);
|
|
1323
|
+
return {
|
|
1324
|
+
pagination: {
|
|
1325
|
+
total,
|
|
1326
|
+
page,
|
|
1327
|
+
perPage,
|
|
1328
|
+
hasMore: (page + 1) * perPage < total
|
|
1329
|
+
},
|
|
1330
|
+
spans
|
|
1331
|
+
};
|
|
1332
|
+
} catch (error) {
|
|
1333
|
+
throw new MastraError(
|
|
1334
|
+
{
|
|
1335
|
+
id: "MSSQL_STORE_GET_AI_TRACES_PAGINATED_FAILED",
|
|
1336
|
+
domain: ErrorDomain.STORAGE,
|
|
1337
|
+
category: ErrorCategory.USER
|
|
1338
|
+
},
|
|
1339
|
+
error
|
|
1340
|
+
);
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
async batchCreateAISpans(args) {
|
|
1344
|
+
if (!args.records || args.records.length === 0) {
|
|
1345
|
+
return;
|
|
1346
|
+
}
|
|
1347
|
+
try {
|
|
1348
|
+
await this.operations.batchInsert({
|
|
1349
|
+
tableName: TABLE_AI_SPANS,
|
|
1350
|
+
records: args.records.map((span) => ({
|
|
1351
|
+
...span,
|
|
1352
|
+
startedAt: span.startedAt instanceof Date ? span.startedAt.toISOString() : span.startedAt,
|
|
1353
|
+
endedAt: span.endedAt instanceof Date ? span.endedAt.toISOString() : span.endedAt
|
|
1354
|
+
}))
|
|
1355
|
+
});
|
|
1356
|
+
} catch (error) {
|
|
1357
|
+
throw new MastraError(
|
|
1358
|
+
{
|
|
1359
|
+
id: "MSSQL_STORE_BATCH_CREATE_AI_SPANS_FAILED",
|
|
1360
|
+
domain: ErrorDomain.STORAGE,
|
|
1361
|
+
category: ErrorCategory.USER,
|
|
1362
|
+
details: {
|
|
1363
|
+
count: args.records.length
|
|
1364
|
+
}
|
|
1365
|
+
},
|
|
1366
|
+
error
|
|
1367
|
+
);
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
async batchUpdateAISpans(args) {
|
|
1371
|
+
if (!args.records || args.records.length === 0) {
|
|
1372
|
+
return;
|
|
1373
|
+
}
|
|
1374
|
+
try {
|
|
1375
|
+
const updates = args.records.map(({ traceId, spanId, updates: data }) => {
|
|
1376
|
+
const processedData = { ...data };
|
|
1377
|
+
if (processedData.endedAt instanceof Date) {
|
|
1378
|
+
processedData.endedAt = processedData.endedAt.toISOString();
|
|
1379
|
+
}
|
|
1380
|
+
if (processedData.startedAt instanceof Date) {
|
|
1381
|
+
processedData.startedAt = processedData.startedAt.toISOString();
|
|
1382
|
+
}
|
|
1383
|
+
return {
|
|
1384
|
+
keys: { spanId, traceId },
|
|
1385
|
+
data: processedData
|
|
1386
|
+
};
|
|
1387
|
+
});
|
|
1388
|
+
await this.operations.batchUpdate({
|
|
1389
|
+
tableName: TABLE_AI_SPANS,
|
|
1390
|
+
updates
|
|
1391
|
+
});
|
|
1392
|
+
} catch (error) {
|
|
1393
|
+
throw new MastraError(
|
|
1394
|
+
{
|
|
1395
|
+
id: "MSSQL_STORE_BATCH_UPDATE_AI_SPANS_FAILED",
|
|
1396
|
+
domain: ErrorDomain.STORAGE,
|
|
1397
|
+
category: ErrorCategory.USER,
|
|
1398
|
+
details: {
|
|
1399
|
+
count: args.records.length
|
|
1400
|
+
}
|
|
1401
|
+
},
|
|
1402
|
+
error
|
|
1403
|
+
);
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
async batchDeleteAITraces(args) {
|
|
1407
|
+
if (!args.traceIds || args.traceIds.length === 0) {
|
|
1408
|
+
return;
|
|
1409
|
+
}
|
|
1410
|
+
try {
|
|
1411
|
+
const keys = args.traceIds.map((traceId) => ({ traceId }));
|
|
1412
|
+
await this.operations.batchDelete({
|
|
1413
|
+
tableName: TABLE_AI_SPANS,
|
|
1414
|
+
keys
|
|
1415
|
+
});
|
|
1416
|
+
} catch (error) {
|
|
1417
|
+
throw new MastraError(
|
|
1418
|
+
{
|
|
1419
|
+
id: "MSSQL_STORE_BATCH_DELETE_AI_TRACES_FAILED",
|
|
1420
|
+
domain: ErrorDomain.STORAGE,
|
|
1421
|
+
category: ErrorCategory.USER,
|
|
1422
|
+
details: {
|
|
1423
|
+
count: args.traceIds.length
|
|
1424
|
+
}
|
|
1425
|
+
},
|
|
1426
|
+
error
|
|
1427
|
+
);
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
};
|
|
1431
|
+
var StoreOperationsMSSQL = class extends StoreOperations {
|
|
1432
|
+
pool;
|
|
1433
|
+
schemaName;
|
|
1434
|
+
setupSchemaPromise = null;
|
|
1435
|
+
schemaSetupComplete = void 0;
|
|
1436
|
+
getSqlType(type, isPrimaryKey = false, useLargeStorage = false) {
|
|
1437
|
+
switch (type) {
|
|
1438
|
+
case "text":
|
|
1439
|
+
if (useLargeStorage) {
|
|
1440
|
+
return "NVARCHAR(MAX)";
|
|
1441
|
+
}
|
|
1442
|
+
return isPrimaryKey ? "NVARCHAR(255)" : "NVARCHAR(400)";
|
|
1443
|
+
case "timestamp":
|
|
1444
|
+
return "DATETIME2(7)";
|
|
1445
|
+
case "uuid":
|
|
1446
|
+
return "UNIQUEIDENTIFIER";
|
|
1447
|
+
case "jsonb":
|
|
1448
|
+
return "NVARCHAR(MAX)";
|
|
1449
|
+
case "integer":
|
|
1450
|
+
return "INT";
|
|
1451
|
+
case "bigint":
|
|
1452
|
+
return "BIGINT";
|
|
1453
|
+
case "float":
|
|
1454
|
+
return "FLOAT";
|
|
1455
|
+
case "boolean":
|
|
1456
|
+
return "BIT";
|
|
1457
|
+
default:
|
|
1458
|
+
throw new MastraError({
|
|
1459
|
+
id: "MASTRA_STORAGE_MSSQL_STORE_TYPE_NOT_SUPPORTED",
|
|
1460
|
+
domain: ErrorDomain.STORAGE,
|
|
1461
|
+
category: ErrorCategory.THIRD_PARTY
|
|
1462
|
+
});
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
constructor({ pool, schemaName }) {
|
|
1466
|
+
super();
|
|
1467
|
+
this.pool = pool;
|
|
1468
|
+
this.schemaName = schemaName;
|
|
1469
|
+
}
|
|
1470
|
+
async hasColumn(table, column) {
|
|
1471
|
+
const schema = this.schemaName || "dbo";
|
|
1472
|
+
const request = this.pool.request();
|
|
1473
|
+
request.input("schema", schema);
|
|
1474
|
+
request.input("table", table);
|
|
1475
|
+
request.input("column", column);
|
|
1476
|
+
request.input("columnLower", column.toLowerCase());
|
|
1477
|
+
const result = await request.query(
|
|
1478
|
+
`SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = @table AND (COLUMN_NAME = @column OR COLUMN_NAME = @columnLower)`
|
|
1479
|
+
);
|
|
1480
|
+
return result.recordset.length > 0;
|
|
1481
|
+
}
|
|
1482
|
+
async setupSchema() {
|
|
1483
|
+
if (!this.schemaName || this.schemaSetupComplete) {
|
|
1484
|
+
return;
|
|
1485
|
+
}
|
|
1486
|
+
if (!this.setupSchemaPromise) {
|
|
1487
|
+
this.setupSchemaPromise = (async () => {
|
|
1488
|
+
try {
|
|
1489
|
+
const checkRequest = this.pool.request();
|
|
1490
|
+
checkRequest.input("schemaName", this.schemaName);
|
|
1491
|
+
const checkResult = await checkRequest.query(`
|
|
1492
|
+
SELECT 1 AS found FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = @schemaName
|
|
1493
|
+
`);
|
|
1494
|
+
const schemaExists = Array.isArray(checkResult.recordset) && checkResult.recordset.length > 0;
|
|
1495
|
+
if (!schemaExists) {
|
|
1496
|
+
try {
|
|
1497
|
+
await this.pool.request().query(`CREATE SCHEMA [${this.schemaName}]`);
|
|
1498
|
+
this.logger?.info?.(`Schema "${this.schemaName}" created successfully`);
|
|
1499
|
+
} catch (error) {
|
|
1500
|
+
this.logger?.error?.(`Failed to create schema "${this.schemaName}"`, { error });
|
|
1501
|
+
throw new Error(
|
|
1502
|
+
`Unable to create schema "${this.schemaName}". This requires CREATE privilege on the database. Either create the schema manually or grant CREATE privilege to the user.`
|
|
1503
|
+
);
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
this.schemaSetupComplete = true;
|
|
1507
|
+
this.logger?.debug?.(`Schema "${this.schemaName}" is ready for use`);
|
|
1508
|
+
} catch (error) {
|
|
1509
|
+
this.schemaSetupComplete = void 0;
|
|
1510
|
+
this.setupSchemaPromise = null;
|
|
1511
|
+
throw error;
|
|
1512
|
+
} finally {
|
|
1513
|
+
this.setupSchemaPromise = null;
|
|
1514
|
+
}
|
|
1515
|
+
})();
|
|
1516
|
+
}
|
|
1517
|
+
await this.setupSchemaPromise;
|
|
1518
|
+
}
|
|
1519
|
+
async insert({
|
|
1520
|
+
tableName,
|
|
1521
|
+
record,
|
|
1522
|
+
transaction
|
|
1523
|
+
}) {
|
|
1524
|
+
try {
|
|
1525
|
+
const columns = Object.keys(record);
|
|
1526
|
+
const parsedColumns = columns.map((col) => parseSqlIdentifier(col, "column name"));
|
|
1527
|
+
const paramNames = columns.map((_, i) => `@param${i}`);
|
|
1528
|
+
const insertSql = `INSERT INTO ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} (${parsedColumns.map((c) => `[${c}]`).join(", ")}) VALUES (${paramNames.join(", ")})`;
|
|
1529
|
+
const request = transaction ? transaction.request() : this.pool.request();
|
|
1530
|
+
columns.forEach((col, i) => {
|
|
1531
|
+
const value = record[col];
|
|
1532
|
+
const preparedValue = this.prepareValue(value, col, tableName);
|
|
1533
|
+
if (preparedValue instanceof Date) {
|
|
1534
|
+
request.input(`param${i}`, sql2.DateTime2, preparedValue);
|
|
1535
|
+
} else if (preparedValue === null || preparedValue === void 0) {
|
|
1536
|
+
request.input(`param${i}`, this.getMssqlType(tableName, col), null);
|
|
1537
|
+
} else {
|
|
1538
|
+
request.input(`param${i}`, preparedValue);
|
|
1539
|
+
}
|
|
1540
|
+
});
|
|
1541
|
+
await request.query(insertSql);
|
|
1542
|
+
} catch (error) {
|
|
1543
|
+
throw new MastraError(
|
|
1544
|
+
{
|
|
1545
|
+
id: "MASTRA_STORAGE_MSSQL_STORE_INSERT_FAILED",
|
|
1546
|
+
domain: ErrorDomain.STORAGE,
|
|
1547
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1548
|
+
details: {
|
|
1549
|
+
tableName
|
|
1550
|
+
}
|
|
1551
|
+
},
|
|
1552
|
+
error
|
|
1553
|
+
);
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
async clearTable({ tableName }) {
|
|
1557
|
+
const fullTableName = getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) });
|
|
1558
|
+
try {
|
|
1559
|
+
try {
|
|
1560
|
+
await this.pool.request().query(`TRUNCATE TABLE ${fullTableName}`);
|
|
1561
|
+
} catch (truncateError) {
|
|
1562
|
+
if (truncateError?.number === 4712) {
|
|
1563
|
+
await this.pool.request().query(`DELETE FROM ${fullTableName}`);
|
|
1564
|
+
} else {
|
|
1565
|
+
throw truncateError;
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
} catch (error) {
|
|
1569
|
+
throw new MastraError(
|
|
1570
|
+
{
|
|
1571
|
+
id: "MASTRA_STORAGE_MSSQL_STORE_CLEAR_TABLE_FAILED",
|
|
1193
1572
|
domain: ErrorDomain.STORAGE,
|
|
1194
1573
|
category: ErrorCategory.THIRD_PARTY,
|
|
1195
1574
|
details: {
|
|
@@ -1203,9 +1582,11 @@ var StoreOperationsMSSQL = class extends StoreOperations {
|
|
|
1203
1582
|
getDefaultValue(type) {
|
|
1204
1583
|
switch (type) {
|
|
1205
1584
|
case "timestamp":
|
|
1206
|
-
return "DEFAULT
|
|
1585
|
+
return "DEFAULT SYSUTCDATETIME()";
|
|
1207
1586
|
case "jsonb":
|
|
1208
1587
|
return "DEFAULT N'{}'";
|
|
1588
|
+
case "boolean":
|
|
1589
|
+
return "DEFAULT 0";
|
|
1209
1590
|
default:
|
|
1210
1591
|
return super.getDefaultValue(type);
|
|
1211
1592
|
}
|
|
@@ -1216,13 +1597,29 @@ var StoreOperationsMSSQL = class extends StoreOperations {
|
|
|
1216
1597
|
}) {
|
|
1217
1598
|
try {
|
|
1218
1599
|
const uniqueConstraintColumns = tableName === TABLE_WORKFLOW_SNAPSHOT ? ["workflow_name", "run_id"] : [];
|
|
1600
|
+
const largeDataColumns = [
|
|
1601
|
+
"workingMemory",
|
|
1602
|
+
"snapshot",
|
|
1603
|
+
"metadata",
|
|
1604
|
+
"content",
|
|
1605
|
+
// messages.content - can be very long conversation content
|
|
1606
|
+
"input",
|
|
1607
|
+
// evals.input - test input data
|
|
1608
|
+
"output",
|
|
1609
|
+
// evals.output - test output data
|
|
1610
|
+
"instructions",
|
|
1611
|
+
// evals.instructions - evaluation instructions
|
|
1612
|
+
"other"
|
|
1613
|
+
// traces.other - additional trace data
|
|
1614
|
+
];
|
|
1219
1615
|
const columns = Object.entries(schema).map(([name, def]) => {
|
|
1220
1616
|
const parsedName = parseSqlIdentifier(name, "column name");
|
|
1221
1617
|
const constraints = [];
|
|
1222
1618
|
if (def.primaryKey) constraints.push("PRIMARY KEY");
|
|
1223
1619
|
if (!def.nullable) constraints.push("NOT NULL");
|
|
1224
1620
|
const isIndexed = !!def.primaryKey || uniqueConstraintColumns.includes(name);
|
|
1225
|
-
|
|
1621
|
+
const useLargeStorage = largeDataColumns.includes(name);
|
|
1622
|
+
return `[${parsedName}] ${this.getSqlType(def.type, isIndexed, useLargeStorage)} ${constraints.join(" ")}`.trim();
|
|
1226
1623
|
}).join(",\n");
|
|
1227
1624
|
if (this.schemaName) {
|
|
1228
1625
|
await this.setupSchema();
|
|
@@ -1309,7 +1706,19 @@ ${columns}
|
|
|
1309
1706
|
const columnExists = Array.isArray(checkResult.recordset) && checkResult.recordset.length > 0;
|
|
1310
1707
|
if (!columnExists) {
|
|
1311
1708
|
const columnDef = schema[columnName];
|
|
1312
|
-
const
|
|
1709
|
+
const largeDataColumns = [
|
|
1710
|
+
"workingMemory",
|
|
1711
|
+
"snapshot",
|
|
1712
|
+
"metadata",
|
|
1713
|
+
"content",
|
|
1714
|
+
"input",
|
|
1715
|
+
"output",
|
|
1716
|
+
"instructions",
|
|
1717
|
+
"other"
|
|
1718
|
+
];
|
|
1719
|
+
const useLargeStorage = largeDataColumns.includes(columnName);
|
|
1720
|
+
const isIndexed = !!columnDef.primaryKey;
|
|
1721
|
+
const sqlType = this.getSqlType(columnDef.type, isIndexed, useLargeStorage);
|
|
1313
1722
|
const nullable = columnDef.nullable === false ? "NOT NULL" : "";
|
|
1314
1723
|
const defaultValue = columnDef.nullable === false ? this.getDefaultValue(columnDef.type) : "";
|
|
1315
1724
|
const parsedColumnName = parseSqlIdentifier(columnName, "column name");
|
|
@@ -1318,120 +1727,660 @@ ${columns}
|
|
|
1318
1727
|
this.logger?.debug?.(`Ensured column ${parsedColumnName} exists in table ${fullTableName}`);
|
|
1319
1728
|
}
|
|
1320
1729
|
}
|
|
1321
|
-
}
|
|
1730
|
+
}
|
|
1731
|
+
} catch (error) {
|
|
1732
|
+
throw new MastraError(
|
|
1733
|
+
{
|
|
1734
|
+
id: "MASTRA_STORAGE_MSSQL_STORE_ALTER_TABLE_FAILED",
|
|
1735
|
+
domain: ErrorDomain.STORAGE,
|
|
1736
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1737
|
+
details: {
|
|
1738
|
+
tableName
|
|
1739
|
+
}
|
|
1740
|
+
},
|
|
1741
|
+
error
|
|
1742
|
+
);
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
async load({ tableName, keys }) {
|
|
1746
|
+
try {
|
|
1747
|
+
const keyEntries = Object.entries(keys).map(([key, value]) => [parseSqlIdentifier(key, "column name"), value]);
|
|
1748
|
+
const conditions = keyEntries.map(([key], i) => `[${key}] = @param${i}`).join(" AND ");
|
|
1749
|
+
const sql5 = `SELECT * FROM ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} WHERE ${conditions}`;
|
|
1750
|
+
const request = this.pool.request();
|
|
1751
|
+
keyEntries.forEach(([key, value], i) => {
|
|
1752
|
+
const preparedValue = this.prepareValue(value, key, tableName);
|
|
1753
|
+
if (preparedValue === null || preparedValue === void 0) {
|
|
1754
|
+
request.input(`param${i}`, this.getMssqlType(tableName, key), null);
|
|
1755
|
+
} else {
|
|
1756
|
+
request.input(`param${i}`, preparedValue);
|
|
1757
|
+
}
|
|
1758
|
+
});
|
|
1759
|
+
const resultSet = await request.query(sql5);
|
|
1760
|
+
const result = resultSet.recordset[0] || null;
|
|
1761
|
+
if (!result) {
|
|
1762
|
+
return null;
|
|
1763
|
+
}
|
|
1764
|
+
if (tableName === TABLE_WORKFLOW_SNAPSHOT) {
|
|
1765
|
+
const snapshot = result;
|
|
1766
|
+
if (typeof snapshot.snapshot === "string") {
|
|
1767
|
+
snapshot.snapshot = JSON.parse(snapshot.snapshot);
|
|
1768
|
+
}
|
|
1769
|
+
return snapshot;
|
|
1770
|
+
}
|
|
1771
|
+
return result;
|
|
1772
|
+
} catch (error) {
|
|
1773
|
+
throw new MastraError(
|
|
1774
|
+
{
|
|
1775
|
+
id: "MASTRA_STORAGE_MSSQL_STORE_LOAD_FAILED",
|
|
1776
|
+
domain: ErrorDomain.STORAGE,
|
|
1777
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1778
|
+
details: {
|
|
1779
|
+
tableName
|
|
1780
|
+
}
|
|
1781
|
+
},
|
|
1782
|
+
error
|
|
1783
|
+
);
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
async batchInsert({ tableName, records }) {
|
|
1787
|
+
const transaction = this.pool.transaction();
|
|
1788
|
+
try {
|
|
1789
|
+
await transaction.begin();
|
|
1790
|
+
for (const record of records) {
|
|
1791
|
+
await this.insert({ tableName, record, transaction });
|
|
1792
|
+
}
|
|
1793
|
+
await transaction.commit();
|
|
1794
|
+
} catch (error) {
|
|
1795
|
+
await transaction.rollback();
|
|
1796
|
+
throw new MastraError(
|
|
1797
|
+
{
|
|
1798
|
+
id: "MASTRA_STORAGE_MSSQL_STORE_BATCH_INSERT_FAILED",
|
|
1799
|
+
domain: ErrorDomain.STORAGE,
|
|
1800
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1801
|
+
details: {
|
|
1802
|
+
tableName,
|
|
1803
|
+
numberOfRecords: records.length
|
|
1804
|
+
}
|
|
1805
|
+
},
|
|
1806
|
+
error
|
|
1807
|
+
);
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
async dropTable({ tableName }) {
|
|
1811
|
+
try {
|
|
1812
|
+
const tableNameWithSchema = getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) });
|
|
1813
|
+
await this.pool.request().query(`DROP TABLE IF EXISTS ${tableNameWithSchema}`);
|
|
1814
|
+
} catch (error) {
|
|
1815
|
+
throw new MastraError(
|
|
1816
|
+
{
|
|
1817
|
+
id: "MASTRA_STORAGE_MSSQL_STORE_DROP_TABLE_FAILED",
|
|
1818
|
+
domain: ErrorDomain.STORAGE,
|
|
1819
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1820
|
+
details: {
|
|
1821
|
+
tableName
|
|
1822
|
+
}
|
|
1823
|
+
},
|
|
1824
|
+
error
|
|
1825
|
+
);
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1828
|
+
/**
|
|
1829
|
+
* Prepares a value for database operations, handling Date objects and JSON serialization
|
|
1830
|
+
*/
|
|
1831
|
+
prepareValue(value, columnName, tableName) {
|
|
1832
|
+
if (value === null || value === void 0) {
|
|
1833
|
+
return value;
|
|
1834
|
+
}
|
|
1835
|
+
if (value instanceof Date) {
|
|
1836
|
+
return value;
|
|
1837
|
+
}
|
|
1838
|
+
const schema = TABLE_SCHEMAS[tableName];
|
|
1839
|
+
const columnSchema = schema?.[columnName];
|
|
1840
|
+
if (columnSchema?.type === "boolean") {
|
|
1841
|
+
return value ? 1 : 0;
|
|
1842
|
+
}
|
|
1843
|
+
if (columnSchema?.type === "jsonb") {
|
|
1844
|
+
return JSON.stringify(value);
|
|
1845
|
+
}
|
|
1846
|
+
if (typeof value === "object") {
|
|
1847
|
+
return JSON.stringify(value);
|
|
1848
|
+
}
|
|
1849
|
+
return value;
|
|
1850
|
+
}
|
|
1851
|
+
/**
|
|
1852
|
+
* Maps TABLE_SCHEMAS types to mssql param types (used when value is null)
|
|
1853
|
+
*/
|
|
1854
|
+
getMssqlType(tableName, columnName) {
|
|
1855
|
+
const col = TABLE_SCHEMAS[tableName]?.[columnName];
|
|
1856
|
+
switch (col?.type) {
|
|
1857
|
+
case "text":
|
|
1858
|
+
return sql2.NVarChar;
|
|
1859
|
+
case "timestamp":
|
|
1860
|
+
return sql2.DateTime2;
|
|
1861
|
+
case "uuid":
|
|
1862
|
+
return sql2.UniqueIdentifier;
|
|
1863
|
+
case "jsonb":
|
|
1864
|
+
return sql2.NVarChar;
|
|
1865
|
+
case "integer":
|
|
1866
|
+
return sql2.Int;
|
|
1867
|
+
case "bigint":
|
|
1868
|
+
return sql2.BigInt;
|
|
1869
|
+
case "float":
|
|
1870
|
+
return sql2.Float;
|
|
1871
|
+
case "boolean":
|
|
1872
|
+
return sql2.Bit;
|
|
1873
|
+
default:
|
|
1874
|
+
return sql2.NVarChar;
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
/**
|
|
1878
|
+
* Update a single record in the database
|
|
1879
|
+
*/
|
|
1880
|
+
async update({
|
|
1881
|
+
tableName,
|
|
1882
|
+
keys,
|
|
1883
|
+
data,
|
|
1884
|
+
transaction
|
|
1885
|
+
}) {
|
|
1886
|
+
try {
|
|
1887
|
+
if (!data || Object.keys(data).length === 0) {
|
|
1888
|
+
throw new MastraError({
|
|
1889
|
+
id: "MASTRA_STORAGE_MSSQL_UPDATE_EMPTY_DATA",
|
|
1890
|
+
domain: ErrorDomain.STORAGE,
|
|
1891
|
+
category: ErrorCategory.USER,
|
|
1892
|
+
text: "Cannot update with empty data payload"
|
|
1893
|
+
});
|
|
1894
|
+
}
|
|
1895
|
+
if (!keys || Object.keys(keys).length === 0) {
|
|
1896
|
+
throw new MastraError({
|
|
1897
|
+
id: "MASTRA_STORAGE_MSSQL_UPDATE_EMPTY_KEYS",
|
|
1898
|
+
domain: ErrorDomain.STORAGE,
|
|
1899
|
+
category: ErrorCategory.USER,
|
|
1900
|
+
text: "Cannot update without keys to identify records"
|
|
1901
|
+
});
|
|
1902
|
+
}
|
|
1903
|
+
const setClauses = [];
|
|
1904
|
+
const request = transaction ? transaction.request() : this.pool.request();
|
|
1905
|
+
let paramIndex = 0;
|
|
1906
|
+
Object.entries(data).forEach(([key, value]) => {
|
|
1907
|
+
const parsedKey = parseSqlIdentifier(key, "column name");
|
|
1908
|
+
const paramName = `set${paramIndex++}`;
|
|
1909
|
+
setClauses.push(`[${parsedKey}] = @${paramName}`);
|
|
1910
|
+
const preparedValue = this.prepareValue(value, key, tableName);
|
|
1911
|
+
if (preparedValue === null || preparedValue === void 0) {
|
|
1912
|
+
request.input(paramName, this.getMssqlType(tableName, key), null);
|
|
1913
|
+
} else {
|
|
1914
|
+
request.input(paramName, preparedValue);
|
|
1915
|
+
}
|
|
1916
|
+
});
|
|
1917
|
+
const whereConditions = [];
|
|
1918
|
+
Object.entries(keys).forEach(([key, value]) => {
|
|
1919
|
+
const parsedKey = parseSqlIdentifier(key, "column name");
|
|
1920
|
+
const paramName = `where${paramIndex++}`;
|
|
1921
|
+
whereConditions.push(`[${parsedKey}] = @${paramName}`);
|
|
1922
|
+
const preparedValue = this.prepareValue(value, key, tableName);
|
|
1923
|
+
if (preparedValue === null || preparedValue === void 0) {
|
|
1924
|
+
request.input(paramName, this.getMssqlType(tableName, key), null);
|
|
1925
|
+
} else {
|
|
1926
|
+
request.input(paramName, preparedValue);
|
|
1927
|
+
}
|
|
1928
|
+
});
|
|
1929
|
+
const tableName_ = getTableName({
|
|
1930
|
+
indexName: tableName,
|
|
1931
|
+
schemaName: getSchemaName(this.schemaName)
|
|
1932
|
+
});
|
|
1933
|
+
const updateSql = `UPDATE ${tableName_} SET ${setClauses.join(", ")} WHERE ${whereConditions.join(" AND ")}`;
|
|
1934
|
+
await request.query(updateSql);
|
|
1935
|
+
} catch (error) {
|
|
1936
|
+
throw new MastraError(
|
|
1937
|
+
{
|
|
1938
|
+
id: "MASTRA_STORAGE_MSSQL_STORE_UPDATE_FAILED",
|
|
1939
|
+
domain: ErrorDomain.STORAGE,
|
|
1940
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1941
|
+
details: {
|
|
1942
|
+
tableName
|
|
1943
|
+
}
|
|
1944
|
+
},
|
|
1945
|
+
error
|
|
1946
|
+
);
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
/**
|
|
1950
|
+
* Update multiple records in a single batch transaction
|
|
1951
|
+
*/
|
|
1952
|
+
async batchUpdate({
|
|
1953
|
+
tableName,
|
|
1954
|
+
updates
|
|
1955
|
+
}) {
|
|
1956
|
+
const transaction = this.pool.transaction();
|
|
1957
|
+
try {
|
|
1958
|
+
await transaction.begin();
|
|
1959
|
+
for (const { keys, data } of updates) {
|
|
1960
|
+
await this.update({ tableName, keys, data, transaction });
|
|
1961
|
+
}
|
|
1962
|
+
await transaction.commit();
|
|
1963
|
+
} catch (error) {
|
|
1964
|
+
await transaction.rollback();
|
|
1965
|
+
throw new MastraError(
|
|
1966
|
+
{
|
|
1967
|
+
id: "MASTRA_STORAGE_MSSQL_STORE_BATCH_UPDATE_FAILED",
|
|
1968
|
+
domain: ErrorDomain.STORAGE,
|
|
1969
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1970
|
+
details: {
|
|
1971
|
+
tableName,
|
|
1972
|
+
numberOfRecords: updates.length
|
|
1973
|
+
}
|
|
1974
|
+
},
|
|
1975
|
+
error
|
|
1976
|
+
);
|
|
1977
|
+
}
|
|
1978
|
+
}
|
|
1979
|
+
/**
|
|
1980
|
+
* Delete multiple records by keys
|
|
1981
|
+
*/
|
|
1982
|
+
async batchDelete({ tableName, keys }) {
|
|
1983
|
+
if (keys.length === 0) {
|
|
1984
|
+
return;
|
|
1985
|
+
}
|
|
1986
|
+
const tableName_ = getTableName({
|
|
1987
|
+
indexName: tableName,
|
|
1988
|
+
schemaName: getSchemaName(this.schemaName)
|
|
1989
|
+
});
|
|
1990
|
+
const transaction = this.pool.transaction();
|
|
1991
|
+
try {
|
|
1992
|
+
await transaction.begin();
|
|
1993
|
+
for (const keySet of keys) {
|
|
1994
|
+
const conditions = [];
|
|
1995
|
+
const request = transaction.request();
|
|
1996
|
+
let paramIndex = 0;
|
|
1997
|
+
Object.entries(keySet).forEach(([key, value]) => {
|
|
1998
|
+
const parsedKey = parseSqlIdentifier(key, "column name");
|
|
1999
|
+
const paramName = `p${paramIndex++}`;
|
|
2000
|
+
conditions.push(`[${parsedKey}] = @${paramName}`);
|
|
2001
|
+
const preparedValue = this.prepareValue(value, key, tableName);
|
|
2002
|
+
if (preparedValue === null || preparedValue === void 0) {
|
|
2003
|
+
request.input(paramName, this.getMssqlType(tableName, key), null);
|
|
2004
|
+
} else {
|
|
2005
|
+
request.input(paramName, preparedValue);
|
|
2006
|
+
}
|
|
2007
|
+
});
|
|
2008
|
+
const deleteSql = `DELETE FROM ${tableName_} WHERE ${conditions.join(" AND ")}`;
|
|
2009
|
+
await request.query(deleteSql);
|
|
2010
|
+
}
|
|
2011
|
+
await transaction.commit();
|
|
2012
|
+
} catch (error) {
|
|
2013
|
+
await transaction.rollback();
|
|
2014
|
+
throw new MastraError(
|
|
2015
|
+
{
|
|
2016
|
+
id: "MASTRA_STORAGE_MSSQL_STORE_BATCH_DELETE_FAILED",
|
|
2017
|
+
domain: ErrorDomain.STORAGE,
|
|
2018
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2019
|
+
details: {
|
|
2020
|
+
tableName,
|
|
2021
|
+
numberOfRecords: keys.length
|
|
2022
|
+
}
|
|
2023
|
+
},
|
|
2024
|
+
error
|
|
2025
|
+
);
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
/**
|
|
2029
|
+
* Create a new index on a table
|
|
2030
|
+
*/
|
|
2031
|
+
async createIndex(options) {
|
|
2032
|
+
try {
|
|
2033
|
+
const { name, table, columns, unique = false, where } = options;
|
|
2034
|
+
const schemaName = this.schemaName || "dbo";
|
|
2035
|
+
const fullTableName = getTableName({
|
|
2036
|
+
indexName: table,
|
|
2037
|
+
schemaName: getSchemaName(this.schemaName)
|
|
2038
|
+
});
|
|
2039
|
+
const indexNameSafe = parseSqlIdentifier(name, "index name");
|
|
2040
|
+
const checkRequest = this.pool.request();
|
|
2041
|
+
checkRequest.input("indexName", indexNameSafe);
|
|
2042
|
+
checkRequest.input("schemaName", schemaName);
|
|
2043
|
+
checkRequest.input("tableName", table);
|
|
2044
|
+
const indexExists = await checkRequest.query(`
|
|
2045
|
+
SELECT 1 as found
|
|
2046
|
+
FROM sys.indexes i
|
|
2047
|
+
INNER JOIN sys.tables t ON i.object_id = t.object_id
|
|
2048
|
+
INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
|
|
2049
|
+
WHERE i.name = @indexName
|
|
2050
|
+
AND s.name = @schemaName
|
|
2051
|
+
AND t.name = @tableName
|
|
2052
|
+
`);
|
|
2053
|
+
if (indexExists.recordset && indexExists.recordset.length > 0) {
|
|
2054
|
+
return;
|
|
2055
|
+
}
|
|
2056
|
+
const uniqueStr = unique ? "UNIQUE " : "";
|
|
2057
|
+
const columnsStr = columns.map((col) => {
|
|
2058
|
+
if (col.includes(" DESC") || col.includes(" ASC")) {
|
|
2059
|
+
const [colName, ...modifiers] = col.split(" ");
|
|
2060
|
+
if (!colName) {
|
|
2061
|
+
throw new Error(`Invalid column specification: ${col}`);
|
|
2062
|
+
}
|
|
2063
|
+
return `[${parseSqlIdentifier(colName, "column name")}] ${modifiers.join(" ")}`;
|
|
2064
|
+
}
|
|
2065
|
+
return `[${parseSqlIdentifier(col, "column name")}]`;
|
|
2066
|
+
}).join(", ");
|
|
2067
|
+
const whereStr = where ? ` WHERE ${where}` : "";
|
|
2068
|
+
const createIndexSql = `CREATE ${uniqueStr}INDEX [${indexNameSafe}] ON ${fullTableName} (${columnsStr})${whereStr}`;
|
|
2069
|
+
await this.pool.request().query(createIndexSql);
|
|
1322
2070
|
} catch (error) {
|
|
1323
2071
|
throw new MastraError(
|
|
1324
2072
|
{
|
|
1325
|
-
id: "
|
|
2073
|
+
id: "MASTRA_STORAGE_MSSQL_INDEX_CREATE_FAILED",
|
|
1326
2074
|
domain: ErrorDomain.STORAGE,
|
|
1327
2075
|
category: ErrorCategory.THIRD_PARTY,
|
|
1328
2076
|
details: {
|
|
1329
|
-
|
|
2077
|
+
indexName: options.name,
|
|
2078
|
+
tableName: options.table
|
|
1330
2079
|
}
|
|
1331
2080
|
},
|
|
1332
2081
|
error
|
|
1333
2082
|
);
|
|
1334
2083
|
}
|
|
1335
2084
|
}
|
|
1336
|
-
|
|
2085
|
+
/**
|
|
2086
|
+
* Drop an existing index
|
|
2087
|
+
*/
|
|
2088
|
+
async dropIndex(indexName) {
|
|
1337
2089
|
try {
|
|
1338
|
-
const
|
|
1339
|
-
const
|
|
1340
|
-
const
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
2090
|
+
const schemaName = this.schemaName || "dbo";
|
|
2091
|
+
const indexNameSafe = parseSqlIdentifier(indexName, "index name");
|
|
2092
|
+
const checkRequest = this.pool.request();
|
|
2093
|
+
checkRequest.input("indexName", indexNameSafe);
|
|
2094
|
+
checkRequest.input("schemaName", schemaName);
|
|
2095
|
+
const result = await checkRequest.query(`
|
|
2096
|
+
SELECT t.name as table_name
|
|
2097
|
+
FROM sys.indexes i
|
|
2098
|
+
INNER JOIN sys.tables t ON i.object_id = t.object_id
|
|
2099
|
+
INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
|
|
2100
|
+
WHERE i.name = @indexName
|
|
2101
|
+
AND s.name = @schemaName
|
|
2102
|
+
`);
|
|
2103
|
+
if (!result.recordset || result.recordset.length === 0) {
|
|
2104
|
+
return;
|
|
1350
2105
|
}
|
|
1351
|
-
if (
|
|
1352
|
-
const
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
2106
|
+
if (result.recordset.length > 1) {
|
|
2107
|
+
const tables = result.recordset.map((r) => r.table_name).join(", ");
|
|
2108
|
+
throw new MastraError({
|
|
2109
|
+
id: "MASTRA_STORAGE_MSSQL_INDEX_AMBIGUOUS",
|
|
2110
|
+
domain: ErrorDomain.STORAGE,
|
|
2111
|
+
category: ErrorCategory.USER,
|
|
2112
|
+
text: `Index "${indexNameSafe}" exists on multiple tables (${tables}) in schema "${schemaName}". Please drop indexes manually or ensure unique index names.`
|
|
2113
|
+
});
|
|
1357
2114
|
}
|
|
1358
|
-
|
|
2115
|
+
const tableName = result.recordset[0].table_name;
|
|
2116
|
+
const fullTableName = getTableName({
|
|
2117
|
+
indexName: tableName,
|
|
2118
|
+
schemaName: getSchemaName(this.schemaName)
|
|
2119
|
+
});
|
|
2120
|
+
const dropSql = `DROP INDEX [${indexNameSafe}] ON ${fullTableName}`;
|
|
2121
|
+
await this.pool.request().query(dropSql);
|
|
1359
2122
|
} catch (error) {
|
|
1360
2123
|
throw new MastraError(
|
|
1361
2124
|
{
|
|
1362
|
-
id: "
|
|
2125
|
+
id: "MASTRA_STORAGE_MSSQL_INDEX_DROP_FAILED",
|
|
1363
2126
|
domain: ErrorDomain.STORAGE,
|
|
1364
2127
|
category: ErrorCategory.THIRD_PARTY,
|
|
1365
2128
|
details: {
|
|
1366
|
-
|
|
2129
|
+
indexName
|
|
1367
2130
|
}
|
|
1368
2131
|
},
|
|
1369
2132
|
error
|
|
1370
2133
|
);
|
|
1371
2134
|
}
|
|
1372
2135
|
}
|
|
1373
|
-
|
|
1374
|
-
|
|
2136
|
+
/**
|
|
2137
|
+
* List indexes for a specific table or all tables
|
|
2138
|
+
*/
|
|
2139
|
+
async listIndexes(tableName) {
|
|
1375
2140
|
try {
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
2141
|
+
const schemaName = this.schemaName || "dbo";
|
|
2142
|
+
let query;
|
|
2143
|
+
const request = this.pool.request();
|
|
2144
|
+
request.input("schemaName", schemaName);
|
|
2145
|
+
if (tableName) {
|
|
2146
|
+
query = `
|
|
2147
|
+
SELECT
|
|
2148
|
+
i.name as name,
|
|
2149
|
+
o.name as [table],
|
|
2150
|
+
i.is_unique as is_unique,
|
|
2151
|
+
CAST(SUM(s.used_page_count) * 8 / 1024.0 AS VARCHAR(50)) + ' MB' as size
|
|
2152
|
+
FROM sys.indexes i
|
|
2153
|
+
INNER JOIN sys.objects o ON i.object_id = o.object_id
|
|
2154
|
+
INNER JOIN sys.schemas sch ON o.schema_id = sch.schema_id
|
|
2155
|
+
LEFT JOIN sys.dm_db_partition_stats s ON i.object_id = s.object_id AND i.index_id = s.index_id
|
|
2156
|
+
WHERE sch.name = @schemaName
|
|
2157
|
+
AND o.name = @tableName
|
|
2158
|
+
AND i.name IS NOT NULL
|
|
2159
|
+
GROUP BY i.name, o.name, i.is_unique
|
|
2160
|
+
`;
|
|
2161
|
+
request.input("tableName", tableName);
|
|
2162
|
+
} else {
|
|
2163
|
+
query = `
|
|
2164
|
+
SELECT
|
|
2165
|
+
i.name as name,
|
|
2166
|
+
o.name as [table],
|
|
2167
|
+
i.is_unique as is_unique,
|
|
2168
|
+
CAST(SUM(s.used_page_count) * 8 / 1024.0 AS VARCHAR(50)) + ' MB' as size
|
|
2169
|
+
FROM sys.indexes i
|
|
2170
|
+
INNER JOIN sys.objects o ON i.object_id = o.object_id
|
|
2171
|
+
INNER JOIN sys.schemas sch ON o.schema_id = sch.schema_id
|
|
2172
|
+
LEFT JOIN sys.dm_db_partition_stats s ON i.object_id = s.object_id AND i.index_id = s.index_id
|
|
2173
|
+
WHERE sch.name = @schemaName
|
|
2174
|
+
AND i.name IS NOT NULL
|
|
2175
|
+
GROUP BY i.name, o.name, i.is_unique
|
|
2176
|
+
`;
|
|
1379
2177
|
}
|
|
1380
|
-
await
|
|
2178
|
+
const result = await request.query(query);
|
|
2179
|
+
const indexes = [];
|
|
2180
|
+
for (const row of result.recordset) {
|
|
2181
|
+
const colRequest = this.pool.request();
|
|
2182
|
+
colRequest.input("indexName", row.name);
|
|
2183
|
+
colRequest.input("schemaName", schemaName);
|
|
2184
|
+
const colResult = await colRequest.query(`
|
|
2185
|
+
SELECT c.name as column_name
|
|
2186
|
+
FROM sys.indexes i
|
|
2187
|
+
INNER JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
|
|
2188
|
+
INNER JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
|
|
2189
|
+
INNER JOIN sys.objects o ON i.object_id = o.object_id
|
|
2190
|
+
INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
|
|
2191
|
+
WHERE i.name = @indexName
|
|
2192
|
+
AND s.name = @schemaName
|
|
2193
|
+
ORDER BY ic.key_ordinal
|
|
2194
|
+
`);
|
|
2195
|
+
indexes.push({
|
|
2196
|
+
name: row.name,
|
|
2197
|
+
table: row.table,
|
|
2198
|
+
columns: colResult.recordset.map((c) => c.column_name),
|
|
2199
|
+
unique: row.is_unique || false,
|
|
2200
|
+
size: row.size || "0 MB",
|
|
2201
|
+
definition: ""
|
|
2202
|
+
// MSSQL doesn't store definition like PG
|
|
2203
|
+
});
|
|
2204
|
+
}
|
|
2205
|
+
return indexes;
|
|
1381
2206
|
} catch (error) {
|
|
1382
|
-
await transaction.rollback();
|
|
1383
2207
|
throw new MastraError(
|
|
1384
2208
|
{
|
|
1385
|
-
id: "
|
|
2209
|
+
id: "MASTRA_STORAGE_MSSQL_INDEX_LIST_FAILED",
|
|
1386
2210
|
domain: ErrorDomain.STORAGE,
|
|
1387
2211
|
category: ErrorCategory.THIRD_PARTY,
|
|
1388
|
-
details: {
|
|
1389
|
-
tableName
|
|
1390
|
-
|
|
1391
|
-
}
|
|
2212
|
+
details: tableName ? {
|
|
2213
|
+
tableName
|
|
2214
|
+
} : {}
|
|
1392
2215
|
},
|
|
1393
2216
|
error
|
|
1394
2217
|
);
|
|
1395
2218
|
}
|
|
1396
2219
|
}
|
|
1397
|
-
|
|
2220
|
+
/**
|
|
2221
|
+
* Get detailed statistics for a specific index
|
|
2222
|
+
*/
|
|
2223
|
+
async describeIndex(indexName) {
|
|
1398
2224
|
try {
|
|
1399
|
-
const
|
|
1400
|
-
|
|
2225
|
+
const schemaName = this.schemaName || "dbo";
|
|
2226
|
+
const request = this.pool.request();
|
|
2227
|
+
request.input("indexName", indexName);
|
|
2228
|
+
request.input("schemaName", schemaName);
|
|
2229
|
+
const query = `
|
|
2230
|
+
SELECT
|
|
2231
|
+
i.name as name,
|
|
2232
|
+
o.name as [table],
|
|
2233
|
+
i.is_unique as is_unique,
|
|
2234
|
+
CAST(SUM(s.used_page_count) * 8 / 1024.0 AS VARCHAR(50)) + ' MB' as size,
|
|
2235
|
+
i.type_desc as method,
|
|
2236
|
+
ISNULL(us.user_scans, 0) as scans,
|
|
2237
|
+
ISNULL(us.user_seeks + us.user_scans, 0) as tuples_read,
|
|
2238
|
+
ISNULL(us.user_lookups, 0) as tuples_fetched
|
|
2239
|
+
FROM sys.indexes i
|
|
2240
|
+
INNER JOIN sys.objects o ON i.object_id = o.object_id
|
|
2241
|
+
INNER JOIN sys.schemas sch ON o.schema_id = sch.schema_id
|
|
2242
|
+
LEFT JOIN sys.dm_db_partition_stats s ON i.object_id = s.object_id AND i.index_id = s.index_id
|
|
2243
|
+
LEFT JOIN sys.dm_db_index_usage_stats us ON i.object_id = us.object_id AND i.index_id = us.index_id
|
|
2244
|
+
WHERE i.name = @indexName
|
|
2245
|
+
AND sch.name = @schemaName
|
|
2246
|
+
GROUP BY i.name, o.name, i.is_unique, i.type_desc, us.user_seeks, us.user_scans, us.user_lookups
|
|
2247
|
+
`;
|
|
2248
|
+
const result = await request.query(query);
|
|
2249
|
+
if (!result.recordset || result.recordset.length === 0) {
|
|
2250
|
+
throw new Error(`Index "${indexName}" not found in schema "${schemaName}"`);
|
|
2251
|
+
}
|
|
2252
|
+
const row = result.recordset[0];
|
|
2253
|
+
const colRequest = this.pool.request();
|
|
2254
|
+
colRequest.input("indexName", indexName);
|
|
2255
|
+
colRequest.input("schemaName", schemaName);
|
|
2256
|
+
const colResult = await colRequest.query(`
|
|
2257
|
+
SELECT c.name as column_name
|
|
2258
|
+
FROM sys.indexes i
|
|
2259
|
+
INNER JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
|
|
2260
|
+
INNER JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
|
|
2261
|
+
INNER JOIN sys.objects o ON i.object_id = o.object_id
|
|
2262
|
+
INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
|
|
2263
|
+
WHERE i.name = @indexName
|
|
2264
|
+
AND s.name = @schemaName
|
|
2265
|
+
ORDER BY ic.key_ordinal
|
|
2266
|
+
`);
|
|
2267
|
+
return {
|
|
2268
|
+
name: row.name,
|
|
2269
|
+
table: row.table,
|
|
2270
|
+
columns: colResult.recordset.map((c) => c.column_name),
|
|
2271
|
+
unique: row.is_unique || false,
|
|
2272
|
+
size: row.size || "0 MB",
|
|
2273
|
+
definition: "",
|
|
2274
|
+
method: row.method?.toLowerCase() || "nonclustered",
|
|
2275
|
+
scans: Number(row.scans) || 0,
|
|
2276
|
+
tuples_read: Number(row.tuples_read) || 0,
|
|
2277
|
+
tuples_fetched: Number(row.tuples_fetched) || 0
|
|
2278
|
+
};
|
|
1401
2279
|
} catch (error) {
|
|
1402
2280
|
throw new MastraError(
|
|
1403
2281
|
{
|
|
1404
|
-
id: "
|
|
2282
|
+
id: "MASTRA_STORAGE_MSSQL_INDEX_DESCRIBE_FAILED",
|
|
1405
2283
|
domain: ErrorDomain.STORAGE,
|
|
1406
2284
|
category: ErrorCategory.THIRD_PARTY,
|
|
1407
2285
|
details: {
|
|
1408
|
-
|
|
2286
|
+
indexName
|
|
1409
2287
|
}
|
|
1410
2288
|
},
|
|
1411
2289
|
error
|
|
1412
2290
|
);
|
|
1413
2291
|
}
|
|
1414
2292
|
}
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
2293
|
+
/**
|
|
2294
|
+
* Returns definitions for automatic performance indexes
|
|
2295
|
+
* IMPORTANT: Uses seq_id DESC instead of createdAt DESC for MSSQL due to millisecond accuracy limitations
|
|
2296
|
+
* NOTE: Using NVARCHAR(400) for text columns (800 bytes) leaves room for composite indexes
|
|
2297
|
+
*/
|
|
2298
|
+
getAutomaticIndexDefinitions() {
|
|
2299
|
+
const schemaPrefix = this.schemaName ? `${this.schemaName}_` : "";
|
|
2300
|
+
return [
|
|
2301
|
+
// Composite indexes for optimal filtering + sorting performance
|
|
2302
|
+
// NVARCHAR(400) = 800 bytes, plus BIGINT (8 bytes) = 808 bytes total (under 900-byte limit)
|
|
2303
|
+
{
|
|
2304
|
+
name: `${schemaPrefix}mastra_threads_resourceid_seqid_idx`,
|
|
2305
|
+
table: TABLE_THREADS,
|
|
2306
|
+
columns: ["resourceId", "seq_id DESC"]
|
|
2307
|
+
},
|
|
2308
|
+
{
|
|
2309
|
+
name: `${schemaPrefix}mastra_messages_thread_id_seqid_idx`,
|
|
2310
|
+
table: TABLE_MESSAGES,
|
|
2311
|
+
columns: ["thread_id", "seq_id DESC"]
|
|
2312
|
+
},
|
|
2313
|
+
{
|
|
2314
|
+
name: `${schemaPrefix}mastra_traces_name_seqid_idx`,
|
|
2315
|
+
table: TABLE_TRACES,
|
|
2316
|
+
columns: ["name", "seq_id DESC"]
|
|
2317
|
+
},
|
|
2318
|
+
{
|
|
2319
|
+
name: `${schemaPrefix}mastra_scores_trace_id_span_id_seqid_idx`,
|
|
2320
|
+
table: TABLE_SCORERS,
|
|
2321
|
+
columns: ["traceId", "spanId", "seq_id DESC"]
|
|
2322
|
+
},
|
|
2323
|
+
// AI Spans indexes for optimal trace querying
|
|
2324
|
+
{
|
|
2325
|
+
name: `${schemaPrefix}mastra_ai_spans_traceid_startedat_idx`,
|
|
2326
|
+
table: TABLE_AI_SPANS,
|
|
2327
|
+
columns: ["traceId", "startedAt DESC"]
|
|
2328
|
+
},
|
|
2329
|
+
{
|
|
2330
|
+
name: `${schemaPrefix}mastra_ai_spans_parentspanid_startedat_idx`,
|
|
2331
|
+
table: TABLE_AI_SPANS,
|
|
2332
|
+
columns: ["parentSpanId", "startedAt DESC"]
|
|
2333
|
+
},
|
|
2334
|
+
{
|
|
2335
|
+
name: `${schemaPrefix}mastra_ai_spans_name_idx`,
|
|
2336
|
+
table: TABLE_AI_SPANS,
|
|
2337
|
+
columns: ["name"]
|
|
2338
|
+
},
|
|
2339
|
+
{
|
|
2340
|
+
name: `${schemaPrefix}mastra_ai_spans_spantype_startedat_idx`,
|
|
2341
|
+
table: TABLE_AI_SPANS,
|
|
2342
|
+
columns: ["spanType", "startedAt DESC"]
|
|
2343
|
+
}
|
|
2344
|
+
];
|
|
1421
2345
|
}
|
|
1422
|
-
|
|
2346
|
+
/**
|
|
2347
|
+
* Creates automatic indexes for optimal query performance
|
|
2348
|
+
* Uses getAutomaticIndexDefinitions() to determine which indexes to create
|
|
2349
|
+
*/
|
|
2350
|
+
async createAutomaticIndexes() {
|
|
2351
|
+
try {
|
|
2352
|
+
const indexes = this.getAutomaticIndexDefinitions();
|
|
2353
|
+
for (const indexOptions of indexes) {
|
|
2354
|
+
try {
|
|
2355
|
+
await this.createIndex(indexOptions);
|
|
2356
|
+
} catch (error) {
|
|
2357
|
+
this.logger?.warn?.(`Failed to create index ${indexOptions.name}:`, error);
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2360
|
+
} catch (error) {
|
|
2361
|
+
throw new MastraError(
|
|
2362
|
+
{
|
|
2363
|
+
id: "MASTRA_STORAGE_MSSQL_STORE_CREATE_PERFORMANCE_INDEXES_FAILED",
|
|
2364
|
+
domain: ErrorDomain.STORAGE,
|
|
2365
|
+
category: ErrorCategory.THIRD_PARTY
|
|
2366
|
+
},
|
|
2367
|
+
error
|
|
2368
|
+
);
|
|
2369
|
+
}
|
|
2370
|
+
}
|
|
2371
|
+
};
|
|
1423
2372
|
function transformScoreRow(row) {
|
|
1424
2373
|
return {
|
|
1425
2374
|
...row,
|
|
1426
|
-
input:
|
|
1427
|
-
scorer:
|
|
1428
|
-
preprocessStepResult:
|
|
1429
|
-
analyzeStepResult:
|
|
1430
|
-
metadata:
|
|
1431
|
-
output:
|
|
1432
|
-
additionalContext:
|
|
1433
|
-
|
|
1434
|
-
entity:
|
|
2375
|
+
input: safelyParseJSON(row.input),
|
|
2376
|
+
scorer: safelyParseJSON(row.scorer),
|
|
2377
|
+
preprocessStepResult: safelyParseJSON(row.preprocessStepResult),
|
|
2378
|
+
analyzeStepResult: safelyParseJSON(row.analyzeStepResult),
|
|
2379
|
+
metadata: safelyParseJSON(row.metadata),
|
|
2380
|
+
output: safelyParseJSON(row.output),
|
|
2381
|
+
additionalContext: safelyParseJSON(row.additionalContext),
|
|
2382
|
+
requestContext: safelyParseJSON(row.requestContext),
|
|
2383
|
+
entity: safelyParseJSON(row.entity),
|
|
1435
2384
|
createdAt: row.createdAt,
|
|
1436
2385
|
updatedAt: row.updatedAt
|
|
1437
2386
|
};
|
|
@@ -1488,7 +2437,7 @@ var ScoresMSSQL = class extends ScoresStorage {
|
|
|
1488
2437
|
);
|
|
1489
2438
|
}
|
|
1490
2439
|
try {
|
|
1491
|
-
const scoreId =
|
|
2440
|
+
const scoreId = randomUUID();
|
|
1492
2441
|
const {
|
|
1493
2442
|
scorer,
|
|
1494
2443
|
preprocessStepResult,
|
|
@@ -1497,7 +2446,7 @@ var ScoresMSSQL = class extends ScoresStorage {
|
|
|
1497
2446
|
input,
|
|
1498
2447
|
output,
|
|
1499
2448
|
additionalContext,
|
|
1500
|
-
|
|
2449
|
+
requestContext,
|
|
1501
2450
|
entity,
|
|
1502
2451
|
...rest
|
|
1503
2452
|
} = validatedScore;
|
|
@@ -1506,15 +2455,15 @@ var ScoresMSSQL = class extends ScoresStorage {
|
|
|
1506
2455
|
record: {
|
|
1507
2456
|
id: scoreId,
|
|
1508
2457
|
...rest,
|
|
1509
|
-
input:
|
|
1510
|
-
output:
|
|
1511
|
-
preprocessStepResult: preprocessStepResult
|
|
1512
|
-
analyzeStepResult: analyzeStepResult
|
|
1513
|
-
metadata: metadata
|
|
1514
|
-
additionalContext: additionalContext
|
|
1515
|
-
|
|
1516
|
-
entity: entity
|
|
1517
|
-
scorer: scorer
|
|
2458
|
+
input: input || "",
|
|
2459
|
+
output: output || "",
|
|
2460
|
+
preprocessStepResult: preprocessStepResult || null,
|
|
2461
|
+
analyzeStepResult: analyzeStepResult || null,
|
|
2462
|
+
metadata: metadata || null,
|
|
2463
|
+
additionalContext: additionalContext || null,
|
|
2464
|
+
requestContext: requestContext || null,
|
|
2465
|
+
entity: entity || null,
|
|
2466
|
+
scorer: scorer || null,
|
|
1518
2467
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1519
2468
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1520
2469
|
}
|
|
@@ -1534,14 +2483,37 @@ var ScoresMSSQL = class extends ScoresStorage {
|
|
|
1534
2483
|
}
|
|
1535
2484
|
async getScoresByScorerId({
|
|
1536
2485
|
scorerId,
|
|
1537
|
-
pagination
|
|
2486
|
+
pagination,
|
|
2487
|
+
entityId,
|
|
2488
|
+
entityType,
|
|
2489
|
+
source
|
|
1538
2490
|
}) {
|
|
1539
2491
|
try {
|
|
1540
|
-
const
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
2492
|
+
const conditions = ["[scorerId] = @p1"];
|
|
2493
|
+
const params = { p1: scorerId };
|
|
2494
|
+
let paramIndex = 2;
|
|
2495
|
+
if (entityId) {
|
|
2496
|
+
conditions.push(`[entityId] = @p${paramIndex}`);
|
|
2497
|
+
params[`p${paramIndex}`] = entityId;
|
|
2498
|
+
paramIndex++;
|
|
2499
|
+
}
|
|
2500
|
+
if (entityType) {
|
|
2501
|
+
conditions.push(`[entityType] = @p${paramIndex}`);
|
|
2502
|
+
params[`p${paramIndex}`] = entityType;
|
|
2503
|
+
paramIndex++;
|
|
2504
|
+
}
|
|
2505
|
+
if (source) {
|
|
2506
|
+
conditions.push(`[source] = @p${paramIndex}`);
|
|
2507
|
+
params[`p${paramIndex}`] = source;
|
|
2508
|
+
paramIndex++;
|
|
2509
|
+
}
|
|
2510
|
+
const whereClause = conditions.join(" AND ");
|
|
2511
|
+
const tableName = getTableName({ indexName: TABLE_SCORERS, schemaName: getSchemaName(this.schema) });
|
|
2512
|
+
const countRequest = this.pool.request();
|
|
2513
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
2514
|
+
countRequest.input(key, value);
|
|
2515
|
+
});
|
|
2516
|
+
const totalResult = await countRequest.query(`SELECT COUNT(*) as count FROM ${tableName} WHERE ${whereClause}`);
|
|
1545
2517
|
const total = totalResult.recordset[0]?.count || 0;
|
|
1546
2518
|
if (total === 0) {
|
|
1547
2519
|
return {
|
|
@@ -1555,12 +2527,13 @@ var ScoresMSSQL = class extends ScoresStorage {
|
|
|
1555
2527
|
};
|
|
1556
2528
|
}
|
|
1557
2529
|
const dataRequest = this.pool.request();
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
2530
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
2531
|
+
dataRequest.input(key, value);
|
|
2532
|
+
});
|
|
2533
|
+
dataRequest.input("perPage", pagination.perPage);
|
|
2534
|
+
dataRequest.input("offset", pagination.page * pagination.perPage);
|
|
2535
|
+
const dataQuery = `SELECT * FROM ${tableName} WHERE ${whereClause} ORDER BY [createdAt] DESC OFFSET @offset ROWS FETCH NEXT @perPage ROWS ONLY`;
|
|
2536
|
+
const result = await dataRequest.query(dataQuery);
|
|
1564
2537
|
return {
|
|
1565
2538
|
pagination: {
|
|
1566
2539
|
total: Number(total),
|
|
@@ -1740,7 +2713,7 @@ var ScoresMSSQL = class extends ScoresStorage {
|
|
|
1740
2713
|
}
|
|
1741
2714
|
}
|
|
1742
2715
|
};
|
|
1743
|
-
var
|
|
2716
|
+
var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
1744
2717
|
pool;
|
|
1745
2718
|
operations;
|
|
1746
2719
|
schema;
|
|
@@ -1754,207 +2727,164 @@ var TracesMSSQL = class extends TracesStorage {
|
|
|
1754
2727
|
this.operations = operations;
|
|
1755
2728
|
this.schema = schema;
|
|
1756
2729
|
}
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
if (
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
const result = await this.getTracesPaginated(args);
|
|
1766
|
-
return result.traces;
|
|
1767
|
-
}
|
|
1768
|
-
async getTracesPaginated(args) {
|
|
1769
|
-
const { name, scope, page = 0, perPage: perPageInput, attributes, filters, dateRange } = args;
|
|
1770
|
-
const fromDate = dateRange?.start;
|
|
1771
|
-
const toDate = dateRange?.end;
|
|
1772
|
-
const perPage = perPageInput !== void 0 ? perPageInput : 100;
|
|
1773
|
-
const currentOffset = page * perPage;
|
|
1774
|
-
const paramMap = {};
|
|
1775
|
-
const conditions = [];
|
|
1776
|
-
let paramIndex = 1;
|
|
1777
|
-
if (name) {
|
|
1778
|
-
const paramName = `p${paramIndex++}`;
|
|
1779
|
-
conditions.push(`[name] LIKE @${paramName}`);
|
|
1780
|
-
paramMap[paramName] = `${name}%`;
|
|
1781
|
-
}
|
|
1782
|
-
if (scope) {
|
|
1783
|
-
const paramName = `p${paramIndex++}`;
|
|
1784
|
-
conditions.push(`[scope] = @${paramName}`);
|
|
1785
|
-
paramMap[paramName] = scope;
|
|
1786
|
-
}
|
|
1787
|
-
if (attributes) {
|
|
1788
|
-
Object.entries(attributes).forEach(([key, value]) => {
|
|
1789
|
-
const parsedKey = parseFieldKey(key);
|
|
1790
|
-
const paramName = `p${paramIndex++}`;
|
|
1791
|
-
conditions.push(`JSON_VALUE([attributes], '$.${parsedKey}') = @${paramName}`);
|
|
1792
|
-
paramMap[paramName] = value;
|
|
1793
|
-
});
|
|
1794
|
-
}
|
|
1795
|
-
if (filters) {
|
|
1796
|
-
Object.entries(filters).forEach(([key, value]) => {
|
|
1797
|
-
const parsedKey = parseFieldKey(key);
|
|
1798
|
-
const paramName = `p${paramIndex++}`;
|
|
1799
|
-
conditions.push(`[${parsedKey}] = @${paramName}`);
|
|
1800
|
-
paramMap[paramName] = value;
|
|
1801
|
-
});
|
|
1802
|
-
}
|
|
1803
|
-
if (fromDate instanceof Date && !isNaN(fromDate.getTime())) {
|
|
1804
|
-
const paramName = `p${paramIndex++}`;
|
|
1805
|
-
conditions.push(`[createdAt] >= @${paramName}`);
|
|
1806
|
-
paramMap[paramName] = fromDate.toISOString();
|
|
1807
|
-
}
|
|
1808
|
-
if (toDate instanceof Date && !isNaN(toDate.getTime())) {
|
|
1809
|
-
const paramName = `p${paramIndex++}`;
|
|
1810
|
-
conditions.push(`[createdAt] <= @${paramName}`);
|
|
1811
|
-
paramMap[paramName] = toDate.toISOString();
|
|
2730
|
+
parseWorkflowRun(row) {
|
|
2731
|
+
let parsedSnapshot = row.snapshot;
|
|
2732
|
+
if (typeof parsedSnapshot === "string") {
|
|
2733
|
+
try {
|
|
2734
|
+
parsedSnapshot = JSON.parse(row.snapshot);
|
|
2735
|
+
} catch (e) {
|
|
2736
|
+
this.logger?.warn?.(`Failed to parse snapshot for workflow ${row.workflow_name}:`, e);
|
|
2737
|
+
}
|
|
1812
2738
|
}
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
2739
|
+
return {
|
|
2740
|
+
workflowName: row.workflow_name,
|
|
2741
|
+
runId: row.run_id,
|
|
2742
|
+
snapshot: parsedSnapshot,
|
|
2743
|
+
createdAt: row.createdAt,
|
|
2744
|
+
updatedAt: row.updatedAt,
|
|
2745
|
+
resourceId: row.resourceId
|
|
2746
|
+
};
|
|
2747
|
+
}
|
|
2748
|
+
async updateWorkflowResults({
|
|
2749
|
+
workflowName,
|
|
2750
|
+
runId,
|
|
2751
|
+
stepId,
|
|
2752
|
+
result,
|
|
2753
|
+
requestContext
|
|
2754
|
+
}) {
|
|
2755
|
+
const table = getTableName({ indexName: TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName(this.schema) });
|
|
2756
|
+
const transaction = this.pool.transaction();
|
|
1816
2757
|
try {
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
2758
|
+
await transaction.begin();
|
|
2759
|
+
const selectRequest = new sql2.Request(transaction);
|
|
2760
|
+
selectRequest.input("workflow_name", workflowName);
|
|
2761
|
+
selectRequest.input("run_id", runId);
|
|
2762
|
+
const existingSnapshotResult = await selectRequest.query(
|
|
2763
|
+
`SELECT snapshot FROM ${table} WITH (UPDLOCK, HOLDLOCK) WHERE workflow_name = @workflow_name AND run_id = @run_id`
|
|
2764
|
+
);
|
|
2765
|
+
let snapshot;
|
|
2766
|
+
if (!existingSnapshotResult.recordset || existingSnapshotResult.recordset.length === 0) {
|
|
2767
|
+
snapshot = {
|
|
2768
|
+
context: {},
|
|
2769
|
+
activePaths: [],
|
|
2770
|
+
timestamp: Date.now(),
|
|
2771
|
+
suspendedPaths: {},
|
|
2772
|
+
resumeLabels: {},
|
|
2773
|
+
serializedStepGraph: [],
|
|
2774
|
+
value: {},
|
|
2775
|
+
waitingPaths: {},
|
|
2776
|
+
status: "pending",
|
|
2777
|
+
runId,
|
|
2778
|
+
requestContext: {}
|
|
2779
|
+
};
|
|
2780
|
+
} else {
|
|
2781
|
+
const existingSnapshot = existingSnapshotResult.recordset[0].snapshot;
|
|
2782
|
+
snapshot = typeof existingSnapshot === "string" ? JSON.parse(existingSnapshot) : existingSnapshot;
|
|
2783
|
+
}
|
|
2784
|
+
snapshot.context[stepId] = result;
|
|
2785
|
+
snapshot.requestContext = { ...snapshot.requestContext, ...requestContext };
|
|
2786
|
+
const upsertReq = new sql2.Request(transaction);
|
|
2787
|
+
upsertReq.input("workflow_name", workflowName);
|
|
2788
|
+
upsertReq.input("run_id", runId);
|
|
2789
|
+
upsertReq.input("snapshot", JSON.stringify(snapshot));
|
|
2790
|
+
upsertReq.input("createdAt", sql2.DateTime2, /* @__PURE__ */ new Date());
|
|
2791
|
+
upsertReq.input("updatedAt", sql2.DateTime2, /* @__PURE__ */ new Date());
|
|
2792
|
+
await upsertReq.query(
|
|
2793
|
+
`MERGE ${table} AS target
|
|
2794
|
+
USING (SELECT @workflow_name AS workflow_name, @run_id AS run_id) AS src
|
|
2795
|
+
ON target.workflow_name = src.workflow_name AND target.run_id = src.run_id
|
|
2796
|
+
WHEN MATCHED THEN UPDATE SET snapshot = @snapshot, [updatedAt] = @updatedAt
|
|
2797
|
+
WHEN NOT MATCHED THEN INSERT (workflow_name, run_id, snapshot, [createdAt], [updatedAt])
|
|
2798
|
+
VALUES (@workflow_name, @run_id, @snapshot, @createdAt, @updatedAt);`
|
|
2799
|
+
);
|
|
2800
|
+
await transaction.commit();
|
|
2801
|
+
return snapshot.context;
|
|
1827
2802
|
} catch (error) {
|
|
2803
|
+
try {
|
|
2804
|
+
await transaction.rollback();
|
|
2805
|
+
} catch {
|
|
2806
|
+
}
|
|
1828
2807
|
throw new MastraError(
|
|
1829
2808
|
{
|
|
1830
|
-
id: "
|
|
2809
|
+
id: "MASTRA_STORAGE_MSSQL_STORE_UPDATE_WORKFLOW_RESULTS_FAILED",
|
|
1831
2810
|
domain: ErrorDomain.STORAGE,
|
|
1832
2811
|
category: ErrorCategory.THIRD_PARTY,
|
|
1833
2812
|
details: {
|
|
1834
|
-
|
|
1835
|
-
|
|
2813
|
+
workflowName,
|
|
2814
|
+
runId,
|
|
2815
|
+
stepId
|
|
1836
2816
|
}
|
|
1837
2817
|
},
|
|
1838
2818
|
error
|
|
1839
2819
|
);
|
|
1840
2820
|
}
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
}
|
|
1850
|
-
const dataQuery = `SELECT * FROM ${getTableName({ indexName: TABLE_TRACES, schemaName: getSchemaName(this.schema) })} ${whereClause} ORDER BY [seq_id] DESC OFFSET @offset ROWS FETCH NEXT @limit ROWS ONLY`;
|
|
1851
|
-
const dataRequest = this.pool.request();
|
|
1852
|
-
Object.entries(paramMap).forEach(([key, value]) => {
|
|
1853
|
-
if (value instanceof Date) {
|
|
1854
|
-
dataRequest.input(key, sql2.DateTime, value);
|
|
1855
|
-
} else {
|
|
1856
|
-
dataRequest.input(key, value);
|
|
1857
|
-
}
|
|
1858
|
-
});
|
|
1859
|
-
dataRequest.input("offset", currentOffset);
|
|
1860
|
-
dataRequest.input("limit", perPage);
|
|
2821
|
+
}
|
|
2822
|
+
async updateWorkflowState({
|
|
2823
|
+
workflowName,
|
|
2824
|
+
runId,
|
|
2825
|
+
opts
|
|
2826
|
+
}) {
|
|
2827
|
+
const table = getTableName({ indexName: TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName(this.schema) });
|
|
2828
|
+
const transaction = this.pool.transaction();
|
|
1861
2829
|
try {
|
|
1862
|
-
|
|
1863
|
-
const
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
2830
|
+
await transaction.begin();
|
|
2831
|
+
const selectRequest = new sql2.Request(transaction);
|
|
2832
|
+
selectRequest.input("workflow_name", workflowName);
|
|
2833
|
+
selectRequest.input("run_id", runId);
|
|
2834
|
+
const existingSnapshotResult = await selectRequest.query(
|
|
2835
|
+
`SELECT snapshot FROM ${table} WITH (UPDLOCK, HOLDLOCK) WHERE workflow_name = @workflow_name AND run_id = @run_id`
|
|
2836
|
+
);
|
|
2837
|
+
if (!existingSnapshotResult.recordset || existingSnapshotResult.recordset.length === 0) {
|
|
2838
|
+
await transaction.rollback();
|
|
2839
|
+
return void 0;
|
|
2840
|
+
}
|
|
2841
|
+
const existingSnapshot = existingSnapshotResult.recordset[0].snapshot;
|
|
2842
|
+
const snapshot = typeof existingSnapshot === "string" ? JSON.parse(existingSnapshot) : existingSnapshot;
|
|
2843
|
+
if (!snapshot || !snapshot?.context) {
|
|
2844
|
+
await transaction.rollback();
|
|
2845
|
+
throw new MastraError(
|
|
2846
|
+
{
|
|
2847
|
+
id: "MASTRA_STORAGE_MSSQL_STORE_UPDATE_WORKFLOW_STATE_SNAPSHOT_NOT_FOUND",
|
|
2848
|
+
domain: ErrorDomain.STORAGE,
|
|
2849
|
+
category: ErrorCategory.SYSTEM,
|
|
2850
|
+
details: {
|
|
2851
|
+
workflowName,
|
|
2852
|
+
runId
|
|
2853
|
+
}
|
|
2854
|
+
},
|
|
2855
|
+
new Error(`Snapshot not found for runId ${runId}`)
|
|
2856
|
+
);
|
|
2857
|
+
}
|
|
2858
|
+
const updatedSnapshot = { ...snapshot, ...opts };
|
|
2859
|
+
const updateRequest = new sql2.Request(transaction);
|
|
2860
|
+
updateRequest.input("snapshot", JSON.stringify(updatedSnapshot));
|
|
2861
|
+
updateRequest.input("workflow_name", workflowName);
|
|
2862
|
+
updateRequest.input("run_id", runId);
|
|
2863
|
+
updateRequest.input("updatedAt", sql2.DateTime2, /* @__PURE__ */ new Date());
|
|
2864
|
+
await updateRequest.query(
|
|
2865
|
+
`UPDATE ${table} SET snapshot = @snapshot, [updatedAt] = @updatedAt WHERE workflow_name = @workflow_name AND run_id = @run_id`
|
|
2866
|
+
);
|
|
2867
|
+
await transaction.commit();
|
|
2868
|
+
return updatedSnapshot;
|
|
1887
2869
|
} catch (error) {
|
|
2870
|
+
try {
|
|
2871
|
+
await transaction.rollback();
|
|
2872
|
+
} catch {
|
|
2873
|
+
}
|
|
1888
2874
|
throw new MastraError(
|
|
1889
2875
|
{
|
|
1890
|
-
id: "
|
|
2876
|
+
id: "MASTRA_STORAGE_MSSQL_STORE_UPDATE_WORKFLOW_STATE_FAILED",
|
|
1891
2877
|
domain: ErrorDomain.STORAGE,
|
|
1892
2878
|
category: ErrorCategory.THIRD_PARTY,
|
|
1893
2879
|
details: {
|
|
1894
|
-
|
|
1895
|
-
|
|
2880
|
+
workflowName,
|
|
2881
|
+
runId
|
|
1896
2882
|
}
|
|
1897
2883
|
},
|
|
1898
2884
|
error
|
|
1899
2885
|
);
|
|
1900
2886
|
}
|
|
1901
2887
|
}
|
|
1902
|
-
async batchTraceInsert({ records }) {
|
|
1903
|
-
this.logger.debug("Batch inserting traces", { count: records.length });
|
|
1904
|
-
await this.operations.batchInsert({
|
|
1905
|
-
tableName: TABLE_TRACES,
|
|
1906
|
-
records
|
|
1907
|
-
});
|
|
1908
|
-
}
|
|
1909
|
-
};
|
|
1910
|
-
function parseWorkflowRun(row) {
|
|
1911
|
-
let parsedSnapshot = row.snapshot;
|
|
1912
|
-
if (typeof parsedSnapshot === "string") {
|
|
1913
|
-
try {
|
|
1914
|
-
parsedSnapshot = JSON.parse(row.snapshot);
|
|
1915
|
-
} catch (e) {
|
|
1916
|
-
console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
|
|
1917
|
-
}
|
|
1918
|
-
}
|
|
1919
|
-
return {
|
|
1920
|
-
workflowName: row.workflow_name,
|
|
1921
|
-
runId: row.run_id,
|
|
1922
|
-
snapshot: parsedSnapshot,
|
|
1923
|
-
createdAt: row.createdAt,
|
|
1924
|
-
updatedAt: row.updatedAt,
|
|
1925
|
-
resourceId: row.resourceId
|
|
1926
|
-
};
|
|
1927
|
-
}
|
|
1928
|
-
var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
1929
|
-
pool;
|
|
1930
|
-
operations;
|
|
1931
|
-
schema;
|
|
1932
|
-
constructor({
|
|
1933
|
-
pool,
|
|
1934
|
-
operations,
|
|
1935
|
-
schema
|
|
1936
|
-
}) {
|
|
1937
|
-
super();
|
|
1938
|
-
this.pool = pool;
|
|
1939
|
-
this.operations = operations;
|
|
1940
|
-
this.schema = schema;
|
|
1941
|
-
}
|
|
1942
|
-
updateWorkflowResults({
|
|
1943
|
-
// workflowName,
|
|
1944
|
-
// runId,
|
|
1945
|
-
// stepId,
|
|
1946
|
-
// result,
|
|
1947
|
-
// runtimeContext,
|
|
1948
|
-
}) {
|
|
1949
|
-
throw new Error("Method not implemented.");
|
|
1950
|
-
}
|
|
1951
|
-
updateWorkflowState({
|
|
1952
|
-
// workflowName,
|
|
1953
|
-
// runId,
|
|
1954
|
-
// opts,
|
|
1955
|
-
}) {
|
|
1956
|
-
throw new Error("Method not implemented.");
|
|
1957
|
-
}
|
|
1958
2888
|
async persistWorkflowSnapshot({
|
|
1959
2889
|
workflowName,
|
|
1960
2890
|
runId,
|
|
@@ -2051,7 +2981,7 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
|
2051
2981
|
if (!result.recordset || result.recordset.length === 0) {
|
|
2052
2982
|
return null;
|
|
2053
2983
|
}
|
|
2054
|
-
return parseWorkflowRun(result.recordset[0]);
|
|
2984
|
+
return this.parseWorkflowRun(result.recordset[0]);
|
|
2055
2985
|
} catch (error) {
|
|
2056
2986
|
throw new MastraError(
|
|
2057
2987
|
{
|
|
@@ -2067,7 +2997,7 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
|
2067
2997
|
);
|
|
2068
2998
|
}
|
|
2069
2999
|
}
|
|
2070
|
-
async
|
|
3000
|
+
async listWorkflowRuns({
|
|
2071
3001
|
workflowName,
|
|
2072
3002
|
fromDate,
|
|
2073
3003
|
toDate,
|
|
@@ -2088,7 +3018,7 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
|
2088
3018
|
conditions.push(`[resourceId] = @resourceId`);
|
|
2089
3019
|
paramMap["resourceId"] = resourceId;
|
|
2090
3020
|
} else {
|
|
2091
|
-
|
|
3021
|
+
this.logger?.warn?.(`[${TABLE_WORKFLOW_SNAPSHOT}] resourceId column not found. Skipping resourceId filter.`);
|
|
2092
3022
|
}
|
|
2093
3023
|
}
|
|
2094
3024
|
if (fromDate instanceof Date && !isNaN(fromDate.getTime())) {
|
|
@@ -2122,7 +3052,7 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
|
2122
3052
|
request.input("offset", offset);
|
|
2123
3053
|
}
|
|
2124
3054
|
const result = await request.query(query);
|
|
2125
|
-
const runs = (result.recordset || []).map((row) => parseWorkflowRun(row));
|
|
3055
|
+
const runs = (result.recordset || []).map((row) => this.parseWorkflowRun(row));
|
|
2126
3056
|
return { runs, total: total || runs.length };
|
|
2127
3057
|
} catch (error) {
|
|
2128
3058
|
throw new MastraError(
|
|
@@ -2170,19 +3100,17 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2170
3100
|
port: config.port,
|
|
2171
3101
|
options: config.options || { encrypt: true, trustServerCertificate: true }
|
|
2172
3102
|
});
|
|
2173
|
-
const legacyEvals = new LegacyEvalsMSSQL({ pool: this.pool, schema: this.schema });
|
|
2174
3103
|
const operations = new StoreOperationsMSSQL({ pool: this.pool, schemaName: this.schema });
|
|
2175
3104
|
const scores = new ScoresMSSQL({ pool: this.pool, operations, schema: this.schema });
|
|
2176
|
-
const traces = new TracesMSSQL({ pool: this.pool, operations, schema: this.schema });
|
|
2177
3105
|
const workflows = new WorkflowsMSSQL({ pool: this.pool, operations, schema: this.schema });
|
|
2178
3106
|
const memory = new MemoryMSSQL({ pool: this.pool, schema: this.schema, operations });
|
|
3107
|
+
const observability = new ObservabilityMSSQL({ pool: this.pool, operations, schema: this.schema });
|
|
2179
3108
|
this.stores = {
|
|
2180
3109
|
operations,
|
|
2181
3110
|
scores,
|
|
2182
|
-
traces,
|
|
2183
3111
|
workflows,
|
|
2184
|
-
|
|
2185
|
-
|
|
3112
|
+
memory,
|
|
3113
|
+
observability
|
|
2186
3114
|
};
|
|
2187
3115
|
} catch (e) {
|
|
2188
3116
|
throw new MastraError(
|
|
@@ -2202,6 +3130,11 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2202
3130
|
try {
|
|
2203
3131
|
await this.isConnected;
|
|
2204
3132
|
await super.init();
|
|
3133
|
+
try {
|
|
3134
|
+
await this.stores.operations.createAutomaticIndexes();
|
|
3135
|
+
} catch (indexError) {
|
|
3136
|
+
this.logger?.warn?.("Failed to create indexes:", indexError);
|
|
3137
|
+
}
|
|
2205
3138
|
} catch (error) {
|
|
2206
3139
|
this.isConnected = null;
|
|
2207
3140
|
throw new MastraError(
|
|
@@ -2229,28 +3162,11 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2229
3162
|
hasColumn: true,
|
|
2230
3163
|
createTable: true,
|
|
2231
3164
|
deleteMessages: true,
|
|
2232
|
-
getScoresBySpan: true
|
|
3165
|
+
getScoresBySpan: true,
|
|
3166
|
+
aiTracing: true,
|
|
3167
|
+
indexManagement: true
|
|
2233
3168
|
};
|
|
2234
3169
|
}
|
|
2235
|
-
/** @deprecated use getEvals instead */
|
|
2236
|
-
async getEvalsByAgentName(agentName, type) {
|
|
2237
|
-
return this.stores.legacyEvals.getEvalsByAgentName(agentName, type);
|
|
2238
|
-
}
|
|
2239
|
-
async getEvals(options = {}) {
|
|
2240
|
-
return this.stores.legacyEvals.getEvals(options);
|
|
2241
|
-
}
|
|
2242
|
-
/**
|
|
2243
|
-
* @deprecated use getTracesPaginated instead
|
|
2244
|
-
*/
|
|
2245
|
-
async getTraces(args) {
|
|
2246
|
-
return this.stores.traces.getTraces(args);
|
|
2247
|
-
}
|
|
2248
|
-
async getTracesPaginated(args) {
|
|
2249
|
-
return this.stores.traces.getTracesPaginated(args);
|
|
2250
|
-
}
|
|
2251
|
-
async batchTraceInsert({ records }) {
|
|
2252
|
-
return this.stores.traces.batchTraceInsert({ records });
|
|
2253
|
-
}
|
|
2254
3170
|
async createTable({
|
|
2255
3171
|
tableName,
|
|
2256
3172
|
schema
|
|
@@ -2310,12 +3226,6 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2310
3226
|
async getMessages(args) {
|
|
2311
3227
|
return this.stores.memory.getMessages(args);
|
|
2312
3228
|
}
|
|
2313
|
-
async getMessagesById({
|
|
2314
|
-
messageIds,
|
|
2315
|
-
format
|
|
2316
|
-
}) {
|
|
2317
|
-
return this.stores.memory.getMessagesById({ messageIds, format });
|
|
2318
|
-
}
|
|
2319
3229
|
async getMessagesPaginated(args) {
|
|
2320
3230
|
return this.stores.memory.getMessagesPaginated(args);
|
|
2321
3231
|
}
|
|
@@ -2351,9 +3261,9 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2351
3261
|
runId,
|
|
2352
3262
|
stepId,
|
|
2353
3263
|
result,
|
|
2354
|
-
|
|
3264
|
+
requestContext
|
|
2355
3265
|
}) {
|
|
2356
|
-
return this.stores.workflows.updateWorkflowResults({ workflowName, runId, stepId, result,
|
|
3266
|
+
return this.stores.workflows.updateWorkflowResults({ workflowName, runId, stepId, result, requestContext });
|
|
2357
3267
|
}
|
|
2358
3268
|
async updateWorkflowState({
|
|
2359
3269
|
workflowName,
|
|
@@ -2376,7 +3286,7 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2376
3286
|
}) {
|
|
2377
3287
|
return this.stores.workflows.loadWorkflowSnapshot({ workflowName, runId });
|
|
2378
3288
|
}
|
|
2379
|
-
async
|
|
3289
|
+
async listWorkflowRuns({
|
|
2380
3290
|
workflowName,
|
|
2381
3291
|
fromDate,
|
|
2382
3292
|
toDate,
|
|
@@ -2384,7 +3294,7 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2384
3294
|
offset,
|
|
2385
3295
|
resourceId
|
|
2386
3296
|
} = {}) {
|
|
2387
|
-
return this.stores.workflows.
|
|
3297
|
+
return this.stores.workflows.listWorkflowRuns({ workflowName, fromDate, toDate, limit, offset, resourceId });
|
|
2388
3298
|
}
|
|
2389
3299
|
async getWorkflowRunById({
|
|
2390
3300
|
runId,
|
|
@@ -2395,6 +3305,60 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2395
3305
|
async close() {
|
|
2396
3306
|
await this.pool.close();
|
|
2397
3307
|
}
|
|
3308
|
+
/**
|
|
3309
|
+
* Index Management
|
|
3310
|
+
*/
|
|
3311
|
+
async createIndex(options) {
|
|
3312
|
+
return this.stores.operations.createIndex(options);
|
|
3313
|
+
}
|
|
3314
|
+
async listIndexes(tableName) {
|
|
3315
|
+
return this.stores.operations.listIndexes(tableName);
|
|
3316
|
+
}
|
|
3317
|
+
async describeIndex(indexName) {
|
|
3318
|
+
return this.stores.operations.describeIndex(indexName);
|
|
3319
|
+
}
|
|
3320
|
+
async dropIndex(indexName) {
|
|
3321
|
+
return this.stores.operations.dropIndex(indexName);
|
|
3322
|
+
}
|
|
3323
|
+
/**
|
|
3324
|
+
* AI Tracing / Observability
|
|
3325
|
+
*/
|
|
3326
|
+
getObservabilityStore() {
|
|
3327
|
+
if (!this.stores.observability) {
|
|
3328
|
+
throw new MastraError({
|
|
3329
|
+
id: "MSSQL_STORE_OBSERVABILITY_NOT_INITIALIZED",
|
|
3330
|
+
domain: ErrorDomain.STORAGE,
|
|
3331
|
+
category: ErrorCategory.SYSTEM,
|
|
3332
|
+
text: "Observability storage is not initialized"
|
|
3333
|
+
});
|
|
3334
|
+
}
|
|
3335
|
+
return this.stores.observability;
|
|
3336
|
+
}
|
|
3337
|
+
async createAISpan(span) {
|
|
3338
|
+
return this.getObservabilityStore().createAISpan(span);
|
|
3339
|
+
}
|
|
3340
|
+
async updateAISpan({
|
|
3341
|
+
spanId,
|
|
3342
|
+
traceId,
|
|
3343
|
+
updates
|
|
3344
|
+
}) {
|
|
3345
|
+
return this.getObservabilityStore().updateAISpan({ spanId, traceId, updates });
|
|
3346
|
+
}
|
|
3347
|
+
async getAITrace(traceId) {
|
|
3348
|
+
return this.getObservabilityStore().getAITrace(traceId);
|
|
3349
|
+
}
|
|
3350
|
+
async getAITracesPaginated(args) {
|
|
3351
|
+
return this.getObservabilityStore().getAITracesPaginated(args);
|
|
3352
|
+
}
|
|
3353
|
+
async batchCreateAISpans(args) {
|
|
3354
|
+
return this.getObservabilityStore().batchCreateAISpans(args);
|
|
3355
|
+
}
|
|
3356
|
+
async batchUpdateAISpans(args) {
|
|
3357
|
+
return this.getObservabilityStore().batchUpdateAISpans(args);
|
|
3358
|
+
}
|
|
3359
|
+
async batchDeleteAITraces(args) {
|
|
3360
|
+
return this.getObservabilityStore().batchDeleteAITraces(args);
|
|
3361
|
+
}
|
|
2398
3362
|
/**
|
|
2399
3363
|
* Scorers
|
|
2400
3364
|
*/
|
|
@@ -2403,9 +3367,18 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2403
3367
|
}
|
|
2404
3368
|
async getScoresByScorerId({
|
|
2405
3369
|
scorerId: _scorerId,
|
|
2406
|
-
pagination: _pagination
|
|
3370
|
+
pagination: _pagination,
|
|
3371
|
+
entityId: _entityId,
|
|
3372
|
+
entityType: _entityType,
|
|
3373
|
+
source: _source
|
|
2407
3374
|
}) {
|
|
2408
|
-
return this.stores.scores.getScoresByScorerId({
|
|
3375
|
+
return this.stores.scores.getScoresByScorerId({
|
|
3376
|
+
scorerId: _scorerId,
|
|
3377
|
+
pagination: _pagination,
|
|
3378
|
+
entityId: _entityId,
|
|
3379
|
+
entityType: _entityType,
|
|
3380
|
+
source: _source
|
|
3381
|
+
});
|
|
2409
3382
|
}
|
|
2410
3383
|
async saveScore(_score) {
|
|
2411
3384
|
return this.stores.scores.saveScore(_score);
|