@mastra/mssql 0.0.0-fix-writer-workflow-resumestream-20251022161252 → 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 +32 -3
- package/README.md +315 -36
- package/dist/index.cjs +1326 -314
- 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 +1327 -315
- 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 -14
- package/dist/storage/index.d.ts.map +1 -1
- package/package.json +6 -6
- package/dist/storage/domains/traces/index.d.ts +0 -37
- package/dist/storage/domains/traces/index.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
|
|
2
|
-
import { MastraStorage, LegacyEvalsStorage, StoreOperations, TABLE_WORKFLOW_SNAPSHOT,
|
|
3
|
-
import
|
|
4
|
-
import { parseSqlIdentifier
|
|
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
|
+
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
|
}
|
|
@@ -194,7 +258,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
194
258
|
}
|
|
195
259
|
async getThreadById({ threadId }) {
|
|
196
260
|
try {
|
|
197
|
-
const
|
|
261
|
+
const sql6 = `SELECT
|
|
198
262
|
id,
|
|
199
263
|
[resourceId],
|
|
200
264
|
title,
|
|
@@ -205,7 +269,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
205
269
|
WHERE id = @threadId`;
|
|
206
270
|
const request = this.pool.request();
|
|
207
271
|
request.input("threadId", threadId);
|
|
208
|
-
const resultSet = await request.query(
|
|
272
|
+
const resultSet = await request.query(sql6);
|
|
209
273
|
const thread = resultSet.recordset[0] || null;
|
|
210
274
|
if (!thread) {
|
|
211
275
|
return null;
|
|
@@ -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);
|
|
@@ -378,7 +449,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
378
449
|
};
|
|
379
450
|
try {
|
|
380
451
|
const table = getTableName({ indexName: TABLE_THREADS, schemaName: getSchemaName(this.schema) });
|
|
381
|
-
const
|
|
452
|
+
const sql6 = `UPDATE ${table}
|
|
382
453
|
SET title = @title,
|
|
383
454
|
metadata = @metadata,
|
|
384
455
|
[updatedAt] = @updatedAt
|
|
@@ -389,7 +460,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
389
460
|
req.input("title", title);
|
|
390
461
|
req.input("metadata", JSON.stringify(mergedMetadata));
|
|
391
462
|
req.input("updatedAt", /* @__PURE__ */ new Date());
|
|
392
|
-
const result = await req.query(
|
|
463
|
+
const result = await req.query(sql6);
|
|
393
464
|
let thread = result.recordset && result.recordset[0];
|
|
394
465
|
if (thread && "seq_id" in thread) {
|
|
395
466
|
const { seq_id, ...rest } = thread;
|
|
@@ -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,13 +1765,17 @@ ${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
|
|
1341
|
-
const sql7 = `SELECT * FROM ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} WHERE ${conditions}`;
|
|
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
|
-
const resultSet = await request.query(
|
|
1778
|
+
const resultSet = await request.query(sql6);
|
|
1347
1779
|
const result = resultSet.recordset[0] || null;
|
|
1348
1780
|
if (!result) {
|
|
1349
1781
|
return 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),
|
|
@@ -1740,7 +2737,7 @@ var ScoresMSSQL = class extends ScoresStorage {
|
|
|
1740
2737
|
}
|
|
1741
2738
|
}
|
|
1742
2739
|
};
|
|
1743
|
-
var
|
|
2740
|
+
var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
1744
2741
|
pool;
|
|
1745
2742
|
operations;
|
|
1746
2743
|
schema;
|
|
@@ -1754,207 +2751,164 @@ var TracesMSSQL = class extends TracesStorage {
|
|
|
1754
2751
|
this.operations = operations;
|
|
1755
2752
|
this.schema = schema;
|
|
1756
2753
|
}
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
if (
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
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
|
+
}
|
|
1764
2762
|
}
|
|
1765
|
-
|
|
1766
|
-
|
|
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
|
+
};
|
|
1767
2771
|
}
|
|
1768
|
-
async
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
const
|
|
1776
|
-
|
|
1777
|
-
if (name) {
|
|
1778
|
-
const paramName = `p${paramIndex++}`;
|
|
1779
|
-
conditions.push(`[name] LIKE @${paramName}`);
|
|
1780
|
-
paramMap[paramName] = `${name}%`;
|
|
1781
|
-
}
|
|
1782
|
-
if (scope) {
|
|
1783
|
-
const paramName = `p${paramIndex++}`;
|
|
1784
|
-
conditions.push(`[scope] = @${paramName}`);
|
|
1785
|
-
paramMap[paramName] = scope;
|
|
1786
|
-
}
|
|
1787
|
-
if (attributes) {
|
|
1788
|
-
Object.entries(attributes).forEach(([key, value]) => {
|
|
1789
|
-
const parsedKey = parseFieldKey(key);
|
|
1790
|
-
const paramName = `p${paramIndex++}`;
|
|
1791
|
-
conditions.push(`JSON_VALUE([attributes], '$.${parsedKey}') = @${paramName}`);
|
|
1792
|
-
paramMap[paramName] = value;
|
|
1793
|
-
});
|
|
1794
|
-
}
|
|
1795
|
-
if (filters) {
|
|
1796
|
-
Object.entries(filters).forEach(([key, value]) => {
|
|
1797
|
-
const parsedKey = parseFieldKey(key);
|
|
1798
|
-
const paramName = `p${paramIndex++}`;
|
|
1799
|
-
conditions.push(`[${parsedKey}] = @${paramName}`);
|
|
1800
|
-
paramMap[paramName] = value;
|
|
1801
|
-
});
|
|
1802
|
-
}
|
|
1803
|
-
if (fromDate instanceof Date && !isNaN(fromDate.getTime())) {
|
|
1804
|
-
const paramName = `p${paramIndex++}`;
|
|
1805
|
-
conditions.push(`[createdAt] >= @${paramName}`);
|
|
1806
|
-
paramMap[paramName] = fromDate.toISOString();
|
|
1807
|
-
}
|
|
1808
|
-
if (toDate instanceof Date && !isNaN(toDate.getTime())) {
|
|
1809
|
-
const paramName = `p${paramIndex++}`;
|
|
1810
|
-
conditions.push(`[createdAt] <= @${paramName}`);
|
|
1811
|
-
paramMap[paramName] = toDate.toISOString();
|
|
1812
|
-
}
|
|
1813
|
-
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
1814
|
-
const countQuery = `SELECT COUNT(*) as total FROM ${getTableName({ indexName: TABLE_TRACES, schemaName: getSchemaName(this.schema) })} ${whereClause}`;
|
|
1815
|
-
let total = 0;
|
|
2772
|
+
async updateWorkflowResults({
|
|
2773
|
+
workflowName,
|
|
2774
|
+
runId,
|
|
2775
|
+
stepId,
|
|
2776
|
+
result,
|
|
2777
|
+
runtimeContext
|
|
2778
|
+
}) {
|
|
2779
|
+
const table = getTableName({ indexName: TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName(this.schema) });
|
|
2780
|
+
const transaction = this.pool.transaction();
|
|
1816
2781
|
try {
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
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;
|
|
1827
2826
|
} catch (error) {
|
|
2827
|
+
try {
|
|
2828
|
+
await transaction.rollback();
|
|
2829
|
+
} catch {
|
|
2830
|
+
}
|
|
1828
2831
|
throw new MastraError(
|
|
1829
2832
|
{
|
|
1830
|
-
id: "
|
|
2833
|
+
id: "MASTRA_STORAGE_MSSQL_STORE_UPDATE_WORKFLOW_RESULTS_FAILED",
|
|
1831
2834
|
domain: ErrorDomain.STORAGE,
|
|
1832
2835
|
category: ErrorCategory.THIRD_PARTY,
|
|
1833
2836
|
details: {
|
|
1834
|
-
|
|
1835
|
-
|
|
2837
|
+
workflowName,
|
|
2838
|
+
runId,
|
|
2839
|
+
stepId
|
|
1836
2840
|
}
|
|
1837
2841
|
},
|
|
1838
2842
|
error
|
|
1839
2843
|
);
|
|
1840
2844
|
}
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
}
|
|
1850
|
-
const dataQuery = `SELECT * FROM ${getTableName({ indexName: TABLE_TRACES, schemaName: getSchemaName(this.schema) })} ${whereClause} ORDER BY [seq_id] DESC OFFSET @offset ROWS FETCH NEXT @limit ROWS ONLY`;
|
|
1851
|
-
const dataRequest = this.pool.request();
|
|
1852
|
-
Object.entries(paramMap).forEach(([key, value]) => {
|
|
1853
|
-
if (value instanceof Date) {
|
|
1854
|
-
dataRequest.input(key, sql2.DateTime, value);
|
|
1855
|
-
} else {
|
|
1856
|
-
dataRequest.input(key, value);
|
|
1857
|
-
}
|
|
1858
|
-
});
|
|
1859
|
-
dataRequest.input("offset", currentOffset);
|
|
1860
|
-
dataRequest.input("limit", perPage);
|
|
2845
|
+
}
|
|
2846
|
+
async updateWorkflowState({
|
|
2847
|
+
workflowName,
|
|
2848
|
+
runId,
|
|
2849
|
+
opts
|
|
2850
|
+
}) {
|
|
2851
|
+
const table = getTableName({ indexName: TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName(this.schema) });
|
|
2852
|
+
const transaction = this.pool.transaction();
|
|
1861
2853
|
try {
|
|
1862
|
-
|
|
1863
|
-
const
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
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;
|
|
1887
2893
|
} catch (error) {
|
|
2894
|
+
try {
|
|
2895
|
+
await transaction.rollback();
|
|
2896
|
+
} catch {
|
|
2897
|
+
}
|
|
1888
2898
|
throw new MastraError(
|
|
1889
2899
|
{
|
|
1890
|
-
id: "
|
|
2900
|
+
id: "MASTRA_STORAGE_MSSQL_STORE_UPDATE_WORKFLOW_STATE_FAILED",
|
|
1891
2901
|
domain: ErrorDomain.STORAGE,
|
|
1892
2902
|
category: ErrorCategory.THIRD_PARTY,
|
|
1893
2903
|
details: {
|
|
1894
|
-
|
|
1895
|
-
|
|
2904
|
+
workflowName,
|
|
2905
|
+
runId
|
|
1896
2906
|
}
|
|
1897
2907
|
},
|
|
1898
2908
|
error
|
|
1899
2909
|
);
|
|
1900
2910
|
}
|
|
1901
2911
|
}
|
|
1902
|
-
async batchTraceInsert({ records }) {
|
|
1903
|
-
this.logger.debug("Batch inserting traces", { count: records.length });
|
|
1904
|
-
await this.operations.batchInsert({
|
|
1905
|
-
tableName: TABLE_TRACES,
|
|
1906
|
-
records
|
|
1907
|
-
});
|
|
1908
|
-
}
|
|
1909
|
-
};
|
|
1910
|
-
function parseWorkflowRun(row) {
|
|
1911
|
-
let parsedSnapshot = row.snapshot;
|
|
1912
|
-
if (typeof parsedSnapshot === "string") {
|
|
1913
|
-
try {
|
|
1914
|
-
parsedSnapshot = JSON.parse(row.snapshot);
|
|
1915
|
-
} catch (e) {
|
|
1916
|
-
console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
|
|
1917
|
-
}
|
|
1918
|
-
}
|
|
1919
|
-
return {
|
|
1920
|
-
workflowName: row.workflow_name,
|
|
1921
|
-
runId: row.run_id,
|
|
1922
|
-
snapshot: parsedSnapshot,
|
|
1923
|
-
createdAt: row.createdAt,
|
|
1924
|
-
updatedAt: row.updatedAt,
|
|
1925
|
-
resourceId: row.resourceId
|
|
1926
|
-
};
|
|
1927
|
-
}
|
|
1928
|
-
var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
1929
|
-
pool;
|
|
1930
|
-
operations;
|
|
1931
|
-
schema;
|
|
1932
|
-
constructor({
|
|
1933
|
-
pool,
|
|
1934
|
-
operations,
|
|
1935
|
-
schema
|
|
1936
|
-
}) {
|
|
1937
|
-
super();
|
|
1938
|
-
this.pool = pool;
|
|
1939
|
-
this.operations = operations;
|
|
1940
|
-
this.schema = schema;
|
|
1941
|
-
}
|
|
1942
|
-
updateWorkflowResults({
|
|
1943
|
-
// workflowName,
|
|
1944
|
-
// runId,
|
|
1945
|
-
// stepId,
|
|
1946
|
-
// result,
|
|
1947
|
-
// runtimeContext,
|
|
1948
|
-
}) {
|
|
1949
|
-
throw new Error("Method not implemented.");
|
|
1950
|
-
}
|
|
1951
|
-
updateWorkflowState({
|
|
1952
|
-
// workflowName,
|
|
1953
|
-
// runId,
|
|
1954
|
-
// opts,
|
|
1955
|
-
}) {
|
|
1956
|
-
throw new Error("Method not implemented.");
|
|
1957
|
-
}
|
|
1958
2912
|
async persistWorkflowSnapshot({
|
|
1959
2913
|
workflowName,
|
|
1960
2914
|
runId,
|
|
@@ -1969,8 +2923,8 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
|
1969
2923
|
request.input("run_id", runId);
|
|
1970
2924
|
request.input("resourceId", resourceId);
|
|
1971
2925
|
request.input("snapshot", JSON.stringify(snapshot));
|
|
1972
|
-
request.input("createdAt",
|
|
1973
|
-
request.input("updatedAt",
|
|
2926
|
+
request.input("createdAt", sql3.DateTime2, new Date(now));
|
|
2927
|
+
request.input("updatedAt", sql3.DateTime2, new Date(now));
|
|
1974
2928
|
const mergeSql = `MERGE INTO ${table} AS target
|
|
1975
2929
|
USING (SELECT @workflow_name AS workflow_name, @run_id AS run_id) AS src
|
|
1976
2930
|
ON target.workflow_name = src.workflow_name AND target.run_id = src.run_id
|
|
@@ -2051,7 +3005,7 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
|
2051
3005
|
if (!result.recordset || result.recordset.length === 0) {
|
|
2052
3006
|
return null;
|
|
2053
3007
|
}
|
|
2054
|
-
return parseWorkflowRun(result.recordset[0]);
|
|
3008
|
+
return this.parseWorkflowRun(result.recordset[0]);
|
|
2055
3009
|
} catch (error) {
|
|
2056
3010
|
throw new MastraError(
|
|
2057
3011
|
{
|
|
@@ -2088,7 +3042,7 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
|
2088
3042
|
conditions.push(`[resourceId] = @resourceId`);
|
|
2089
3043
|
paramMap["resourceId"] = resourceId;
|
|
2090
3044
|
} else {
|
|
2091
|
-
|
|
3045
|
+
this.logger?.warn?.(`[${TABLE_WORKFLOW_SNAPSHOT}] resourceId column not found. Skipping resourceId filter.`);
|
|
2092
3046
|
}
|
|
2093
3047
|
}
|
|
2094
3048
|
if (fromDate instanceof Date && !isNaN(fromDate.getTime())) {
|
|
@@ -2105,7 +3059,7 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
|
2105
3059
|
const request = this.pool.request();
|
|
2106
3060
|
Object.entries(paramMap).forEach(([key, value]) => {
|
|
2107
3061
|
if (value instanceof Date) {
|
|
2108
|
-
request.input(key,
|
|
3062
|
+
request.input(key, sql3.DateTime, value);
|
|
2109
3063
|
} else {
|
|
2110
3064
|
request.input(key, value);
|
|
2111
3065
|
}
|
|
@@ -2122,7 +3076,7 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
|
2122
3076
|
request.input("offset", offset);
|
|
2123
3077
|
}
|
|
2124
3078
|
const result = await request.query(query);
|
|
2125
|
-
const runs = (result.recordset || []).map((row) => parseWorkflowRun(row));
|
|
3079
|
+
const runs = (result.recordset || []).map((row) => this.parseWorkflowRun(row));
|
|
2126
3080
|
return { runs, total: total || runs.length };
|
|
2127
3081
|
} catch (error) {
|
|
2128
3082
|
throw new MastraError(
|
|
@@ -2162,7 +3116,7 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2162
3116
|
}
|
|
2163
3117
|
}
|
|
2164
3118
|
this.schema = config.schemaName || "dbo";
|
|
2165
|
-
this.pool = "connectionString" in config ? new
|
|
3119
|
+
this.pool = "connectionString" in config ? new sql3.ConnectionPool(config.connectionString) : new sql3.ConnectionPool({
|
|
2166
3120
|
server: config.server,
|
|
2167
3121
|
database: config.database,
|
|
2168
3122
|
user: config.user,
|
|
@@ -2173,16 +3127,16 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2173
3127
|
const legacyEvals = new LegacyEvalsMSSQL({ pool: this.pool, schema: this.schema });
|
|
2174
3128
|
const operations = new StoreOperationsMSSQL({ pool: this.pool, schemaName: this.schema });
|
|
2175
3129
|
const scores = new ScoresMSSQL({ pool: this.pool, operations, schema: this.schema });
|
|
2176
|
-
const traces = new TracesMSSQL({ pool: this.pool, operations, schema: this.schema });
|
|
2177
3130
|
const workflows = new WorkflowsMSSQL({ pool: this.pool, operations, schema: this.schema });
|
|
2178
3131
|
const memory = new MemoryMSSQL({ pool: this.pool, schema: this.schema, operations });
|
|
3132
|
+
const observability = new ObservabilityMSSQL({ pool: this.pool, operations, schema: this.schema });
|
|
2179
3133
|
this.stores = {
|
|
2180
3134
|
operations,
|
|
2181
3135
|
scores,
|
|
2182
|
-
traces,
|
|
2183
3136
|
workflows,
|
|
2184
3137
|
legacyEvals,
|
|
2185
|
-
memory
|
|
3138
|
+
memory,
|
|
3139
|
+
observability
|
|
2186
3140
|
};
|
|
2187
3141
|
} catch (e) {
|
|
2188
3142
|
throw new MastraError(
|
|
@@ -2202,6 +3156,11 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2202
3156
|
try {
|
|
2203
3157
|
await this.isConnected;
|
|
2204
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
|
+
}
|
|
2205
3164
|
} catch (error) {
|
|
2206
3165
|
this.isConnected = null;
|
|
2207
3166
|
throw new MastraError(
|
|
@@ -2229,7 +3188,9 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2229
3188
|
hasColumn: true,
|
|
2230
3189
|
createTable: true,
|
|
2231
3190
|
deleteMessages: true,
|
|
2232
|
-
getScoresBySpan: true
|
|
3191
|
+
getScoresBySpan: true,
|
|
3192
|
+
aiTracing: true,
|
|
3193
|
+
indexManagement: true
|
|
2233
3194
|
};
|
|
2234
3195
|
}
|
|
2235
3196
|
/** @deprecated use getEvals instead */
|
|
@@ -2239,18 +3200,6 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2239
3200
|
async getEvals(options = {}) {
|
|
2240
3201
|
return this.stores.legacyEvals.getEvals(options);
|
|
2241
3202
|
}
|
|
2242
|
-
/**
|
|
2243
|
-
* @deprecated use getTracesPaginated instead
|
|
2244
|
-
*/
|
|
2245
|
-
async getTraces(args) {
|
|
2246
|
-
return this.stores.traces.getTraces(args);
|
|
2247
|
-
}
|
|
2248
|
-
async getTracesPaginated(args) {
|
|
2249
|
-
return this.stores.traces.getTracesPaginated(args);
|
|
2250
|
-
}
|
|
2251
|
-
async batchTraceInsert({ records }) {
|
|
2252
|
-
return this.stores.traces.batchTraceInsert({ records });
|
|
2253
|
-
}
|
|
2254
3203
|
async createTable({
|
|
2255
3204
|
tableName,
|
|
2256
3205
|
schema
|
|
@@ -2395,6 +3344,60 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2395
3344
|
async close() {
|
|
2396
3345
|
await this.pool.close();
|
|
2397
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
|
+
}
|
|
2398
3401
|
/**
|
|
2399
3402
|
* Scorers
|
|
2400
3403
|
*/
|
|
@@ -2403,9 +3406,18 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2403
3406
|
}
|
|
2404
3407
|
async getScoresByScorerId({
|
|
2405
3408
|
scorerId: _scorerId,
|
|
2406
|
-
pagination: _pagination
|
|
3409
|
+
pagination: _pagination,
|
|
3410
|
+
entityId: _entityId,
|
|
3411
|
+
entityType: _entityType,
|
|
3412
|
+
source: _source
|
|
2407
3413
|
}) {
|
|
2408
|
-
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
|
+
});
|
|
2409
3421
|
}
|
|
2410
3422
|
async saveScore(_score) {
|
|
2411
3423
|
return this.stores.scores.saveScore(_score);
|