@mastra/mssql 0.4.6 → 0.5.0-alpha.0
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 +39 -0
- package/README.md +315 -36
- package/dist/index.cjs +1344 -151
- 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 +1344 -151
- package/dist/index.js.map +1 -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 +3 -2
- package/dist/storage/domains/workflows/index.d.ts.map +1 -1
- package/dist/storage/index.d.ts +43 -3
- package/dist/storage/index.d.ts.map +1 -1
- package/package.json +13 -6
package/dist/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
|
|
2
|
-
import { MastraStorage, LegacyEvalsStorage, StoreOperations, TABLE_WORKFLOW_SNAPSHOT,
|
|
3
|
-
import
|
|
2
|
+
import { MastraStorage, LegacyEvalsStorage, StoreOperations, TABLE_WORKFLOW_SNAPSHOT, TABLE_SCHEMAS, TABLE_THREADS, TABLE_MESSAGES, TABLE_TRACES, TABLE_EVALS, TABLE_SCORERS, TABLE_AI_SPANS, ScoresStorage, TracesStorage, WorkflowsStorage, MemoryStorage, resolveMessageLimit, TABLE_RESOURCES, ObservabilityStorage, safelyParseJSON } from '@mastra/core/storage';
|
|
3
|
+
import sql3 from 'mssql';
|
|
4
4
|
import { parseSqlIdentifier, parseFieldKey } from '@mastra/core/utils';
|
|
5
5
|
import { MessageList } from '@mastra/core/agent';
|
|
6
|
+
import { randomUUID } from 'crypto';
|
|
6
7
|
import { saveScorePayloadSchema } from '@mastra/core/scores';
|
|
7
8
|
|
|
8
9
|
// src/storage/index.ts
|
|
@@ -15,6 +16,69 @@ function getTableName({ indexName, schemaName }) {
|
|
|
15
16
|
const quotedSchemaName = schemaName;
|
|
16
17
|
return quotedSchemaName ? `${quotedSchemaName}.${quotedIndexName}` : quotedIndexName;
|
|
17
18
|
}
|
|
19
|
+
function buildDateRangeFilter(dateRange, fieldName) {
|
|
20
|
+
const filters = {};
|
|
21
|
+
if (dateRange?.start) {
|
|
22
|
+
filters[`${fieldName}_gte`] = dateRange.start;
|
|
23
|
+
}
|
|
24
|
+
if (dateRange?.end) {
|
|
25
|
+
filters[`${fieldName}_lte`] = dateRange.end;
|
|
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
|
+
});
|
|
51
|
+
return {
|
|
52
|
+
sql: conditions.length > 0 ? ` WHERE ${conditions.join(" AND ")}` : "",
|
|
53
|
+
params
|
|
54
|
+
};
|
|
55
|
+
}
|
|
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;
|
|
69
|
+
}
|
|
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;
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
18
82
|
|
|
19
83
|
// src/storage/domains/legacy-evals/index.ts
|
|
20
84
|
function transformEvalRow(row) {
|
|
@@ -25,7 +89,7 @@ function transformEvalRow(row) {
|
|
|
25
89
|
} catch {
|
|
26
90
|
}
|
|
27
91
|
}
|
|
28
|
-
if (row.
|
|
92
|
+
if (row.result) {
|
|
29
93
|
try {
|
|
30
94
|
resultValue = typeof row.result === "string" ? JSON.parse(row.result) : row.result;
|
|
31
95
|
} catch {
|
|
@@ -71,7 +135,7 @@ var LegacyEvalsMSSQL = class extends LegacyEvalsStorage {
|
|
|
71
135
|
if (error && error.number === 208 && error.message && error.message.includes("Invalid object name")) {
|
|
72
136
|
return [];
|
|
73
137
|
}
|
|
74
|
-
|
|
138
|
+
this.logger?.error?.("Failed to get evals for the specified agent:", error);
|
|
75
139
|
throw error;
|
|
76
140
|
}
|
|
77
141
|
}
|
|
@@ -107,7 +171,7 @@ var LegacyEvalsMSSQL = class extends LegacyEvalsStorage {
|
|
|
107
171
|
const countReq = this.pool.request();
|
|
108
172
|
Object.entries(params).forEach(([key, value]) => {
|
|
109
173
|
if (value instanceof Date) {
|
|
110
|
-
countReq.input(key,
|
|
174
|
+
countReq.input(key, sql3.DateTime, value);
|
|
111
175
|
} else {
|
|
112
176
|
countReq.input(key, value);
|
|
113
177
|
}
|
|
@@ -126,7 +190,7 @@ var LegacyEvalsMSSQL = class extends LegacyEvalsStorage {
|
|
|
126
190
|
const req = this.pool.request();
|
|
127
191
|
Object.entries(params).forEach(([key, value]) => {
|
|
128
192
|
if (value instanceof Date) {
|
|
129
|
-
req.input(key,
|
|
193
|
+
req.input(key, sql3.DateTime, value);
|
|
130
194
|
} else {
|
|
131
195
|
req.input(key, value);
|
|
132
196
|
}
|
|
@@ -158,7 +222,7 @@ var LegacyEvalsMSSQL = class extends LegacyEvalsStorage {
|
|
|
158
222
|
error
|
|
159
223
|
);
|
|
160
224
|
this.logger?.error?.(mastraError.toString());
|
|
161
|
-
this.logger?.trackException(mastraError);
|
|
225
|
+
this.logger?.trackException?.(mastraError);
|
|
162
226
|
throw mastraError;
|
|
163
227
|
}
|
|
164
228
|
}
|
|
@@ -251,7 +315,8 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
251
315
|
};
|
|
252
316
|
}
|
|
253
317
|
const orderByField = orderBy === "createdAt" ? "[createdAt]" : "[updatedAt]";
|
|
254
|
-
const
|
|
318
|
+
const dir = (sortDirection || "DESC").toUpperCase() === "ASC" ? "ASC" : "DESC";
|
|
319
|
+
const dataQuery = `SELECT id, [resourceId], title, metadata, [createdAt], [updatedAt] ${baseQuery} ORDER BY ${orderByField} ${dir} OFFSET @offset ROWS FETCH NEXT @perPage ROWS ONLY`;
|
|
255
320
|
const dataRequest = this.pool.request();
|
|
256
321
|
dataRequest.input("resourceId", resourceId);
|
|
257
322
|
dataRequest.input("perPage", perPage);
|
|
@@ -308,9 +373,14 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
308
373
|
req.input("id", thread.id);
|
|
309
374
|
req.input("resourceId", thread.resourceId);
|
|
310
375
|
req.input("title", thread.title);
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
376
|
+
const metadata = thread.metadata ? JSON.stringify(thread.metadata) : null;
|
|
377
|
+
if (metadata === null) {
|
|
378
|
+
req.input("metadata", sql3.NVarChar, null);
|
|
379
|
+
} else {
|
|
380
|
+
req.input("metadata", metadata);
|
|
381
|
+
}
|
|
382
|
+
req.input("createdAt", sql3.DateTime2, thread.createdAt);
|
|
383
|
+
req.input("updatedAt", sql3.DateTime2, thread.updatedAt);
|
|
314
384
|
await req.query(mergeSql);
|
|
315
385
|
return thread;
|
|
316
386
|
} catch (error) {
|
|
@@ -335,7 +405,8 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
335
405
|
try {
|
|
336
406
|
const baseQuery = `FROM ${getTableName({ indexName: TABLE_THREADS, schemaName: getSchemaName(this.schema) })} WHERE [resourceId] = @resourceId`;
|
|
337
407
|
const orderByField = orderBy === "createdAt" ? "[createdAt]" : "[updatedAt]";
|
|
338
|
-
const
|
|
408
|
+
const dir = (sortDirection || "DESC").toUpperCase() === "ASC" ? "ASC" : "DESC";
|
|
409
|
+
const dataQuery = `SELECT id, [resourceId], title, metadata, [createdAt], [updatedAt] ${baseQuery} ORDER BY ${orderByField} ${dir}`;
|
|
339
410
|
const request = this.pool.request();
|
|
340
411
|
request.input("resourceId", resourceId);
|
|
341
412
|
const resultSet = await request.query(dataQuery);
|
|
@@ -584,7 +655,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
584
655
|
error
|
|
585
656
|
);
|
|
586
657
|
this.logger?.error?.(mastraError.toString());
|
|
587
|
-
this.logger?.trackException(mastraError);
|
|
658
|
+
this.logger?.trackException?.(mastraError);
|
|
588
659
|
return [];
|
|
589
660
|
}
|
|
590
661
|
}
|
|
@@ -624,7 +695,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
624
695
|
error
|
|
625
696
|
);
|
|
626
697
|
this.logger?.error?.(mastraError.toString());
|
|
627
|
-
this.logger?.trackException(mastraError);
|
|
698
|
+
this.logger?.trackException?.(mastraError);
|
|
628
699
|
return [];
|
|
629
700
|
}
|
|
630
701
|
}
|
|
@@ -686,7 +757,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
686
757
|
const parsed = this._parseAndFormatMessages(messages, format);
|
|
687
758
|
return {
|
|
688
759
|
messages: parsed,
|
|
689
|
-
total
|
|
760
|
+
total,
|
|
690
761
|
page,
|
|
691
762
|
perPage,
|
|
692
763
|
hasMore: currentOffset + rows.length < total
|
|
@@ -706,7 +777,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
706
777
|
error
|
|
707
778
|
);
|
|
708
779
|
this.logger?.error?.(mastraError.toString());
|
|
709
|
-
this.logger?.trackException(mastraError);
|
|
780
|
+
this.logger?.trackException?.(mastraError);
|
|
710
781
|
return { messages: [], total: 0, page, perPage: perPageInput || 40, hasMore: false };
|
|
711
782
|
}
|
|
712
783
|
}
|
|
@@ -758,7 +829,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
758
829
|
"content",
|
|
759
830
|
typeof message.content === "string" ? message.content : JSON.stringify(message.content)
|
|
760
831
|
);
|
|
761
|
-
request.input("createdAt",
|
|
832
|
+
request.input("createdAt", sql3.DateTime2, message.createdAt);
|
|
762
833
|
request.input("role", message.role);
|
|
763
834
|
request.input("type", message.type || "v2");
|
|
764
835
|
request.input("resourceId", message.resourceId);
|
|
@@ -777,7 +848,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
777
848
|
await request.query(mergeSql);
|
|
778
849
|
}
|
|
779
850
|
const threadReq = transaction.request();
|
|
780
|
-
threadReq.input("updatedAt",
|
|
851
|
+
threadReq.input("updatedAt", sql3.DateTime2, /* @__PURE__ */ new Date());
|
|
781
852
|
threadReq.input("id", threadId);
|
|
782
853
|
await threadReq.query(`UPDATE ${tableThreads} SET [updatedAt] = @updatedAt WHERE id = @id`);
|
|
783
854
|
await transaction.commit();
|
|
@@ -973,8 +1044,10 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
973
1044
|
return null;
|
|
974
1045
|
}
|
|
975
1046
|
return {
|
|
976
|
-
|
|
977
|
-
|
|
1047
|
+
id: result.id,
|
|
1048
|
+
createdAt: result.createdAt,
|
|
1049
|
+
updatedAt: result.updatedAt,
|
|
1050
|
+
workingMemory: result.workingMemory,
|
|
978
1051
|
metadata: typeof result.metadata === "string" ? JSON.parse(result.metadata) : result.metadata
|
|
979
1052
|
};
|
|
980
1053
|
} catch (error) {
|
|
@@ -988,7 +1061,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
988
1061
|
error
|
|
989
1062
|
);
|
|
990
1063
|
this.logger?.error?.(mastraError.toString());
|
|
991
|
-
this.logger?.trackException(mastraError);
|
|
1064
|
+
this.logger?.trackException?.(mastraError);
|
|
992
1065
|
throw mastraError;
|
|
993
1066
|
}
|
|
994
1067
|
}
|
|
@@ -997,7 +1070,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
997
1070
|
tableName: TABLE_RESOURCES,
|
|
998
1071
|
record: {
|
|
999
1072
|
...resource,
|
|
1000
|
-
metadata:
|
|
1073
|
+
metadata: resource.metadata
|
|
1001
1074
|
}
|
|
1002
1075
|
});
|
|
1003
1076
|
return resource;
|
|
@@ -1055,20 +1128,337 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
1055
1128
|
error
|
|
1056
1129
|
);
|
|
1057
1130
|
this.logger?.error?.(mastraError.toString());
|
|
1058
|
-
this.logger?.trackException(mastraError);
|
|
1131
|
+
this.logger?.trackException?.(mastraError);
|
|
1059
1132
|
throw mastraError;
|
|
1060
1133
|
}
|
|
1061
1134
|
}
|
|
1062
1135
|
};
|
|
1136
|
+
var ObservabilityMSSQL = class extends ObservabilityStorage {
|
|
1137
|
+
pool;
|
|
1138
|
+
operations;
|
|
1139
|
+
schema;
|
|
1140
|
+
constructor({
|
|
1141
|
+
pool,
|
|
1142
|
+
operations,
|
|
1143
|
+
schema
|
|
1144
|
+
}) {
|
|
1145
|
+
super();
|
|
1146
|
+
this.pool = pool;
|
|
1147
|
+
this.operations = operations;
|
|
1148
|
+
this.schema = schema;
|
|
1149
|
+
}
|
|
1150
|
+
get aiTracingStrategy() {
|
|
1151
|
+
return {
|
|
1152
|
+
preferred: "batch-with-updates",
|
|
1153
|
+
supported: ["batch-with-updates", "insert-only"]
|
|
1154
|
+
};
|
|
1155
|
+
}
|
|
1156
|
+
async createAISpan(span) {
|
|
1157
|
+
try {
|
|
1158
|
+
const startedAt = span.startedAt instanceof Date ? span.startedAt.toISOString() : span.startedAt;
|
|
1159
|
+
const endedAt = span.endedAt instanceof Date ? span.endedAt.toISOString() : span.endedAt;
|
|
1160
|
+
const record = {
|
|
1161
|
+
...span,
|
|
1162
|
+
startedAt,
|
|
1163
|
+
endedAt
|
|
1164
|
+
// Note: createdAt/updatedAt will be set by default values
|
|
1165
|
+
};
|
|
1166
|
+
return this.operations.insert({ tableName: TABLE_AI_SPANS, record });
|
|
1167
|
+
} catch (error) {
|
|
1168
|
+
throw new MastraError(
|
|
1169
|
+
{
|
|
1170
|
+
id: "MSSQL_STORE_CREATE_AI_SPAN_FAILED",
|
|
1171
|
+
domain: ErrorDomain.STORAGE,
|
|
1172
|
+
category: ErrorCategory.USER,
|
|
1173
|
+
details: {
|
|
1174
|
+
spanId: span.spanId,
|
|
1175
|
+
traceId: span.traceId,
|
|
1176
|
+
spanType: span.spanType,
|
|
1177
|
+
spanName: span.name
|
|
1178
|
+
}
|
|
1179
|
+
},
|
|
1180
|
+
error
|
|
1181
|
+
);
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
async getAITrace(traceId) {
|
|
1185
|
+
try {
|
|
1186
|
+
const tableName = getTableName({
|
|
1187
|
+
indexName: TABLE_AI_SPANS,
|
|
1188
|
+
schemaName: getSchemaName(this.schema)
|
|
1189
|
+
});
|
|
1190
|
+
const request = this.pool.request();
|
|
1191
|
+
request.input("traceId", traceId);
|
|
1192
|
+
const result = await request.query(
|
|
1193
|
+
`SELECT
|
|
1194
|
+
[traceId], [spanId], [parentSpanId], [name], [scope], [spanType],
|
|
1195
|
+
[attributes], [metadata], [links], [input], [output], [error], [isEvent],
|
|
1196
|
+
[startedAt], [endedAt], [createdAt], [updatedAt]
|
|
1197
|
+
FROM ${tableName}
|
|
1198
|
+
WHERE [traceId] = @traceId
|
|
1199
|
+
ORDER BY [startedAt] DESC`
|
|
1200
|
+
);
|
|
1201
|
+
if (!result.recordset || result.recordset.length === 0) {
|
|
1202
|
+
return null;
|
|
1203
|
+
}
|
|
1204
|
+
return {
|
|
1205
|
+
traceId,
|
|
1206
|
+
spans: result.recordset.map(
|
|
1207
|
+
(span) => transformFromSqlRow({
|
|
1208
|
+
tableName: TABLE_AI_SPANS,
|
|
1209
|
+
sqlRow: span
|
|
1210
|
+
})
|
|
1211
|
+
)
|
|
1212
|
+
};
|
|
1213
|
+
} catch (error) {
|
|
1214
|
+
throw new MastraError(
|
|
1215
|
+
{
|
|
1216
|
+
id: "MSSQL_STORE_GET_AI_TRACE_FAILED",
|
|
1217
|
+
domain: ErrorDomain.STORAGE,
|
|
1218
|
+
category: ErrorCategory.USER,
|
|
1219
|
+
details: {
|
|
1220
|
+
traceId
|
|
1221
|
+
}
|
|
1222
|
+
},
|
|
1223
|
+
error
|
|
1224
|
+
);
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
async updateAISpan({
|
|
1228
|
+
spanId,
|
|
1229
|
+
traceId,
|
|
1230
|
+
updates
|
|
1231
|
+
}) {
|
|
1232
|
+
try {
|
|
1233
|
+
const data = { ...updates };
|
|
1234
|
+
if (data.endedAt instanceof Date) {
|
|
1235
|
+
data.endedAt = data.endedAt.toISOString();
|
|
1236
|
+
}
|
|
1237
|
+
if (data.startedAt instanceof Date) {
|
|
1238
|
+
data.startedAt = data.startedAt.toISOString();
|
|
1239
|
+
}
|
|
1240
|
+
await this.operations.update({
|
|
1241
|
+
tableName: TABLE_AI_SPANS,
|
|
1242
|
+
keys: { spanId, traceId },
|
|
1243
|
+
data
|
|
1244
|
+
});
|
|
1245
|
+
} catch (error) {
|
|
1246
|
+
throw new MastraError(
|
|
1247
|
+
{
|
|
1248
|
+
id: "MSSQL_STORE_UPDATE_AI_SPAN_FAILED",
|
|
1249
|
+
domain: ErrorDomain.STORAGE,
|
|
1250
|
+
category: ErrorCategory.USER,
|
|
1251
|
+
details: {
|
|
1252
|
+
spanId,
|
|
1253
|
+
traceId
|
|
1254
|
+
}
|
|
1255
|
+
},
|
|
1256
|
+
error
|
|
1257
|
+
);
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
async getAITracesPaginated({
|
|
1261
|
+
filters,
|
|
1262
|
+
pagination
|
|
1263
|
+
}) {
|
|
1264
|
+
const page = pagination?.page ?? 0;
|
|
1265
|
+
const perPage = pagination?.perPage ?? 10;
|
|
1266
|
+
const { entityId, entityType, ...actualFilters } = filters || {};
|
|
1267
|
+
const filtersWithDateRange = {
|
|
1268
|
+
...actualFilters,
|
|
1269
|
+
...buildDateRangeFilter(pagination?.dateRange, "startedAt"),
|
|
1270
|
+
parentSpanId: null
|
|
1271
|
+
// Only get root spans for traces
|
|
1272
|
+
};
|
|
1273
|
+
const whereClause = prepareWhereClause(filtersWithDateRange);
|
|
1274
|
+
let actualWhereClause = whereClause.sql;
|
|
1275
|
+
const params = { ...whereClause.params };
|
|
1276
|
+
let currentParamIndex = Object.keys(params).length + 1;
|
|
1277
|
+
if (entityId && entityType) {
|
|
1278
|
+
let name = "";
|
|
1279
|
+
if (entityType === "workflow") {
|
|
1280
|
+
name = `workflow run: '${entityId}'`;
|
|
1281
|
+
} else if (entityType === "agent") {
|
|
1282
|
+
name = `agent run: '${entityId}'`;
|
|
1283
|
+
} else {
|
|
1284
|
+
const error = new MastraError({
|
|
1285
|
+
id: "MSSQL_STORE_GET_AI_TRACES_PAGINATED_FAILED",
|
|
1286
|
+
domain: ErrorDomain.STORAGE,
|
|
1287
|
+
category: ErrorCategory.USER,
|
|
1288
|
+
details: {
|
|
1289
|
+
entityType
|
|
1290
|
+
},
|
|
1291
|
+
text: `Cannot filter by entity type: ${entityType}`
|
|
1292
|
+
});
|
|
1293
|
+
throw error;
|
|
1294
|
+
}
|
|
1295
|
+
const entityParam = `p${currentParamIndex++}`;
|
|
1296
|
+
if (actualWhereClause) {
|
|
1297
|
+
actualWhereClause += ` AND [name] = @${entityParam}`;
|
|
1298
|
+
} else {
|
|
1299
|
+
actualWhereClause = ` WHERE [name] = @${entityParam}`;
|
|
1300
|
+
}
|
|
1301
|
+
params[entityParam] = name;
|
|
1302
|
+
}
|
|
1303
|
+
const tableName = getTableName({
|
|
1304
|
+
indexName: TABLE_AI_SPANS,
|
|
1305
|
+
schemaName: getSchemaName(this.schema)
|
|
1306
|
+
});
|
|
1307
|
+
try {
|
|
1308
|
+
const countRequest = this.pool.request();
|
|
1309
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
1310
|
+
countRequest.input(key, value);
|
|
1311
|
+
});
|
|
1312
|
+
const countResult = await countRequest.query(
|
|
1313
|
+
`SELECT COUNT(*) as count FROM ${tableName}${actualWhereClause}`
|
|
1314
|
+
);
|
|
1315
|
+
const total = countResult.recordset[0]?.count ?? 0;
|
|
1316
|
+
if (total === 0) {
|
|
1317
|
+
return {
|
|
1318
|
+
pagination: {
|
|
1319
|
+
total: 0,
|
|
1320
|
+
page,
|
|
1321
|
+
perPage,
|
|
1322
|
+
hasMore: false
|
|
1323
|
+
},
|
|
1324
|
+
spans: []
|
|
1325
|
+
};
|
|
1326
|
+
}
|
|
1327
|
+
const dataRequest = this.pool.request();
|
|
1328
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
1329
|
+
dataRequest.input(key, value);
|
|
1330
|
+
});
|
|
1331
|
+
dataRequest.input("offset", page * perPage);
|
|
1332
|
+
dataRequest.input("limit", perPage);
|
|
1333
|
+
const dataResult = await dataRequest.query(
|
|
1334
|
+
`SELECT * FROM ${tableName}${actualWhereClause} ORDER BY [startedAt] DESC OFFSET @offset ROWS FETCH NEXT @limit ROWS ONLY`
|
|
1335
|
+
);
|
|
1336
|
+
const spans = dataResult.recordset.map(
|
|
1337
|
+
(row) => transformFromSqlRow({
|
|
1338
|
+
tableName: TABLE_AI_SPANS,
|
|
1339
|
+
sqlRow: row
|
|
1340
|
+
})
|
|
1341
|
+
);
|
|
1342
|
+
return {
|
|
1343
|
+
pagination: {
|
|
1344
|
+
total,
|
|
1345
|
+
page,
|
|
1346
|
+
perPage,
|
|
1347
|
+
hasMore: (page + 1) * perPage < total
|
|
1348
|
+
},
|
|
1349
|
+
spans
|
|
1350
|
+
};
|
|
1351
|
+
} catch (error) {
|
|
1352
|
+
throw new MastraError(
|
|
1353
|
+
{
|
|
1354
|
+
id: "MSSQL_STORE_GET_AI_TRACES_PAGINATED_FAILED",
|
|
1355
|
+
domain: ErrorDomain.STORAGE,
|
|
1356
|
+
category: ErrorCategory.USER
|
|
1357
|
+
},
|
|
1358
|
+
error
|
|
1359
|
+
);
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
async batchCreateAISpans(args) {
|
|
1363
|
+
if (!args.records || args.records.length === 0) {
|
|
1364
|
+
return;
|
|
1365
|
+
}
|
|
1366
|
+
try {
|
|
1367
|
+
await this.operations.batchInsert({
|
|
1368
|
+
tableName: TABLE_AI_SPANS,
|
|
1369
|
+
records: args.records.map((span) => ({
|
|
1370
|
+
...span,
|
|
1371
|
+
startedAt: span.startedAt instanceof Date ? span.startedAt.toISOString() : span.startedAt,
|
|
1372
|
+
endedAt: span.endedAt instanceof Date ? span.endedAt.toISOString() : span.endedAt
|
|
1373
|
+
}))
|
|
1374
|
+
});
|
|
1375
|
+
} catch (error) {
|
|
1376
|
+
throw new MastraError(
|
|
1377
|
+
{
|
|
1378
|
+
id: "MSSQL_STORE_BATCH_CREATE_AI_SPANS_FAILED",
|
|
1379
|
+
domain: ErrorDomain.STORAGE,
|
|
1380
|
+
category: ErrorCategory.USER,
|
|
1381
|
+
details: {
|
|
1382
|
+
count: args.records.length
|
|
1383
|
+
}
|
|
1384
|
+
},
|
|
1385
|
+
error
|
|
1386
|
+
);
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
async batchUpdateAISpans(args) {
|
|
1390
|
+
if (!args.records || args.records.length === 0) {
|
|
1391
|
+
return;
|
|
1392
|
+
}
|
|
1393
|
+
try {
|
|
1394
|
+
const updates = args.records.map(({ traceId, spanId, updates: data }) => {
|
|
1395
|
+
const processedData = { ...data };
|
|
1396
|
+
if (processedData.endedAt instanceof Date) {
|
|
1397
|
+
processedData.endedAt = processedData.endedAt.toISOString();
|
|
1398
|
+
}
|
|
1399
|
+
if (processedData.startedAt instanceof Date) {
|
|
1400
|
+
processedData.startedAt = processedData.startedAt.toISOString();
|
|
1401
|
+
}
|
|
1402
|
+
return {
|
|
1403
|
+
keys: { spanId, traceId },
|
|
1404
|
+
data: processedData
|
|
1405
|
+
};
|
|
1406
|
+
});
|
|
1407
|
+
await this.operations.batchUpdate({
|
|
1408
|
+
tableName: TABLE_AI_SPANS,
|
|
1409
|
+
updates
|
|
1410
|
+
});
|
|
1411
|
+
} catch (error) {
|
|
1412
|
+
throw new MastraError(
|
|
1413
|
+
{
|
|
1414
|
+
id: "MSSQL_STORE_BATCH_UPDATE_AI_SPANS_FAILED",
|
|
1415
|
+
domain: ErrorDomain.STORAGE,
|
|
1416
|
+
category: ErrorCategory.USER,
|
|
1417
|
+
details: {
|
|
1418
|
+
count: args.records.length
|
|
1419
|
+
}
|
|
1420
|
+
},
|
|
1421
|
+
error
|
|
1422
|
+
);
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
async batchDeleteAITraces(args) {
|
|
1426
|
+
if (!args.traceIds || args.traceIds.length === 0) {
|
|
1427
|
+
return;
|
|
1428
|
+
}
|
|
1429
|
+
try {
|
|
1430
|
+
const keys = args.traceIds.map((traceId) => ({ traceId }));
|
|
1431
|
+
await this.operations.batchDelete({
|
|
1432
|
+
tableName: TABLE_AI_SPANS,
|
|
1433
|
+
keys
|
|
1434
|
+
});
|
|
1435
|
+
} catch (error) {
|
|
1436
|
+
throw new MastraError(
|
|
1437
|
+
{
|
|
1438
|
+
id: "MSSQL_STORE_BATCH_DELETE_AI_TRACES_FAILED",
|
|
1439
|
+
domain: ErrorDomain.STORAGE,
|
|
1440
|
+
category: ErrorCategory.USER,
|
|
1441
|
+
details: {
|
|
1442
|
+
count: args.traceIds.length
|
|
1443
|
+
}
|
|
1444
|
+
},
|
|
1445
|
+
error
|
|
1446
|
+
);
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
};
|
|
1063
1450
|
var StoreOperationsMSSQL = class extends StoreOperations {
|
|
1064
1451
|
pool;
|
|
1065
1452
|
schemaName;
|
|
1066
1453
|
setupSchemaPromise = null;
|
|
1067
1454
|
schemaSetupComplete = void 0;
|
|
1068
|
-
getSqlType(type, isPrimaryKey = false) {
|
|
1455
|
+
getSqlType(type, isPrimaryKey = false, useLargeStorage = false) {
|
|
1069
1456
|
switch (type) {
|
|
1070
1457
|
case "text":
|
|
1071
|
-
|
|
1458
|
+
if (useLargeStorage) {
|
|
1459
|
+
return "NVARCHAR(MAX)";
|
|
1460
|
+
}
|
|
1461
|
+
return isPrimaryKey ? "NVARCHAR(255)" : "NVARCHAR(400)";
|
|
1072
1462
|
case "timestamp":
|
|
1073
1463
|
return "DATETIME2(7)";
|
|
1074
1464
|
case "uuid":
|
|
@@ -1081,6 +1471,8 @@ var StoreOperationsMSSQL = class extends StoreOperations {
|
|
|
1081
1471
|
return "BIGINT";
|
|
1082
1472
|
case "float":
|
|
1083
1473
|
return "FLOAT";
|
|
1474
|
+
case "boolean":
|
|
1475
|
+
return "BIT";
|
|
1084
1476
|
default:
|
|
1085
1477
|
throw new MastraError({
|
|
1086
1478
|
id: "MASTRA_STORAGE_MSSQL_STORE_TYPE_NOT_SUPPORTED",
|
|
@@ -1143,20 +1535,26 @@ var StoreOperationsMSSQL = class extends StoreOperations {
|
|
|
1143
1535
|
}
|
|
1144
1536
|
await this.setupSchemaPromise;
|
|
1145
1537
|
}
|
|
1146
|
-
async insert({
|
|
1538
|
+
async insert({
|
|
1539
|
+
tableName,
|
|
1540
|
+
record,
|
|
1541
|
+
transaction
|
|
1542
|
+
}) {
|
|
1147
1543
|
try {
|
|
1148
|
-
const columns = Object.keys(record)
|
|
1149
|
-
const
|
|
1150
|
-
const paramNames =
|
|
1151
|
-
const insertSql = `INSERT INTO ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} (${
|
|
1152
|
-
const request = this.pool.request();
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
request.input(`param${i}`,
|
|
1544
|
+
const columns = Object.keys(record);
|
|
1545
|
+
const parsedColumns = columns.map((col) => parseSqlIdentifier(col, "column name"));
|
|
1546
|
+
const paramNames = columns.map((_, i) => `@param${i}`);
|
|
1547
|
+
const insertSql = `INSERT INTO ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} (${parsedColumns.map((c) => `[${c}]`).join(", ")}) VALUES (${paramNames.join(", ")})`;
|
|
1548
|
+
const request = transaction ? transaction.request() : this.pool.request();
|
|
1549
|
+
columns.forEach((col, i) => {
|
|
1550
|
+
const value = record[col];
|
|
1551
|
+
const preparedValue = this.prepareValue(value, col, tableName);
|
|
1552
|
+
if (preparedValue instanceof Date) {
|
|
1553
|
+
request.input(`param${i}`, sql3.DateTime2, preparedValue);
|
|
1554
|
+
} else if (preparedValue === null || preparedValue === void 0) {
|
|
1555
|
+
request.input(`param${i}`, this.getMssqlType(tableName, col), null);
|
|
1158
1556
|
} else {
|
|
1159
|
-
request.input(`param${i}`,
|
|
1557
|
+
request.input(`param${i}`, preparedValue);
|
|
1160
1558
|
}
|
|
1161
1559
|
});
|
|
1162
1560
|
await request.query(insertSql);
|
|
@@ -1180,7 +1578,7 @@ var StoreOperationsMSSQL = class extends StoreOperations {
|
|
|
1180
1578
|
try {
|
|
1181
1579
|
await this.pool.request().query(`TRUNCATE TABLE ${fullTableName}`);
|
|
1182
1580
|
} catch (truncateError) {
|
|
1183
|
-
if (truncateError
|
|
1581
|
+
if (truncateError?.number === 4712) {
|
|
1184
1582
|
await this.pool.request().query(`DELETE FROM ${fullTableName}`);
|
|
1185
1583
|
} else {
|
|
1186
1584
|
throw truncateError;
|
|
@@ -1203,9 +1601,11 @@ var StoreOperationsMSSQL = class extends StoreOperations {
|
|
|
1203
1601
|
getDefaultValue(type) {
|
|
1204
1602
|
switch (type) {
|
|
1205
1603
|
case "timestamp":
|
|
1206
|
-
return "DEFAULT
|
|
1604
|
+
return "DEFAULT SYSUTCDATETIME()";
|
|
1207
1605
|
case "jsonb":
|
|
1208
1606
|
return "DEFAULT N'{}'";
|
|
1607
|
+
case "boolean":
|
|
1608
|
+
return "DEFAULT 0";
|
|
1209
1609
|
default:
|
|
1210
1610
|
return super.getDefaultValue(type);
|
|
1211
1611
|
}
|
|
@@ -1216,13 +1616,29 @@ var StoreOperationsMSSQL = class extends StoreOperations {
|
|
|
1216
1616
|
}) {
|
|
1217
1617
|
try {
|
|
1218
1618
|
const uniqueConstraintColumns = tableName === TABLE_WORKFLOW_SNAPSHOT ? ["workflow_name", "run_id"] : [];
|
|
1619
|
+
const largeDataColumns = [
|
|
1620
|
+
"workingMemory",
|
|
1621
|
+
"snapshot",
|
|
1622
|
+
"metadata",
|
|
1623
|
+
"content",
|
|
1624
|
+
// messages.content - can be very long conversation content
|
|
1625
|
+
"input",
|
|
1626
|
+
// evals.input - test input data
|
|
1627
|
+
"output",
|
|
1628
|
+
// evals.output - test output data
|
|
1629
|
+
"instructions",
|
|
1630
|
+
// evals.instructions - evaluation instructions
|
|
1631
|
+
"other"
|
|
1632
|
+
// traces.other - additional trace data
|
|
1633
|
+
];
|
|
1219
1634
|
const columns = Object.entries(schema).map(([name, def]) => {
|
|
1220
1635
|
const parsedName = parseSqlIdentifier(name, "column name");
|
|
1221
1636
|
const constraints = [];
|
|
1222
1637
|
if (def.primaryKey) constraints.push("PRIMARY KEY");
|
|
1223
1638
|
if (!def.nullable) constraints.push("NOT NULL");
|
|
1224
1639
|
const isIndexed = !!def.primaryKey || uniqueConstraintColumns.includes(name);
|
|
1225
|
-
|
|
1640
|
+
const useLargeStorage = largeDataColumns.includes(name);
|
|
1641
|
+
return `[${parsedName}] ${this.getSqlType(def.type, isIndexed, useLargeStorage)} ${constraints.join(" ")}`.trim();
|
|
1226
1642
|
}).join(",\n");
|
|
1227
1643
|
if (this.schemaName) {
|
|
1228
1644
|
await this.setupSchema();
|
|
@@ -1309,7 +1725,19 @@ ${columns}
|
|
|
1309
1725
|
const columnExists = Array.isArray(checkResult.recordset) && checkResult.recordset.length > 0;
|
|
1310
1726
|
if (!columnExists) {
|
|
1311
1727
|
const columnDef = schema[columnName];
|
|
1312
|
-
const
|
|
1728
|
+
const largeDataColumns = [
|
|
1729
|
+
"workingMemory",
|
|
1730
|
+
"snapshot",
|
|
1731
|
+
"metadata",
|
|
1732
|
+
"content",
|
|
1733
|
+
"input",
|
|
1734
|
+
"output",
|
|
1735
|
+
"instructions",
|
|
1736
|
+
"other"
|
|
1737
|
+
];
|
|
1738
|
+
const useLargeStorage = largeDataColumns.includes(columnName);
|
|
1739
|
+
const isIndexed = !!columnDef.primaryKey;
|
|
1740
|
+
const sqlType = this.getSqlType(columnDef.type, isIndexed, useLargeStorage);
|
|
1313
1741
|
const nullable = columnDef.nullable === false ? "NOT NULL" : "";
|
|
1314
1742
|
const defaultValue = columnDef.nullable === false ? this.getDefaultValue(columnDef.type) : "";
|
|
1315
1743
|
const parsedColumnName = parseSqlIdentifier(columnName, "column name");
|
|
@@ -1337,11 +1765,15 @@ ${columns}
|
|
|
1337
1765
|
try {
|
|
1338
1766
|
const keyEntries = Object.entries(keys).map(([key, value]) => [parseSqlIdentifier(key, "column name"), value]);
|
|
1339
1767
|
const conditions = keyEntries.map(([key], i) => `[${key}] = @param${i}`).join(" AND ");
|
|
1340
|
-
const values = keyEntries.map(([_, value]) => value);
|
|
1341
1768
|
const sql7 = `SELECT * FROM ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} WHERE ${conditions}`;
|
|
1342
1769
|
const request = this.pool.request();
|
|
1343
|
-
|
|
1344
|
-
|
|
1770
|
+
keyEntries.forEach(([key, value], i) => {
|
|
1771
|
+
const preparedValue = this.prepareValue(value, key, tableName);
|
|
1772
|
+
if (preparedValue === null || preparedValue === void 0) {
|
|
1773
|
+
request.input(`param${i}`, this.getMssqlType(tableName, key), null);
|
|
1774
|
+
} else {
|
|
1775
|
+
request.input(`param${i}`, preparedValue);
|
|
1776
|
+
}
|
|
1345
1777
|
});
|
|
1346
1778
|
const resultSet = await request.query(sql7);
|
|
1347
1779
|
const result = resultSet.recordset[0] || null;
|
|
@@ -1375,7 +1807,7 @@ ${columns}
|
|
|
1375
1807
|
try {
|
|
1376
1808
|
await transaction.begin();
|
|
1377
1809
|
for (const record of records) {
|
|
1378
|
-
await this.insert({ tableName, record });
|
|
1810
|
+
await this.insert({ tableName, record, transaction });
|
|
1379
1811
|
}
|
|
1380
1812
|
await transaction.commit();
|
|
1381
1813
|
} catch (error) {
|
|
@@ -1412,50 +1844,591 @@ ${columns}
|
|
|
1412
1844
|
);
|
|
1413
1845
|
}
|
|
1414
1846
|
}
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1847
|
+
/**
|
|
1848
|
+
* Prepares a value for database operations, handling Date objects and JSON serialization
|
|
1849
|
+
*/
|
|
1850
|
+
prepareValue(value, columnName, tableName) {
|
|
1851
|
+
if (value === null || value === void 0) {
|
|
1852
|
+
return value;
|
|
1853
|
+
}
|
|
1854
|
+
if (value instanceof Date) {
|
|
1855
|
+
return value;
|
|
1856
|
+
}
|
|
1857
|
+
const schema = TABLE_SCHEMAS[tableName];
|
|
1858
|
+
const columnSchema = schema?.[columnName];
|
|
1859
|
+
if (columnSchema?.type === "boolean") {
|
|
1860
|
+
return value ? 1 : 0;
|
|
1861
|
+
}
|
|
1862
|
+
if (columnSchema?.type === "jsonb") {
|
|
1863
|
+
return JSON.stringify(value);
|
|
1864
|
+
}
|
|
1865
|
+
if (typeof value === "object") {
|
|
1866
|
+
return JSON.stringify(value);
|
|
1867
|
+
}
|
|
1868
|
+
return value;
|
|
1421
1869
|
}
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
}) {
|
|
1448
|
-
super();
|
|
1449
|
-
this.pool = pool;
|
|
1450
|
-
this.operations = operations;
|
|
1451
|
-
this.schema = schema;
|
|
1870
|
+
/**
|
|
1871
|
+
* Maps TABLE_SCHEMAS types to mssql param types (used when value is null)
|
|
1872
|
+
*/
|
|
1873
|
+
getMssqlType(tableName, columnName) {
|
|
1874
|
+
const col = TABLE_SCHEMAS[tableName]?.[columnName];
|
|
1875
|
+
switch (col?.type) {
|
|
1876
|
+
case "text":
|
|
1877
|
+
return sql3.NVarChar;
|
|
1878
|
+
case "timestamp":
|
|
1879
|
+
return sql3.DateTime2;
|
|
1880
|
+
case "uuid":
|
|
1881
|
+
return sql3.UniqueIdentifier;
|
|
1882
|
+
case "jsonb":
|
|
1883
|
+
return sql3.NVarChar;
|
|
1884
|
+
case "integer":
|
|
1885
|
+
return sql3.Int;
|
|
1886
|
+
case "bigint":
|
|
1887
|
+
return sql3.BigInt;
|
|
1888
|
+
case "float":
|
|
1889
|
+
return sql3.Float;
|
|
1890
|
+
case "boolean":
|
|
1891
|
+
return sql3.Bit;
|
|
1892
|
+
default:
|
|
1893
|
+
return sql3.NVarChar;
|
|
1894
|
+
}
|
|
1452
1895
|
}
|
|
1453
|
-
|
|
1896
|
+
/**
|
|
1897
|
+
* Update a single record in the database
|
|
1898
|
+
*/
|
|
1899
|
+
async update({
|
|
1900
|
+
tableName,
|
|
1901
|
+
keys,
|
|
1902
|
+
data,
|
|
1903
|
+
transaction
|
|
1904
|
+
}) {
|
|
1454
1905
|
try {
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1906
|
+
if (!data || Object.keys(data).length === 0) {
|
|
1907
|
+
throw new MastraError({
|
|
1908
|
+
id: "MASTRA_STORAGE_MSSQL_UPDATE_EMPTY_DATA",
|
|
1909
|
+
domain: ErrorDomain.STORAGE,
|
|
1910
|
+
category: ErrorCategory.USER,
|
|
1911
|
+
text: "Cannot update with empty data payload"
|
|
1912
|
+
});
|
|
1913
|
+
}
|
|
1914
|
+
if (!keys || Object.keys(keys).length === 0) {
|
|
1915
|
+
throw new MastraError({
|
|
1916
|
+
id: "MASTRA_STORAGE_MSSQL_UPDATE_EMPTY_KEYS",
|
|
1917
|
+
domain: ErrorDomain.STORAGE,
|
|
1918
|
+
category: ErrorCategory.USER,
|
|
1919
|
+
text: "Cannot update without keys to identify records"
|
|
1920
|
+
});
|
|
1921
|
+
}
|
|
1922
|
+
const setClauses = [];
|
|
1923
|
+
const request = transaction ? transaction.request() : this.pool.request();
|
|
1924
|
+
let paramIndex = 0;
|
|
1925
|
+
Object.entries(data).forEach(([key, value]) => {
|
|
1926
|
+
const parsedKey = parseSqlIdentifier(key, "column name");
|
|
1927
|
+
const paramName = `set${paramIndex++}`;
|
|
1928
|
+
setClauses.push(`[${parsedKey}] = @${paramName}`);
|
|
1929
|
+
const preparedValue = this.prepareValue(value, key, tableName);
|
|
1930
|
+
if (preparedValue === null || preparedValue === void 0) {
|
|
1931
|
+
request.input(paramName, this.getMssqlType(tableName, key), null);
|
|
1932
|
+
} else {
|
|
1933
|
+
request.input(paramName, preparedValue);
|
|
1934
|
+
}
|
|
1935
|
+
});
|
|
1936
|
+
const whereConditions = [];
|
|
1937
|
+
Object.entries(keys).forEach(([key, value]) => {
|
|
1938
|
+
const parsedKey = parseSqlIdentifier(key, "column name");
|
|
1939
|
+
const paramName = `where${paramIndex++}`;
|
|
1940
|
+
whereConditions.push(`[${parsedKey}] = @${paramName}`);
|
|
1941
|
+
const preparedValue = this.prepareValue(value, key, tableName);
|
|
1942
|
+
if (preparedValue === null || preparedValue === void 0) {
|
|
1943
|
+
request.input(paramName, this.getMssqlType(tableName, key), null);
|
|
1944
|
+
} else {
|
|
1945
|
+
request.input(paramName, preparedValue);
|
|
1946
|
+
}
|
|
1947
|
+
});
|
|
1948
|
+
const tableName_ = getTableName({
|
|
1949
|
+
indexName: tableName,
|
|
1950
|
+
schemaName: getSchemaName(this.schemaName)
|
|
1951
|
+
});
|
|
1952
|
+
const updateSql = `UPDATE ${tableName_} SET ${setClauses.join(", ")} WHERE ${whereConditions.join(" AND ")}`;
|
|
1953
|
+
await request.query(updateSql);
|
|
1954
|
+
} catch (error) {
|
|
1955
|
+
throw new MastraError(
|
|
1956
|
+
{
|
|
1957
|
+
id: "MASTRA_STORAGE_MSSQL_STORE_UPDATE_FAILED",
|
|
1958
|
+
domain: ErrorDomain.STORAGE,
|
|
1959
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1960
|
+
details: {
|
|
1961
|
+
tableName
|
|
1962
|
+
}
|
|
1963
|
+
},
|
|
1964
|
+
error
|
|
1965
|
+
);
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
/**
|
|
1969
|
+
* Update multiple records in a single batch transaction
|
|
1970
|
+
*/
|
|
1971
|
+
async batchUpdate({
|
|
1972
|
+
tableName,
|
|
1973
|
+
updates
|
|
1974
|
+
}) {
|
|
1975
|
+
const transaction = this.pool.transaction();
|
|
1976
|
+
try {
|
|
1977
|
+
await transaction.begin();
|
|
1978
|
+
for (const { keys, data } of updates) {
|
|
1979
|
+
await this.update({ tableName, keys, data, transaction });
|
|
1980
|
+
}
|
|
1981
|
+
await transaction.commit();
|
|
1982
|
+
} catch (error) {
|
|
1983
|
+
await transaction.rollback();
|
|
1984
|
+
throw new MastraError(
|
|
1985
|
+
{
|
|
1986
|
+
id: "MASTRA_STORAGE_MSSQL_STORE_BATCH_UPDATE_FAILED",
|
|
1987
|
+
domain: ErrorDomain.STORAGE,
|
|
1988
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1989
|
+
details: {
|
|
1990
|
+
tableName,
|
|
1991
|
+
numberOfRecords: updates.length
|
|
1992
|
+
}
|
|
1993
|
+
},
|
|
1994
|
+
error
|
|
1995
|
+
);
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
/**
|
|
1999
|
+
* Delete multiple records by keys
|
|
2000
|
+
*/
|
|
2001
|
+
async batchDelete({ tableName, keys }) {
|
|
2002
|
+
if (keys.length === 0) {
|
|
2003
|
+
return;
|
|
2004
|
+
}
|
|
2005
|
+
const tableName_ = getTableName({
|
|
2006
|
+
indexName: tableName,
|
|
2007
|
+
schemaName: getSchemaName(this.schemaName)
|
|
2008
|
+
});
|
|
2009
|
+
const transaction = this.pool.transaction();
|
|
2010
|
+
try {
|
|
2011
|
+
await transaction.begin();
|
|
2012
|
+
for (const keySet of keys) {
|
|
2013
|
+
const conditions = [];
|
|
2014
|
+
const request = transaction.request();
|
|
2015
|
+
let paramIndex = 0;
|
|
2016
|
+
Object.entries(keySet).forEach(([key, value]) => {
|
|
2017
|
+
const parsedKey = parseSqlIdentifier(key, "column name");
|
|
2018
|
+
const paramName = `p${paramIndex++}`;
|
|
2019
|
+
conditions.push(`[${parsedKey}] = @${paramName}`);
|
|
2020
|
+
const preparedValue = this.prepareValue(value, key, tableName);
|
|
2021
|
+
if (preparedValue === null || preparedValue === void 0) {
|
|
2022
|
+
request.input(paramName, this.getMssqlType(tableName, key), null);
|
|
2023
|
+
} else {
|
|
2024
|
+
request.input(paramName, preparedValue);
|
|
2025
|
+
}
|
|
2026
|
+
});
|
|
2027
|
+
const deleteSql = `DELETE FROM ${tableName_} WHERE ${conditions.join(" AND ")}`;
|
|
2028
|
+
await request.query(deleteSql);
|
|
2029
|
+
}
|
|
2030
|
+
await transaction.commit();
|
|
2031
|
+
} catch (error) {
|
|
2032
|
+
await transaction.rollback();
|
|
2033
|
+
throw new MastraError(
|
|
2034
|
+
{
|
|
2035
|
+
id: "MASTRA_STORAGE_MSSQL_STORE_BATCH_DELETE_FAILED",
|
|
2036
|
+
domain: ErrorDomain.STORAGE,
|
|
2037
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2038
|
+
details: {
|
|
2039
|
+
tableName,
|
|
2040
|
+
numberOfRecords: keys.length
|
|
2041
|
+
}
|
|
2042
|
+
},
|
|
2043
|
+
error
|
|
2044
|
+
);
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
/**
|
|
2048
|
+
* Create a new index on a table
|
|
2049
|
+
*/
|
|
2050
|
+
async createIndex(options) {
|
|
2051
|
+
try {
|
|
2052
|
+
const { name, table, columns, unique = false, where } = options;
|
|
2053
|
+
const schemaName = this.schemaName || "dbo";
|
|
2054
|
+
const fullTableName = getTableName({
|
|
2055
|
+
indexName: table,
|
|
2056
|
+
schemaName: getSchemaName(this.schemaName)
|
|
2057
|
+
});
|
|
2058
|
+
const indexNameSafe = parseSqlIdentifier(name, "index name");
|
|
2059
|
+
const checkRequest = this.pool.request();
|
|
2060
|
+
checkRequest.input("indexName", indexNameSafe);
|
|
2061
|
+
checkRequest.input("schemaName", schemaName);
|
|
2062
|
+
checkRequest.input("tableName", table);
|
|
2063
|
+
const indexExists = await checkRequest.query(`
|
|
2064
|
+
SELECT 1 as found
|
|
2065
|
+
FROM sys.indexes i
|
|
2066
|
+
INNER JOIN sys.tables t ON i.object_id = t.object_id
|
|
2067
|
+
INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
|
|
2068
|
+
WHERE i.name = @indexName
|
|
2069
|
+
AND s.name = @schemaName
|
|
2070
|
+
AND t.name = @tableName
|
|
2071
|
+
`);
|
|
2072
|
+
if (indexExists.recordset && indexExists.recordset.length > 0) {
|
|
2073
|
+
return;
|
|
2074
|
+
}
|
|
2075
|
+
const uniqueStr = unique ? "UNIQUE " : "";
|
|
2076
|
+
const columnsStr = columns.map((col) => {
|
|
2077
|
+
if (col.includes(" DESC") || col.includes(" ASC")) {
|
|
2078
|
+
const [colName, ...modifiers] = col.split(" ");
|
|
2079
|
+
if (!colName) {
|
|
2080
|
+
throw new Error(`Invalid column specification: ${col}`);
|
|
2081
|
+
}
|
|
2082
|
+
return `[${parseSqlIdentifier(colName, "column name")}] ${modifiers.join(" ")}`;
|
|
2083
|
+
}
|
|
2084
|
+
return `[${parseSqlIdentifier(col, "column name")}]`;
|
|
2085
|
+
}).join(", ");
|
|
2086
|
+
const whereStr = where ? ` WHERE ${where}` : "";
|
|
2087
|
+
const createIndexSql = `CREATE ${uniqueStr}INDEX [${indexNameSafe}] ON ${fullTableName} (${columnsStr})${whereStr}`;
|
|
2088
|
+
await this.pool.request().query(createIndexSql);
|
|
2089
|
+
} catch (error) {
|
|
2090
|
+
throw new MastraError(
|
|
2091
|
+
{
|
|
2092
|
+
id: "MASTRA_STORAGE_MSSQL_INDEX_CREATE_FAILED",
|
|
2093
|
+
domain: ErrorDomain.STORAGE,
|
|
2094
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2095
|
+
details: {
|
|
2096
|
+
indexName: options.name,
|
|
2097
|
+
tableName: options.table
|
|
2098
|
+
}
|
|
2099
|
+
},
|
|
2100
|
+
error
|
|
2101
|
+
);
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
/**
|
|
2105
|
+
* Drop an existing index
|
|
2106
|
+
*/
|
|
2107
|
+
async dropIndex(indexName) {
|
|
2108
|
+
try {
|
|
2109
|
+
const schemaName = this.schemaName || "dbo";
|
|
2110
|
+
const indexNameSafe = parseSqlIdentifier(indexName, "index name");
|
|
2111
|
+
const checkRequest = this.pool.request();
|
|
2112
|
+
checkRequest.input("indexName", indexNameSafe);
|
|
2113
|
+
checkRequest.input("schemaName", schemaName);
|
|
2114
|
+
const result = await checkRequest.query(`
|
|
2115
|
+
SELECT t.name as table_name
|
|
2116
|
+
FROM sys.indexes i
|
|
2117
|
+
INNER JOIN sys.tables t ON i.object_id = t.object_id
|
|
2118
|
+
INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
|
|
2119
|
+
WHERE i.name = @indexName
|
|
2120
|
+
AND s.name = @schemaName
|
|
2121
|
+
`);
|
|
2122
|
+
if (!result.recordset || result.recordset.length === 0) {
|
|
2123
|
+
return;
|
|
2124
|
+
}
|
|
2125
|
+
if (result.recordset.length > 1) {
|
|
2126
|
+
const tables = result.recordset.map((r) => r.table_name).join(", ");
|
|
2127
|
+
throw new MastraError({
|
|
2128
|
+
id: "MASTRA_STORAGE_MSSQL_INDEX_AMBIGUOUS",
|
|
2129
|
+
domain: ErrorDomain.STORAGE,
|
|
2130
|
+
category: ErrorCategory.USER,
|
|
2131
|
+
text: `Index "${indexNameSafe}" exists on multiple tables (${tables}) in schema "${schemaName}". Please drop indexes manually or ensure unique index names.`
|
|
2132
|
+
});
|
|
2133
|
+
}
|
|
2134
|
+
const tableName = result.recordset[0].table_name;
|
|
2135
|
+
const fullTableName = getTableName({
|
|
2136
|
+
indexName: tableName,
|
|
2137
|
+
schemaName: getSchemaName(this.schemaName)
|
|
2138
|
+
});
|
|
2139
|
+
const dropSql = `DROP INDEX [${indexNameSafe}] ON ${fullTableName}`;
|
|
2140
|
+
await this.pool.request().query(dropSql);
|
|
2141
|
+
} catch (error) {
|
|
2142
|
+
throw new MastraError(
|
|
2143
|
+
{
|
|
2144
|
+
id: "MASTRA_STORAGE_MSSQL_INDEX_DROP_FAILED",
|
|
2145
|
+
domain: ErrorDomain.STORAGE,
|
|
2146
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2147
|
+
details: {
|
|
2148
|
+
indexName
|
|
2149
|
+
}
|
|
2150
|
+
},
|
|
2151
|
+
error
|
|
2152
|
+
);
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2155
|
+
/**
|
|
2156
|
+
* List indexes for a specific table or all tables
|
|
2157
|
+
*/
|
|
2158
|
+
async listIndexes(tableName) {
|
|
2159
|
+
try {
|
|
2160
|
+
const schemaName = this.schemaName || "dbo";
|
|
2161
|
+
let query;
|
|
2162
|
+
const request = this.pool.request();
|
|
2163
|
+
request.input("schemaName", schemaName);
|
|
2164
|
+
if (tableName) {
|
|
2165
|
+
query = `
|
|
2166
|
+
SELECT
|
|
2167
|
+
i.name as name,
|
|
2168
|
+
o.name as [table],
|
|
2169
|
+
i.is_unique as is_unique,
|
|
2170
|
+
CAST(SUM(s.used_page_count) * 8 / 1024.0 AS VARCHAR(50)) + ' MB' as size
|
|
2171
|
+
FROM sys.indexes i
|
|
2172
|
+
INNER JOIN sys.objects o ON i.object_id = o.object_id
|
|
2173
|
+
INNER JOIN sys.schemas sch ON o.schema_id = sch.schema_id
|
|
2174
|
+
LEFT JOIN sys.dm_db_partition_stats s ON i.object_id = s.object_id AND i.index_id = s.index_id
|
|
2175
|
+
WHERE sch.name = @schemaName
|
|
2176
|
+
AND o.name = @tableName
|
|
2177
|
+
AND i.name IS NOT NULL
|
|
2178
|
+
GROUP BY i.name, o.name, i.is_unique
|
|
2179
|
+
`;
|
|
2180
|
+
request.input("tableName", tableName);
|
|
2181
|
+
} else {
|
|
2182
|
+
query = `
|
|
2183
|
+
SELECT
|
|
2184
|
+
i.name as name,
|
|
2185
|
+
o.name as [table],
|
|
2186
|
+
i.is_unique as is_unique,
|
|
2187
|
+
CAST(SUM(s.used_page_count) * 8 / 1024.0 AS VARCHAR(50)) + ' MB' as size
|
|
2188
|
+
FROM sys.indexes i
|
|
2189
|
+
INNER JOIN sys.objects o ON i.object_id = o.object_id
|
|
2190
|
+
INNER JOIN sys.schemas sch ON o.schema_id = sch.schema_id
|
|
2191
|
+
LEFT JOIN sys.dm_db_partition_stats s ON i.object_id = s.object_id AND i.index_id = s.index_id
|
|
2192
|
+
WHERE sch.name = @schemaName
|
|
2193
|
+
AND i.name IS NOT NULL
|
|
2194
|
+
GROUP BY i.name, o.name, i.is_unique
|
|
2195
|
+
`;
|
|
2196
|
+
}
|
|
2197
|
+
const result = await request.query(query);
|
|
2198
|
+
const indexes = [];
|
|
2199
|
+
for (const row of result.recordset) {
|
|
2200
|
+
const colRequest = this.pool.request();
|
|
2201
|
+
colRequest.input("indexName", row.name);
|
|
2202
|
+
colRequest.input("schemaName", schemaName);
|
|
2203
|
+
const colResult = await colRequest.query(`
|
|
2204
|
+
SELECT c.name as column_name
|
|
2205
|
+
FROM sys.indexes i
|
|
2206
|
+
INNER JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
|
|
2207
|
+
INNER JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
|
|
2208
|
+
INNER JOIN sys.objects o ON i.object_id = o.object_id
|
|
2209
|
+
INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
|
|
2210
|
+
WHERE i.name = @indexName
|
|
2211
|
+
AND s.name = @schemaName
|
|
2212
|
+
ORDER BY ic.key_ordinal
|
|
2213
|
+
`);
|
|
2214
|
+
indexes.push({
|
|
2215
|
+
name: row.name,
|
|
2216
|
+
table: row.table,
|
|
2217
|
+
columns: colResult.recordset.map((c) => c.column_name),
|
|
2218
|
+
unique: row.is_unique || false,
|
|
2219
|
+
size: row.size || "0 MB",
|
|
2220
|
+
definition: ""
|
|
2221
|
+
// MSSQL doesn't store definition like PG
|
|
2222
|
+
});
|
|
2223
|
+
}
|
|
2224
|
+
return indexes;
|
|
2225
|
+
} catch (error) {
|
|
2226
|
+
throw new MastraError(
|
|
2227
|
+
{
|
|
2228
|
+
id: "MASTRA_STORAGE_MSSQL_INDEX_LIST_FAILED",
|
|
2229
|
+
domain: ErrorDomain.STORAGE,
|
|
2230
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2231
|
+
details: tableName ? {
|
|
2232
|
+
tableName
|
|
2233
|
+
} : {}
|
|
2234
|
+
},
|
|
2235
|
+
error
|
|
2236
|
+
);
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
/**
|
|
2240
|
+
* Get detailed statistics for a specific index
|
|
2241
|
+
*/
|
|
2242
|
+
async describeIndex(indexName) {
|
|
2243
|
+
try {
|
|
2244
|
+
const schemaName = this.schemaName || "dbo";
|
|
2245
|
+
const request = this.pool.request();
|
|
2246
|
+
request.input("indexName", indexName);
|
|
2247
|
+
request.input("schemaName", schemaName);
|
|
2248
|
+
const query = `
|
|
2249
|
+
SELECT
|
|
2250
|
+
i.name as name,
|
|
2251
|
+
o.name as [table],
|
|
2252
|
+
i.is_unique as is_unique,
|
|
2253
|
+
CAST(SUM(s.used_page_count) * 8 / 1024.0 AS VARCHAR(50)) + ' MB' as size,
|
|
2254
|
+
i.type_desc as method,
|
|
2255
|
+
ISNULL(us.user_scans, 0) as scans,
|
|
2256
|
+
ISNULL(us.user_seeks + us.user_scans, 0) as tuples_read,
|
|
2257
|
+
ISNULL(us.user_lookups, 0) as tuples_fetched
|
|
2258
|
+
FROM sys.indexes i
|
|
2259
|
+
INNER JOIN sys.objects o ON i.object_id = o.object_id
|
|
2260
|
+
INNER JOIN sys.schemas sch ON o.schema_id = sch.schema_id
|
|
2261
|
+
LEFT JOIN sys.dm_db_partition_stats s ON i.object_id = s.object_id AND i.index_id = s.index_id
|
|
2262
|
+
LEFT JOIN sys.dm_db_index_usage_stats us ON i.object_id = us.object_id AND i.index_id = us.index_id
|
|
2263
|
+
WHERE i.name = @indexName
|
|
2264
|
+
AND sch.name = @schemaName
|
|
2265
|
+
GROUP BY i.name, o.name, i.is_unique, i.type_desc, us.user_seeks, us.user_scans, us.user_lookups
|
|
2266
|
+
`;
|
|
2267
|
+
const result = await request.query(query);
|
|
2268
|
+
if (!result.recordset || result.recordset.length === 0) {
|
|
2269
|
+
throw new Error(`Index "${indexName}" not found in schema "${schemaName}"`);
|
|
2270
|
+
}
|
|
2271
|
+
const row = result.recordset[0];
|
|
2272
|
+
const colRequest = this.pool.request();
|
|
2273
|
+
colRequest.input("indexName", indexName);
|
|
2274
|
+
colRequest.input("schemaName", schemaName);
|
|
2275
|
+
const colResult = await colRequest.query(`
|
|
2276
|
+
SELECT c.name as column_name
|
|
2277
|
+
FROM sys.indexes i
|
|
2278
|
+
INNER JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
|
|
2279
|
+
INNER JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
|
|
2280
|
+
INNER JOIN sys.objects o ON i.object_id = o.object_id
|
|
2281
|
+
INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
|
|
2282
|
+
WHERE i.name = @indexName
|
|
2283
|
+
AND s.name = @schemaName
|
|
2284
|
+
ORDER BY ic.key_ordinal
|
|
2285
|
+
`);
|
|
2286
|
+
return {
|
|
2287
|
+
name: row.name,
|
|
2288
|
+
table: row.table,
|
|
2289
|
+
columns: colResult.recordset.map((c) => c.column_name),
|
|
2290
|
+
unique: row.is_unique || false,
|
|
2291
|
+
size: row.size || "0 MB",
|
|
2292
|
+
definition: "",
|
|
2293
|
+
method: row.method?.toLowerCase() || "nonclustered",
|
|
2294
|
+
scans: Number(row.scans) || 0,
|
|
2295
|
+
tuples_read: Number(row.tuples_read) || 0,
|
|
2296
|
+
tuples_fetched: Number(row.tuples_fetched) || 0
|
|
2297
|
+
};
|
|
2298
|
+
} catch (error) {
|
|
2299
|
+
throw new MastraError(
|
|
2300
|
+
{
|
|
2301
|
+
id: "MASTRA_STORAGE_MSSQL_INDEX_DESCRIBE_FAILED",
|
|
2302
|
+
domain: ErrorDomain.STORAGE,
|
|
2303
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2304
|
+
details: {
|
|
2305
|
+
indexName
|
|
2306
|
+
}
|
|
2307
|
+
},
|
|
2308
|
+
error
|
|
2309
|
+
);
|
|
2310
|
+
}
|
|
2311
|
+
}
|
|
2312
|
+
/**
|
|
2313
|
+
* Returns definitions for automatic performance indexes
|
|
2314
|
+
* IMPORTANT: Uses seq_id DESC instead of createdAt DESC for MSSQL due to millisecond accuracy limitations
|
|
2315
|
+
* NOTE: Using NVARCHAR(400) for text columns (800 bytes) leaves room for composite indexes
|
|
2316
|
+
*/
|
|
2317
|
+
getAutomaticIndexDefinitions() {
|
|
2318
|
+
const schemaPrefix = this.schemaName ? `${this.schemaName}_` : "";
|
|
2319
|
+
return [
|
|
2320
|
+
// Composite indexes for optimal filtering + sorting performance
|
|
2321
|
+
// NVARCHAR(400) = 800 bytes, plus BIGINT (8 bytes) = 808 bytes total (under 900-byte limit)
|
|
2322
|
+
{
|
|
2323
|
+
name: `${schemaPrefix}mastra_threads_resourceid_seqid_idx`,
|
|
2324
|
+
table: TABLE_THREADS,
|
|
2325
|
+
columns: ["resourceId", "seq_id DESC"]
|
|
2326
|
+
},
|
|
2327
|
+
{
|
|
2328
|
+
name: `${schemaPrefix}mastra_messages_thread_id_seqid_idx`,
|
|
2329
|
+
table: TABLE_MESSAGES,
|
|
2330
|
+
columns: ["thread_id", "seq_id DESC"]
|
|
2331
|
+
},
|
|
2332
|
+
{
|
|
2333
|
+
name: `${schemaPrefix}mastra_traces_name_seqid_idx`,
|
|
2334
|
+
table: TABLE_TRACES,
|
|
2335
|
+
columns: ["name", "seq_id DESC"]
|
|
2336
|
+
},
|
|
2337
|
+
{
|
|
2338
|
+
name: `${schemaPrefix}mastra_evals_agent_name_seqid_idx`,
|
|
2339
|
+
table: TABLE_EVALS,
|
|
2340
|
+
columns: ["agent_name", "seq_id DESC"]
|
|
2341
|
+
},
|
|
2342
|
+
{
|
|
2343
|
+
name: `${schemaPrefix}mastra_scores_trace_id_span_id_seqid_idx`,
|
|
2344
|
+
table: TABLE_SCORERS,
|
|
2345
|
+
columns: ["traceId", "spanId", "seq_id DESC"]
|
|
2346
|
+
},
|
|
2347
|
+
// AI Spans indexes for optimal trace querying
|
|
2348
|
+
{
|
|
2349
|
+
name: `${schemaPrefix}mastra_ai_spans_traceid_startedat_idx`,
|
|
2350
|
+
table: TABLE_AI_SPANS,
|
|
2351
|
+
columns: ["traceId", "startedAt DESC"]
|
|
2352
|
+
},
|
|
2353
|
+
{
|
|
2354
|
+
name: `${schemaPrefix}mastra_ai_spans_parentspanid_startedat_idx`,
|
|
2355
|
+
table: TABLE_AI_SPANS,
|
|
2356
|
+
columns: ["parentSpanId", "startedAt DESC"]
|
|
2357
|
+
},
|
|
2358
|
+
{
|
|
2359
|
+
name: `${schemaPrefix}mastra_ai_spans_name_idx`,
|
|
2360
|
+
table: TABLE_AI_SPANS,
|
|
2361
|
+
columns: ["name"]
|
|
2362
|
+
},
|
|
2363
|
+
{
|
|
2364
|
+
name: `${schemaPrefix}mastra_ai_spans_spantype_startedat_idx`,
|
|
2365
|
+
table: TABLE_AI_SPANS,
|
|
2366
|
+
columns: ["spanType", "startedAt DESC"]
|
|
2367
|
+
}
|
|
2368
|
+
];
|
|
2369
|
+
}
|
|
2370
|
+
/**
|
|
2371
|
+
* Creates automatic indexes for optimal query performance
|
|
2372
|
+
* Uses getAutomaticIndexDefinitions() to determine which indexes to create
|
|
2373
|
+
*/
|
|
2374
|
+
async createAutomaticIndexes() {
|
|
2375
|
+
try {
|
|
2376
|
+
const indexes = this.getAutomaticIndexDefinitions();
|
|
2377
|
+
for (const indexOptions of indexes) {
|
|
2378
|
+
try {
|
|
2379
|
+
await this.createIndex(indexOptions);
|
|
2380
|
+
} catch (error) {
|
|
2381
|
+
this.logger?.warn?.(`Failed to create index ${indexOptions.name}:`, error);
|
|
2382
|
+
}
|
|
2383
|
+
}
|
|
2384
|
+
} catch (error) {
|
|
2385
|
+
throw new MastraError(
|
|
2386
|
+
{
|
|
2387
|
+
id: "MASTRA_STORAGE_MSSQL_STORE_CREATE_PERFORMANCE_INDEXES_FAILED",
|
|
2388
|
+
domain: ErrorDomain.STORAGE,
|
|
2389
|
+
category: ErrorCategory.THIRD_PARTY
|
|
2390
|
+
},
|
|
2391
|
+
error
|
|
2392
|
+
);
|
|
2393
|
+
}
|
|
2394
|
+
}
|
|
2395
|
+
};
|
|
2396
|
+
function transformScoreRow(row) {
|
|
2397
|
+
return {
|
|
2398
|
+
...row,
|
|
2399
|
+
input: safelyParseJSON(row.input),
|
|
2400
|
+
scorer: safelyParseJSON(row.scorer),
|
|
2401
|
+
preprocessStepResult: safelyParseJSON(row.preprocessStepResult),
|
|
2402
|
+
analyzeStepResult: safelyParseJSON(row.analyzeStepResult),
|
|
2403
|
+
metadata: safelyParseJSON(row.metadata),
|
|
2404
|
+
output: safelyParseJSON(row.output),
|
|
2405
|
+
additionalContext: safelyParseJSON(row.additionalContext),
|
|
2406
|
+
runtimeContext: safelyParseJSON(row.runtimeContext),
|
|
2407
|
+
entity: safelyParseJSON(row.entity),
|
|
2408
|
+
createdAt: row.createdAt,
|
|
2409
|
+
updatedAt: row.updatedAt
|
|
2410
|
+
};
|
|
2411
|
+
}
|
|
2412
|
+
var ScoresMSSQL = class extends ScoresStorage {
|
|
2413
|
+
pool;
|
|
2414
|
+
operations;
|
|
2415
|
+
schema;
|
|
2416
|
+
constructor({
|
|
2417
|
+
pool,
|
|
2418
|
+
operations,
|
|
2419
|
+
schema
|
|
2420
|
+
}) {
|
|
2421
|
+
super();
|
|
2422
|
+
this.pool = pool;
|
|
2423
|
+
this.operations = operations;
|
|
2424
|
+
this.schema = schema;
|
|
2425
|
+
}
|
|
2426
|
+
async getScoreById({ id }) {
|
|
2427
|
+
try {
|
|
2428
|
+
const request = this.pool.request();
|
|
2429
|
+
request.input("p1", id);
|
|
2430
|
+
const result = await request.query(
|
|
2431
|
+
`SELECT * FROM ${getTableName({ indexName: TABLE_SCORERS, schemaName: getSchemaName(this.schema) })} WHERE id = @p1`
|
|
1459
2432
|
);
|
|
1460
2433
|
if (result.recordset.length === 0) {
|
|
1461
2434
|
return null;
|
|
@@ -1488,7 +2461,7 @@ var ScoresMSSQL = class extends ScoresStorage {
|
|
|
1488
2461
|
);
|
|
1489
2462
|
}
|
|
1490
2463
|
try {
|
|
1491
|
-
const scoreId =
|
|
2464
|
+
const scoreId = randomUUID();
|
|
1492
2465
|
const {
|
|
1493
2466
|
scorer,
|
|
1494
2467
|
preprocessStepResult,
|
|
@@ -1506,15 +2479,15 @@ var ScoresMSSQL = class extends ScoresStorage {
|
|
|
1506
2479
|
record: {
|
|
1507
2480
|
id: scoreId,
|
|
1508
2481
|
...rest,
|
|
1509
|
-
input:
|
|
1510
|
-
output:
|
|
1511
|
-
preprocessStepResult: preprocessStepResult
|
|
1512
|
-
analyzeStepResult: analyzeStepResult
|
|
1513
|
-
metadata: metadata
|
|
1514
|
-
additionalContext: additionalContext
|
|
1515
|
-
runtimeContext: runtimeContext
|
|
1516
|
-
entity: entity
|
|
1517
|
-
scorer: scorer
|
|
2482
|
+
input: input || "",
|
|
2483
|
+
output: output || "",
|
|
2484
|
+
preprocessStepResult: preprocessStepResult || null,
|
|
2485
|
+
analyzeStepResult: analyzeStepResult || null,
|
|
2486
|
+
metadata: metadata || null,
|
|
2487
|
+
additionalContext: additionalContext || null,
|
|
2488
|
+
runtimeContext: runtimeContext || null,
|
|
2489
|
+
entity: entity || null,
|
|
2490
|
+
scorer: scorer || null,
|
|
1518
2491
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1519
2492
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1520
2493
|
}
|
|
@@ -1534,14 +2507,37 @@ var ScoresMSSQL = class extends ScoresStorage {
|
|
|
1534
2507
|
}
|
|
1535
2508
|
async getScoresByScorerId({
|
|
1536
2509
|
scorerId,
|
|
1537
|
-
pagination
|
|
2510
|
+
pagination,
|
|
2511
|
+
entityId,
|
|
2512
|
+
entityType,
|
|
2513
|
+
source
|
|
1538
2514
|
}) {
|
|
1539
2515
|
try {
|
|
1540
|
-
const
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
2516
|
+
const conditions = ["[scorerId] = @p1"];
|
|
2517
|
+
const params = { p1: scorerId };
|
|
2518
|
+
let paramIndex = 2;
|
|
2519
|
+
if (entityId) {
|
|
2520
|
+
conditions.push(`[entityId] = @p${paramIndex}`);
|
|
2521
|
+
params[`p${paramIndex}`] = entityId;
|
|
2522
|
+
paramIndex++;
|
|
2523
|
+
}
|
|
2524
|
+
if (entityType) {
|
|
2525
|
+
conditions.push(`[entityType] = @p${paramIndex}`);
|
|
2526
|
+
params[`p${paramIndex}`] = entityType;
|
|
2527
|
+
paramIndex++;
|
|
2528
|
+
}
|
|
2529
|
+
if (source) {
|
|
2530
|
+
conditions.push(`[source] = @p${paramIndex}`);
|
|
2531
|
+
params[`p${paramIndex}`] = source;
|
|
2532
|
+
paramIndex++;
|
|
2533
|
+
}
|
|
2534
|
+
const whereClause = conditions.join(" AND ");
|
|
2535
|
+
const tableName = getTableName({ indexName: TABLE_SCORERS, schemaName: getSchemaName(this.schema) });
|
|
2536
|
+
const countRequest = this.pool.request();
|
|
2537
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
2538
|
+
countRequest.input(key, value);
|
|
2539
|
+
});
|
|
2540
|
+
const totalResult = await countRequest.query(`SELECT COUNT(*) as count FROM ${tableName} WHERE ${whereClause}`);
|
|
1545
2541
|
const total = totalResult.recordset[0]?.count || 0;
|
|
1546
2542
|
if (total === 0) {
|
|
1547
2543
|
return {
|
|
@@ -1555,12 +2551,13 @@ var ScoresMSSQL = class extends ScoresStorage {
|
|
|
1555
2551
|
};
|
|
1556
2552
|
}
|
|
1557
2553
|
const dataRequest = this.pool.request();
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
2554
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
2555
|
+
dataRequest.input(key, value);
|
|
2556
|
+
});
|
|
2557
|
+
dataRequest.input("perPage", pagination.perPage);
|
|
2558
|
+
dataRequest.input("offset", pagination.page * pagination.perPage);
|
|
2559
|
+
const dataQuery = `SELECT * FROM ${tableName} WHERE ${whereClause} ORDER BY [createdAt] DESC OFFSET @offset ROWS FETCH NEXT @perPage ROWS ONLY`;
|
|
2560
|
+
const result = await dataRequest.query(dataQuery);
|
|
1564
2561
|
return {
|
|
1565
2562
|
pagination: {
|
|
1566
2563
|
total: Number(total),
|
|
@@ -1817,7 +2814,7 @@ var TracesMSSQL = class extends TracesStorage {
|
|
|
1817
2814
|
const countRequest = this.pool.request();
|
|
1818
2815
|
Object.entries(paramMap).forEach(([key, value]) => {
|
|
1819
2816
|
if (value instanceof Date) {
|
|
1820
|
-
countRequest.input(key,
|
|
2817
|
+
countRequest.input(key, sql3.DateTime, value);
|
|
1821
2818
|
} else {
|
|
1822
2819
|
countRequest.input(key, value);
|
|
1823
2820
|
}
|
|
@@ -1851,7 +2848,7 @@ var TracesMSSQL = class extends TracesStorage {
|
|
|
1851
2848
|
const dataRequest = this.pool.request();
|
|
1852
2849
|
Object.entries(paramMap).forEach(([key, value]) => {
|
|
1853
2850
|
if (value instanceof Date) {
|
|
1854
|
-
dataRequest.input(key,
|
|
2851
|
+
dataRequest.input(key, sql3.DateTime, value);
|
|
1855
2852
|
} else {
|
|
1856
2853
|
dataRequest.input(key, value);
|
|
1857
2854
|
}
|
|
@@ -1907,24 +2904,6 @@ var TracesMSSQL = class extends TracesStorage {
|
|
|
1907
2904
|
});
|
|
1908
2905
|
}
|
|
1909
2906
|
};
|
|
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
2907
|
var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
1929
2908
|
pool;
|
|
1930
2909
|
operations;
|
|
@@ -1939,21 +2918,163 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
|
1939
2918
|
this.operations = operations;
|
|
1940
2919
|
this.schema = schema;
|
|
1941
2920
|
}
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
2921
|
+
parseWorkflowRun(row) {
|
|
2922
|
+
let parsedSnapshot = row.snapshot;
|
|
2923
|
+
if (typeof parsedSnapshot === "string") {
|
|
2924
|
+
try {
|
|
2925
|
+
parsedSnapshot = JSON.parse(row.snapshot);
|
|
2926
|
+
} catch (e) {
|
|
2927
|
+
this.logger?.warn?.(`Failed to parse snapshot for workflow ${row.workflow_name}:`, e);
|
|
2928
|
+
}
|
|
2929
|
+
}
|
|
2930
|
+
return {
|
|
2931
|
+
workflowName: row.workflow_name,
|
|
2932
|
+
runId: row.run_id,
|
|
2933
|
+
snapshot: parsedSnapshot,
|
|
2934
|
+
createdAt: row.createdAt,
|
|
2935
|
+
updatedAt: row.updatedAt,
|
|
2936
|
+
resourceId: row.resourceId
|
|
2937
|
+
};
|
|
2938
|
+
}
|
|
2939
|
+
async updateWorkflowResults({
|
|
2940
|
+
workflowName,
|
|
2941
|
+
runId,
|
|
2942
|
+
stepId,
|
|
2943
|
+
result,
|
|
2944
|
+
runtimeContext
|
|
1948
2945
|
}) {
|
|
1949
|
-
|
|
2946
|
+
const table = getTableName({ indexName: TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName(this.schema) });
|
|
2947
|
+
const transaction = this.pool.transaction();
|
|
2948
|
+
try {
|
|
2949
|
+
await transaction.begin();
|
|
2950
|
+
const selectRequest = new sql3.Request(transaction);
|
|
2951
|
+
selectRequest.input("workflow_name", workflowName);
|
|
2952
|
+
selectRequest.input("run_id", runId);
|
|
2953
|
+
const existingSnapshotResult = await selectRequest.query(
|
|
2954
|
+
`SELECT snapshot FROM ${table} WITH (UPDLOCK, HOLDLOCK) WHERE workflow_name = @workflow_name AND run_id = @run_id`
|
|
2955
|
+
);
|
|
2956
|
+
let snapshot;
|
|
2957
|
+
if (!existingSnapshotResult.recordset || existingSnapshotResult.recordset.length === 0) {
|
|
2958
|
+
snapshot = {
|
|
2959
|
+
context: {},
|
|
2960
|
+
activePaths: [],
|
|
2961
|
+
timestamp: Date.now(),
|
|
2962
|
+
suspendedPaths: {},
|
|
2963
|
+
resumeLabels: {},
|
|
2964
|
+
serializedStepGraph: [],
|
|
2965
|
+
value: {},
|
|
2966
|
+
waitingPaths: {},
|
|
2967
|
+
status: "pending",
|
|
2968
|
+
runId,
|
|
2969
|
+
runtimeContext: {}
|
|
2970
|
+
};
|
|
2971
|
+
} else {
|
|
2972
|
+
const existingSnapshot = existingSnapshotResult.recordset[0].snapshot;
|
|
2973
|
+
snapshot = typeof existingSnapshot === "string" ? JSON.parse(existingSnapshot) : existingSnapshot;
|
|
2974
|
+
}
|
|
2975
|
+
snapshot.context[stepId] = result;
|
|
2976
|
+
snapshot.runtimeContext = { ...snapshot.runtimeContext, ...runtimeContext };
|
|
2977
|
+
const upsertReq = new sql3.Request(transaction);
|
|
2978
|
+
upsertReq.input("workflow_name", workflowName);
|
|
2979
|
+
upsertReq.input("run_id", runId);
|
|
2980
|
+
upsertReq.input("snapshot", JSON.stringify(snapshot));
|
|
2981
|
+
upsertReq.input("createdAt", sql3.DateTime2, /* @__PURE__ */ new Date());
|
|
2982
|
+
upsertReq.input("updatedAt", sql3.DateTime2, /* @__PURE__ */ new Date());
|
|
2983
|
+
await upsertReq.query(
|
|
2984
|
+
`MERGE ${table} AS target
|
|
2985
|
+
USING (SELECT @workflow_name AS workflow_name, @run_id AS run_id) AS src
|
|
2986
|
+
ON target.workflow_name = src.workflow_name AND target.run_id = src.run_id
|
|
2987
|
+
WHEN MATCHED THEN UPDATE SET snapshot = @snapshot, [updatedAt] = @updatedAt
|
|
2988
|
+
WHEN NOT MATCHED THEN INSERT (workflow_name, run_id, snapshot, [createdAt], [updatedAt])
|
|
2989
|
+
VALUES (@workflow_name, @run_id, @snapshot, @createdAt, @updatedAt);`
|
|
2990
|
+
);
|
|
2991
|
+
await transaction.commit();
|
|
2992
|
+
return snapshot.context;
|
|
2993
|
+
} catch (error) {
|
|
2994
|
+
try {
|
|
2995
|
+
await transaction.rollback();
|
|
2996
|
+
} catch {
|
|
2997
|
+
}
|
|
2998
|
+
throw new MastraError(
|
|
2999
|
+
{
|
|
3000
|
+
id: "MASTRA_STORAGE_MSSQL_STORE_UPDATE_WORKFLOW_RESULTS_FAILED",
|
|
3001
|
+
domain: ErrorDomain.STORAGE,
|
|
3002
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
3003
|
+
details: {
|
|
3004
|
+
workflowName,
|
|
3005
|
+
runId,
|
|
3006
|
+
stepId
|
|
3007
|
+
}
|
|
3008
|
+
},
|
|
3009
|
+
error
|
|
3010
|
+
);
|
|
3011
|
+
}
|
|
1950
3012
|
}
|
|
1951
|
-
updateWorkflowState({
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
3013
|
+
async updateWorkflowState({
|
|
3014
|
+
workflowName,
|
|
3015
|
+
runId,
|
|
3016
|
+
opts
|
|
1955
3017
|
}) {
|
|
1956
|
-
|
|
3018
|
+
const table = getTableName({ indexName: TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName(this.schema) });
|
|
3019
|
+
const transaction = this.pool.transaction();
|
|
3020
|
+
try {
|
|
3021
|
+
await transaction.begin();
|
|
3022
|
+
const selectRequest = new sql3.Request(transaction);
|
|
3023
|
+
selectRequest.input("workflow_name", workflowName);
|
|
3024
|
+
selectRequest.input("run_id", runId);
|
|
3025
|
+
const existingSnapshotResult = await selectRequest.query(
|
|
3026
|
+
`SELECT snapshot FROM ${table} WITH (UPDLOCK, HOLDLOCK) WHERE workflow_name = @workflow_name AND run_id = @run_id`
|
|
3027
|
+
);
|
|
3028
|
+
if (!existingSnapshotResult.recordset || existingSnapshotResult.recordset.length === 0) {
|
|
3029
|
+
await transaction.rollback();
|
|
3030
|
+
return void 0;
|
|
3031
|
+
}
|
|
3032
|
+
const existingSnapshot = existingSnapshotResult.recordset[0].snapshot;
|
|
3033
|
+
const snapshot = typeof existingSnapshot === "string" ? JSON.parse(existingSnapshot) : existingSnapshot;
|
|
3034
|
+
if (!snapshot || !snapshot?.context) {
|
|
3035
|
+
await transaction.rollback();
|
|
3036
|
+
throw new MastraError(
|
|
3037
|
+
{
|
|
3038
|
+
id: "MASTRA_STORAGE_MSSQL_STORE_UPDATE_WORKFLOW_STATE_SNAPSHOT_NOT_FOUND",
|
|
3039
|
+
domain: ErrorDomain.STORAGE,
|
|
3040
|
+
category: ErrorCategory.SYSTEM,
|
|
3041
|
+
details: {
|
|
3042
|
+
workflowName,
|
|
3043
|
+
runId
|
|
3044
|
+
}
|
|
3045
|
+
},
|
|
3046
|
+
new Error(`Snapshot not found for runId ${runId}`)
|
|
3047
|
+
);
|
|
3048
|
+
}
|
|
3049
|
+
const updatedSnapshot = { ...snapshot, ...opts };
|
|
3050
|
+
const updateRequest = new sql3.Request(transaction);
|
|
3051
|
+
updateRequest.input("snapshot", JSON.stringify(updatedSnapshot));
|
|
3052
|
+
updateRequest.input("workflow_name", workflowName);
|
|
3053
|
+
updateRequest.input("run_id", runId);
|
|
3054
|
+
updateRequest.input("updatedAt", sql3.DateTime2, /* @__PURE__ */ new Date());
|
|
3055
|
+
await updateRequest.query(
|
|
3056
|
+
`UPDATE ${table} SET snapshot = @snapshot, [updatedAt] = @updatedAt WHERE workflow_name = @workflow_name AND run_id = @run_id`
|
|
3057
|
+
);
|
|
3058
|
+
await transaction.commit();
|
|
3059
|
+
return updatedSnapshot;
|
|
3060
|
+
} catch (error) {
|
|
3061
|
+
try {
|
|
3062
|
+
await transaction.rollback();
|
|
3063
|
+
} catch {
|
|
3064
|
+
}
|
|
3065
|
+
throw new MastraError(
|
|
3066
|
+
{
|
|
3067
|
+
id: "MASTRA_STORAGE_MSSQL_STORE_UPDATE_WORKFLOW_STATE_FAILED",
|
|
3068
|
+
domain: ErrorDomain.STORAGE,
|
|
3069
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
3070
|
+
details: {
|
|
3071
|
+
workflowName,
|
|
3072
|
+
runId
|
|
3073
|
+
}
|
|
3074
|
+
},
|
|
3075
|
+
error
|
|
3076
|
+
);
|
|
3077
|
+
}
|
|
1957
3078
|
}
|
|
1958
3079
|
async persistWorkflowSnapshot({
|
|
1959
3080
|
workflowName,
|
|
@@ -1969,8 +3090,8 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
|
1969
3090
|
request.input("run_id", runId);
|
|
1970
3091
|
request.input("resourceId", resourceId);
|
|
1971
3092
|
request.input("snapshot", JSON.stringify(snapshot));
|
|
1972
|
-
request.input("createdAt",
|
|
1973
|
-
request.input("updatedAt",
|
|
3093
|
+
request.input("createdAt", sql3.DateTime2, new Date(now));
|
|
3094
|
+
request.input("updatedAt", sql3.DateTime2, new Date(now));
|
|
1974
3095
|
const mergeSql = `MERGE INTO ${table} AS target
|
|
1975
3096
|
USING (SELECT @workflow_name AS workflow_name, @run_id AS run_id) AS src
|
|
1976
3097
|
ON target.workflow_name = src.workflow_name AND target.run_id = src.run_id
|
|
@@ -2051,7 +3172,7 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
|
2051
3172
|
if (!result.recordset || result.recordset.length === 0) {
|
|
2052
3173
|
return null;
|
|
2053
3174
|
}
|
|
2054
|
-
return parseWorkflowRun(result.recordset[0]);
|
|
3175
|
+
return this.parseWorkflowRun(result.recordset[0]);
|
|
2055
3176
|
} catch (error) {
|
|
2056
3177
|
throw new MastraError(
|
|
2057
3178
|
{
|
|
@@ -2088,7 +3209,7 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
|
2088
3209
|
conditions.push(`[resourceId] = @resourceId`);
|
|
2089
3210
|
paramMap["resourceId"] = resourceId;
|
|
2090
3211
|
} else {
|
|
2091
|
-
|
|
3212
|
+
this.logger?.warn?.(`[${TABLE_WORKFLOW_SNAPSHOT}] resourceId column not found. Skipping resourceId filter.`);
|
|
2092
3213
|
}
|
|
2093
3214
|
}
|
|
2094
3215
|
if (fromDate instanceof Date && !isNaN(fromDate.getTime())) {
|
|
@@ -2105,7 +3226,7 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
|
2105
3226
|
const request = this.pool.request();
|
|
2106
3227
|
Object.entries(paramMap).forEach(([key, value]) => {
|
|
2107
3228
|
if (value instanceof Date) {
|
|
2108
|
-
request.input(key,
|
|
3229
|
+
request.input(key, sql3.DateTime, value);
|
|
2109
3230
|
} else {
|
|
2110
3231
|
request.input(key, value);
|
|
2111
3232
|
}
|
|
@@ -2122,7 +3243,7 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
|
2122
3243
|
request.input("offset", offset);
|
|
2123
3244
|
}
|
|
2124
3245
|
const result = await request.query(query);
|
|
2125
|
-
const runs = (result.recordset || []).map((row) => parseWorkflowRun(row));
|
|
3246
|
+
const runs = (result.recordset || []).map((row) => this.parseWorkflowRun(row));
|
|
2126
3247
|
return { runs, total: total || runs.length };
|
|
2127
3248
|
} catch (error) {
|
|
2128
3249
|
throw new MastraError(
|
|
@@ -2162,7 +3283,7 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2162
3283
|
}
|
|
2163
3284
|
}
|
|
2164
3285
|
this.schema = config.schemaName || "dbo";
|
|
2165
|
-
this.pool = "connectionString" in config ? new
|
|
3286
|
+
this.pool = "connectionString" in config ? new sql3.ConnectionPool(config.connectionString) : new sql3.ConnectionPool({
|
|
2166
3287
|
server: config.server,
|
|
2167
3288
|
database: config.database,
|
|
2168
3289
|
user: config.user,
|
|
@@ -2176,13 +3297,15 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2176
3297
|
const traces = new TracesMSSQL({ pool: this.pool, operations, schema: this.schema });
|
|
2177
3298
|
const workflows = new WorkflowsMSSQL({ pool: this.pool, operations, schema: this.schema });
|
|
2178
3299
|
const memory = new MemoryMSSQL({ pool: this.pool, schema: this.schema, operations });
|
|
3300
|
+
const observability = new ObservabilityMSSQL({ pool: this.pool, operations, schema: this.schema });
|
|
2179
3301
|
this.stores = {
|
|
2180
3302
|
operations,
|
|
2181
3303
|
scores,
|
|
2182
3304
|
traces,
|
|
2183
3305
|
workflows,
|
|
2184
3306
|
legacyEvals,
|
|
2185
|
-
memory
|
|
3307
|
+
memory,
|
|
3308
|
+
observability
|
|
2186
3309
|
};
|
|
2187
3310
|
} catch (e) {
|
|
2188
3311
|
throw new MastraError(
|
|
@@ -2202,6 +3325,11 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2202
3325
|
try {
|
|
2203
3326
|
await this.isConnected;
|
|
2204
3327
|
await super.init();
|
|
3328
|
+
try {
|
|
3329
|
+
await this.stores.operations.createAutomaticIndexes();
|
|
3330
|
+
} catch (indexError) {
|
|
3331
|
+
this.logger?.warn?.("Failed to create indexes:", indexError);
|
|
3332
|
+
}
|
|
2205
3333
|
} catch (error) {
|
|
2206
3334
|
this.isConnected = null;
|
|
2207
3335
|
throw new MastraError(
|
|
@@ -2229,7 +3357,9 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2229
3357
|
hasColumn: true,
|
|
2230
3358
|
createTable: true,
|
|
2231
3359
|
deleteMessages: true,
|
|
2232
|
-
getScoresBySpan: true
|
|
3360
|
+
getScoresBySpan: true,
|
|
3361
|
+
aiTracing: true,
|
|
3362
|
+
indexManagement: true
|
|
2233
3363
|
};
|
|
2234
3364
|
}
|
|
2235
3365
|
/** @deprecated use getEvals instead */
|
|
@@ -2395,6 +3525,60 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2395
3525
|
async close() {
|
|
2396
3526
|
await this.pool.close();
|
|
2397
3527
|
}
|
|
3528
|
+
/**
|
|
3529
|
+
* Index Management
|
|
3530
|
+
*/
|
|
3531
|
+
async createIndex(options) {
|
|
3532
|
+
return this.stores.operations.createIndex(options);
|
|
3533
|
+
}
|
|
3534
|
+
async listIndexes(tableName) {
|
|
3535
|
+
return this.stores.operations.listIndexes(tableName);
|
|
3536
|
+
}
|
|
3537
|
+
async describeIndex(indexName) {
|
|
3538
|
+
return this.stores.operations.describeIndex(indexName);
|
|
3539
|
+
}
|
|
3540
|
+
async dropIndex(indexName) {
|
|
3541
|
+
return this.stores.operations.dropIndex(indexName);
|
|
3542
|
+
}
|
|
3543
|
+
/**
|
|
3544
|
+
* AI Tracing / Observability
|
|
3545
|
+
*/
|
|
3546
|
+
getObservabilityStore() {
|
|
3547
|
+
if (!this.stores.observability) {
|
|
3548
|
+
throw new MastraError({
|
|
3549
|
+
id: "MSSQL_STORE_OBSERVABILITY_NOT_INITIALIZED",
|
|
3550
|
+
domain: ErrorDomain.STORAGE,
|
|
3551
|
+
category: ErrorCategory.SYSTEM,
|
|
3552
|
+
text: "Observability storage is not initialized"
|
|
3553
|
+
});
|
|
3554
|
+
}
|
|
3555
|
+
return this.stores.observability;
|
|
3556
|
+
}
|
|
3557
|
+
async createAISpan(span) {
|
|
3558
|
+
return this.getObservabilityStore().createAISpan(span);
|
|
3559
|
+
}
|
|
3560
|
+
async updateAISpan({
|
|
3561
|
+
spanId,
|
|
3562
|
+
traceId,
|
|
3563
|
+
updates
|
|
3564
|
+
}) {
|
|
3565
|
+
return this.getObservabilityStore().updateAISpan({ spanId, traceId, updates });
|
|
3566
|
+
}
|
|
3567
|
+
async getAITrace(traceId) {
|
|
3568
|
+
return this.getObservabilityStore().getAITrace(traceId);
|
|
3569
|
+
}
|
|
3570
|
+
async getAITracesPaginated(args) {
|
|
3571
|
+
return this.getObservabilityStore().getAITracesPaginated(args);
|
|
3572
|
+
}
|
|
3573
|
+
async batchCreateAISpans(args) {
|
|
3574
|
+
return this.getObservabilityStore().batchCreateAISpans(args);
|
|
3575
|
+
}
|
|
3576
|
+
async batchUpdateAISpans(args) {
|
|
3577
|
+
return this.getObservabilityStore().batchUpdateAISpans(args);
|
|
3578
|
+
}
|
|
3579
|
+
async batchDeleteAITraces(args) {
|
|
3580
|
+
return this.getObservabilityStore().batchDeleteAITraces(args);
|
|
3581
|
+
}
|
|
2398
3582
|
/**
|
|
2399
3583
|
* Scorers
|
|
2400
3584
|
*/
|
|
@@ -2403,9 +3587,18 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2403
3587
|
}
|
|
2404
3588
|
async getScoresByScorerId({
|
|
2405
3589
|
scorerId: _scorerId,
|
|
2406
|
-
pagination: _pagination
|
|
3590
|
+
pagination: _pagination,
|
|
3591
|
+
entityId: _entityId,
|
|
3592
|
+
entityType: _entityType,
|
|
3593
|
+
source: _source
|
|
2407
3594
|
}) {
|
|
2408
|
-
return this.stores.scores.getScoresByScorerId({
|
|
3595
|
+
return this.stores.scores.getScoresByScorerId({
|
|
3596
|
+
scorerId: _scorerId,
|
|
3597
|
+
pagination: _pagination,
|
|
3598
|
+
entityId: _entityId,
|
|
3599
|
+
entityType: _entityType,
|
|
3600
|
+
source: _source
|
|
3601
|
+
});
|
|
2409
3602
|
}
|
|
2410
3603
|
async saveScore(_score) {
|
|
2411
3604
|
return this.stores.scores.saveScore(_score);
|