@mastra/mssql 0.0.0-fix-memory-search-fetch-20251027160505 → 0.0.0-fix-multi-modal-for-cloud-20251028082043
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 +24 -3
- package/README.md +315 -36
- package/dist/index.cjs +1343 -150
- 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 +1343 -150
- 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 +5 -5
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, WorkflowsStorage, MemoryStorage, resolveMessageLimit, TABLE_RESOURCES, ObservabilityStorage, safelyParseJSON } from '@mastra/core/storage';
|
|
3
|
+
import sql3 from 'mssql';
|
|
4
4
|
import { parseSqlIdentifier } 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 sql6 = `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(sql6);
|
|
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,51 +1844,592 @@ ${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
|
-
|
|
1459
|
-
|
|
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`
|
|
2432
|
+
);
|
|
1460
2433
|
if (result.recordset.length === 0) {
|
|
1461
2434
|
return null;
|
|
1462
2435
|
}
|
|
@@ -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),
|
|
@@ -1740,24 +2737,6 @@ var ScoresMSSQL = class extends ScoresStorage {
|
|
|
1740
2737
|
}
|
|
1741
2738
|
}
|
|
1742
2739
|
};
|
|
1743
|
-
function parseWorkflowRun(row) {
|
|
1744
|
-
let parsedSnapshot = row.snapshot;
|
|
1745
|
-
if (typeof parsedSnapshot === "string") {
|
|
1746
|
-
try {
|
|
1747
|
-
parsedSnapshot = JSON.parse(row.snapshot);
|
|
1748
|
-
} catch (e) {
|
|
1749
|
-
console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
|
|
1750
|
-
}
|
|
1751
|
-
}
|
|
1752
|
-
return {
|
|
1753
|
-
workflowName: row.workflow_name,
|
|
1754
|
-
runId: row.run_id,
|
|
1755
|
-
snapshot: parsedSnapshot,
|
|
1756
|
-
createdAt: row.createdAt,
|
|
1757
|
-
updatedAt: row.updatedAt,
|
|
1758
|
-
resourceId: row.resourceId
|
|
1759
|
-
};
|
|
1760
|
-
}
|
|
1761
2740
|
var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
1762
2741
|
pool;
|
|
1763
2742
|
operations;
|
|
@@ -1772,21 +2751,163 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
|
1772
2751
|
this.operations = operations;
|
|
1773
2752
|
this.schema = schema;
|
|
1774
2753
|
}
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
2754
|
+
parseWorkflowRun(row) {
|
|
2755
|
+
let parsedSnapshot = row.snapshot;
|
|
2756
|
+
if (typeof parsedSnapshot === "string") {
|
|
2757
|
+
try {
|
|
2758
|
+
parsedSnapshot = JSON.parse(row.snapshot);
|
|
2759
|
+
} catch (e) {
|
|
2760
|
+
this.logger?.warn?.(`Failed to parse snapshot for workflow ${row.workflow_name}:`, e);
|
|
2761
|
+
}
|
|
2762
|
+
}
|
|
2763
|
+
return {
|
|
2764
|
+
workflowName: row.workflow_name,
|
|
2765
|
+
runId: row.run_id,
|
|
2766
|
+
snapshot: parsedSnapshot,
|
|
2767
|
+
createdAt: row.createdAt,
|
|
2768
|
+
updatedAt: row.updatedAt,
|
|
2769
|
+
resourceId: row.resourceId
|
|
2770
|
+
};
|
|
2771
|
+
}
|
|
2772
|
+
async updateWorkflowResults({
|
|
2773
|
+
workflowName,
|
|
2774
|
+
runId,
|
|
2775
|
+
stepId,
|
|
2776
|
+
result,
|
|
2777
|
+
runtimeContext
|
|
1781
2778
|
}) {
|
|
1782
|
-
|
|
2779
|
+
const table = getTableName({ indexName: TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName(this.schema) });
|
|
2780
|
+
const transaction = this.pool.transaction();
|
|
2781
|
+
try {
|
|
2782
|
+
await transaction.begin();
|
|
2783
|
+
const selectRequest = new sql3.Request(transaction);
|
|
2784
|
+
selectRequest.input("workflow_name", workflowName);
|
|
2785
|
+
selectRequest.input("run_id", runId);
|
|
2786
|
+
const existingSnapshotResult = await selectRequest.query(
|
|
2787
|
+
`SELECT snapshot FROM ${table} WITH (UPDLOCK, HOLDLOCK) WHERE workflow_name = @workflow_name AND run_id = @run_id`
|
|
2788
|
+
);
|
|
2789
|
+
let snapshot;
|
|
2790
|
+
if (!existingSnapshotResult.recordset || existingSnapshotResult.recordset.length === 0) {
|
|
2791
|
+
snapshot = {
|
|
2792
|
+
context: {},
|
|
2793
|
+
activePaths: [],
|
|
2794
|
+
timestamp: Date.now(),
|
|
2795
|
+
suspendedPaths: {},
|
|
2796
|
+
resumeLabels: {},
|
|
2797
|
+
serializedStepGraph: [],
|
|
2798
|
+
value: {},
|
|
2799
|
+
waitingPaths: {},
|
|
2800
|
+
status: "pending",
|
|
2801
|
+
runId,
|
|
2802
|
+
runtimeContext: {}
|
|
2803
|
+
};
|
|
2804
|
+
} else {
|
|
2805
|
+
const existingSnapshot = existingSnapshotResult.recordset[0].snapshot;
|
|
2806
|
+
snapshot = typeof existingSnapshot === "string" ? JSON.parse(existingSnapshot) : existingSnapshot;
|
|
2807
|
+
}
|
|
2808
|
+
snapshot.context[stepId] = result;
|
|
2809
|
+
snapshot.runtimeContext = { ...snapshot.runtimeContext, ...runtimeContext };
|
|
2810
|
+
const upsertReq = new sql3.Request(transaction);
|
|
2811
|
+
upsertReq.input("workflow_name", workflowName);
|
|
2812
|
+
upsertReq.input("run_id", runId);
|
|
2813
|
+
upsertReq.input("snapshot", JSON.stringify(snapshot));
|
|
2814
|
+
upsertReq.input("createdAt", sql3.DateTime2, /* @__PURE__ */ new Date());
|
|
2815
|
+
upsertReq.input("updatedAt", sql3.DateTime2, /* @__PURE__ */ new Date());
|
|
2816
|
+
await upsertReq.query(
|
|
2817
|
+
`MERGE ${table} AS target
|
|
2818
|
+
USING (SELECT @workflow_name AS workflow_name, @run_id AS run_id) AS src
|
|
2819
|
+
ON target.workflow_name = src.workflow_name AND target.run_id = src.run_id
|
|
2820
|
+
WHEN MATCHED THEN UPDATE SET snapshot = @snapshot, [updatedAt] = @updatedAt
|
|
2821
|
+
WHEN NOT MATCHED THEN INSERT (workflow_name, run_id, snapshot, [createdAt], [updatedAt])
|
|
2822
|
+
VALUES (@workflow_name, @run_id, @snapshot, @createdAt, @updatedAt);`
|
|
2823
|
+
);
|
|
2824
|
+
await transaction.commit();
|
|
2825
|
+
return snapshot.context;
|
|
2826
|
+
} catch (error) {
|
|
2827
|
+
try {
|
|
2828
|
+
await transaction.rollback();
|
|
2829
|
+
} catch {
|
|
2830
|
+
}
|
|
2831
|
+
throw new MastraError(
|
|
2832
|
+
{
|
|
2833
|
+
id: "MASTRA_STORAGE_MSSQL_STORE_UPDATE_WORKFLOW_RESULTS_FAILED",
|
|
2834
|
+
domain: ErrorDomain.STORAGE,
|
|
2835
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2836
|
+
details: {
|
|
2837
|
+
workflowName,
|
|
2838
|
+
runId,
|
|
2839
|
+
stepId
|
|
2840
|
+
}
|
|
2841
|
+
},
|
|
2842
|
+
error
|
|
2843
|
+
);
|
|
2844
|
+
}
|
|
1783
2845
|
}
|
|
1784
|
-
updateWorkflowState({
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
2846
|
+
async updateWorkflowState({
|
|
2847
|
+
workflowName,
|
|
2848
|
+
runId,
|
|
2849
|
+
opts
|
|
1788
2850
|
}) {
|
|
1789
|
-
|
|
2851
|
+
const table = getTableName({ indexName: TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName(this.schema) });
|
|
2852
|
+
const transaction = this.pool.transaction();
|
|
2853
|
+
try {
|
|
2854
|
+
await transaction.begin();
|
|
2855
|
+
const selectRequest = new sql3.Request(transaction);
|
|
2856
|
+
selectRequest.input("workflow_name", workflowName);
|
|
2857
|
+
selectRequest.input("run_id", runId);
|
|
2858
|
+
const existingSnapshotResult = await selectRequest.query(
|
|
2859
|
+
`SELECT snapshot FROM ${table} WITH (UPDLOCK, HOLDLOCK) WHERE workflow_name = @workflow_name AND run_id = @run_id`
|
|
2860
|
+
);
|
|
2861
|
+
if (!existingSnapshotResult.recordset || existingSnapshotResult.recordset.length === 0) {
|
|
2862
|
+
await transaction.rollback();
|
|
2863
|
+
return void 0;
|
|
2864
|
+
}
|
|
2865
|
+
const existingSnapshot = existingSnapshotResult.recordset[0].snapshot;
|
|
2866
|
+
const snapshot = typeof existingSnapshot === "string" ? JSON.parse(existingSnapshot) : existingSnapshot;
|
|
2867
|
+
if (!snapshot || !snapshot?.context) {
|
|
2868
|
+
await transaction.rollback();
|
|
2869
|
+
throw new MastraError(
|
|
2870
|
+
{
|
|
2871
|
+
id: "MASTRA_STORAGE_MSSQL_STORE_UPDATE_WORKFLOW_STATE_SNAPSHOT_NOT_FOUND",
|
|
2872
|
+
domain: ErrorDomain.STORAGE,
|
|
2873
|
+
category: ErrorCategory.SYSTEM,
|
|
2874
|
+
details: {
|
|
2875
|
+
workflowName,
|
|
2876
|
+
runId
|
|
2877
|
+
}
|
|
2878
|
+
},
|
|
2879
|
+
new Error(`Snapshot not found for runId ${runId}`)
|
|
2880
|
+
);
|
|
2881
|
+
}
|
|
2882
|
+
const updatedSnapshot = { ...snapshot, ...opts };
|
|
2883
|
+
const updateRequest = new sql3.Request(transaction);
|
|
2884
|
+
updateRequest.input("snapshot", JSON.stringify(updatedSnapshot));
|
|
2885
|
+
updateRequest.input("workflow_name", workflowName);
|
|
2886
|
+
updateRequest.input("run_id", runId);
|
|
2887
|
+
updateRequest.input("updatedAt", sql3.DateTime2, /* @__PURE__ */ new Date());
|
|
2888
|
+
await updateRequest.query(
|
|
2889
|
+
`UPDATE ${table} SET snapshot = @snapshot, [updatedAt] = @updatedAt WHERE workflow_name = @workflow_name AND run_id = @run_id`
|
|
2890
|
+
);
|
|
2891
|
+
await transaction.commit();
|
|
2892
|
+
return updatedSnapshot;
|
|
2893
|
+
} catch (error) {
|
|
2894
|
+
try {
|
|
2895
|
+
await transaction.rollback();
|
|
2896
|
+
} catch {
|
|
2897
|
+
}
|
|
2898
|
+
throw new MastraError(
|
|
2899
|
+
{
|
|
2900
|
+
id: "MASTRA_STORAGE_MSSQL_STORE_UPDATE_WORKFLOW_STATE_FAILED",
|
|
2901
|
+
domain: ErrorDomain.STORAGE,
|
|
2902
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2903
|
+
details: {
|
|
2904
|
+
workflowName,
|
|
2905
|
+
runId
|
|
2906
|
+
}
|
|
2907
|
+
},
|
|
2908
|
+
error
|
|
2909
|
+
);
|
|
2910
|
+
}
|
|
1790
2911
|
}
|
|
1791
2912
|
async persistWorkflowSnapshot({
|
|
1792
2913
|
workflowName,
|
|
@@ -1802,8 +2923,8 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
|
1802
2923
|
request.input("run_id", runId);
|
|
1803
2924
|
request.input("resourceId", resourceId);
|
|
1804
2925
|
request.input("snapshot", JSON.stringify(snapshot));
|
|
1805
|
-
request.input("createdAt",
|
|
1806
|
-
request.input("updatedAt",
|
|
2926
|
+
request.input("createdAt", sql3.DateTime2, new Date(now));
|
|
2927
|
+
request.input("updatedAt", sql3.DateTime2, new Date(now));
|
|
1807
2928
|
const mergeSql = `MERGE INTO ${table} AS target
|
|
1808
2929
|
USING (SELECT @workflow_name AS workflow_name, @run_id AS run_id) AS src
|
|
1809
2930
|
ON target.workflow_name = src.workflow_name AND target.run_id = src.run_id
|
|
@@ -1884,7 +3005,7 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
|
1884
3005
|
if (!result.recordset || result.recordset.length === 0) {
|
|
1885
3006
|
return null;
|
|
1886
3007
|
}
|
|
1887
|
-
return parseWorkflowRun(result.recordset[0]);
|
|
3008
|
+
return this.parseWorkflowRun(result.recordset[0]);
|
|
1888
3009
|
} catch (error) {
|
|
1889
3010
|
throw new MastraError(
|
|
1890
3011
|
{
|
|
@@ -1921,7 +3042,7 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
|
1921
3042
|
conditions.push(`[resourceId] = @resourceId`);
|
|
1922
3043
|
paramMap["resourceId"] = resourceId;
|
|
1923
3044
|
} else {
|
|
1924
|
-
|
|
3045
|
+
this.logger?.warn?.(`[${TABLE_WORKFLOW_SNAPSHOT}] resourceId column not found. Skipping resourceId filter.`);
|
|
1925
3046
|
}
|
|
1926
3047
|
}
|
|
1927
3048
|
if (fromDate instanceof Date && !isNaN(fromDate.getTime())) {
|
|
@@ -1938,7 +3059,7 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
|
1938
3059
|
const request = this.pool.request();
|
|
1939
3060
|
Object.entries(paramMap).forEach(([key, value]) => {
|
|
1940
3061
|
if (value instanceof Date) {
|
|
1941
|
-
request.input(key,
|
|
3062
|
+
request.input(key, sql3.DateTime, value);
|
|
1942
3063
|
} else {
|
|
1943
3064
|
request.input(key, value);
|
|
1944
3065
|
}
|
|
@@ -1955,7 +3076,7 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
|
1955
3076
|
request.input("offset", offset);
|
|
1956
3077
|
}
|
|
1957
3078
|
const result = await request.query(query);
|
|
1958
|
-
const runs = (result.recordset || []).map((row) => parseWorkflowRun(row));
|
|
3079
|
+
const runs = (result.recordset || []).map((row) => this.parseWorkflowRun(row));
|
|
1959
3080
|
return { runs, total: total || runs.length };
|
|
1960
3081
|
} catch (error) {
|
|
1961
3082
|
throw new MastraError(
|
|
@@ -1995,7 +3116,7 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
1995
3116
|
}
|
|
1996
3117
|
}
|
|
1997
3118
|
this.schema = config.schemaName || "dbo";
|
|
1998
|
-
this.pool = "connectionString" in config ? new
|
|
3119
|
+
this.pool = "connectionString" in config ? new sql3.ConnectionPool(config.connectionString) : new sql3.ConnectionPool({
|
|
1999
3120
|
server: config.server,
|
|
2000
3121
|
database: config.database,
|
|
2001
3122
|
user: config.user,
|
|
@@ -2008,12 +3129,14 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2008
3129
|
const scores = new ScoresMSSQL({ pool: this.pool, operations, schema: this.schema });
|
|
2009
3130
|
const workflows = new WorkflowsMSSQL({ pool: this.pool, operations, schema: this.schema });
|
|
2010
3131
|
const memory = new MemoryMSSQL({ pool: this.pool, schema: this.schema, operations });
|
|
3132
|
+
const observability = new ObservabilityMSSQL({ pool: this.pool, operations, schema: this.schema });
|
|
2011
3133
|
this.stores = {
|
|
2012
3134
|
operations,
|
|
2013
3135
|
scores,
|
|
2014
3136
|
workflows,
|
|
2015
3137
|
legacyEvals,
|
|
2016
|
-
memory
|
|
3138
|
+
memory,
|
|
3139
|
+
observability
|
|
2017
3140
|
};
|
|
2018
3141
|
} catch (e) {
|
|
2019
3142
|
throw new MastraError(
|
|
@@ -2033,6 +3156,11 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2033
3156
|
try {
|
|
2034
3157
|
await this.isConnected;
|
|
2035
3158
|
await super.init();
|
|
3159
|
+
try {
|
|
3160
|
+
await this.stores.operations.createAutomaticIndexes();
|
|
3161
|
+
} catch (indexError) {
|
|
3162
|
+
this.logger?.warn?.("Failed to create indexes:", indexError);
|
|
3163
|
+
}
|
|
2036
3164
|
} catch (error) {
|
|
2037
3165
|
this.isConnected = null;
|
|
2038
3166
|
throw new MastraError(
|
|
@@ -2060,7 +3188,9 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2060
3188
|
hasColumn: true,
|
|
2061
3189
|
createTable: true,
|
|
2062
3190
|
deleteMessages: true,
|
|
2063
|
-
getScoresBySpan: true
|
|
3191
|
+
getScoresBySpan: true,
|
|
3192
|
+
aiTracing: true,
|
|
3193
|
+
indexManagement: true
|
|
2064
3194
|
};
|
|
2065
3195
|
}
|
|
2066
3196
|
/** @deprecated use getEvals instead */
|
|
@@ -2214,6 +3344,60 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2214
3344
|
async close() {
|
|
2215
3345
|
await this.pool.close();
|
|
2216
3346
|
}
|
|
3347
|
+
/**
|
|
3348
|
+
* Index Management
|
|
3349
|
+
*/
|
|
3350
|
+
async createIndex(options) {
|
|
3351
|
+
return this.stores.operations.createIndex(options);
|
|
3352
|
+
}
|
|
3353
|
+
async listIndexes(tableName) {
|
|
3354
|
+
return this.stores.operations.listIndexes(tableName);
|
|
3355
|
+
}
|
|
3356
|
+
async describeIndex(indexName) {
|
|
3357
|
+
return this.stores.operations.describeIndex(indexName);
|
|
3358
|
+
}
|
|
3359
|
+
async dropIndex(indexName) {
|
|
3360
|
+
return this.stores.operations.dropIndex(indexName);
|
|
3361
|
+
}
|
|
3362
|
+
/**
|
|
3363
|
+
* AI Tracing / Observability
|
|
3364
|
+
*/
|
|
3365
|
+
getObservabilityStore() {
|
|
3366
|
+
if (!this.stores.observability) {
|
|
3367
|
+
throw new MastraError({
|
|
3368
|
+
id: "MSSQL_STORE_OBSERVABILITY_NOT_INITIALIZED",
|
|
3369
|
+
domain: ErrorDomain.STORAGE,
|
|
3370
|
+
category: ErrorCategory.SYSTEM,
|
|
3371
|
+
text: "Observability storage is not initialized"
|
|
3372
|
+
});
|
|
3373
|
+
}
|
|
3374
|
+
return this.stores.observability;
|
|
3375
|
+
}
|
|
3376
|
+
async createAISpan(span) {
|
|
3377
|
+
return this.getObservabilityStore().createAISpan(span);
|
|
3378
|
+
}
|
|
3379
|
+
async updateAISpan({
|
|
3380
|
+
spanId,
|
|
3381
|
+
traceId,
|
|
3382
|
+
updates
|
|
3383
|
+
}) {
|
|
3384
|
+
return this.getObservabilityStore().updateAISpan({ spanId, traceId, updates });
|
|
3385
|
+
}
|
|
3386
|
+
async getAITrace(traceId) {
|
|
3387
|
+
return this.getObservabilityStore().getAITrace(traceId);
|
|
3388
|
+
}
|
|
3389
|
+
async getAITracesPaginated(args) {
|
|
3390
|
+
return this.getObservabilityStore().getAITracesPaginated(args);
|
|
3391
|
+
}
|
|
3392
|
+
async batchCreateAISpans(args) {
|
|
3393
|
+
return this.getObservabilityStore().batchCreateAISpans(args);
|
|
3394
|
+
}
|
|
3395
|
+
async batchUpdateAISpans(args) {
|
|
3396
|
+
return this.getObservabilityStore().batchUpdateAISpans(args);
|
|
3397
|
+
}
|
|
3398
|
+
async batchDeleteAITraces(args) {
|
|
3399
|
+
return this.getObservabilityStore().batchDeleteAITraces(args);
|
|
3400
|
+
}
|
|
2217
3401
|
/**
|
|
2218
3402
|
* Scorers
|
|
2219
3403
|
*/
|
|
@@ -2222,9 +3406,18 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2222
3406
|
}
|
|
2223
3407
|
async getScoresByScorerId({
|
|
2224
3408
|
scorerId: _scorerId,
|
|
2225
|
-
pagination: _pagination
|
|
3409
|
+
pagination: _pagination,
|
|
3410
|
+
entityId: _entityId,
|
|
3411
|
+
entityType: _entityType,
|
|
3412
|
+
source: _source
|
|
2226
3413
|
}) {
|
|
2227
|
-
return this.stores.scores.getScoresByScorerId({
|
|
3414
|
+
return this.stores.scores.getScoresByScorerId({
|
|
3415
|
+
scorerId: _scorerId,
|
|
3416
|
+
pagination: _pagination,
|
|
3417
|
+
entityId: _entityId,
|
|
3418
|
+
entityType: _entityType,
|
|
3419
|
+
source: _source
|
|
3420
|
+
});
|
|
2228
3421
|
}
|
|
2229
3422
|
async saveScore(_score) {
|
|
2230
3423
|
return this.stores.scores.saveScore(_score);
|