@mastra/mssql 0.0.0-fix-memory-search-fetch-20251027160505 → 0.0.0-fix-persist-session-cache-option-mcp-server-20251030161352
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 +32 -3
- package/README.md +315 -36
- package/dist/index.cjs +1318 -268
- 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 +1320 -270
- package/dist/index.js.map +1 -1
- package/dist/storage/domains/memory/index.d.ts +6 -1
- 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 -3
- package/dist/storage/domains/workflows/index.d.ts.map +1 -1
- package/dist/storage/index.d.ts +45 -13
- package/dist/storage/index.d.ts.map +1 -1
- package/package.json +5 -5
- 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/index.cjs
CHANGED
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
var error = require('@mastra/core/error');
|
|
4
4
|
var storage = require('@mastra/core/storage');
|
|
5
5
|
var sql2 = require('mssql');
|
|
6
|
-
var utils = require('@mastra/core/utils');
|
|
7
6
|
var agent = require('@mastra/core/agent');
|
|
7
|
+
var utils = require('@mastra/core/utils');
|
|
8
|
+
var crypto = require('crypto');
|
|
8
9
|
var scores = require('@mastra/core/scores');
|
|
9
10
|
|
|
10
11
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
@@ -21,154 +22,71 @@ function getTableName({ indexName, schemaName }) {
|
|
|
21
22
|
const quotedSchemaName = schemaName;
|
|
22
23
|
return quotedSchemaName ? `${quotedSchemaName}.${quotedIndexName}` : quotedIndexName;
|
|
23
24
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
if (row.test_info) {
|
|
29
|
-
try {
|
|
30
|
-
testInfoValue = typeof row.test_info === "string" ? JSON.parse(row.test_info) : row.test_info;
|
|
31
|
-
} catch {
|
|
32
|
-
}
|
|
25
|
+
function buildDateRangeFilter(dateRange, fieldName) {
|
|
26
|
+
const filters = {};
|
|
27
|
+
if (dateRange?.start) {
|
|
28
|
+
filters[`${fieldName}_gte`] = dateRange.start;
|
|
33
29
|
}
|
|
34
|
-
if (
|
|
35
|
-
|
|
36
|
-
resultValue = typeof row.result === "string" ? JSON.parse(row.result) : row.result;
|
|
37
|
-
} catch {
|
|
38
|
-
}
|
|
30
|
+
if (dateRange?.end) {
|
|
31
|
+
filters[`${fieldName}_lte`] = dateRange.end;
|
|
39
32
|
}
|
|
33
|
+
return filters;
|
|
34
|
+
}
|
|
35
|
+
function prepareWhereClause(filters, _schema) {
|
|
36
|
+
const conditions = [];
|
|
37
|
+
const params = {};
|
|
38
|
+
let paramIndex = 1;
|
|
39
|
+
Object.entries(filters).forEach(([key, value]) => {
|
|
40
|
+
if (value === void 0) return;
|
|
41
|
+
const paramName = `p${paramIndex++}`;
|
|
42
|
+
if (key.endsWith("_gte")) {
|
|
43
|
+
const fieldName = key.slice(0, -4);
|
|
44
|
+
conditions.push(`[${utils.parseSqlIdentifier(fieldName, "field name")}] >= @${paramName}`);
|
|
45
|
+
params[paramName] = value instanceof Date ? value.toISOString() : value;
|
|
46
|
+
} else if (key.endsWith("_lte")) {
|
|
47
|
+
const fieldName = key.slice(0, -4);
|
|
48
|
+
conditions.push(`[${utils.parseSqlIdentifier(fieldName, "field name")}] <= @${paramName}`);
|
|
49
|
+
params[paramName] = value instanceof Date ? value.toISOString() : value;
|
|
50
|
+
} else if (value === null) {
|
|
51
|
+
conditions.push(`[${utils.parseSqlIdentifier(key, "field name")}] IS NULL`);
|
|
52
|
+
} else {
|
|
53
|
+
conditions.push(`[${utils.parseSqlIdentifier(key, "field name")}] = @${paramName}`);
|
|
54
|
+
params[paramName] = value instanceof Date ? value.toISOString() : value;
|
|
55
|
+
}
|
|
56
|
+
});
|
|
40
57
|
return {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
output: row.output,
|
|
44
|
-
result: resultValue,
|
|
45
|
-
metricName: row.metric_name,
|
|
46
|
-
instructions: row.instructions,
|
|
47
|
-
testInfo: testInfoValue,
|
|
48
|
-
globalRunId: row.global_run_id,
|
|
49
|
-
runId: row.run_id,
|
|
50
|
-
createdAt: row.created_at
|
|
58
|
+
sql: conditions.length > 0 ? ` WHERE ${conditions.join(" AND ")}` : "",
|
|
59
|
+
params
|
|
51
60
|
};
|
|
52
61
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
query += " AND test_info IS NOT NULL AND JSON_VALUE(test_info, '$.testPath') IS NOT NULL";
|
|
67
|
-
} else if (type === "live") {
|
|
68
|
-
query += " AND (test_info IS NULL OR JSON_VALUE(test_info, '$.testPath') IS NULL)";
|
|
69
|
-
}
|
|
70
|
-
query += " ORDER BY created_at DESC";
|
|
71
|
-
const request = this.pool.request();
|
|
72
|
-
request.input("p1", agentName);
|
|
73
|
-
const result = await request.query(query);
|
|
74
|
-
const rows = result.recordset;
|
|
75
|
-
return typeof transformEvalRow === "function" ? rows?.map((row) => transformEvalRow(row)) ?? [] : rows ?? [];
|
|
76
|
-
} catch (error) {
|
|
77
|
-
if (error && error.number === 208 && error.message && error.message.includes("Invalid object name")) {
|
|
78
|
-
return [];
|
|
79
|
-
}
|
|
80
|
-
console.error("Failed to get evals for the specified agent: " + error?.message);
|
|
81
|
-
throw error;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
async getEvals(options = {}) {
|
|
85
|
-
const { agentName, type, page = 0, perPage = 100, dateRange } = options;
|
|
86
|
-
const fromDate = dateRange?.start;
|
|
87
|
-
const toDate = dateRange?.end;
|
|
88
|
-
const where = [];
|
|
89
|
-
const params = {};
|
|
90
|
-
if (agentName) {
|
|
91
|
-
where.push("agent_name = @agentName");
|
|
92
|
-
params["agentName"] = agentName;
|
|
93
|
-
}
|
|
94
|
-
if (type === "test") {
|
|
95
|
-
where.push("test_info IS NOT NULL AND JSON_VALUE(test_info, '$.testPath') IS NOT NULL");
|
|
96
|
-
} else if (type === "live") {
|
|
97
|
-
where.push("(test_info IS NULL OR JSON_VALUE(test_info, '$.testPath') IS NULL)");
|
|
98
|
-
}
|
|
99
|
-
if (fromDate instanceof Date && !isNaN(fromDate.getTime())) {
|
|
100
|
-
where.push(`[created_at] >= @fromDate`);
|
|
101
|
-
params[`fromDate`] = fromDate.toISOString();
|
|
102
|
-
}
|
|
103
|
-
if (toDate instanceof Date && !isNaN(toDate.getTime())) {
|
|
104
|
-
where.push(`[created_at] <= @toDate`);
|
|
105
|
-
params[`toDate`] = toDate.toISOString();
|
|
106
|
-
}
|
|
107
|
-
const whereClause = where.length > 0 ? `WHERE ${where.join(" AND ")}` : "";
|
|
108
|
-
const tableName = getTableName({ indexName: storage.TABLE_EVALS, schemaName: getSchemaName(this.schema) });
|
|
109
|
-
const offset = page * perPage;
|
|
110
|
-
const countQuery = `SELECT COUNT(*) as total FROM ${tableName} ${whereClause}`;
|
|
111
|
-
const dataQuery = `SELECT * FROM ${tableName} ${whereClause} ORDER BY seq_id DESC OFFSET @offset ROWS FETCH NEXT @perPage ROWS ONLY`;
|
|
112
|
-
try {
|
|
113
|
-
const countReq = this.pool.request();
|
|
114
|
-
Object.entries(params).forEach(([key, value]) => {
|
|
115
|
-
if (value instanceof Date) {
|
|
116
|
-
countReq.input(key, sql2__default.default.DateTime, value);
|
|
117
|
-
} else {
|
|
118
|
-
countReq.input(key, value);
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
const countResult = await countReq.query(countQuery);
|
|
122
|
-
const total = countResult.recordset[0]?.total || 0;
|
|
123
|
-
if (total === 0) {
|
|
124
|
-
return {
|
|
125
|
-
evals: [],
|
|
126
|
-
total: 0,
|
|
127
|
-
page,
|
|
128
|
-
perPage,
|
|
129
|
-
hasMore: false
|
|
130
|
-
};
|
|
62
|
+
function transformFromSqlRow({
|
|
63
|
+
tableName,
|
|
64
|
+
sqlRow
|
|
65
|
+
}) {
|
|
66
|
+
const schema = storage.TABLE_SCHEMAS[tableName];
|
|
67
|
+
const result = {};
|
|
68
|
+
Object.entries(sqlRow).forEach(([key, value]) => {
|
|
69
|
+
const columnSchema = schema?.[key];
|
|
70
|
+
if (columnSchema?.type === "jsonb" && typeof value === "string") {
|
|
71
|
+
try {
|
|
72
|
+
result[key] = JSON.parse(value);
|
|
73
|
+
} catch {
|
|
74
|
+
result[key] = value;
|
|
131
75
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
req.input("offset", offset);
|
|
141
|
-
req.input("perPage", perPage);
|
|
142
|
-
const result = await req.query(dataQuery);
|
|
143
|
-
const rows = result.recordset;
|
|
144
|
-
return {
|
|
145
|
-
evals: rows?.map((row) => transformEvalRow(row)) ?? [],
|
|
146
|
-
total,
|
|
147
|
-
page,
|
|
148
|
-
perPage,
|
|
149
|
-
hasMore: offset + (rows?.length ?? 0) < total
|
|
150
|
-
};
|
|
151
|
-
} catch (error$1) {
|
|
152
|
-
const mastraError = new error.MastraError(
|
|
153
|
-
{
|
|
154
|
-
id: "MASTRA_STORAGE_MSSQL_STORE_GET_EVALS_FAILED",
|
|
155
|
-
domain: error.ErrorDomain.STORAGE,
|
|
156
|
-
category: error.ErrorCategory.THIRD_PARTY,
|
|
157
|
-
details: {
|
|
158
|
-
agentName: agentName || "all",
|
|
159
|
-
type: type || "all",
|
|
160
|
-
page,
|
|
161
|
-
perPage
|
|
162
|
-
}
|
|
163
|
-
},
|
|
164
|
-
error$1
|
|
165
|
-
);
|
|
166
|
-
this.logger?.error?.(mastraError.toString());
|
|
167
|
-
this.logger?.trackException(mastraError);
|
|
168
|
-
throw mastraError;
|
|
76
|
+
} else if (columnSchema?.type === "timestamp" && value && typeof value === "string") {
|
|
77
|
+
result[key] = new Date(value);
|
|
78
|
+
} else if (columnSchema?.type === "timestamp" && value instanceof Date) {
|
|
79
|
+
result[key] = value;
|
|
80
|
+
} else if (columnSchema?.type === "boolean") {
|
|
81
|
+
result[key] = Boolean(value);
|
|
82
|
+
} else {
|
|
83
|
+
result[key] = value;
|
|
169
84
|
}
|
|
170
|
-
}
|
|
171
|
-
|
|
85
|
+
});
|
|
86
|
+
return result;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// src/storage/domains/memory/index.ts
|
|
172
90
|
var MemoryMSSQL = class extends storage.MemoryStorage {
|
|
173
91
|
pool;
|
|
174
92
|
schema;
|
|
@@ -200,7 +118,7 @@ var MemoryMSSQL = class extends storage.MemoryStorage {
|
|
|
200
118
|
}
|
|
201
119
|
async getThreadById({ threadId }) {
|
|
202
120
|
try {
|
|
203
|
-
const
|
|
121
|
+
const sql5 = `SELECT
|
|
204
122
|
id,
|
|
205
123
|
[resourceId],
|
|
206
124
|
title,
|
|
@@ -211,7 +129,7 @@ var MemoryMSSQL = class extends storage.MemoryStorage {
|
|
|
211
129
|
WHERE id = @threadId`;
|
|
212
130
|
const request = this.pool.request();
|
|
213
131
|
request.input("threadId", threadId);
|
|
214
|
-
const resultSet = await request.query(
|
|
132
|
+
const resultSet = await request.query(sql5);
|
|
215
133
|
const thread = resultSet.recordset[0] || null;
|
|
216
134
|
if (!thread) {
|
|
217
135
|
return null;
|
|
@@ -257,7 +175,8 @@ var MemoryMSSQL = class extends storage.MemoryStorage {
|
|
|
257
175
|
};
|
|
258
176
|
}
|
|
259
177
|
const orderByField = orderBy === "createdAt" ? "[createdAt]" : "[updatedAt]";
|
|
260
|
-
const
|
|
178
|
+
const dir = (sortDirection || "DESC").toUpperCase() === "ASC" ? "ASC" : "DESC";
|
|
179
|
+
const dataQuery = `SELECT id, [resourceId], title, metadata, [createdAt], [updatedAt] ${baseQuery} ORDER BY ${orderByField} ${dir} OFFSET @offset ROWS FETCH NEXT @perPage ROWS ONLY`;
|
|
261
180
|
const dataRequest = this.pool.request();
|
|
262
181
|
dataRequest.input("resourceId", resourceId);
|
|
263
182
|
dataRequest.input("perPage", perPage);
|
|
@@ -314,7 +233,12 @@ var MemoryMSSQL = class extends storage.MemoryStorage {
|
|
|
314
233
|
req.input("id", thread.id);
|
|
315
234
|
req.input("resourceId", thread.resourceId);
|
|
316
235
|
req.input("title", thread.title);
|
|
317
|
-
|
|
236
|
+
const metadata = thread.metadata ? JSON.stringify(thread.metadata) : null;
|
|
237
|
+
if (metadata === null) {
|
|
238
|
+
req.input("metadata", sql2__default.default.NVarChar, null);
|
|
239
|
+
} else {
|
|
240
|
+
req.input("metadata", metadata);
|
|
241
|
+
}
|
|
318
242
|
req.input("createdAt", sql2__default.default.DateTime2, thread.createdAt);
|
|
319
243
|
req.input("updatedAt", sql2__default.default.DateTime2, thread.updatedAt);
|
|
320
244
|
await req.query(mergeSql);
|
|
@@ -341,7 +265,8 @@ var MemoryMSSQL = class extends storage.MemoryStorage {
|
|
|
341
265
|
try {
|
|
342
266
|
const baseQuery = `FROM ${getTableName({ indexName: storage.TABLE_THREADS, schemaName: getSchemaName(this.schema) })} WHERE [resourceId] = @resourceId`;
|
|
343
267
|
const orderByField = orderBy === "createdAt" ? "[createdAt]" : "[updatedAt]";
|
|
344
|
-
const
|
|
268
|
+
const dir = (sortDirection || "DESC").toUpperCase() === "ASC" ? "ASC" : "DESC";
|
|
269
|
+
const dataQuery = `SELECT id, [resourceId], title, metadata, [createdAt], [updatedAt] ${baseQuery} ORDER BY ${orderByField} ${dir}`;
|
|
345
270
|
const request = this.pool.request();
|
|
346
271
|
request.input("resourceId", resourceId);
|
|
347
272
|
const resultSet = await request.query(dataQuery);
|
|
@@ -384,7 +309,7 @@ var MemoryMSSQL = class extends storage.MemoryStorage {
|
|
|
384
309
|
};
|
|
385
310
|
try {
|
|
386
311
|
const table = getTableName({ indexName: storage.TABLE_THREADS, schemaName: getSchemaName(this.schema) });
|
|
387
|
-
const
|
|
312
|
+
const sql5 = `UPDATE ${table}
|
|
388
313
|
SET title = @title,
|
|
389
314
|
metadata = @metadata,
|
|
390
315
|
[updatedAt] = @updatedAt
|
|
@@ -395,7 +320,7 @@ var MemoryMSSQL = class extends storage.MemoryStorage {
|
|
|
395
320
|
req.input("title", title);
|
|
396
321
|
req.input("metadata", JSON.stringify(mergedMetadata));
|
|
397
322
|
req.input("updatedAt", /* @__PURE__ */ new Date());
|
|
398
|
-
const result = await req.query(
|
|
323
|
+
const result = await req.query(sql5);
|
|
399
324
|
let thread = result.recordset && result.recordset[0];
|
|
400
325
|
if (thread && "seq_id" in thread) {
|
|
401
326
|
const { seq_id, ...rest } = thread;
|
|
@@ -590,7 +515,7 @@ var MemoryMSSQL = class extends storage.MemoryStorage {
|
|
|
590
515
|
error$1
|
|
591
516
|
);
|
|
592
517
|
this.logger?.error?.(mastraError.toString());
|
|
593
|
-
this.logger?.trackException(mastraError);
|
|
518
|
+
this.logger?.trackException?.(mastraError);
|
|
594
519
|
return [];
|
|
595
520
|
}
|
|
596
521
|
}
|
|
@@ -630,10 +555,24 @@ var MemoryMSSQL = class extends storage.MemoryStorage {
|
|
|
630
555
|
error$1
|
|
631
556
|
);
|
|
632
557
|
this.logger?.error?.(mastraError.toString());
|
|
633
|
-
this.logger?.trackException(mastraError);
|
|
558
|
+
this.logger?.trackException?.(mastraError);
|
|
634
559
|
return [];
|
|
635
560
|
}
|
|
636
561
|
}
|
|
562
|
+
async listMessages(_args) {
|
|
563
|
+
throw new Error(
|
|
564
|
+
`listMessages is not yet implemented by this storage adapter (${this.constructor.name}). This method is currently being rolled out across all storage adapters. Please use getMessages or getMessagesPaginated as an alternative, or wait for the implementation.`
|
|
565
|
+
);
|
|
566
|
+
}
|
|
567
|
+
async listMessagesById({ messageIds }) {
|
|
568
|
+
return this.getMessagesById({ messageIds, format: "v2" });
|
|
569
|
+
}
|
|
570
|
+
async listThreadsByResourceId(args) {
|
|
571
|
+
const { resourceId, limit, offset, orderBy, sortDirection } = args;
|
|
572
|
+
const page = Math.floor(offset / limit);
|
|
573
|
+
const perPage = limit;
|
|
574
|
+
return this.getThreadsByResourceIdPaginated({ resourceId, page, perPage, orderBy, sortDirection });
|
|
575
|
+
}
|
|
637
576
|
async getMessagesPaginated(args) {
|
|
638
577
|
const { threadId, resourceId, format, selectBy } = args;
|
|
639
578
|
const { page = 0, perPage: perPageInput, dateRange } = selectBy?.pagination || {};
|
|
@@ -692,7 +631,7 @@ var MemoryMSSQL = class extends storage.MemoryStorage {
|
|
|
692
631
|
const parsed = this._parseAndFormatMessages(messages, format);
|
|
693
632
|
return {
|
|
694
633
|
messages: parsed,
|
|
695
|
-
total
|
|
634
|
+
total,
|
|
696
635
|
page,
|
|
697
636
|
perPage,
|
|
698
637
|
hasMore: currentOffset + rows.length < total
|
|
@@ -712,7 +651,7 @@ var MemoryMSSQL = class extends storage.MemoryStorage {
|
|
|
712
651
|
error$1
|
|
713
652
|
);
|
|
714
653
|
this.logger?.error?.(mastraError.toString());
|
|
715
|
-
this.logger?.trackException(mastraError);
|
|
654
|
+
this.logger?.trackException?.(mastraError);
|
|
716
655
|
return { messages: [], total: 0, page, perPage: perPageInput || 40, hasMore: false };
|
|
717
656
|
}
|
|
718
657
|
}
|
|
@@ -979,8 +918,10 @@ var MemoryMSSQL = class extends storage.MemoryStorage {
|
|
|
979
918
|
return null;
|
|
980
919
|
}
|
|
981
920
|
return {
|
|
982
|
-
|
|
983
|
-
|
|
921
|
+
id: result.id,
|
|
922
|
+
createdAt: result.createdAt,
|
|
923
|
+
updatedAt: result.updatedAt,
|
|
924
|
+
workingMemory: result.workingMemory,
|
|
984
925
|
metadata: typeof result.metadata === "string" ? JSON.parse(result.metadata) : result.metadata
|
|
985
926
|
};
|
|
986
927
|
} catch (error$1) {
|
|
@@ -994,7 +935,7 @@ var MemoryMSSQL = class extends storage.MemoryStorage {
|
|
|
994
935
|
error$1
|
|
995
936
|
);
|
|
996
937
|
this.logger?.error?.(mastraError.toString());
|
|
997
|
-
this.logger?.trackException(mastraError);
|
|
938
|
+
this.logger?.trackException?.(mastraError);
|
|
998
939
|
throw mastraError;
|
|
999
940
|
}
|
|
1000
941
|
}
|
|
@@ -1003,7 +944,7 @@ var MemoryMSSQL = class extends storage.MemoryStorage {
|
|
|
1003
944
|
tableName: storage.TABLE_RESOURCES,
|
|
1004
945
|
record: {
|
|
1005
946
|
...resource,
|
|
1006
|
-
metadata:
|
|
947
|
+
metadata: resource.metadata
|
|
1007
948
|
}
|
|
1008
949
|
});
|
|
1009
950
|
return resource;
|
|
@@ -1061,20 +1002,337 @@ var MemoryMSSQL = class extends storage.MemoryStorage {
|
|
|
1061
1002
|
error$1
|
|
1062
1003
|
);
|
|
1063
1004
|
this.logger?.error?.(mastraError.toString());
|
|
1064
|
-
this.logger?.trackException(mastraError);
|
|
1005
|
+
this.logger?.trackException?.(mastraError);
|
|
1065
1006
|
throw mastraError;
|
|
1066
1007
|
}
|
|
1067
1008
|
}
|
|
1068
1009
|
};
|
|
1010
|
+
var ObservabilityMSSQL = class extends storage.ObservabilityStorage {
|
|
1011
|
+
pool;
|
|
1012
|
+
operations;
|
|
1013
|
+
schema;
|
|
1014
|
+
constructor({
|
|
1015
|
+
pool,
|
|
1016
|
+
operations,
|
|
1017
|
+
schema
|
|
1018
|
+
}) {
|
|
1019
|
+
super();
|
|
1020
|
+
this.pool = pool;
|
|
1021
|
+
this.operations = operations;
|
|
1022
|
+
this.schema = schema;
|
|
1023
|
+
}
|
|
1024
|
+
get aiTracingStrategy() {
|
|
1025
|
+
return {
|
|
1026
|
+
preferred: "batch-with-updates",
|
|
1027
|
+
supported: ["batch-with-updates", "insert-only"]
|
|
1028
|
+
};
|
|
1029
|
+
}
|
|
1030
|
+
async createAISpan(span) {
|
|
1031
|
+
try {
|
|
1032
|
+
const startedAt = span.startedAt instanceof Date ? span.startedAt.toISOString() : span.startedAt;
|
|
1033
|
+
const endedAt = span.endedAt instanceof Date ? span.endedAt.toISOString() : span.endedAt;
|
|
1034
|
+
const record = {
|
|
1035
|
+
...span,
|
|
1036
|
+
startedAt,
|
|
1037
|
+
endedAt
|
|
1038
|
+
// Note: createdAt/updatedAt will be set by default values
|
|
1039
|
+
};
|
|
1040
|
+
return this.operations.insert({ tableName: storage.TABLE_AI_SPANS, record });
|
|
1041
|
+
} catch (error$1) {
|
|
1042
|
+
throw new error.MastraError(
|
|
1043
|
+
{
|
|
1044
|
+
id: "MSSQL_STORE_CREATE_AI_SPAN_FAILED",
|
|
1045
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1046
|
+
category: error.ErrorCategory.USER,
|
|
1047
|
+
details: {
|
|
1048
|
+
spanId: span.spanId,
|
|
1049
|
+
traceId: span.traceId,
|
|
1050
|
+
spanType: span.spanType,
|
|
1051
|
+
spanName: span.name
|
|
1052
|
+
}
|
|
1053
|
+
},
|
|
1054
|
+
error$1
|
|
1055
|
+
);
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
async getAITrace(traceId) {
|
|
1059
|
+
try {
|
|
1060
|
+
const tableName = getTableName({
|
|
1061
|
+
indexName: storage.TABLE_AI_SPANS,
|
|
1062
|
+
schemaName: getSchemaName(this.schema)
|
|
1063
|
+
});
|
|
1064
|
+
const request = this.pool.request();
|
|
1065
|
+
request.input("traceId", traceId);
|
|
1066
|
+
const result = await request.query(
|
|
1067
|
+
`SELECT
|
|
1068
|
+
[traceId], [spanId], [parentSpanId], [name], [scope], [spanType],
|
|
1069
|
+
[attributes], [metadata], [links], [input], [output], [error], [isEvent],
|
|
1070
|
+
[startedAt], [endedAt], [createdAt], [updatedAt]
|
|
1071
|
+
FROM ${tableName}
|
|
1072
|
+
WHERE [traceId] = @traceId
|
|
1073
|
+
ORDER BY [startedAt] DESC`
|
|
1074
|
+
);
|
|
1075
|
+
if (!result.recordset || result.recordset.length === 0) {
|
|
1076
|
+
return null;
|
|
1077
|
+
}
|
|
1078
|
+
return {
|
|
1079
|
+
traceId,
|
|
1080
|
+
spans: result.recordset.map(
|
|
1081
|
+
(span) => transformFromSqlRow({
|
|
1082
|
+
tableName: storage.TABLE_AI_SPANS,
|
|
1083
|
+
sqlRow: span
|
|
1084
|
+
})
|
|
1085
|
+
)
|
|
1086
|
+
};
|
|
1087
|
+
} catch (error$1) {
|
|
1088
|
+
throw new error.MastraError(
|
|
1089
|
+
{
|
|
1090
|
+
id: "MSSQL_STORE_GET_AI_TRACE_FAILED",
|
|
1091
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1092
|
+
category: error.ErrorCategory.USER,
|
|
1093
|
+
details: {
|
|
1094
|
+
traceId
|
|
1095
|
+
}
|
|
1096
|
+
},
|
|
1097
|
+
error$1
|
|
1098
|
+
);
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
async updateAISpan({
|
|
1102
|
+
spanId,
|
|
1103
|
+
traceId,
|
|
1104
|
+
updates
|
|
1105
|
+
}) {
|
|
1106
|
+
try {
|
|
1107
|
+
const data = { ...updates };
|
|
1108
|
+
if (data.endedAt instanceof Date) {
|
|
1109
|
+
data.endedAt = data.endedAt.toISOString();
|
|
1110
|
+
}
|
|
1111
|
+
if (data.startedAt instanceof Date) {
|
|
1112
|
+
data.startedAt = data.startedAt.toISOString();
|
|
1113
|
+
}
|
|
1114
|
+
await this.operations.update({
|
|
1115
|
+
tableName: storage.TABLE_AI_SPANS,
|
|
1116
|
+
keys: { spanId, traceId },
|
|
1117
|
+
data
|
|
1118
|
+
});
|
|
1119
|
+
} catch (error$1) {
|
|
1120
|
+
throw new error.MastraError(
|
|
1121
|
+
{
|
|
1122
|
+
id: "MSSQL_STORE_UPDATE_AI_SPAN_FAILED",
|
|
1123
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1124
|
+
category: error.ErrorCategory.USER,
|
|
1125
|
+
details: {
|
|
1126
|
+
spanId,
|
|
1127
|
+
traceId
|
|
1128
|
+
}
|
|
1129
|
+
},
|
|
1130
|
+
error$1
|
|
1131
|
+
);
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
async getAITracesPaginated({
|
|
1135
|
+
filters,
|
|
1136
|
+
pagination
|
|
1137
|
+
}) {
|
|
1138
|
+
const page = pagination?.page ?? 0;
|
|
1139
|
+
const perPage = pagination?.perPage ?? 10;
|
|
1140
|
+
const { entityId, entityType, ...actualFilters } = filters || {};
|
|
1141
|
+
const filtersWithDateRange = {
|
|
1142
|
+
...actualFilters,
|
|
1143
|
+
...buildDateRangeFilter(pagination?.dateRange, "startedAt"),
|
|
1144
|
+
parentSpanId: null
|
|
1145
|
+
// Only get root spans for traces
|
|
1146
|
+
};
|
|
1147
|
+
const whereClause = prepareWhereClause(filtersWithDateRange);
|
|
1148
|
+
let actualWhereClause = whereClause.sql;
|
|
1149
|
+
const params = { ...whereClause.params };
|
|
1150
|
+
let currentParamIndex = Object.keys(params).length + 1;
|
|
1151
|
+
if (entityId && entityType) {
|
|
1152
|
+
let name = "";
|
|
1153
|
+
if (entityType === "workflow") {
|
|
1154
|
+
name = `workflow run: '${entityId}'`;
|
|
1155
|
+
} else if (entityType === "agent") {
|
|
1156
|
+
name = `agent run: '${entityId}'`;
|
|
1157
|
+
} else {
|
|
1158
|
+
const error$1 = new error.MastraError({
|
|
1159
|
+
id: "MSSQL_STORE_GET_AI_TRACES_PAGINATED_FAILED",
|
|
1160
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1161
|
+
category: error.ErrorCategory.USER,
|
|
1162
|
+
details: {
|
|
1163
|
+
entityType
|
|
1164
|
+
},
|
|
1165
|
+
text: `Cannot filter by entity type: ${entityType}`
|
|
1166
|
+
});
|
|
1167
|
+
throw error$1;
|
|
1168
|
+
}
|
|
1169
|
+
const entityParam = `p${currentParamIndex++}`;
|
|
1170
|
+
if (actualWhereClause) {
|
|
1171
|
+
actualWhereClause += ` AND [name] = @${entityParam}`;
|
|
1172
|
+
} else {
|
|
1173
|
+
actualWhereClause = ` WHERE [name] = @${entityParam}`;
|
|
1174
|
+
}
|
|
1175
|
+
params[entityParam] = name;
|
|
1176
|
+
}
|
|
1177
|
+
const tableName = getTableName({
|
|
1178
|
+
indexName: storage.TABLE_AI_SPANS,
|
|
1179
|
+
schemaName: getSchemaName(this.schema)
|
|
1180
|
+
});
|
|
1181
|
+
try {
|
|
1182
|
+
const countRequest = this.pool.request();
|
|
1183
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
1184
|
+
countRequest.input(key, value);
|
|
1185
|
+
});
|
|
1186
|
+
const countResult = await countRequest.query(
|
|
1187
|
+
`SELECT COUNT(*) as count FROM ${tableName}${actualWhereClause}`
|
|
1188
|
+
);
|
|
1189
|
+
const total = countResult.recordset[0]?.count ?? 0;
|
|
1190
|
+
if (total === 0) {
|
|
1191
|
+
return {
|
|
1192
|
+
pagination: {
|
|
1193
|
+
total: 0,
|
|
1194
|
+
page,
|
|
1195
|
+
perPage,
|
|
1196
|
+
hasMore: false
|
|
1197
|
+
},
|
|
1198
|
+
spans: []
|
|
1199
|
+
};
|
|
1200
|
+
}
|
|
1201
|
+
const dataRequest = this.pool.request();
|
|
1202
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
1203
|
+
dataRequest.input(key, value);
|
|
1204
|
+
});
|
|
1205
|
+
dataRequest.input("offset", page * perPage);
|
|
1206
|
+
dataRequest.input("limit", perPage);
|
|
1207
|
+
const dataResult = await dataRequest.query(
|
|
1208
|
+
`SELECT * FROM ${tableName}${actualWhereClause} ORDER BY [startedAt] DESC OFFSET @offset ROWS FETCH NEXT @limit ROWS ONLY`
|
|
1209
|
+
);
|
|
1210
|
+
const spans = dataResult.recordset.map(
|
|
1211
|
+
(row) => transformFromSqlRow({
|
|
1212
|
+
tableName: storage.TABLE_AI_SPANS,
|
|
1213
|
+
sqlRow: row
|
|
1214
|
+
})
|
|
1215
|
+
);
|
|
1216
|
+
return {
|
|
1217
|
+
pagination: {
|
|
1218
|
+
total,
|
|
1219
|
+
page,
|
|
1220
|
+
perPage,
|
|
1221
|
+
hasMore: (page + 1) * perPage < total
|
|
1222
|
+
},
|
|
1223
|
+
spans
|
|
1224
|
+
};
|
|
1225
|
+
} catch (error$1) {
|
|
1226
|
+
throw new error.MastraError(
|
|
1227
|
+
{
|
|
1228
|
+
id: "MSSQL_STORE_GET_AI_TRACES_PAGINATED_FAILED",
|
|
1229
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1230
|
+
category: error.ErrorCategory.USER
|
|
1231
|
+
},
|
|
1232
|
+
error$1
|
|
1233
|
+
);
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
async batchCreateAISpans(args) {
|
|
1237
|
+
if (!args.records || args.records.length === 0) {
|
|
1238
|
+
return;
|
|
1239
|
+
}
|
|
1240
|
+
try {
|
|
1241
|
+
await this.operations.batchInsert({
|
|
1242
|
+
tableName: storage.TABLE_AI_SPANS,
|
|
1243
|
+
records: args.records.map((span) => ({
|
|
1244
|
+
...span,
|
|
1245
|
+
startedAt: span.startedAt instanceof Date ? span.startedAt.toISOString() : span.startedAt,
|
|
1246
|
+
endedAt: span.endedAt instanceof Date ? span.endedAt.toISOString() : span.endedAt
|
|
1247
|
+
}))
|
|
1248
|
+
});
|
|
1249
|
+
} catch (error$1) {
|
|
1250
|
+
throw new error.MastraError(
|
|
1251
|
+
{
|
|
1252
|
+
id: "MSSQL_STORE_BATCH_CREATE_AI_SPANS_FAILED",
|
|
1253
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1254
|
+
category: error.ErrorCategory.USER,
|
|
1255
|
+
details: {
|
|
1256
|
+
count: args.records.length
|
|
1257
|
+
}
|
|
1258
|
+
},
|
|
1259
|
+
error$1
|
|
1260
|
+
);
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
async batchUpdateAISpans(args) {
|
|
1264
|
+
if (!args.records || args.records.length === 0) {
|
|
1265
|
+
return;
|
|
1266
|
+
}
|
|
1267
|
+
try {
|
|
1268
|
+
const updates = args.records.map(({ traceId, spanId, updates: data }) => {
|
|
1269
|
+
const processedData = { ...data };
|
|
1270
|
+
if (processedData.endedAt instanceof Date) {
|
|
1271
|
+
processedData.endedAt = processedData.endedAt.toISOString();
|
|
1272
|
+
}
|
|
1273
|
+
if (processedData.startedAt instanceof Date) {
|
|
1274
|
+
processedData.startedAt = processedData.startedAt.toISOString();
|
|
1275
|
+
}
|
|
1276
|
+
return {
|
|
1277
|
+
keys: { spanId, traceId },
|
|
1278
|
+
data: processedData
|
|
1279
|
+
};
|
|
1280
|
+
});
|
|
1281
|
+
await this.operations.batchUpdate({
|
|
1282
|
+
tableName: storage.TABLE_AI_SPANS,
|
|
1283
|
+
updates
|
|
1284
|
+
});
|
|
1285
|
+
} catch (error$1) {
|
|
1286
|
+
throw new error.MastraError(
|
|
1287
|
+
{
|
|
1288
|
+
id: "MSSQL_STORE_BATCH_UPDATE_AI_SPANS_FAILED",
|
|
1289
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1290
|
+
category: error.ErrorCategory.USER,
|
|
1291
|
+
details: {
|
|
1292
|
+
count: args.records.length
|
|
1293
|
+
}
|
|
1294
|
+
},
|
|
1295
|
+
error$1
|
|
1296
|
+
);
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
async batchDeleteAITraces(args) {
|
|
1300
|
+
if (!args.traceIds || args.traceIds.length === 0) {
|
|
1301
|
+
return;
|
|
1302
|
+
}
|
|
1303
|
+
try {
|
|
1304
|
+
const keys = args.traceIds.map((traceId) => ({ traceId }));
|
|
1305
|
+
await this.operations.batchDelete({
|
|
1306
|
+
tableName: storage.TABLE_AI_SPANS,
|
|
1307
|
+
keys
|
|
1308
|
+
});
|
|
1309
|
+
} catch (error$1) {
|
|
1310
|
+
throw new error.MastraError(
|
|
1311
|
+
{
|
|
1312
|
+
id: "MSSQL_STORE_BATCH_DELETE_AI_TRACES_FAILED",
|
|
1313
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1314
|
+
category: error.ErrorCategory.USER,
|
|
1315
|
+
details: {
|
|
1316
|
+
count: args.traceIds.length
|
|
1317
|
+
}
|
|
1318
|
+
},
|
|
1319
|
+
error$1
|
|
1320
|
+
);
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
};
|
|
1069
1324
|
var StoreOperationsMSSQL = class extends storage.StoreOperations {
|
|
1070
1325
|
pool;
|
|
1071
1326
|
schemaName;
|
|
1072
1327
|
setupSchemaPromise = null;
|
|
1073
1328
|
schemaSetupComplete = void 0;
|
|
1074
|
-
getSqlType(type, isPrimaryKey = false) {
|
|
1329
|
+
getSqlType(type, isPrimaryKey = false, useLargeStorage = false) {
|
|
1075
1330
|
switch (type) {
|
|
1076
1331
|
case "text":
|
|
1077
|
-
|
|
1332
|
+
if (useLargeStorage) {
|
|
1333
|
+
return "NVARCHAR(MAX)";
|
|
1334
|
+
}
|
|
1335
|
+
return isPrimaryKey ? "NVARCHAR(255)" : "NVARCHAR(400)";
|
|
1078
1336
|
case "timestamp":
|
|
1079
1337
|
return "DATETIME2(7)";
|
|
1080
1338
|
case "uuid":
|
|
@@ -1087,6 +1345,8 @@ var StoreOperationsMSSQL = class extends storage.StoreOperations {
|
|
|
1087
1345
|
return "BIGINT";
|
|
1088
1346
|
case "float":
|
|
1089
1347
|
return "FLOAT";
|
|
1348
|
+
case "boolean":
|
|
1349
|
+
return "BIT";
|
|
1090
1350
|
default:
|
|
1091
1351
|
throw new error.MastraError({
|
|
1092
1352
|
id: "MASTRA_STORAGE_MSSQL_STORE_TYPE_NOT_SUPPORTED",
|
|
@@ -1149,20 +1409,26 @@ var StoreOperationsMSSQL = class extends storage.StoreOperations {
|
|
|
1149
1409
|
}
|
|
1150
1410
|
await this.setupSchemaPromise;
|
|
1151
1411
|
}
|
|
1152
|
-
async insert({
|
|
1412
|
+
async insert({
|
|
1413
|
+
tableName,
|
|
1414
|
+
record,
|
|
1415
|
+
transaction
|
|
1416
|
+
}) {
|
|
1153
1417
|
try {
|
|
1154
|
-
const columns = Object.keys(record)
|
|
1155
|
-
const
|
|
1156
|
-
const paramNames =
|
|
1157
|
-
const insertSql = `INSERT INTO ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} (${
|
|
1158
|
-
const request = this.pool.request();
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
request.input(`param${i}`,
|
|
1418
|
+
const columns = Object.keys(record);
|
|
1419
|
+
const parsedColumns = columns.map((col) => utils.parseSqlIdentifier(col, "column name"));
|
|
1420
|
+
const paramNames = columns.map((_, i) => `@param${i}`);
|
|
1421
|
+
const insertSql = `INSERT INTO ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} (${parsedColumns.map((c) => `[${c}]`).join(", ")}) VALUES (${paramNames.join(", ")})`;
|
|
1422
|
+
const request = transaction ? transaction.request() : this.pool.request();
|
|
1423
|
+
columns.forEach((col, i) => {
|
|
1424
|
+
const value = record[col];
|
|
1425
|
+
const preparedValue = this.prepareValue(value, col, tableName);
|
|
1426
|
+
if (preparedValue instanceof Date) {
|
|
1427
|
+
request.input(`param${i}`, sql2__default.default.DateTime2, preparedValue);
|
|
1428
|
+
} else if (preparedValue === null || preparedValue === void 0) {
|
|
1429
|
+
request.input(`param${i}`, this.getMssqlType(tableName, col), null);
|
|
1164
1430
|
} else {
|
|
1165
|
-
request.input(`param${i}`,
|
|
1431
|
+
request.input(`param${i}`, preparedValue);
|
|
1166
1432
|
}
|
|
1167
1433
|
});
|
|
1168
1434
|
await request.query(insertSql);
|
|
@@ -1186,7 +1452,7 @@ var StoreOperationsMSSQL = class extends storage.StoreOperations {
|
|
|
1186
1452
|
try {
|
|
1187
1453
|
await this.pool.request().query(`TRUNCATE TABLE ${fullTableName}`);
|
|
1188
1454
|
} catch (truncateError) {
|
|
1189
|
-
if (truncateError
|
|
1455
|
+
if (truncateError?.number === 4712) {
|
|
1190
1456
|
await this.pool.request().query(`DELETE FROM ${fullTableName}`);
|
|
1191
1457
|
} else {
|
|
1192
1458
|
throw truncateError;
|
|
@@ -1209,9 +1475,11 @@ var StoreOperationsMSSQL = class extends storage.StoreOperations {
|
|
|
1209
1475
|
getDefaultValue(type) {
|
|
1210
1476
|
switch (type) {
|
|
1211
1477
|
case "timestamp":
|
|
1212
|
-
return "DEFAULT
|
|
1478
|
+
return "DEFAULT SYSUTCDATETIME()";
|
|
1213
1479
|
case "jsonb":
|
|
1214
1480
|
return "DEFAULT N'{}'";
|
|
1481
|
+
case "boolean":
|
|
1482
|
+
return "DEFAULT 0";
|
|
1215
1483
|
default:
|
|
1216
1484
|
return super.getDefaultValue(type);
|
|
1217
1485
|
}
|
|
@@ -1222,13 +1490,29 @@ var StoreOperationsMSSQL = class extends storage.StoreOperations {
|
|
|
1222
1490
|
}) {
|
|
1223
1491
|
try {
|
|
1224
1492
|
const uniqueConstraintColumns = tableName === storage.TABLE_WORKFLOW_SNAPSHOT ? ["workflow_name", "run_id"] : [];
|
|
1493
|
+
const largeDataColumns = [
|
|
1494
|
+
"workingMemory",
|
|
1495
|
+
"snapshot",
|
|
1496
|
+
"metadata",
|
|
1497
|
+
"content",
|
|
1498
|
+
// messages.content - can be very long conversation content
|
|
1499
|
+
"input",
|
|
1500
|
+
// evals.input - test input data
|
|
1501
|
+
"output",
|
|
1502
|
+
// evals.output - test output data
|
|
1503
|
+
"instructions",
|
|
1504
|
+
// evals.instructions - evaluation instructions
|
|
1505
|
+
"other"
|
|
1506
|
+
// traces.other - additional trace data
|
|
1507
|
+
];
|
|
1225
1508
|
const columns = Object.entries(schema).map(([name, def]) => {
|
|
1226
1509
|
const parsedName = utils.parseSqlIdentifier(name, "column name");
|
|
1227
1510
|
const constraints = [];
|
|
1228
1511
|
if (def.primaryKey) constraints.push("PRIMARY KEY");
|
|
1229
1512
|
if (!def.nullable) constraints.push("NOT NULL");
|
|
1230
1513
|
const isIndexed = !!def.primaryKey || uniqueConstraintColumns.includes(name);
|
|
1231
|
-
|
|
1514
|
+
const useLargeStorage = largeDataColumns.includes(name);
|
|
1515
|
+
return `[${parsedName}] ${this.getSqlType(def.type, isIndexed, useLargeStorage)} ${constraints.join(" ")}`.trim();
|
|
1232
1516
|
}).join(",\n");
|
|
1233
1517
|
if (this.schemaName) {
|
|
1234
1518
|
await this.setupSchema();
|
|
@@ -1315,7 +1599,19 @@ ${columns}
|
|
|
1315
1599
|
const columnExists = Array.isArray(checkResult.recordset) && checkResult.recordset.length > 0;
|
|
1316
1600
|
if (!columnExists) {
|
|
1317
1601
|
const columnDef = schema[columnName];
|
|
1318
|
-
const
|
|
1602
|
+
const largeDataColumns = [
|
|
1603
|
+
"workingMemory",
|
|
1604
|
+
"snapshot",
|
|
1605
|
+
"metadata",
|
|
1606
|
+
"content",
|
|
1607
|
+
"input",
|
|
1608
|
+
"output",
|
|
1609
|
+
"instructions",
|
|
1610
|
+
"other"
|
|
1611
|
+
];
|
|
1612
|
+
const useLargeStorage = largeDataColumns.includes(columnName);
|
|
1613
|
+
const isIndexed = !!columnDef.primaryKey;
|
|
1614
|
+
const sqlType = this.getSqlType(columnDef.type, isIndexed, useLargeStorage);
|
|
1319
1615
|
const nullable = columnDef.nullable === false ? "NOT NULL" : "";
|
|
1320
1616
|
const defaultValue = columnDef.nullable === false ? this.getDefaultValue(columnDef.type) : "";
|
|
1321
1617
|
const parsedColumnName = utils.parseSqlIdentifier(columnName, "column name");
|
|
@@ -1343,13 +1639,17 @@ ${columns}
|
|
|
1343
1639
|
try {
|
|
1344
1640
|
const keyEntries = Object.entries(keys).map(([key, value]) => [utils.parseSqlIdentifier(key, "column name"), value]);
|
|
1345
1641
|
const conditions = keyEntries.map(([key], i) => `[${key}] = @param${i}`).join(" AND ");
|
|
1346
|
-
const
|
|
1347
|
-
const sql6 = `SELECT * FROM ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} WHERE ${conditions}`;
|
|
1642
|
+
const sql5 = `SELECT * FROM ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} WHERE ${conditions}`;
|
|
1348
1643
|
const request = this.pool.request();
|
|
1349
|
-
|
|
1350
|
-
|
|
1644
|
+
keyEntries.forEach(([key, value], i) => {
|
|
1645
|
+
const preparedValue = this.prepareValue(value, key, tableName);
|
|
1646
|
+
if (preparedValue === null || preparedValue === void 0) {
|
|
1647
|
+
request.input(`param${i}`, this.getMssqlType(tableName, key), null);
|
|
1648
|
+
} else {
|
|
1649
|
+
request.input(`param${i}`, preparedValue);
|
|
1650
|
+
}
|
|
1351
1651
|
});
|
|
1352
|
-
const resultSet = await request.query(
|
|
1652
|
+
const resultSet = await request.query(sql5);
|
|
1353
1653
|
const result = resultSet.recordset[0] || null;
|
|
1354
1654
|
if (!result) {
|
|
1355
1655
|
return null;
|
|
@@ -1381,7 +1681,7 @@ ${columns}
|
|
|
1381
1681
|
try {
|
|
1382
1682
|
await transaction.begin();
|
|
1383
1683
|
for (const record of records) {
|
|
1384
|
-
await this.insert({ tableName, record });
|
|
1684
|
+
await this.insert({ tableName, record, transaction });
|
|
1385
1685
|
}
|
|
1386
1686
|
await transaction.commit();
|
|
1387
1687
|
} catch (error$1) {
|
|
@@ -1418,26 +1718,562 @@ ${columns}
|
|
|
1418
1718
|
);
|
|
1419
1719
|
}
|
|
1420
1720
|
}
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1721
|
+
/**
|
|
1722
|
+
* Prepares a value for database operations, handling Date objects and JSON serialization
|
|
1723
|
+
*/
|
|
1724
|
+
prepareValue(value, columnName, tableName) {
|
|
1725
|
+
if (value === null || value === void 0) {
|
|
1726
|
+
return value;
|
|
1727
|
+
}
|
|
1728
|
+
if (value instanceof Date) {
|
|
1729
|
+
return value;
|
|
1730
|
+
}
|
|
1731
|
+
const schema = storage.TABLE_SCHEMAS[tableName];
|
|
1732
|
+
const columnSchema = schema?.[columnName];
|
|
1733
|
+
if (columnSchema?.type === "boolean") {
|
|
1734
|
+
return value ? 1 : 0;
|
|
1735
|
+
}
|
|
1736
|
+
if (columnSchema?.type === "jsonb") {
|
|
1737
|
+
return JSON.stringify(value);
|
|
1738
|
+
}
|
|
1739
|
+
if (typeof value === "object") {
|
|
1740
|
+
return JSON.stringify(value);
|
|
1741
|
+
}
|
|
1742
|
+
return value;
|
|
1427
1743
|
}
|
|
1428
|
-
|
|
1744
|
+
/**
|
|
1745
|
+
* Maps TABLE_SCHEMAS types to mssql param types (used when value is null)
|
|
1746
|
+
*/
|
|
1747
|
+
getMssqlType(tableName, columnName) {
|
|
1748
|
+
const col = storage.TABLE_SCHEMAS[tableName]?.[columnName];
|
|
1749
|
+
switch (col?.type) {
|
|
1750
|
+
case "text":
|
|
1751
|
+
return sql2__default.default.NVarChar;
|
|
1752
|
+
case "timestamp":
|
|
1753
|
+
return sql2__default.default.DateTime2;
|
|
1754
|
+
case "uuid":
|
|
1755
|
+
return sql2__default.default.UniqueIdentifier;
|
|
1756
|
+
case "jsonb":
|
|
1757
|
+
return sql2__default.default.NVarChar;
|
|
1758
|
+
case "integer":
|
|
1759
|
+
return sql2__default.default.Int;
|
|
1760
|
+
case "bigint":
|
|
1761
|
+
return sql2__default.default.BigInt;
|
|
1762
|
+
case "float":
|
|
1763
|
+
return sql2__default.default.Float;
|
|
1764
|
+
case "boolean":
|
|
1765
|
+
return sql2__default.default.Bit;
|
|
1766
|
+
default:
|
|
1767
|
+
return sql2__default.default.NVarChar;
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
/**
|
|
1771
|
+
* Update a single record in the database
|
|
1772
|
+
*/
|
|
1773
|
+
async update({
|
|
1774
|
+
tableName,
|
|
1775
|
+
keys,
|
|
1776
|
+
data,
|
|
1777
|
+
transaction
|
|
1778
|
+
}) {
|
|
1779
|
+
try {
|
|
1780
|
+
if (!data || Object.keys(data).length === 0) {
|
|
1781
|
+
throw new error.MastraError({
|
|
1782
|
+
id: "MASTRA_STORAGE_MSSQL_UPDATE_EMPTY_DATA",
|
|
1783
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1784
|
+
category: error.ErrorCategory.USER,
|
|
1785
|
+
text: "Cannot update with empty data payload"
|
|
1786
|
+
});
|
|
1787
|
+
}
|
|
1788
|
+
if (!keys || Object.keys(keys).length === 0) {
|
|
1789
|
+
throw new error.MastraError({
|
|
1790
|
+
id: "MASTRA_STORAGE_MSSQL_UPDATE_EMPTY_KEYS",
|
|
1791
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1792
|
+
category: error.ErrorCategory.USER,
|
|
1793
|
+
text: "Cannot update without keys to identify records"
|
|
1794
|
+
});
|
|
1795
|
+
}
|
|
1796
|
+
const setClauses = [];
|
|
1797
|
+
const request = transaction ? transaction.request() : this.pool.request();
|
|
1798
|
+
let paramIndex = 0;
|
|
1799
|
+
Object.entries(data).forEach(([key, value]) => {
|
|
1800
|
+
const parsedKey = utils.parseSqlIdentifier(key, "column name");
|
|
1801
|
+
const paramName = `set${paramIndex++}`;
|
|
1802
|
+
setClauses.push(`[${parsedKey}] = @${paramName}`);
|
|
1803
|
+
const preparedValue = this.prepareValue(value, key, tableName);
|
|
1804
|
+
if (preparedValue === null || preparedValue === void 0) {
|
|
1805
|
+
request.input(paramName, this.getMssqlType(tableName, key), null);
|
|
1806
|
+
} else {
|
|
1807
|
+
request.input(paramName, preparedValue);
|
|
1808
|
+
}
|
|
1809
|
+
});
|
|
1810
|
+
const whereConditions = [];
|
|
1811
|
+
Object.entries(keys).forEach(([key, value]) => {
|
|
1812
|
+
const parsedKey = utils.parseSqlIdentifier(key, "column name");
|
|
1813
|
+
const paramName = `where${paramIndex++}`;
|
|
1814
|
+
whereConditions.push(`[${parsedKey}] = @${paramName}`);
|
|
1815
|
+
const preparedValue = this.prepareValue(value, key, tableName);
|
|
1816
|
+
if (preparedValue === null || preparedValue === void 0) {
|
|
1817
|
+
request.input(paramName, this.getMssqlType(tableName, key), null);
|
|
1818
|
+
} else {
|
|
1819
|
+
request.input(paramName, preparedValue);
|
|
1820
|
+
}
|
|
1821
|
+
});
|
|
1822
|
+
const tableName_ = getTableName({
|
|
1823
|
+
indexName: tableName,
|
|
1824
|
+
schemaName: getSchemaName(this.schemaName)
|
|
1825
|
+
});
|
|
1826
|
+
const updateSql = `UPDATE ${tableName_} SET ${setClauses.join(", ")} WHERE ${whereConditions.join(" AND ")}`;
|
|
1827
|
+
await request.query(updateSql);
|
|
1828
|
+
} catch (error$1) {
|
|
1829
|
+
throw new error.MastraError(
|
|
1830
|
+
{
|
|
1831
|
+
id: "MASTRA_STORAGE_MSSQL_STORE_UPDATE_FAILED",
|
|
1832
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1833
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
1834
|
+
details: {
|
|
1835
|
+
tableName
|
|
1836
|
+
}
|
|
1837
|
+
},
|
|
1838
|
+
error$1
|
|
1839
|
+
);
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
/**
|
|
1843
|
+
* Update multiple records in a single batch transaction
|
|
1844
|
+
*/
|
|
1845
|
+
async batchUpdate({
|
|
1846
|
+
tableName,
|
|
1847
|
+
updates
|
|
1848
|
+
}) {
|
|
1849
|
+
const transaction = this.pool.transaction();
|
|
1850
|
+
try {
|
|
1851
|
+
await transaction.begin();
|
|
1852
|
+
for (const { keys, data } of updates) {
|
|
1853
|
+
await this.update({ tableName, keys, data, transaction });
|
|
1854
|
+
}
|
|
1855
|
+
await transaction.commit();
|
|
1856
|
+
} catch (error$1) {
|
|
1857
|
+
await transaction.rollback();
|
|
1858
|
+
throw new error.MastraError(
|
|
1859
|
+
{
|
|
1860
|
+
id: "MASTRA_STORAGE_MSSQL_STORE_BATCH_UPDATE_FAILED",
|
|
1861
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1862
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
1863
|
+
details: {
|
|
1864
|
+
tableName,
|
|
1865
|
+
numberOfRecords: updates.length
|
|
1866
|
+
}
|
|
1867
|
+
},
|
|
1868
|
+
error$1
|
|
1869
|
+
);
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
/**
|
|
1873
|
+
* Delete multiple records by keys
|
|
1874
|
+
*/
|
|
1875
|
+
async batchDelete({ tableName, keys }) {
|
|
1876
|
+
if (keys.length === 0) {
|
|
1877
|
+
return;
|
|
1878
|
+
}
|
|
1879
|
+
const tableName_ = getTableName({
|
|
1880
|
+
indexName: tableName,
|
|
1881
|
+
schemaName: getSchemaName(this.schemaName)
|
|
1882
|
+
});
|
|
1883
|
+
const transaction = this.pool.transaction();
|
|
1884
|
+
try {
|
|
1885
|
+
await transaction.begin();
|
|
1886
|
+
for (const keySet of keys) {
|
|
1887
|
+
const conditions = [];
|
|
1888
|
+
const request = transaction.request();
|
|
1889
|
+
let paramIndex = 0;
|
|
1890
|
+
Object.entries(keySet).forEach(([key, value]) => {
|
|
1891
|
+
const parsedKey = utils.parseSqlIdentifier(key, "column name");
|
|
1892
|
+
const paramName = `p${paramIndex++}`;
|
|
1893
|
+
conditions.push(`[${parsedKey}] = @${paramName}`);
|
|
1894
|
+
const preparedValue = this.prepareValue(value, key, tableName);
|
|
1895
|
+
if (preparedValue === null || preparedValue === void 0) {
|
|
1896
|
+
request.input(paramName, this.getMssqlType(tableName, key), null);
|
|
1897
|
+
} else {
|
|
1898
|
+
request.input(paramName, preparedValue);
|
|
1899
|
+
}
|
|
1900
|
+
});
|
|
1901
|
+
const deleteSql = `DELETE FROM ${tableName_} WHERE ${conditions.join(" AND ")}`;
|
|
1902
|
+
await request.query(deleteSql);
|
|
1903
|
+
}
|
|
1904
|
+
await transaction.commit();
|
|
1905
|
+
} catch (error$1) {
|
|
1906
|
+
await transaction.rollback();
|
|
1907
|
+
throw new error.MastraError(
|
|
1908
|
+
{
|
|
1909
|
+
id: "MASTRA_STORAGE_MSSQL_STORE_BATCH_DELETE_FAILED",
|
|
1910
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1911
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
1912
|
+
details: {
|
|
1913
|
+
tableName,
|
|
1914
|
+
numberOfRecords: keys.length
|
|
1915
|
+
}
|
|
1916
|
+
},
|
|
1917
|
+
error$1
|
|
1918
|
+
);
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
/**
|
|
1922
|
+
* Create a new index on a table
|
|
1923
|
+
*/
|
|
1924
|
+
async createIndex(options) {
|
|
1925
|
+
try {
|
|
1926
|
+
const { name, table, columns, unique = false, where } = options;
|
|
1927
|
+
const schemaName = this.schemaName || "dbo";
|
|
1928
|
+
const fullTableName = getTableName({
|
|
1929
|
+
indexName: table,
|
|
1930
|
+
schemaName: getSchemaName(this.schemaName)
|
|
1931
|
+
});
|
|
1932
|
+
const indexNameSafe = utils.parseSqlIdentifier(name, "index name");
|
|
1933
|
+
const checkRequest = this.pool.request();
|
|
1934
|
+
checkRequest.input("indexName", indexNameSafe);
|
|
1935
|
+
checkRequest.input("schemaName", schemaName);
|
|
1936
|
+
checkRequest.input("tableName", table);
|
|
1937
|
+
const indexExists = await checkRequest.query(`
|
|
1938
|
+
SELECT 1 as found
|
|
1939
|
+
FROM sys.indexes i
|
|
1940
|
+
INNER JOIN sys.tables t ON i.object_id = t.object_id
|
|
1941
|
+
INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
|
|
1942
|
+
WHERE i.name = @indexName
|
|
1943
|
+
AND s.name = @schemaName
|
|
1944
|
+
AND t.name = @tableName
|
|
1945
|
+
`);
|
|
1946
|
+
if (indexExists.recordset && indexExists.recordset.length > 0) {
|
|
1947
|
+
return;
|
|
1948
|
+
}
|
|
1949
|
+
const uniqueStr = unique ? "UNIQUE " : "";
|
|
1950
|
+
const columnsStr = columns.map((col) => {
|
|
1951
|
+
if (col.includes(" DESC") || col.includes(" ASC")) {
|
|
1952
|
+
const [colName, ...modifiers] = col.split(" ");
|
|
1953
|
+
if (!colName) {
|
|
1954
|
+
throw new Error(`Invalid column specification: ${col}`);
|
|
1955
|
+
}
|
|
1956
|
+
return `[${utils.parseSqlIdentifier(colName, "column name")}] ${modifiers.join(" ")}`;
|
|
1957
|
+
}
|
|
1958
|
+
return `[${utils.parseSqlIdentifier(col, "column name")}]`;
|
|
1959
|
+
}).join(", ");
|
|
1960
|
+
const whereStr = where ? ` WHERE ${where}` : "";
|
|
1961
|
+
const createIndexSql = `CREATE ${uniqueStr}INDEX [${indexNameSafe}] ON ${fullTableName} (${columnsStr})${whereStr}`;
|
|
1962
|
+
await this.pool.request().query(createIndexSql);
|
|
1963
|
+
} catch (error$1) {
|
|
1964
|
+
throw new error.MastraError(
|
|
1965
|
+
{
|
|
1966
|
+
id: "MASTRA_STORAGE_MSSQL_INDEX_CREATE_FAILED",
|
|
1967
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1968
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
1969
|
+
details: {
|
|
1970
|
+
indexName: options.name,
|
|
1971
|
+
tableName: options.table
|
|
1972
|
+
}
|
|
1973
|
+
},
|
|
1974
|
+
error$1
|
|
1975
|
+
);
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
/**
|
|
1979
|
+
* Drop an existing index
|
|
1980
|
+
*/
|
|
1981
|
+
async dropIndex(indexName) {
|
|
1982
|
+
try {
|
|
1983
|
+
const schemaName = this.schemaName || "dbo";
|
|
1984
|
+
const indexNameSafe = utils.parseSqlIdentifier(indexName, "index name");
|
|
1985
|
+
const checkRequest = this.pool.request();
|
|
1986
|
+
checkRequest.input("indexName", indexNameSafe);
|
|
1987
|
+
checkRequest.input("schemaName", schemaName);
|
|
1988
|
+
const result = await checkRequest.query(`
|
|
1989
|
+
SELECT t.name as table_name
|
|
1990
|
+
FROM sys.indexes i
|
|
1991
|
+
INNER JOIN sys.tables t ON i.object_id = t.object_id
|
|
1992
|
+
INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
|
|
1993
|
+
WHERE i.name = @indexName
|
|
1994
|
+
AND s.name = @schemaName
|
|
1995
|
+
`);
|
|
1996
|
+
if (!result.recordset || result.recordset.length === 0) {
|
|
1997
|
+
return;
|
|
1998
|
+
}
|
|
1999
|
+
if (result.recordset.length > 1) {
|
|
2000
|
+
const tables = result.recordset.map((r) => r.table_name).join(", ");
|
|
2001
|
+
throw new error.MastraError({
|
|
2002
|
+
id: "MASTRA_STORAGE_MSSQL_INDEX_AMBIGUOUS",
|
|
2003
|
+
domain: error.ErrorDomain.STORAGE,
|
|
2004
|
+
category: error.ErrorCategory.USER,
|
|
2005
|
+
text: `Index "${indexNameSafe}" exists on multiple tables (${tables}) in schema "${schemaName}". Please drop indexes manually or ensure unique index names.`
|
|
2006
|
+
});
|
|
2007
|
+
}
|
|
2008
|
+
const tableName = result.recordset[0].table_name;
|
|
2009
|
+
const fullTableName = getTableName({
|
|
2010
|
+
indexName: tableName,
|
|
2011
|
+
schemaName: getSchemaName(this.schemaName)
|
|
2012
|
+
});
|
|
2013
|
+
const dropSql = `DROP INDEX [${indexNameSafe}] ON ${fullTableName}`;
|
|
2014
|
+
await this.pool.request().query(dropSql);
|
|
2015
|
+
} catch (error$1) {
|
|
2016
|
+
throw new error.MastraError(
|
|
2017
|
+
{
|
|
2018
|
+
id: "MASTRA_STORAGE_MSSQL_INDEX_DROP_FAILED",
|
|
2019
|
+
domain: error.ErrorDomain.STORAGE,
|
|
2020
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
2021
|
+
details: {
|
|
2022
|
+
indexName
|
|
2023
|
+
}
|
|
2024
|
+
},
|
|
2025
|
+
error$1
|
|
2026
|
+
);
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
/**
|
|
2030
|
+
* List indexes for a specific table or all tables
|
|
2031
|
+
*/
|
|
2032
|
+
async listIndexes(tableName) {
|
|
2033
|
+
try {
|
|
2034
|
+
const schemaName = this.schemaName || "dbo";
|
|
2035
|
+
let query;
|
|
2036
|
+
const request = this.pool.request();
|
|
2037
|
+
request.input("schemaName", schemaName);
|
|
2038
|
+
if (tableName) {
|
|
2039
|
+
query = `
|
|
2040
|
+
SELECT
|
|
2041
|
+
i.name as name,
|
|
2042
|
+
o.name as [table],
|
|
2043
|
+
i.is_unique as is_unique,
|
|
2044
|
+
CAST(SUM(s.used_page_count) * 8 / 1024.0 AS VARCHAR(50)) + ' MB' as size
|
|
2045
|
+
FROM sys.indexes i
|
|
2046
|
+
INNER JOIN sys.objects o ON i.object_id = o.object_id
|
|
2047
|
+
INNER JOIN sys.schemas sch ON o.schema_id = sch.schema_id
|
|
2048
|
+
LEFT JOIN sys.dm_db_partition_stats s ON i.object_id = s.object_id AND i.index_id = s.index_id
|
|
2049
|
+
WHERE sch.name = @schemaName
|
|
2050
|
+
AND o.name = @tableName
|
|
2051
|
+
AND i.name IS NOT NULL
|
|
2052
|
+
GROUP BY i.name, o.name, i.is_unique
|
|
2053
|
+
`;
|
|
2054
|
+
request.input("tableName", tableName);
|
|
2055
|
+
} else {
|
|
2056
|
+
query = `
|
|
2057
|
+
SELECT
|
|
2058
|
+
i.name as name,
|
|
2059
|
+
o.name as [table],
|
|
2060
|
+
i.is_unique as is_unique,
|
|
2061
|
+
CAST(SUM(s.used_page_count) * 8 / 1024.0 AS VARCHAR(50)) + ' MB' as size
|
|
2062
|
+
FROM sys.indexes i
|
|
2063
|
+
INNER JOIN sys.objects o ON i.object_id = o.object_id
|
|
2064
|
+
INNER JOIN sys.schemas sch ON o.schema_id = sch.schema_id
|
|
2065
|
+
LEFT JOIN sys.dm_db_partition_stats s ON i.object_id = s.object_id AND i.index_id = s.index_id
|
|
2066
|
+
WHERE sch.name = @schemaName
|
|
2067
|
+
AND i.name IS NOT NULL
|
|
2068
|
+
GROUP BY i.name, o.name, i.is_unique
|
|
2069
|
+
`;
|
|
2070
|
+
}
|
|
2071
|
+
const result = await request.query(query);
|
|
2072
|
+
const indexes = [];
|
|
2073
|
+
for (const row of result.recordset) {
|
|
2074
|
+
const colRequest = this.pool.request();
|
|
2075
|
+
colRequest.input("indexName", row.name);
|
|
2076
|
+
colRequest.input("schemaName", schemaName);
|
|
2077
|
+
const colResult = await colRequest.query(`
|
|
2078
|
+
SELECT c.name as column_name
|
|
2079
|
+
FROM sys.indexes i
|
|
2080
|
+
INNER JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
|
|
2081
|
+
INNER JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
|
|
2082
|
+
INNER JOIN sys.objects o ON i.object_id = o.object_id
|
|
2083
|
+
INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
|
|
2084
|
+
WHERE i.name = @indexName
|
|
2085
|
+
AND s.name = @schemaName
|
|
2086
|
+
ORDER BY ic.key_ordinal
|
|
2087
|
+
`);
|
|
2088
|
+
indexes.push({
|
|
2089
|
+
name: row.name,
|
|
2090
|
+
table: row.table,
|
|
2091
|
+
columns: colResult.recordset.map((c) => c.column_name),
|
|
2092
|
+
unique: row.is_unique || false,
|
|
2093
|
+
size: row.size || "0 MB",
|
|
2094
|
+
definition: ""
|
|
2095
|
+
// MSSQL doesn't store definition like PG
|
|
2096
|
+
});
|
|
2097
|
+
}
|
|
2098
|
+
return indexes;
|
|
2099
|
+
} catch (error$1) {
|
|
2100
|
+
throw new error.MastraError(
|
|
2101
|
+
{
|
|
2102
|
+
id: "MASTRA_STORAGE_MSSQL_INDEX_LIST_FAILED",
|
|
2103
|
+
domain: error.ErrorDomain.STORAGE,
|
|
2104
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
2105
|
+
details: tableName ? {
|
|
2106
|
+
tableName
|
|
2107
|
+
} : {}
|
|
2108
|
+
},
|
|
2109
|
+
error$1
|
|
2110
|
+
);
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
2113
|
+
/**
|
|
2114
|
+
* Get detailed statistics for a specific index
|
|
2115
|
+
*/
|
|
2116
|
+
async describeIndex(indexName) {
|
|
2117
|
+
try {
|
|
2118
|
+
const schemaName = this.schemaName || "dbo";
|
|
2119
|
+
const request = this.pool.request();
|
|
2120
|
+
request.input("indexName", indexName);
|
|
2121
|
+
request.input("schemaName", schemaName);
|
|
2122
|
+
const query = `
|
|
2123
|
+
SELECT
|
|
2124
|
+
i.name as name,
|
|
2125
|
+
o.name as [table],
|
|
2126
|
+
i.is_unique as is_unique,
|
|
2127
|
+
CAST(SUM(s.used_page_count) * 8 / 1024.0 AS VARCHAR(50)) + ' MB' as size,
|
|
2128
|
+
i.type_desc as method,
|
|
2129
|
+
ISNULL(us.user_scans, 0) as scans,
|
|
2130
|
+
ISNULL(us.user_seeks + us.user_scans, 0) as tuples_read,
|
|
2131
|
+
ISNULL(us.user_lookups, 0) as tuples_fetched
|
|
2132
|
+
FROM sys.indexes i
|
|
2133
|
+
INNER JOIN sys.objects o ON i.object_id = o.object_id
|
|
2134
|
+
INNER JOIN sys.schemas sch ON o.schema_id = sch.schema_id
|
|
2135
|
+
LEFT JOIN sys.dm_db_partition_stats s ON i.object_id = s.object_id AND i.index_id = s.index_id
|
|
2136
|
+
LEFT JOIN sys.dm_db_index_usage_stats us ON i.object_id = us.object_id AND i.index_id = us.index_id
|
|
2137
|
+
WHERE i.name = @indexName
|
|
2138
|
+
AND sch.name = @schemaName
|
|
2139
|
+
GROUP BY i.name, o.name, i.is_unique, i.type_desc, us.user_seeks, us.user_scans, us.user_lookups
|
|
2140
|
+
`;
|
|
2141
|
+
const result = await request.query(query);
|
|
2142
|
+
if (!result.recordset || result.recordset.length === 0) {
|
|
2143
|
+
throw new Error(`Index "${indexName}" not found in schema "${schemaName}"`);
|
|
2144
|
+
}
|
|
2145
|
+
const row = result.recordset[0];
|
|
2146
|
+
const colRequest = this.pool.request();
|
|
2147
|
+
colRequest.input("indexName", indexName);
|
|
2148
|
+
colRequest.input("schemaName", schemaName);
|
|
2149
|
+
const colResult = await colRequest.query(`
|
|
2150
|
+
SELECT c.name as column_name
|
|
2151
|
+
FROM sys.indexes i
|
|
2152
|
+
INNER JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
|
|
2153
|
+
INNER JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
|
|
2154
|
+
INNER JOIN sys.objects o ON i.object_id = o.object_id
|
|
2155
|
+
INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
|
|
2156
|
+
WHERE i.name = @indexName
|
|
2157
|
+
AND s.name = @schemaName
|
|
2158
|
+
ORDER BY ic.key_ordinal
|
|
2159
|
+
`);
|
|
2160
|
+
return {
|
|
2161
|
+
name: row.name,
|
|
2162
|
+
table: row.table,
|
|
2163
|
+
columns: colResult.recordset.map((c) => c.column_name),
|
|
2164
|
+
unique: row.is_unique || false,
|
|
2165
|
+
size: row.size || "0 MB",
|
|
2166
|
+
definition: "",
|
|
2167
|
+
method: row.method?.toLowerCase() || "nonclustered",
|
|
2168
|
+
scans: Number(row.scans) || 0,
|
|
2169
|
+
tuples_read: Number(row.tuples_read) || 0,
|
|
2170
|
+
tuples_fetched: Number(row.tuples_fetched) || 0
|
|
2171
|
+
};
|
|
2172
|
+
} catch (error$1) {
|
|
2173
|
+
throw new error.MastraError(
|
|
2174
|
+
{
|
|
2175
|
+
id: "MASTRA_STORAGE_MSSQL_INDEX_DESCRIBE_FAILED",
|
|
2176
|
+
domain: error.ErrorDomain.STORAGE,
|
|
2177
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
2178
|
+
details: {
|
|
2179
|
+
indexName
|
|
2180
|
+
}
|
|
2181
|
+
},
|
|
2182
|
+
error$1
|
|
2183
|
+
);
|
|
2184
|
+
}
|
|
2185
|
+
}
|
|
2186
|
+
/**
|
|
2187
|
+
* Returns definitions for automatic performance indexes
|
|
2188
|
+
* IMPORTANT: Uses seq_id DESC instead of createdAt DESC for MSSQL due to millisecond accuracy limitations
|
|
2189
|
+
* NOTE: Using NVARCHAR(400) for text columns (800 bytes) leaves room for composite indexes
|
|
2190
|
+
*/
|
|
2191
|
+
getAutomaticIndexDefinitions() {
|
|
2192
|
+
const schemaPrefix = this.schemaName ? `${this.schemaName}_` : "";
|
|
2193
|
+
return [
|
|
2194
|
+
// Composite indexes for optimal filtering + sorting performance
|
|
2195
|
+
// NVARCHAR(400) = 800 bytes, plus BIGINT (8 bytes) = 808 bytes total (under 900-byte limit)
|
|
2196
|
+
{
|
|
2197
|
+
name: `${schemaPrefix}mastra_threads_resourceid_seqid_idx`,
|
|
2198
|
+
table: storage.TABLE_THREADS,
|
|
2199
|
+
columns: ["resourceId", "seq_id DESC"]
|
|
2200
|
+
},
|
|
2201
|
+
{
|
|
2202
|
+
name: `${schemaPrefix}mastra_messages_thread_id_seqid_idx`,
|
|
2203
|
+
table: storage.TABLE_MESSAGES,
|
|
2204
|
+
columns: ["thread_id", "seq_id DESC"]
|
|
2205
|
+
},
|
|
2206
|
+
{
|
|
2207
|
+
name: `${schemaPrefix}mastra_traces_name_seqid_idx`,
|
|
2208
|
+
table: storage.TABLE_TRACES,
|
|
2209
|
+
columns: ["name", "seq_id DESC"]
|
|
2210
|
+
},
|
|
2211
|
+
{
|
|
2212
|
+
name: `${schemaPrefix}mastra_scores_trace_id_span_id_seqid_idx`,
|
|
2213
|
+
table: storage.TABLE_SCORERS,
|
|
2214
|
+
columns: ["traceId", "spanId", "seq_id DESC"]
|
|
2215
|
+
},
|
|
2216
|
+
// AI Spans indexes for optimal trace querying
|
|
2217
|
+
{
|
|
2218
|
+
name: `${schemaPrefix}mastra_ai_spans_traceid_startedat_idx`,
|
|
2219
|
+
table: storage.TABLE_AI_SPANS,
|
|
2220
|
+
columns: ["traceId", "startedAt DESC"]
|
|
2221
|
+
},
|
|
2222
|
+
{
|
|
2223
|
+
name: `${schemaPrefix}mastra_ai_spans_parentspanid_startedat_idx`,
|
|
2224
|
+
table: storage.TABLE_AI_SPANS,
|
|
2225
|
+
columns: ["parentSpanId", "startedAt DESC"]
|
|
2226
|
+
},
|
|
2227
|
+
{
|
|
2228
|
+
name: `${schemaPrefix}mastra_ai_spans_name_idx`,
|
|
2229
|
+
table: storage.TABLE_AI_SPANS,
|
|
2230
|
+
columns: ["name"]
|
|
2231
|
+
},
|
|
2232
|
+
{
|
|
2233
|
+
name: `${schemaPrefix}mastra_ai_spans_spantype_startedat_idx`,
|
|
2234
|
+
table: storage.TABLE_AI_SPANS,
|
|
2235
|
+
columns: ["spanType", "startedAt DESC"]
|
|
2236
|
+
}
|
|
2237
|
+
];
|
|
2238
|
+
}
|
|
2239
|
+
/**
|
|
2240
|
+
* Creates automatic indexes for optimal query performance
|
|
2241
|
+
* Uses getAutomaticIndexDefinitions() to determine which indexes to create
|
|
2242
|
+
*/
|
|
2243
|
+
async createAutomaticIndexes() {
|
|
2244
|
+
try {
|
|
2245
|
+
const indexes = this.getAutomaticIndexDefinitions();
|
|
2246
|
+
for (const indexOptions of indexes) {
|
|
2247
|
+
try {
|
|
2248
|
+
await this.createIndex(indexOptions);
|
|
2249
|
+
} catch (error) {
|
|
2250
|
+
this.logger?.warn?.(`Failed to create index ${indexOptions.name}:`, error);
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
} catch (error$1) {
|
|
2254
|
+
throw new error.MastraError(
|
|
2255
|
+
{
|
|
2256
|
+
id: "MASTRA_STORAGE_MSSQL_STORE_CREATE_PERFORMANCE_INDEXES_FAILED",
|
|
2257
|
+
domain: error.ErrorDomain.STORAGE,
|
|
2258
|
+
category: error.ErrorCategory.THIRD_PARTY
|
|
2259
|
+
},
|
|
2260
|
+
error$1
|
|
2261
|
+
);
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
|
+
};
|
|
1429
2265
|
function transformScoreRow(row) {
|
|
1430
2266
|
return {
|
|
1431
2267
|
...row,
|
|
1432
|
-
input:
|
|
1433
|
-
scorer:
|
|
1434
|
-
preprocessStepResult:
|
|
1435
|
-
analyzeStepResult:
|
|
1436
|
-
metadata:
|
|
1437
|
-
output:
|
|
1438
|
-
additionalContext:
|
|
1439
|
-
|
|
1440
|
-
entity:
|
|
2268
|
+
input: storage.safelyParseJSON(row.input),
|
|
2269
|
+
scorer: storage.safelyParseJSON(row.scorer),
|
|
2270
|
+
preprocessStepResult: storage.safelyParseJSON(row.preprocessStepResult),
|
|
2271
|
+
analyzeStepResult: storage.safelyParseJSON(row.analyzeStepResult),
|
|
2272
|
+
metadata: storage.safelyParseJSON(row.metadata),
|
|
2273
|
+
output: storage.safelyParseJSON(row.output),
|
|
2274
|
+
additionalContext: storage.safelyParseJSON(row.additionalContext),
|
|
2275
|
+
requestContext: storage.safelyParseJSON(row.requestContext),
|
|
2276
|
+
entity: storage.safelyParseJSON(row.entity),
|
|
1441
2277
|
createdAt: row.createdAt,
|
|
1442
2278
|
updatedAt: row.updatedAt
|
|
1443
2279
|
};
|
|
@@ -1503,7 +2339,7 @@ var ScoresMSSQL = class extends storage.ScoresStorage {
|
|
|
1503
2339
|
input,
|
|
1504
2340
|
output,
|
|
1505
2341
|
additionalContext,
|
|
1506
|
-
|
|
2342
|
+
requestContext,
|
|
1507
2343
|
entity,
|
|
1508
2344
|
...rest
|
|
1509
2345
|
} = validatedScore;
|
|
@@ -1512,15 +2348,15 @@ var ScoresMSSQL = class extends storage.ScoresStorage {
|
|
|
1512
2348
|
record: {
|
|
1513
2349
|
id: scoreId,
|
|
1514
2350
|
...rest,
|
|
1515
|
-
input:
|
|
1516
|
-
output:
|
|
1517
|
-
preprocessStepResult: preprocessStepResult
|
|
1518
|
-
analyzeStepResult: analyzeStepResult
|
|
1519
|
-
metadata: metadata
|
|
1520
|
-
additionalContext: additionalContext
|
|
1521
|
-
|
|
1522
|
-
entity: entity
|
|
1523
|
-
scorer: scorer
|
|
2351
|
+
input: input || "",
|
|
2352
|
+
output: output || "",
|
|
2353
|
+
preprocessStepResult: preprocessStepResult || null,
|
|
2354
|
+
analyzeStepResult: analyzeStepResult || null,
|
|
2355
|
+
metadata: metadata || null,
|
|
2356
|
+
additionalContext: additionalContext || null,
|
|
2357
|
+
requestContext: requestContext || null,
|
|
2358
|
+
entity: entity || null,
|
|
2359
|
+
scorer: scorer || null,
|
|
1524
2360
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1525
2361
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1526
2362
|
}
|
|
@@ -1540,14 +2376,37 @@ var ScoresMSSQL = class extends storage.ScoresStorage {
|
|
|
1540
2376
|
}
|
|
1541
2377
|
async getScoresByScorerId({
|
|
1542
2378
|
scorerId,
|
|
1543
|
-
pagination
|
|
2379
|
+
pagination,
|
|
2380
|
+
entityId,
|
|
2381
|
+
entityType,
|
|
2382
|
+
source
|
|
1544
2383
|
}) {
|
|
1545
2384
|
try {
|
|
1546
|
-
const
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
2385
|
+
const conditions = ["[scorerId] = @p1"];
|
|
2386
|
+
const params = { p1: scorerId };
|
|
2387
|
+
let paramIndex = 2;
|
|
2388
|
+
if (entityId) {
|
|
2389
|
+
conditions.push(`[entityId] = @p${paramIndex}`);
|
|
2390
|
+
params[`p${paramIndex}`] = entityId;
|
|
2391
|
+
paramIndex++;
|
|
2392
|
+
}
|
|
2393
|
+
if (entityType) {
|
|
2394
|
+
conditions.push(`[entityType] = @p${paramIndex}`);
|
|
2395
|
+
params[`p${paramIndex}`] = entityType;
|
|
2396
|
+
paramIndex++;
|
|
2397
|
+
}
|
|
2398
|
+
if (source) {
|
|
2399
|
+
conditions.push(`[source] = @p${paramIndex}`);
|
|
2400
|
+
params[`p${paramIndex}`] = source;
|
|
2401
|
+
paramIndex++;
|
|
2402
|
+
}
|
|
2403
|
+
const whereClause = conditions.join(" AND ");
|
|
2404
|
+
const tableName = getTableName({ indexName: storage.TABLE_SCORERS, schemaName: getSchemaName(this.schema) });
|
|
2405
|
+
const countRequest = this.pool.request();
|
|
2406
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
2407
|
+
countRequest.input(key, value);
|
|
2408
|
+
});
|
|
2409
|
+
const totalResult = await countRequest.query(`SELECT COUNT(*) as count FROM ${tableName} WHERE ${whereClause}`);
|
|
1551
2410
|
const total = totalResult.recordset[0]?.count || 0;
|
|
1552
2411
|
if (total === 0) {
|
|
1553
2412
|
return {
|
|
@@ -1561,12 +2420,13 @@ var ScoresMSSQL = class extends storage.ScoresStorage {
|
|
|
1561
2420
|
};
|
|
1562
2421
|
}
|
|
1563
2422
|
const dataRequest = this.pool.request();
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
2423
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
2424
|
+
dataRequest.input(key, value);
|
|
2425
|
+
});
|
|
2426
|
+
dataRequest.input("perPage", pagination.perPage);
|
|
2427
|
+
dataRequest.input("offset", pagination.page * pagination.perPage);
|
|
2428
|
+
const dataQuery = `SELECT * FROM ${tableName} WHERE ${whereClause} ORDER BY [createdAt] DESC OFFSET @offset ROWS FETCH NEXT @perPage ROWS ONLY`;
|
|
2429
|
+
const result = await dataRequest.query(dataQuery);
|
|
1570
2430
|
return {
|
|
1571
2431
|
pagination: {
|
|
1572
2432
|
total: Number(total),
|
|
@@ -1746,24 +2606,6 @@ var ScoresMSSQL = class extends storage.ScoresStorage {
|
|
|
1746
2606
|
}
|
|
1747
2607
|
}
|
|
1748
2608
|
};
|
|
1749
|
-
function parseWorkflowRun(row) {
|
|
1750
|
-
let parsedSnapshot = row.snapshot;
|
|
1751
|
-
if (typeof parsedSnapshot === "string") {
|
|
1752
|
-
try {
|
|
1753
|
-
parsedSnapshot = JSON.parse(row.snapshot);
|
|
1754
|
-
} catch (e) {
|
|
1755
|
-
console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
|
|
1756
|
-
}
|
|
1757
|
-
}
|
|
1758
|
-
return {
|
|
1759
|
-
workflowName: row.workflow_name,
|
|
1760
|
-
runId: row.run_id,
|
|
1761
|
-
snapshot: parsedSnapshot,
|
|
1762
|
-
createdAt: row.createdAt,
|
|
1763
|
-
updatedAt: row.updatedAt,
|
|
1764
|
-
resourceId: row.resourceId
|
|
1765
|
-
};
|
|
1766
|
-
}
|
|
1767
2609
|
var WorkflowsMSSQL = class extends storage.WorkflowsStorage {
|
|
1768
2610
|
pool;
|
|
1769
2611
|
operations;
|
|
@@ -1778,21 +2620,163 @@ var WorkflowsMSSQL = class extends storage.WorkflowsStorage {
|
|
|
1778
2620
|
this.operations = operations;
|
|
1779
2621
|
this.schema = schema;
|
|
1780
2622
|
}
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
2623
|
+
parseWorkflowRun(row) {
|
|
2624
|
+
let parsedSnapshot = row.snapshot;
|
|
2625
|
+
if (typeof parsedSnapshot === "string") {
|
|
2626
|
+
try {
|
|
2627
|
+
parsedSnapshot = JSON.parse(row.snapshot);
|
|
2628
|
+
} catch (e) {
|
|
2629
|
+
this.logger?.warn?.(`Failed to parse snapshot for workflow ${row.workflow_name}:`, e);
|
|
2630
|
+
}
|
|
2631
|
+
}
|
|
2632
|
+
return {
|
|
2633
|
+
workflowName: row.workflow_name,
|
|
2634
|
+
runId: row.run_id,
|
|
2635
|
+
snapshot: parsedSnapshot,
|
|
2636
|
+
createdAt: row.createdAt,
|
|
2637
|
+
updatedAt: row.updatedAt,
|
|
2638
|
+
resourceId: row.resourceId
|
|
2639
|
+
};
|
|
2640
|
+
}
|
|
2641
|
+
async updateWorkflowResults({
|
|
2642
|
+
workflowName,
|
|
2643
|
+
runId,
|
|
2644
|
+
stepId,
|
|
2645
|
+
result,
|
|
2646
|
+
requestContext
|
|
1787
2647
|
}) {
|
|
1788
|
-
|
|
2648
|
+
const table = getTableName({ indexName: storage.TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName(this.schema) });
|
|
2649
|
+
const transaction = this.pool.transaction();
|
|
2650
|
+
try {
|
|
2651
|
+
await transaction.begin();
|
|
2652
|
+
const selectRequest = new sql2__default.default.Request(transaction);
|
|
2653
|
+
selectRequest.input("workflow_name", workflowName);
|
|
2654
|
+
selectRequest.input("run_id", runId);
|
|
2655
|
+
const existingSnapshotResult = await selectRequest.query(
|
|
2656
|
+
`SELECT snapshot FROM ${table} WITH (UPDLOCK, HOLDLOCK) WHERE workflow_name = @workflow_name AND run_id = @run_id`
|
|
2657
|
+
);
|
|
2658
|
+
let snapshot;
|
|
2659
|
+
if (!existingSnapshotResult.recordset || existingSnapshotResult.recordset.length === 0) {
|
|
2660
|
+
snapshot = {
|
|
2661
|
+
context: {},
|
|
2662
|
+
activePaths: [],
|
|
2663
|
+
timestamp: Date.now(),
|
|
2664
|
+
suspendedPaths: {},
|
|
2665
|
+
resumeLabels: {},
|
|
2666
|
+
serializedStepGraph: [],
|
|
2667
|
+
value: {},
|
|
2668
|
+
waitingPaths: {},
|
|
2669
|
+
status: "pending",
|
|
2670
|
+
runId,
|
|
2671
|
+
requestContext: {}
|
|
2672
|
+
};
|
|
2673
|
+
} else {
|
|
2674
|
+
const existingSnapshot = existingSnapshotResult.recordset[0].snapshot;
|
|
2675
|
+
snapshot = typeof existingSnapshot === "string" ? JSON.parse(existingSnapshot) : existingSnapshot;
|
|
2676
|
+
}
|
|
2677
|
+
snapshot.context[stepId] = result;
|
|
2678
|
+
snapshot.requestContext = { ...snapshot.requestContext, ...requestContext };
|
|
2679
|
+
const upsertReq = new sql2__default.default.Request(transaction);
|
|
2680
|
+
upsertReq.input("workflow_name", workflowName);
|
|
2681
|
+
upsertReq.input("run_id", runId);
|
|
2682
|
+
upsertReq.input("snapshot", JSON.stringify(snapshot));
|
|
2683
|
+
upsertReq.input("createdAt", sql2__default.default.DateTime2, /* @__PURE__ */ new Date());
|
|
2684
|
+
upsertReq.input("updatedAt", sql2__default.default.DateTime2, /* @__PURE__ */ new Date());
|
|
2685
|
+
await upsertReq.query(
|
|
2686
|
+
`MERGE ${table} AS target
|
|
2687
|
+
USING (SELECT @workflow_name AS workflow_name, @run_id AS run_id) AS src
|
|
2688
|
+
ON target.workflow_name = src.workflow_name AND target.run_id = src.run_id
|
|
2689
|
+
WHEN MATCHED THEN UPDATE SET snapshot = @snapshot, [updatedAt] = @updatedAt
|
|
2690
|
+
WHEN NOT MATCHED THEN INSERT (workflow_name, run_id, snapshot, [createdAt], [updatedAt])
|
|
2691
|
+
VALUES (@workflow_name, @run_id, @snapshot, @createdAt, @updatedAt);`
|
|
2692
|
+
);
|
|
2693
|
+
await transaction.commit();
|
|
2694
|
+
return snapshot.context;
|
|
2695
|
+
} catch (error$1) {
|
|
2696
|
+
try {
|
|
2697
|
+
await transaction.rollback();
|
|
2698
|
+
} catch {
|
|
2699
|
+
}
|
|
2700
|
+
throw new error.MastraError(
|
|
2701
|
+
{
|
|
2702
|
+
id: "MASTRA_STORAGE_MSSQL_STORE_UPDATE_WORKFLOW_RESULTS_FAILED",
|
|
2703
|
+
domain: error.ErrorDomain.STORAGE,
|
|
2704
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
2705
|
+
details: {
|
|
2706
|
+
workflowName,
|
|
2707
|
+
runId,
|
|
2708
|
+
stepId
|
|
2709
|
+
}
|
|
2710
|
+
},
|
|
2711
|
+
error$1
|
|
2712
|
+
);
|
|
2713
|
+
}
|
|
1789
2714
|
}
|
|
1790
|
-
updateWorkflowState({
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
2715
|
+
async updateWorkflowState({
|
|
2716
|
+
workflowName,
|
|
2717
|
+
runId,
|
|
2718
|
+
opts
|
|
1794
2719
|
}) {
|
|
1795
|
-
|
|
2720
|
+
const table = getTableName({ indexName: storage.TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName(this.schema) });
|
|
2721
|
+
const transaction = this.pool.transaction();
|
|
2722
|
+
try {
|
|
2723
|
+
await transaction.begin();
|
|
2724
|
+
const selectRequest = new sql2__default.default.Request(transaction);
|
|
2725
|
+
selectRequest.input("workflow_name", workflowName);
|
|
2726
|
+
selectRequest.input("run_id", runId);
|
|
2727
|
+
const existingSnapshotResult = await selectRequest.query(
|
|
2728
|
+
`SELECT snapshot FROM ${table} WITH (UPDLOCK, HOLDLOCK) WHERE workflow_name = @workflow_name AND run_id = @run_id`
|
|
2729
|
+
);
|
|
2730
|
+
if (!existingSnapshotResult.recordset || existingSnapshotResult.recordset.length === 0) {
|
|
2731
|
+
await transaction.rollback();
|
|
2732
|
+
return void 0;
|
|
2733
|
+
}
|
|
2734
|
+
const existingSnapshot = existingSnapshotResult.recordset[0].snapshot;
|
|
2735
|
+
const snapshot = typeof existingSnapshot === "string" ? JSON.parse(existingSnapshot) : existingSnapshot;
|
|
2736
|
+
if (!snapshot || !snapshot?.context) {
|
|
2737
|
+
await transaction.rollback();
|
|
2738
|
+
throw new error.MastraError(
|
|
2739
|
+
{
|
|
2740
|
+
id: "MASTRA_STORAGE_MSSQL_STORE_UPDATE_WORKFLOW_STATE_SNAPSHOT_NOT_FOUND",
|
|
2741
|
+
domain: error.ErrorDomain.STORAGE,
|
|
2742
|
+
category: error.ErrorCategory.SYSTEM,
|
|
2743
|
+
details: {
|
|
2744
|
+
workflowName,
|
|
2745
|
+
runId
|
|
2746
|
+
}
|
|
2747
|
+
},
|
|
2748
|
+
new Error(`Snapshot not found for runId ${runId}`)
|
|
2749
|
+
);
|
|
2750
|
+
}
|
|
2751
|
+
const updatedSnapshot = { ...snapshot, ...opts };
|
|
2752
|
+
const updateRequest = new sql2__default.default.Request(transaction);
|
|
2753
|
+
updateRequest.input("snapshot", JSON.stringify(updatedSnapshot));
|
|
2754
|
+
updateRequest.input("workflow_name", workflowName);
|
|
2755
|
+
updateRequest.input("run_id", runId);
|
|
2756
|
+
updateRequest.input("updatedAt", sql2__default.default.DateTime2, /* @__PURE__ */ new Date());
|
|
2757
|
+
await updateRequest.query(
|
|
2758
|
+
`UPDATE ${table} SET snapshot = @snapshot, [updatedAt] = @updatedAt WHERE workflow_name = @workflow_name AND run_id = @run_id`
|
|
2759
|
+
);
|
|
2760
|
+
await transaction.commit();
|
|
2761
|
+
return updatedSnapshot;
|
|
2762
|
+
} catch (error$1) {
|
|
2763
|
+
try {
|
|
2764
|
+
await transaction.rollback();
|
|
2765
|
+
} catch {
|
|
2766
|
+
}
|
|
2767
|
+
throw new error.MastraError(
|
|
2768
|
+
{
|
|
2769
|
+
id: "MASTRA_STORAGE_MSSQL_STORE_UPDATE_WORKFLOW_STATE_FAILED",
|
|
2770
|
+
domain: error.ErrorDomain.STORAGE,
|
|
2771
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
2772
|
+
details: {
|
|
2773
|
+
workflowName,
|
|
2774
|
+
runId
|
|
2775
|
+
}
|
|
2776
|
+
},
|
|
2777
|
+
error$1
|
|
2778
|
+
);
|
|
2779
|
+
}
|
|
1796
2780
|
}
|
|
1797
2781
|
async persistWorkflowSnapshot({
|
|
1798
2782
|
workflowName,
|
|
@@ -1890,7 +2874,7 @@ var WorkflowsMSSQL = class extends storage.WorkflowsStorage {
|
|
|
1890
2874
|
if (!result.recordset || result.recordset.length === 0) {
|
|
1891
2875
|
return null;
|
|
1892
2876
|
}
|
|
1893
|
-
return parseWorkflowRun(result.recordset[0]);
|
|
2877
|
+
return this.parseWorkflowRun(result.recordset[0]);
|
|
1894
2878
|
} catch (error$1) {
|
|
1895
2879
|
throw new error.MastraError(
|
|
1896
2880
|
{
|
|
@@ -1927,7 +2911,7 @@ var WorkflowsMSSQL = class extends storage.WorkflowsStorage {
|
|
|
1927
2911
|
conditions.push(`[resourceId] = @resourceId`);
|
|
1928
2912
|
paramMap["resourceId"] = resourceId;
|
|
1929
2913
|
} else {
|
|
1930
|
-
|
|
2914
|
+
this.logger?.warn?.(`[${storage.TABLE_WORKFLOW_SNAPSHOT}] resourceId column not found. Skipping resourceId filter.`);
|
|
1931
2915
|
}
|
|
1932
2916
|
}
|
|
1933
2917
|
if (fromDate instanceof Date && !isNaN(fromDate.getTime())) {
|
|
@@ -1961,7 +2945,7 @@ var WorkflowsMSSQL = class extends storage.WorkflowsStorage {
|
|
|
1961
2945
|
request.input("offset", offset);
|
|
1962
2946
|
}
|
|
1963
2947
|
const result = await request.query(query);
|
|
1964
|
-
const runs = (result.recordset || []).map((row) => parseWorkflowRun(row));
|
|
2948
|
+
const runs = (result.recordset || []).map((row) => this.parseWorkflowRun(row));
|
|
1965
2949
|
return { runs, total: total || runs.length };
|
|
1966
2950
|
} catch (error$1) {
|
|
1967
2951
|
throw new error.MastraError(
|
|
@@ -1977,6 +2961,9 @@ var WorkflowsMSSQL = class extends storage.WorkflowsStorage {
|
|
|
1977
2961
|
);
|
|
1978
2962
|
}
|
|
1979
2963
|
}
|
|
2964
|
+
async listWorkflowRuns(args) {
|
|
2965
|
+
return this.getWorkflowRuns(args);
|
|
2966
|
+
}
|
|
1980
2967
|
};
|
|
1981
2968
|
|
|
1982
2969
|
// src/storage/index.ts
|
|
@@ -2009,17 +2996,17 @@ var MSSQLStore = class extends storage.MastraStorage {
|
|
|
2009
2996
|
port: config.port,
|
|
2010
2997
|
options: config.options || { encrypt: true, trustServerCertificate: true }
|
|
2011
2998
|
});
|
|
2012
|
-
const legacyEvals = new LegacyEvalsMSSQL({ pool: this.pool, schema: this.schema });
|
|
2013
2999
|
const operations = new StoreOperationsMSSQL({ pool: this.pool, schemaName: this.schema });
|
|
2014
3000
|
const scores = new ScoresMSSQL({ pool: this.pool, operations, schema: this.schema });
|
|
2015
3001
|
const workflows = new WorkflowsMSSQL({ pool: this.pool, operations, schema: this.schema });
|
|
2016
3002
|
const memory = new MemoryMSSQL({ pool: this.pool, schema: this.schema, operations });
|
|
3003
|
+
const observability = new ObservabilityMSSQL({ pool: this.pool, operations, schema: this.schema });
|
|
2017
3004
|
this.stores = {
|
|
2018
3005
|
operations,
|
|
2019
3006
|
scores,
|
|
2020
3007
|
workflows,
|
|
2021
|
-
|
|
2022
|
-
|
|
3008
|
+
memory,
|
|
3009
|
+
observability
|
|
2023
3010
|
};
|
|
2024
3011
|
} catch (e) {
|
|
2025
3012
|
throw new error.MastraError(
|
|
@@ -2039,6 +3026,11 @@ var MSSQLStore = class extends storage.MastraStorage {
|
|
|
2039
3026
|
try {
|
|
2040
3027
|
await this.isConnected;
|
|
2041
3028
|
await super.init();
|
|
3029
|
+
try {
|
|
3030
|
+
await this.stores.operations.createAutomaticIndexes();
|
|
3031
|
+
} catch (indexError) {
|
|
3032
|
+
this.logger?.warn?.("Failed to create indexes:", indexError);
|
|
3033
|
+
}
|
|
2042
3034
|
} catch (error$1) {
|
|
2043
3035
|
this.isConnected = null;
|
|
2044
3036
|
throw new error.MastraError(
|
|
@@ -2066,16 +3058,11 @@ var MSSQLStore = class extends storage.MastraStorage {
|
|
|
2066
3058
|
hasColumn: true,
|
|
2067
3059
|
createTable: true,
|
|
2068
3060
|
deleteMessages: true,
|
|
2069
|
-
getScoresBySpan: true
|
|
3061
|
+
getScoresBySpan: true,
|
|
3062
|
+
aiTracing: true,
|
|
3063
|
+
indexManagement: true
|
|
2070
3064
|
};
|
|
2071
3065
|
}
|
|
2072
|
-
/** @deprecated use getEvals instead */
|
|
2073
|
-
async getEvalsByAgentName(agentName, type) {
|
|
2074
|
-
return this.stores.legacyEvals.getEvalsByAgentName(agentName, type);
|
|
2075
|
-
}
|
|
2076
|
-
async getEvals(options = {}) {
|
|
2077
|
-
return this.stores.legacyEvals.getEvals(options);
|
|
2078
|
-
}
|
|
2079
3066
|
async createTable({
|
|
2080
3067
|
tableName,
|
|
2081
3068
|
schema
|
|
@@ -2176,9 +3163,9 @@ var MSSQLStore = class extends storage.MastraStorage {
|
|
|
2176
3163
|
runId,
|
|
2177
3164
|
stepId,
|
|
2178
3165
|
result,
|
|
2179
|
-
|
|
3166
|
+
requestContext
|
|
2180
3167
|
}) {
|
|
2181
|
-
return this.stores.workflows.updateWorkflowResults({ workflowName, runId, stepId, result,
|
|
3168
|
+
return this.stores.workflows.updateWorkflowResults({ workflowName, runId, stepId, result, requestContext });
|
|
2182
3169
|
}
|
|
2183
3170
|
async updateWorkflowState({
|
|
2184
3171
|
workflowName,
|
|
@@ -2220,6 +3207,60 @@ var MSSQLStore = class extends storage.MastraStorage {
|
|
|
2220
3207
|
async close() {
|
|
2221
3208
|
await this.pool.close();
|
|
2222
3209
|
}
|
|
3210
|
+
/**
|
|
3211
|
+
* Index Management
|
|
3212
|
+
*/
|
|
3213
|
+
async createIndex(options) {
|
|
3214
|
+
return this.stores.operations.createIndex(options);
|
|
3215
|
+
}
|
|
3216
|
+
async listIndexes(tableName) {
|
|
3217
|
+
return this.stores.operations.listIndexes(tableName);
|
|
3218
|
+
}
|
|
3219
|
+
async describeIndex(indexName) {
|
|
3220
|
+
return this.stores.operations.describeIndex(indexName);
|
|
3221
|
+
}
|
|
3222
|
+
async dropIndex(indexName) {
|
|
3223
|
+
return this.stores.operations.dropIndex(indexName);
|
|
3224
|
+
}
|
|
3225
|
+
/**
|
|
3226
|
+
* AI Tracing / Observability
|
|
3227
|
+
*/
|
|
3228
|
+
getObservabilityStore() {
|
|
3229
|
+
if (!this.stores.observability) {
|
|
3230
|
+
throw new error.MastraError({
|
|
3231
|
+
id: "MSSQL_STORE_OBSERVABILITY_NOT_INITIALIZED",
|
|
3232
|
+
domain: error.ErrorDomain.STORAGE,
|
|
3233
|
+
category: error.ErrorCategory.SYSTEM,
|
|
3234
|
+
text: "Observability storage is not initialized"
|
|
3235
|
+
});
|
|
3236
|
+
}
|
|
3237
|
+
return this.stores.observability;
|
|
3238
|
+
}
|
|
3239
|
+
async createAISpan(span) {
|
|
3240
|
+
return this.getObservabilityStore().createAISpan(span);
|
|
3241
|
+
}
|
|
3242
|
+
async updateAISpan({
|
|
3243
|
+
spanId,
|
|
3244
|
+
traceId,
|
|
3245
|
+
updates
|
|
3246
|
+
}) {
|
|
3247
|
+
return this.getObservabilityStore().updateAISpan({ spanId, traceId, updates });
|
|
3248
|
+
}
|
|
3249
|
+
async getAITrace(traceId) {
|
|
3250
|
+
return this.getObservabilityStore().getAITrace(traceId);
|
|
3251
|
+
}
|
|
3252
|
+
async getAITracesPaginated(args) {
|
|
3253
|
+
return this.getObservabilityStore().getAITracesPaginated(args);
|
|
3254
|
+
}
|
|
3255
|
+
async batchCreateAISpans(args) {
|
|
3256
|
+
return this.getObservabilityStore().batchCreateAISpans(args);
|
|
3257
|
+
}
|
|
3258
|
+
async batchUpdateAISpans(args) {
|
|
3259
|
+
return this.getObservabilityStore().batchUpdateAISpans(args);
|
|
3260
|
+
}
|
|
3261
|
+
async batchDeleteAITraces(args) {
|
|
3262
|
+
return this.getObservabilityStore().batchDeleteAITraces(args);
|
|
3263
|
+
}
|
|
2223
3264
|
/**
|
|
2224
3265
|
* Scorers
|
|
2225
3266
|
*/
|
|
@@ -2228,9 +3269,18 @@ var MSSQLStore = class extends storage.MastraStorage {
|
|
|
2228
3269
|
}
|
|
2229
3270
|
async getScoresByScorerId({
|
|
2230
3271
|
scorerId: _scorerId,
|
|
2231
|
-
pagination: _pagination
|
|
3272
|
+
pagination: _pagination,
|
|
3273
|
+
entityId: _entityId,
|
|
3274
|
+
entityType: _entityType,
|
|
3275
|
+
source: _source
|
|
2232
3276
|
}) {
|
|
2233
|
-
return this.stores.scores.getScoresByScorerId({
|
|
3277
|
+
return this.stores.scores.getScoresByScorerId({
|
|
3278
|
+
scorerId: _scorerId,
|
|
3279
|
+
pagination: _pagination,
|
|
3280
|
+
entityId: _entityId,
|
|
3281
|
+
entityType: _entityType,
|
|
3282
|
+
source: _source
|
|
3283
|
+
});
|
|
2234
3284
|
}
|
|
2235
3285
|
async saveScore(_score) {
|
|
2236
3286
|
return this.stores.scores.saveScore(_score);
|