@mastra/mssql 0.0.0-feat-support-ai-sdk-5-again-20250813225910 → 0.0.0-feat-add-query-option-to-playground-20251209160219
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 +1004 -3
- package/README.md +324 -37
- package/dist/index.cjs +1847 -756
- 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 +1848 -757
- package/dist/index.js.map +1 -1
- package/dist/storage/domains/memory/index.d.ts +15 -36
- 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 +15 -6
- 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 +28 -10
- package/dist/storage/domains/workflows/index.d.ts.map +1 -1
- package/dist/storage/index.d.ts +116 -74
- package/dist/storage/index.d.ts.map +1 -1
- package/package.json +30 -12
- package/dist/storage/domains/legacy-evals/index.d.ts +0 -20
- package/dist/storage/domains/legacy-evals/index.d.ts.map +0 -1
- package/dist/storage/domains/traces/index.d.ts +0 -37
- package/dist/storage/domains/traces/index.d.ts.map +0 -1
- package/docker-compose.yaml +0 -14
- package/eslint.config.js +0 -6
- package/src/index.ts +0 -2
- package/src/storage/domains/legacy-evals/index.ts +0 -175
- package/src/storage/domains/memory/index.ts +0 -1024
- package/src/storage/domains/operations/index.ts +0 -401
- package/src/storage/domains/scores/index.ts +0 -316
- package/src/storage/domains/traces/index.ts +0 -212
- package/src/storage/domains/utils.ts +0 -12
- package/src/storage/domains/workflows/index.ts +0 -259
- package/src/storage/index.test.ts +0 -2228
- package/src/storage/index.ts +0 -448
- package/tsconfig.build.json +0 -9
- package/tsconfig.json +0 -5
- package/tsup.config.ts +0 -17
- package/vitest.config.ts +0 -12
package/dist/index.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
|
|
2
|
-
import { MastraStorage,
|
|
3
|
-
import
|
|
4
|
-
import { parseSqlIdentifier, parseFieldKey } from '@mastra/core/utils';
|
|
2
|
+
import { MastraStorage, createStorageErrorId, StoreOperations, TABLE_WORKFLOW_SNAPSHOT, TABLE_SCHEMAS, TABLE_THREADS, TABLE_MESSAGES, TABLE_TRACES, TABLE_SCORERS, TABLE_SPANS, ScoresStorage, normalizePerPage, calculatePagination, WorkflowsStorage, MemoryStorage, TABLE_RESOURCES, ObservabilityStorage, transformScoreRow as transformScoreRow$1 } from '@mastra/core/storage';
|
|
3
|
+
import sql3 from 'mssql';
|
|
5
4
|
import { MessageList } from '@mastra/core/agent';
|
|
5
|
+
import { parseSqlIdentifier } from '@mastra/core/utils';
|
|
6
|
+
import { randomUUID } from 'crypto';
|
|
7
|
+
import { saveScorePayloadSchema } from '@mastra/core/evals';
|
|
6
8
|
|
|
7
9
|
// src/storage/index.ts
|
|
8
10
|
function getSchemaName(schema) {
|
|
@@ -14,154 +16,109 @@ function getTableName({ indexName, schemaName }) {
|
|
|
14
16
|
const quotedSchemaName = schemaName;
|
|
15
17
|
return quotedSchemaName ? `${quotedSchemaName}.${quotedIndexName}` : quotedIndexName;
|
|
16
18
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
if (row.test_info) {
|
|
22
|
-
try {
|
|
23
|
-
testInfoValue = typeof row.test_info === "string" ? JSON.parse(row.test_info) : row.test_info;
|
|
24
|
-
} catch {
|
|
25
|
-
}
|
|
19
|
+
function buildDateRangeFilter(dateRange, fieldName) {
|
|
20
|
+
const filters = {};
|
|
21
|
+
if (dateRange?.start) {
|
|
22
|
+
filters[`${fieldName}_gte`] = dateRange.start;
|
|
26
23
|
}
|
|
27
|
-
if (
|
|
28
|
-
|
|
29
|
-
resultValue = typeof row.result === "string" ? JSON.parse(row.result) : row.result;
|
|
30
|
-
} catch {
|
|
31
|
-
}
|
|
24
|
+
if (dateRange?.end) {
|
|
25
|
+
filters[`${fieldName}_lte`] = dateRange.end;
|
|
32
26
|
}
|
|
33
|
-
return
|
|
34
|
-
agentName: row.agent_name,
|
|
35
|
-
input: row.input,
|
|
36
|
-
output: row.output,
|
|
37
|
-
result: resultValue,
|
|
38
|
-
metricName: row.metric_name,
|
|
39
|
-
instructions: row.instructions,
|
|
40
|
-
testInfo: testInfoValue,
|
|
41
|
-
globalRunId: row.global_run_id,
|
|
42
|
-
runId: row.run_id,
|
|
43
|
-
createdAt: row.created_at
|
|
44
|
-
};
|
|
27
|
+
return filters;
|
|
45
28
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const where = [];
|
|
82
|
-
const params = {};
|
|
83
|
-
if (agentName) {
|
|
84
|
-
where.push("agent_name = @agentName");
|
|
85
|
-
params["agentName"] = agentName;
|
|
86
|
-
}
|
|
87
|
-
if (type === "test") {
|
|
88
|
-
where.push("test_info IS NOT NULL AND JSON_VALUE(test_info, '$.testPath') IS NOT NULL");
|
|
89
|
-
} else if (type === "live") {
|
|
90
|
-
where.push("(test_info IS NULL OR JSON_VALUE(test_info, '$.testPath') IS NULL)");
|
|
91
|
-
}
|
|
92
|
-
if (fromDate instanceof Date && !isNaN(fromDate.getTime())) {
|
|
93
|
-
where.push(`[created_at] >= @fromDate`);
|
|
94
|
-
params[`fromDate`] = fromDate.toISOString();
|
|
95
|
-
}
|
|
96
|
-
if (toDate instanceof Date && !isNaN(toDate.getTime())) {
|
|
97
|
-
where.push(`[created_at] <= @toDate`);
|
|
98
|
-
params[`toDate`] = toDate.toISOString();
|
|
99
|
-
}
|
|
100
|
-
const whereClause = where.length > 0 ? `WHERE ${where.join(" AND ")}` : "";
|
|
101
|
-
const tableName = getTableName({ indexName: TABLE_EVALS, schemaName: getSchemaName(this.schema) });
|
|
102
|
-
const offset = page * perPage;
|
|
103
|
-
const countQuery = `SELECT COUNT(*) as total FROM ${tableName} ${whereClause}`;
|
|
104
|
-
const dataQuery = `SELECT * FROM ${tableName} ${whereClause} ORDER BY seq_id DESC OFFSET @offset ROWS FETCH NEXT @perPage ROWS ONLY`;
|
|
105
|
-
try {
|
|
106
|
-
const countReq = this.pool.request();
|
|
107
|
-
Object.entries(params).forEach(([key, value]) => {
|
|
108
|
-
if (value instanceof Date) {
|
|
109
|
-
countReq.input(key, sql2.DateTime, value);
|
|
110
|
-
} else {
|
|
111
|
-
countReq.input(key, value);
|
|
29
|
+
function isInOperator(value) {
|
|
30
|
+
return typeof value === "object" && value !== null && "$in" in value && Array.isArray(value.$in);
|
|
31
|
+
}
|
|
32
|
+
function prepareWhereClause(filters, _schema) {
|
|
33
|
+
const conditions = [];
|
|
34
|
+
const params = {};
|
|
35
|
+
let paramIndex = 1;
|
|
36
|
+
Object.entries(filters).forEach(([key, value]) => {
|
|
37
|
+
if (value === void 0) return;
|
|
38
|
+
if (key.endsWith("_gte")) {
|
|
39
|
+
const paramName = `p${paramIndex++}`;
|
|
40
|
+
const fieldName = key.slice(0, -4);
|
|
41
|
+
conditions.push(`[${parseSqlIdentifier(fieldName, "field name")}] >= @${paramName}`);
|
|
42
|
+
params[paramName] = value instanceof Date ? value.toISOString() : value;
|
|
43
|
+
} else if (key.endsWith("_lte")) {
|
|
44
|
+
const paramName = `p${paramIndex++}`;
|
|
45
|
+
const fieldName = key.slice(0, -4);
|
|
46
|
+
conditions.push(`[${parseSqlIdentifier(fieldName, "field name")}] <= @${paramName}`);
|
|
47
|
+
params[paramName] = value instanceof Date ? value.toISOString() : value;
|
|
48
|
+
} else if (value === null) {
|
|
49
|
+
conditions.push(`[${parseSqlIdentifier(key, "field name")}] IS NULL`);
|
|
50
|
+
} else if (isInOperator(value)) {
|
|
51
|
+
const inValues = value.$in;
|
|
52
|
+
if (inValues.length === 0) {
|
|
53
|
+
conditions.push("1 = 0");
|
|
54
|
+
} else if (inValues.length === 1) {
|
|
55
|
+
const paramName = `p${paramIndex++}`;
|
|
56
|
+
conditions.push(`[${parseSqlIdentifier(key, "field name")}] = @${paramName}`);
|
|
57
|
+
params[paramName] = inValues[0] instanceof Date ? inValues[0].toISOString() : inValues[0];
|
|
58
|
+
} else {
|
|
59
|
+
const inParamNames = [];
|
|
60
|
+
for (const item of inValues) {
|
|
61
|
+
const paramName = `p${paramIndex++}`;
|
|
62
|
+
inParamNames.push(`@${paramName}`);
|
|
63
|
+
params[paramName] = item instanceof Date ? item.toISOString() : item;
|
|
112
64
|
}
|
|
113
|
-
|
|
114
|
-
const countResult = await countReq.query(countQuery);
|
|
115
|
-
const total = countResult.recordset[0]?.total || 0;
|
|
116
|
-
if (total === 0) {
|
|
117
|
-
return {
|
|
118
|
-
evals: [],
|
|
119
|
-
total: 0,
|
|
120
|
-
page,
|
|
121
|
-
perPage,
|
|
122
|
-
hasMore: false
|
|
123
|
-
};
|
|
65
|
+
conditions.push(`[${parseSqlIdentifier(key, "field name")}] IN (${inParamNames.join(", ")})`);
|
|
124
66
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
67
|
+
} else if (Array.isArray(value)) {
|
|
68
|
+
if (value.length === 0) {
|
|
69
|
+
conditions.push("1 = 0");
|
|
70
|
+
} else if (value.length === 1) {
|
|
71
|
+
const paramName = `p${paramIndex++}`;
|
|
72
|
+
conditions.push(`[${parseSqlIdentifier(key, "field name")}] = @${paramName}`);
|
|
73
|
+
params[paramName] = value[0] instanceof Date ? value[0].toISOString() : value[0];
|
|
74
|
+
} else {
|
|
75
|
+
const inParamNames = [];
|
|
76
|
+
for (const item of value) {
|
|
77
|
+
const paramName = `p${paramIndex++}`;
|
|
78
|
+
inParamNames.push(`@${paramName}`);
|
|
79
|
+
params[paramName] = item instanceof Date ? item.toISOString() : item;
|
|
131
80
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
evals: rows?.map((row) => transformEvalRow(row)) ?? [],
|
|
139
|
-
total,
|
|
140
|
-
page,
|
|
141
|
-
perPage,
|
|
142
|
-
hasMore: offset + (rows?.length ?? 0) < total
|
|
143
|
-
};
|
|
144
|
-
} catch (error) {
|
|
145
|
-
const mastraError = new MastraError(
|
|
146
|
-
{
|
|
147
|
-
id: "MASTRA_STORAGE_MSSQL_STORE_GET_EVALS_FAILED",
|
|
148
|
-
domain: ErrorDomain.STORAGE,
|
|
149
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
150
|
-
details: {
|
|
151
|
-
agentName: agentName || "all",
|
|
152
|
-
type: type || "all",
|
|
153
|
-
page,
|
|
154
|
-
perPage
|
|
155
|
-
}
|
|
156
|
-
},
|
|
157
|
-
error
|
|
158
|
-
);
|
|
159
|
-
this.logger?.error?.(mastraError.toString());
|
|
160
|
-
this.logger?.trackException(mastraError);
|
|
161
|
-
throw mastraError;
|
|
81
|
+
conditions.push(`[${parseSqlIdentifier(key, "field name")}] IN (${inParamNames.join(", ")})`);
|
|
82
|
+
}
|
|
83
|
+
} else {
|
|
84
|
+
const paramName = `p${paramIndex++}`;
|
|
85
|
+
conditions.push(`[${parseSqlIdentifier(key, "field name")}] = @${paramName}`);
|
|
86
|
+
params[paramName] = value instanceof Date ? value.toISOString() : value;
|
|
162
87
|
}
|
|
163
|
-
}
|
|
164
|
-
|
|
88
|
+
});
|
|
89
|
+
return {
|
|
90
|
+
sql: conditions.length > 0 ? ` WHERE ${conditions.join(" AND ")}` : "",
|
|
91
|
+
params
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function transformFromSqlRow({
|
|
95
|
+
tableName,
|
|
96
|
+
sqlRow
|
|
97
|
+
}) {
|
|
98
|
+
const schema = TABLE_SCHEMAS[tableName];
|
|
99
|
+
const result = {};
|
|
100
|
+
Object.entries(sqlRow).forEach(([key, value]) => {
|
|
101
|
+
const columnSchema = schema?.[key];
|
|
102
|
+
if (columnSchema?.type === "jsonb" && typeof value === "string") {
|
|
103
|
+
try {
|
|
104
|
+
result[key] = JSON.parse(value);
|
|
105
|
+
} catch {
|
|
106
|
+
result[key] = value;
|
|
107
|
+
}
|
|
108
|
+
} else if (columnSchema?.type === "timestamp" && value && typeof value === "string") {
|
|
109
|
+
result[key] = new Date(value);
|
|
110
|
+
} else if (columnSchema?.type === "timestamp" && value instanceof Date) {
|
|
111
|
+
result[key] = value;
|
|
112
|
+
} else if (columnSchema?.type === "boolean") {
|
|
113
|
+
result[key] = Boolean(value);
|
|
114
|
+
} else {
|
|
115
|
+
result[key] = value;
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
return result;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// src/storage/domains/memory/index.ts
|
|
165
122
|
var MemoryMSSQL = class extends MemoryStorage {
|
|
166
123
|
pool;
|
|
167
124
|
schema;
|
|
@@ -179,7 +136,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
179
136
|
});
|
|
180
137
|
const cleanMessages = messagesWithParsedContent.map(({ seq_id, ...rest }) => rest);
|
|
181
138
|
const list = new MessageList().add(cleanMessages, "memory");
|
|
182
|
-
return format === "v2" ? list.get.all.
|
|
139
|
+
return format === "v2" ? list.get.all.db() : list.get.all.v1();
|
|
183
140
|
}
|
|
184
141
|
constructor({
|
|
185
142
|
pool,
|
|
@@ -193,7 +150,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
193
150
|
}
|
|
194
151
|
async getThreadById({ threadId }) {
|
|
195
152
|
try {
|
|
196
|
-
const
|
|
153
|
+
const sql5 = `SELECT
|
|
197
154
|
id,
|
|
198
155
|
[resourceId],
|
|
199
156
|
title,
|
|
@@ -204,7 +161,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
204
161
|
WHERE id = @threadId`;
|
|
205
162
|
const request = this.pool.request();
|
|
206
163
|
request.input("threadId", threadId);
|
|
207
|
-
const resultSet = await request.query(
|
|
164
|
+
const resultSet = await request.query(sql5);
|
|
208
165
|
const thread = resultSet.recordset[0] || null;
|
|
209
166
|
if (!thread) {
|
|
210
167
|
return null;
|
|
@@ -218,7 +175,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
218
175
|
} catch (error) {
|
|
219
176
|
throw new MastraError(
|
|
220
177
|
{
|
|
221
|
-
id: "
|
|
178
|
+
id: createStorageErrorId("MSSQL", "GET_THREAD_BY_ID", "FAILED"),
|
|
222
179
|
domain: ErrorDomain.STORAGE,
|
|
223
180
|
category: ErrorCategory.THIRD_PARTY,
|
|
224
181
|
details: {
|
|
@@ -229,11 +186,24 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
229
186
|
);
|
|
230
187
|
}
|
|
231
188
|
}
|
|
232
|
-
async
|
|
233
|
-
const { resourceId, page = 0, perPage: perPageInput, orderBy
|
|
189
|
+
async listThreadsByResourceId(args) {
|
|
190
|
+
const { resourceId, page = 0, perPage: perPageInput, orderBy } = args;
|
|
191
|
+
if (page < 0) {
|
|
192
|
+
throw new MastraError({
|
|
193
|
+
id: createStorageErrorId("MSSQL", "LIST_THREADS_BY_RESOURCE_ID", "INVALID_PAGE"),
|
|
194
|
+
domain: ErrorDomain.STORAGE,
|
|
195
|
+
category: ErrorCategory.USER,
|
|
196
|
+
text: "Page number must be non-negative",
|
|
197
|
+
details: {
|
|
198
|
+
resourceId,
|
|
199
|
+
page
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
const perPage = normalizePerPage(perPageInput, 100);
|
|
204
|
+
const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
205
|
+
const { field, direction } = this.parseOrderBy(orderBy);
|
|
234
206
|
try {
|
|
235
|
-
const perPage = perPageInput !== void 0 ? perPageInput : 100;
|
|
236
|
-
const currentOffset = page * perPage;
|
|
237
207
|
const baseQuery = `FROM ${getTableName({ indexName: TABLE_THREADS, schemaName: getSchemaName(this.schema) })} WHERE [resourceId] = @resourceId`;
|
|
238
208
|
const countQuery = `SELECT COUNT(*) as count ${baseQuery}`;
|
|
239
209
|
const countRequest = this.pool.request();
|
|
@@ -245,16 +215,22 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
245
215
|
threads: [],
|
|
246
216
|
total: 0,
|
|
247
217
|
page,
|
|
248
|
-
perPage,
|
|
218
|
+
perPage: perPageForResponse,
|
|
249
219
|
hasMore: false
|
|
250
220
|
};
|
|
251
221
|
}
|
|
252
|
-
const orderByField =
|
|
253
|
-
const
|
|
222
|
+
const orderByField = field === "createdAt" ? "[createdAt]" : "[updatedAt]";
|
|
223
|
+
const dir = (direction || "DESC").toUpperCase() === "ASC" ? "ASC" : "DESC";
|
|
224
|
+
const limitValue = perPageInput === false ? total : perPage;
|
|
225
|
+
const dataQuery = `SELECT id, [resourceId], title, metadata, [createdAt], [updatedAt] ${baseQuery} ORDER BY ${orderByField} ${dir} OFFSET @offset ROWS FETCH NEXT @perPage ROWS ONLY`;
|
|
254
226
|
const dataRequest = this.pool.request();
|
|
255
227
|
dataRequest.input("resourceId", resourceId);
|
|
256
|
-
dataRequest.input("
|
|
257
|
-
|
|
228
|
+
dataRequest.input("offset", offset);
|
|
229
|
+
if (limitValue > 2147483647) {
|
|
230
|
+
dataRequest.input("perPage", sql3.BigInt, limitValue);
|
|
231
|
+
} else {
|
|
232
|
+
dataRequest.input("perPage", limitValue);
|
|
233
|
+
}
|
|
258
234
|
const rowsResult = await dataRequest.query(dataQuery);
|
|
259
235
|
const rows = rowsResult.recordset || [];
|
|
260
236
|
const threads = rows.map((thread) => ({
|
|
@@ -267,13 +243,13 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
267
243
|
threads,
|
|
268
244
|
total,
|
|
269
245
|
page,
|
|
270
|
-
perPage,
|
|
271
|
-
hasMore:
|
|
246
|
+
perPage: perPageForResponse,
|
|
247
|
+
hasMore: perPageInput === false ? false : offset + perPage < total
|
|
272
248
|
};
|
|
273
249
|
} catch (error) {
|
|
274
250
|
const mastraError = new MastraError(
|
|
275
251
|
{
|
|
276
|
-
id: "
|
|
252
|
+
id: createStorageErrorId("MSSQL", "LIST_THREADS_BY_RESOURCE_ID", "FAILED"),
|
|
277
253
|
domain: ErrorDomain.STORAGE,
|
|
278
254
|
category: ErrorCategory.THIRD_PARTY,
|
|
279
255
|
details: {
|
|
@@ -285,7 +261,13 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
285
261
|
);
|
|
286
262
|
this.logger?.error?.(mastraError.toString());
|
|
287
263
|
this.logger?.trackException?.(mastraError);
|
|
288
|
-
return {
|
|
264
|
+
return {
|
|
265
|
+
threads: [],
|
|
266
|
+
total: 0,
|
|
267
|
+
page,
|
|
268
|
+
perPage: perPageForResponse,
|
|
269
|
+
hasMore: false
|
|
270
|
+
};
|
|
289
271
|
}
|
|
290
272
|
}
|
|
291
273
|
async saveThread({ thread }) {
|
|
@@ -307,15 +289,20 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
307
289
|
req.input("id", thread.id);
|
|
308
290
|
req.input("resourceId", thread.resourceId);
|
|
309
291
|
req.input("title", thread.title);
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
292
|
+
const metadata = thread.metadata ? JSON.stringify(thread.metadata) : null;
|
|
293
|
+
if (metadata === null) {
|
|
294
|
+
req.input("metadata", sql3.NVarChar, null);
|
|
295
|
+
} else {
|
|
296
|
+
req.input("metadata", metadata);
|
|
297
|
+
}
|
|
298
|
+
req.input("createdAt", sql3.DateTime2, thread.createdAt);
|
|
299
|
+
req.input("updatedAt", sql3.DateTime2, thread.updatedAt);
|
|
313
300
|
await req.query(mergeSql);
|
|
314
301
|
return thread;
|
|
315
302
|
} catch (error) {
|
|
316
303
|
throw new MastraError(
|
|
317
304
|
{
|
|
318
|
-
id: "
|
|
305
|
+
id: createStorageErrorId("MSSQL", "SAVE_THREAD", "FAILED"),
|
|
319
306
|
domain: ErrorDomain.STORAGE,
|
|
320
307
|
category: ErrorCategory.THIRD_PARTY,
|
|
321
308
|
details: {
|
|
@@ -326,30 +313,6 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
326
313
|
);
|
|
327
314
|
}
|
|
328
315
|
}
|
|
329
|
-
/**
|
|
330
|
-
* @deprecated use getThreadsByResourceIdPaginated instead
|
|
331
|
-
*/
|
|
332
|
-
async getThreadsByResourceId(args) {
|
|
333
|
-
const { resourceId, orderBy = "createdAt", sortDirection = "DESC" } = args;
|
|
334
|
-
try {
|
|
335
|
-
const baseQuery = `FROM ${getTableName({ indexName: TABLE_THREADS, schemaName: getSchemaName(this.schema) })} WHERE [resourceId] = @resourceId`;
|
|
336
|
-
const orderByField = orderBy === "createdAt" ? "[createdAt]" : "[updatedAt]";
|
|
337
|
-
const dataQuery = `SELECT id, [resourceId], title, metadata, [createdAt], [updatedAt] ${baseQuery} ORDER BY ${orderByField} ${sortDirection}`;
|
|
338
|
-
const request = this.pool.request();
|
|
339
|
-
request.input("resourceId", resourceId);
|
|
340
|
-
const resultSet = await request.query(dataQuery);
|
|
341
|
-
const rows = resultSet.recordset || [];
|
|
342
|
-
return rows.map((thread) => ({
|
|
343
|
-
...thread,
|
|
344
|
-
metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
|
|
345
|
-
createdAt: thread.createdAt,
|
|
346
|
-
updatedAt: thread.updatedAt
|
|
347
|
-
}));
|
|
348
|
-
} catch (error) {
|
|
349
|
-
this.logger?.error?.(`Error getting threads for resource ${resourceId}:`, error);
|
|
350
|
-
return [];
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
316
|
/**
|
|
354
317
|
* Updates a thread's title and metadata, merging with existing metadata. Returns the updated thread.
|
|
355
318
|
*/
|
|
@@ -361,7 +324,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
361
324
|
const existingThread = await this.getThreadById({ threadId: id });
|
|
362
325
|
if (!existingThread) {
|
|
363
326
|
throw new MastraError({
|
|
364
|
-
id: "
|
|
327
|
+
id: createStorageErrorId("MSSQL", "UPDATE_THREAD", "NOT_FOUND"),
|
|
365
328
|
domain: ErrorDomain.STORAGE,
|
|
366
329
|
category: ErrorCategory.USER,
|
|
367
330
|
text: `Thread ${id} not found`,
|
|
@@ -377,7 +340,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
377
340
|
};
|
|
378
341
|
try {
|
|
379
342
|
const table = getTableName({ indexName: TABLE_THREADS, schemaName: getSchemaName(this.schema) });
|
|
380
|
-
const
|
|
343
|
+
const sql5 = `UPDATE ${table}
|
|
381
344
|
SET title = @title,
|
|
382
345
|
metadata = @metadata,
|
|
383
346
|
[updatedAt] = @updatedAt
|
|
@@ -388,7 +351,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
388
351
|
req.input("title", title);
|
|
389
352
|
req.input("metadata", JSON.stringify(mergedMetadata));
|
|
390
353
|
req.input("updatedAt", /* @__PURE__ */ new Date());
|
|
391
|
-
const result = await req.query(
|
|
354
|
+
const result = await req.query(sql5);
|
|
392
355
|
let thread = result.recordset && result.recordset[0];
|
|
393
356
|
if (thread && "seq_id" in thread) {
|
|
394
357
|
const { seq_id, ...rest } = thread;
|
|
@@ -396,7 +359,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
396
359
|
}
|
|
397
360
|
if (!thread) {
|
|
398
361
|
throw new MastraError({
|
|
399
|
-
id: "
|
|
362
|
+
id: createStorageErrorId("MSSQL", "UPDATE_THREAD", "NOT_FOUND"),
|
|
400
363
|
domain: ErrorDomain.STORAGE,
|
|
401
364
|
category: ErrorCategory.USER,
|
|
402
365
|
text: `Thread ${id} not found after update`,
|
|
@@ -415,7 +378,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
415
378
|
} catch (error) {
|
|
416
379
|
throw new MastraError(
|
|
417
380
|
{
|
|
418
|
-
id: "
|
|
381
|
+
id: createStorageErrorId("MSSQL", "UPDATE_THREAD", "FAILED"),
|
|
419
382
|
domain: ErrorDomain.STORAGE,
|
|
420
383
|
category: ErrorCategory.THIRD_PARTY,
|
|
421
384
|
details: {
|
|
@@ -445,7 +408,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
445
408
|
});
|
|
446
409
|
throw new MastraError(
|
|
447
410
|
{
|
|
448
|
-
id: "
|
|
411
|
+
id: createStorageErrorId("MSSQL", "DELETE_THREAD", "FAILED"),
|
|
449
412
|
domain: ErrorDomain.STORAGE,
|
|
450
413
|
category: ErrorCategory.THIRD_PARTY,
|
|
451
414
|
details: {
|
|
@@ -456,24 +419,18 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
456
419
|
);
|
|
457
420
|
}
|
|
458
421
|
}
|
|
459
|
-
async _getIncludedMessages({
|
|
460
|
-
|
|
461
|
-
selectBy,
|
|
462
|
-
orderByStatement
|
|
463
|
-
}) {
|
|
464
|
-
const include = selectBy?.include;
|
|
465
|
-
if (!include) return null;
|
|
422
|
+
async _getIncludedMessages({ include }) {
|
|
423
|
+
if (!include || include.length === 0) return null;
|
|
466
424
|
const unionQueries = [];
|
|
467
425
|
const paramValues = [];
|
|
468
426
|
let paramIdx = 1;
|
|
469
427
|
const paramNames = [];
|
|
428
|
+
const tableName = getTableName({ indexName: TABLE_MESSAGES, schemaName: getSchemaName(this.schema) });
|
|
470
429
|
for (const inc of include) {
|
|
471
430
|
const { id, withPreviousMessages = 0, withNextMessages = 0 } = inc;
|
|
472
|
-
const
|
|
473
|
-
const
|
|
474
|
-
const
|
|
475
|
-
const pPrev = `@p${paramIdx + 2}`;
|
|
476
|
-
const pNext = `@p${paramIdx + 3}`;
|
|
431
|
+
const pId = `@p${paramIdx}`;
|
|
432
|
+
const pPrev = `@p${paramIdx + 1}`;
|
|
433
|
+
const pNext = `@p${paramIdx + 2}`;
|
|
477
434
|
unionQueries.push(
|
|
478
435
|
`
|
|
479
436
|
SELECT
|
|
@@ -486,30 +443,32 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
486
443
|
m.[resourceId],
|
|
487
444
|
m.seq_id
|
|
488
445
|
FROM (
|
|
489
|
-
SELECT *, ROW_NUMBER() OVER (
|
|
490
|
-
FROM ${
|
|
491
|
-
WHERE [thread_id] = ${
|
|
446
|
+
SELECT *, ROW_NUMBER() OVER (ORDER BY [createdAt] ASC) as row_num
|
|
447
|
+
FROM ${tableName}
|
|
448
|
+
WHERE [thread_id] = (SELECT thread_id FROM ${tableName} WHERE id = ${pId})
|
|
492
449
|
) AS m
|
|
493
450
|
WHERE m.id = ${pId}
|
|
494
451
|
OR EXISTS (
|
|
495
452
|
SELECT 1
|
|
496
453
|
FROM (
|
|
497
|
-
SELECT *, ROW_NUMBER() OVER (
|
|
498
|
-
FROM ${
|
|
499
|
-
WHERE [thread_id] = ${
|
|
454
|
+
SELECT *, ROW_NUMBER() OVER (ORDER BY [createdAt] ASC) as row_num
|
|
455
|
+
FROM ${tableName}
|
|
456
|
+
WHERE [thread_id] = (SELECT thread_id FROM ${tableName} WHERE id = ${pId})
|
|
500
457
|
) AS target
|
|
501
458
|
WHERE target.id = ${pId}
|
|
502
459
|
AND (
|
|
503
|
-
|
|
460
|
+
-- Get previous messages (messages that come BEFORE the target)
|
|
461
|
+
(m.row_num < target.row_num AND m.row_num >= target.row_num - ${pPrev})
|
|
504
462
|
OR
|
|
505
|
-
|
|
463
|
+
-- Get next messages (messages that come AFTER the target)
|
|
464
|
+
(m.row_num > target.row_num AND m.row_num <= target.row_num + ${pNext})
|
|
506
465
|
)
|
|
507
466
|
)
|
|
508
467
|
`
|
|
509
468
|
);
|
|
510
|
-
paramValues.push(
|
|
511
|
-
paramNames.push(`p${paramIdx}`, `p${paramIdx + 1}`, `p${paramIdx + 2}
|
|
512
|
-
paramIdx +=
|
|
469
|
+
paramValues.push(id, withPreviousMessages, withNextMessages);
|
|
470
|
+
paramNames.push(`p${paramIdx}`, `p${paramIdx + 1}`, `p${paramIdx + 2}`);
|
|
471
|
+
paramIdx += 3;
|
|
513
472
|
}
|
|
514
473
|
const finalQuery = `
|
|
515
474
|
SELECT * FROM (
|
|
@@ -531,33 +490,16 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
531
490
|
});
|
|
532
491
|
return dedupedRows;
|
|
533
492
|
}
|
|
534
|
-
async
|
|
535
|
-
|
|
493
|
+
async listMessagesById({ messageIds }) {
|
|
494
|
+
if (messageIds.length === 0) return { messages: [] };
|
|
536
495
|
const selectStatement = `SELECT seq_id, id, content, role, type, [createdAt], thread_id AS threadId, resourceId`;
|
|
537
496
|
const orderByStatement = `ORDER BY [seq_id] DESC`;
|
|
538
|
-
const limit = resolveMessageLimit({ last: selectBy?.last, defaultLimit: 40 });
|
|
539
497
|
try {
|
|
540
498
|
let rows = [];
|
|
541
|
-
|
|
542
|
-
if (include?.length) {
|
|
543
|
-
const includeMessages = await this._getIncludedMessages({ threadId, selectBy, orderByStatement });
|
|
544
|
-
if (includeMessages) {
|
|
545
|
-
rows.push(...includeMessages);
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
const excludeIds = rows.map((m) => m.id).filter(Boolean);
|
|
549
|
-
let query = `${selectStatement} FROM ${getTableName({ indexName: TABLE_MESSAGES, schemaName: getSchemaName(this.schema) })} WHERE [thread_id] = @threadId`;
|
|
499
|
+
let query = `${selectStatement} FROM ${getTableName({ indexName: TABLE_MESSAGES, schemaName: getSchemaName(this.schema) })} WHERE [id] IN (${messageIds.map((_, i) => `@id${i}`).join(", ")})`;
|
|
550
500
|
const request = this.pool.request();
|
|
551
|
-
request.input(
|
|
552
|
-
|
|
553
|
-
const excludeParams = excludeIds.map((_, idx) => `@id${idx}`);
|
|
554
|
-
query += ` AND id NOT IN (${excludeParams.join(", ")})`;
|
|
555
|
-
excludeIds.forEach((id, idx) => {
|
|
556
|
-
request.input(`id${idx}`, id);
|
|
557
|
-
});
|
|
558
|
-
}
|
|
559
|
-
query += ` ${orderByStatement} OFFSET 0 ROWS FETCH NEXT @limit ROWS ONLY`;
|
|
560
|
-
request.input("limit", limit);
|
|
501
|
+
messageIds.forEach((id, i) => request.input(`id${i}`, id));
|
|
502
|
+
query += ` ${orderByStatement}`;
|
|
561
503
|
const result = await request.query(query);
|
|
562
504
|
const remainingRows = result.recordset || [];
|
|
563
505
|
rows.push(...remainingRows);
|
|
@@ -565,120 +507,177 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
565
507
|
const timeDiff = a.seq_id - b.seq_id;
|
|
566
508
|
return timeDiff;
|
|
567
509
|
});
|
|
568
|
-
|
|
569
|
-
|
|
510
|
+
const messagesWithParsedContent = rows.map((row) => {
|
|
511
|
+
if (typeof row.content === "string") {
|
|
512
|
+
try {
|
|
513
|
+
return { ...row, content: JSON.parse(row.content) };
|
|
514
|
+
} catch {
|
|
515
|
+
return row;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
return row;
|
|
519
|
+
});
|
|
520
|
+
const cleanMessages = messagesWithParsedContent.map(({ seq_id, ...rest }) => rest);
|
|
521
|
+
const list = new MessageList().add(cleanMessages, "memory");
|
|
522
|
+
return { messages: list.get.all.db() };
|
|
570
523
|
} catch (error) {
|
|
571
524
|
const mastraError = new MastraError(
|
|
572
525
|
{
|
|
573
|
-
id: "
|
|
526
|
+
id: createStorageErrorId("MSSQL", "LIST_MESSAGES_BY_ID", "FAILED"),
|
|
574
527
|
domain: ErrorDomain.STORAGE,
|
|
575
528
|
category: ErrorCategory.THIRD_PARTY,
|
|
576
529
|
details: {
|
|
577
|
-
|
|
530
|
+
messageIds: JSON.stringify(messageIds)
|
|
578
531
|
}
|
|
579
532
|
},
|
|
580
533
|
error
|
|
581
534
|
);
|
|
582
535
|
this.logger?.error?.(mastraError.toString());
|
|
583
|
-
this.logger?.trackException(mastraError);
|
|
584
|
-
return [];
|
|
536
|
+
this.logger?.trackException?.(mastraError);
|
|
537
|
+
return { messages: [] };
|
|
585
538
|
}
|
|
586
539
|
}
|
|
587
|
-
async
|
|
588
|
-
const { threadId,
|
|
589
|
-
const
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
540
|
+
async listMessages(args) {
|
|
541
|
+
const { threadId, resourceId, include, filter, perPage: perPageInput, page = 0, orderBy } = args;
|
|
542
|
+
const threadIds = Array.isArray(threadId) ? threadId : [threadId];
|
|
543
|
+
if (threadIds.length === 0 || threadIds.some((id) => !id.trim())) {
|
|
544
|
+
throw new MastraError(
|
|
545
|
+
{
|
|
546
|
+
id: createStorageErrorId("MSSQL", "LIST_MESSAGES", "INVALID_THREAD_ID"),
|
|
547
|
+
domain: ErrorDomain.STORAGE,
|
|
548
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
549
|
+
details: { threadId: Array.isArray(threadId) ? threadId.join(",") : threadId }
|
|
550
|
+
},
|
|
551
|
+
new Error("threadId must be a non-empty string or array of non-empty strings")
|
|
552
|
+
);
|
|
553
|
+
}
|
|
554
|
+
if (page < 0) {
|
|
555
|
+
throw new MastraError({
|
|
556
|
+
id: createStorageErrorId("MSSQL", "LIST_MESSAGES", "INVALID_PAGE"),
|
|
557
|
+
domain: ErrorDomain.STORAGE,
|
|
558
|
+
category: ErrorCategory.USER,
|
|
559
|
+
text: "Page number must be non-negative",
|
|
560
|
+
details: {
|
|
561
|
+
threadId: Array.isArray(threadId) ? threadId.join(",") : threadId,
|
|
562
|
+
page
|
|
563
|
+
}
|
|
564
|
+
});
|
|
593
565
|
}
|
|
566
|
+
const perPage = normalizePerPage(perPageInput, 40);
|
|
567
|
+
const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
594
568
|
try {
|
|
595
|
-
const {
|
|
596
|
-
const
|
|
597
|
-
const
|
|
598
|
-
const
|
|
599
|
-
const
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
const
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
const
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
conditions.push("[createdAt] >= @fromDate");
|
|
613
|
-
request.input("fromDate", fromDate.toISOString());
|
|
614
|
-
}
|
|
615
|
-
if (toDate instanceof Date && !isNaN(toDate.getTime())) {
|
|
616
|
-
conditions.push("[createdAt] <= @toDate");
|
|
617
|
-
request.input("toDate", toDate.toISOString());
|
|
618
|
-
}
|
|
619
|
-
const whereClause = `WHERE ${conditions.join(" AND ")}`;
|
|
620
|
-
const countQuery = `SELECT COUNT(*) as total FROM ${getTableName({ indexName: TABLE_MESSAGES, schemaName: getSchemaName(this.schema) })} ${whereClause}`;
|
|
621
|
-
const countResult = await request.query(countQuery);
|
|
569
|
+
const { field, direction } = this.parseOrderBy(orderBy, "ASC");
|
|
570
|
+
const orderByStatement = `ORDER BY [${field}] ${direction}, [seq_id] ${direction}`;
|
|
571
|
+
const tableName = getTableName({ indexName: TABLE_MESSAGES, schemaName: getSchemaName(this.schema) });
|
|
572
|
+
const baseQuery = `SELECT seq_id, id, content, role, type, [createdAt], thread_id AS threadId, resourceId FROM ${tableName}`;
|
|
573
|
+
const filters = {
|
|
574
|
+
thread_id: threadIds.length === 1 ? threadIds[0] : { $in: threadIds },
|
|
575
|
+
...resourceId ? { resourceId } : {},
|
|
576
|
+
...buildDateRangeFilter(filter?.dateRange, "createdAt")
|
|
577
|
+
};
|
|
578
|
+
const { sql: actualWhereClause = "", params: whereParams } = prepareWhereClause(
|
|
579
|
+
filters);
|
|
580
|
+
const bindWhereParams = (req) => {
|
|
581
|
+
Object.entries(whereParams).forEach(([paramName, paramValue]) => req.input(paramName, paramValue));
|
|
582
|
+
};
|
|
583
|
+
const countRequest = this.pool.request();
|
|
584
|
+
bindWhereParams(countRequest);
|
|
585
|
+
const countResult = await countRequest.query(`SELECT COUNT(*) as total FROM ${tableName}${actualWhereClause}`);
|
|
622
586
|
const total = parseInt(countResult.recordset[0]?.total, 10) || 0;
|
|
623
|
-
|
|
624
|
-
const
|
|
587
|
+
const fetchBaseMessages = async () => {
|
|
588
|
+
const request = this.pool.request();
|
|
589
|
+
bindWhereParams(request);
|
|
590
|
+
if (perPageInput === false) {
|
|
591
|
+
const result2 = await request.query(`${baseQuery}${actualWhereClause} ${orderByStatement}`);
|
|
592
|
+
return result2.recordset || [];
|
|
593
|
+
}
|
|
594
|
+
request.input("offset", offset);
|
|
595
|
+
request.input("limit", perPage > 2147483647 ? sql3.BigInt : sql3.Int, perPage);
|
|
596
|
+
const result = await request.query(
|
|
597
|
+
`${baseQuery}${actualWhereClause} ${orderByStatement} OFFSET @offset ROWS FETCH NEXT @limit ROWS ONLY`
|
|
598
|
+
);
|
|
599
|
+
return result.recordset || [];
|
|
600
|
+
};
|
|
601
|
+
const baseRows = perPage === 0 ? [] : await fetchBaseMessages();
|
|
602
|
+
const messages = [...baseRows];
|
|
603
|
+
const seqById = /* @__PURE__ */ new Map();
|
|
604
|
+
messages.forEach((msg) => {
|
|
605
|
+
if (typeof msg.seq_id === "number") seqById.set(msg.id, msg.seq_id);
|
|
606
|
+
});
|
|
607
|
+
if (total === 0 && messages.length === 0 && (!include || include.length === 0)) {
|
|
625
608
|
return {
|
|
626
|
-
messages:
|
|
627
|
-
total:
|
|
628
|
-
page
|
|
629
|
-
perPage,
|
|
609
|
+
messages: [],
|
|
610
|
+
total: 0,
|
|
611
|
+
page,
|
|
612
|
+
perPage: perPageForResponse,
|
|
630
613
|
hasMore: false
|
|
631
614
|
};
|
|
632
615
|
}
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
const
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
const
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
616
|
+
if (include?.length) {
|
|
617
|
+
const messageIds = new Set(messages.map((m) => m.id));
|
|
618
|
+
const includeMessages = await this._getIncludedMessages({ include });
|
|
619
|
+
includeMessages?.forEach((msg) => {
|
|
620
|
+
if (!messageIds.has(msg.id)) {
|
|
621
|
+
messages.push(msg);
|
|
622
|
+
messageIds.add(msg.id);
|
|
623
|
+
if (typeof msg.seq_id === "number") seqById.set(msg.id, msg.seq_id);
|
|
624
|
+
}
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
const parsed = this._parseAndFormatMessages(messages, "v2");
|
|
628
|
+
const mult = direction === "ASC" ? 1 : -1;
|
|
629
|
+
const finalMessages = parsed.sort((a, b) => {
|
|
630
|
+
const aVal = field === "createdAt" ? new Date(a.createdAt).getTime() : a[field];
|
|
631
|
+
const bVal = field === "createdAt" ? new Date(b.createdAt).getTime() : b[field];
|
|
632
|
+
if (aVal == null || bVal == null) {
|
|
633
|
+
return aVal == null && bVal == null ? a.id.localeCompare(b.id) : aVal == null ? 1 : -1;
|
|
634
|
+
}
|
|
635
|
+
const diff = (typeof aVal === "number" && typeof bVal === "number" ? aVal - bVal : String(aVal).localeCompare(String(bVal))) * mult;
|
|
636
|
+
if (diff !== 0) return diff;
|
|
637
|
+
const seqA = seqById.get(a.id);
|
|
638
|
+
const seqB = seqById.get(b.id);
|
|
639
|
+
return seqA != null && seqB != null ? (seqA - seqB) * mult : a.id.localeCompare(b.id);
|
|
640
|
+
});
|
|
641
|
+
const threadIdSet = new Set(threadIds);
|
|
642
|
+
const returnedThreadMessageCount = finalMessages.filter((m) => m.threadId && threadIdSet.has(m.threadId)).length;
|
|
643
|
+
const hasMore = perPageInput !== false && returnedThreadMessageCount < total && offset + perPage < total;
|
|
648
644
|
return {
|
|
649
|
-
messages:
|
|
650
|
-
total
|
|
651
|
-
page
|
|
652
|
-
perPage,
|
|
653
|
-
hasMore
|
|
645
|
+
messages: finalMessages,
|
|
646
|
+
total,
|
|
647
|
+
page,
|
|
648
|
+
perPage: perPageForResponse,
|
|
649
|
+
hasMore
|
|
654
650
|
};
|
|
655
651
|
} catch (error) {
|
|
656
652
|
const mastraError = new MastraError(
|
|
657
653
|
{
|
|
658
|
-
id: "
|
|
654
|
+
id: createStorageErrorId("MSSQL", "LIST_MESSAGES", "FAILED"),
|
|
659
655
|
domain: ErrorDomain.STORAGE,
|
|
660
656
|
category: ErrorCategory.THIRD_PARTY,
|
|
661
657
|
details: {
|
|
662
|
-
threadId,
|
|
663
|
-
|
|
658
|
+
threadId: Array.isArray(threadId) ? threadId.join(",") : threadId,
|
|
659
|
+
resourceId: resourceId ?? ""
|
|
664
660
|
}
|
|
665
661
|
},
|
|
666
662
|
error
|
|
667
663
|
);
|
|
668
664
|
this.logger?.error?.(mastraError.toString());
|
|
669
|
-
this.logger?.trackException(mastraError);
|
|
670
|
-
return {
|
|
665
|
+
this.logger?.trackException?.(mastraError);
|
|
666
|
+
return {
|
|
667
|
+
messages: [],
|
|
668
|
+
total: 0,
|
|
669
|
+
page,
|
|
670
|
+
perPage: perPageForResponse,
|
|
671
|
+
hasMore: false
|
|
672
|
+
};
|
|
671
673
|
}
|
|
672
674
|
}
|
|
673
|
-
async saveMessages({
|
|
674
|
-
messages
|
|
675
|
-
format
|
|
676
|
-
}) {
|
|
677
|
-
if (messages.length === 0) return messages;
|
|
675
|
+
async saveMessages({ messages }) {
|
|
676
|
+
if (messages.length === 0) return { messages: [] };
|
|
678
677
|
const threadId = messages[0]?.threadId;
|
|
679
678
|
if (!threadId) {
|
|
680
679
|
throw new MastraError({
|
|
681
|
-
id: "
|
|
680
|
+
id: createStorageErrorId("MSSQL", "SAVE_MESSAGES", "INVALID_THREAD_ID"),
|
|
682
681
|
domain: ErrorDomain.STORAGE,
|
|
683
682
|
category: ErrorCategory.THIRD_PARTY,
|
|
684
683
|
text: `Thread ID is required`
|
|
@@ -687,7 +686,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
687
686
|
const thread = await this.getThreadById({ threadId });
|
|
688
687
|
if (!thread) {
|
|
689
688
|
throw new MastraError({
|
|
690
|
-
id: "
|
|
689
|
+
id: createStorageErrorId("MSSQL", "SAVE_MESSAGES", "THREAD_NOT_FOUND"),
|
|
691
690
|
domain: ErrorDomain.STORAGE,
|
|
692
691
|
category: ErrorCategory.THIRD_PARTY,
|
|
693
692
|
text: `Thread ${threadId} not found`,
|
|
@@ -718,7 +717,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
718
717
|
"content",
|
|
719
718
|
typeof message.content === "string" ? message.content : JSON.stringify(message.content)
|
|
720
719
|
);
|
|
721
|
-
request.input("createdAt",
|
|
720
|
+
request.input("createdAt", sql3.DateTime2, message.createdAt);
|
|
722
721
|
request.input("role", message.role);
|
|
723
722
|
request.input("type", message.type || "v2");
|
|
724
723
|
request.input("resourceId", message.resourceId);
|
|
@@ -737,7 +736,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
737
736
|
await request.query(mergeSql);
|
|
738
737
|
}
|
|
739
738
|
const threadReq = transaction.request();
|
|
740
|
-
threadReq.input("updatedAt",
|
|
739
|
+
threadReq.input("updatedAt", sql3.DateTime2, /* @__PURE__ */ new Date());
|
|
741
740
|
threadReq.input("id", threadId);
|
|
742
741
|
await threadReq.query(`UPDATE ${tableThreads} SET [updatedAt] = @updatedAt WHERE id = @id`);
|
|
743
742
|
await transaction.commit();
|
|
@@ -756,12 +755,11 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
756
755
|
return message;
|
|
757
756
|
});
|
|
758
757
|
const list = new MessageList().add(messagesWithParsedContent, "memory");
|
|
759
|
-
|
|
760
|
-
return list.get.all.v1();
|
|
758
|
+
return { messages: list.get.all.db() };
|
|
761
759
|
} catch (error) {
|
|
762
760
|
throw new MastraError(
|
|
763
761
|
{
|
|
764
|
-
id: "
|
|
762
|
+
id: createStorageErrorId("MSSQL", "SAVE_MESSAGES", "FAILED"),
|
|
765
763
|
domain: ErrorDomain.STORAGE,
|
|
766
764
|
category: ErrorCategory.THIRD_PARTY,
|
|
767
765
|
details: { threadId }
|
|
@@ -852,7 +850,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
852
850
|
await transaction.rollback();
|
|
853
851
|
throw new MastraError(
|
|
854
852
|
{
|
|
855
|
-
id: "
|
|
853
|
+
id: createStorageErrorId("MSSQL", "UPDATE_MESSAGES", "FAILED"),
|
|
856
854
|
domain: ErrorDomain.STORAGE,
|
|
857
855
|
category: ErrorCategory.THIRD_PARTY
|
|
858
856
|
},
|
|
@@ -914,7 +912,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
914
912
|
} catch (error) {
|
|
915
913
|
throw new MastraError(
|
|
916
914
|
{
|
|
917
|
-
id: "
|
|
915
|
+
id: createStorageErrorId("MSSQL", "DELETE_MESSAGES", "FAILED"),
|
|
918
916
|
domain: ErrorDomain.STORAGE,
|
|
919
917
|
category: ErrorCategory.THIRD_PARTY,
|
|
920
918
|
details: { messageIds: messageIds.join(", ") }
|
|
@@ -933,14 +931,16 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
933
931
|
return null;
|
|
934
932
|
}
|
|
935
933
|
return {
|
|
936
|
-
|
|
937
|
-
|
|
934
|
+
id: result.id,
|
|
935
|
+
createdAt: result.createdAt,
|
|
936
|
+
updatedAt: result.updatedAt,
|
|
937
|
+
workingMemory: result.workingMemory,
|
|
938
938
|
metadata: typeof result.metadata === "string" ? JSON.parse(result.metadata) : result.metadata
|
|
939
939
|
};
|
|
940
940
|
} catch (error) {
|
|
941
941
|
const mastraError = new MastraError(
|
|
942
942
|
{
|
|
943
|
-
id: "
|
|
943
|
+
id: createStorageErrorId("MSSQL", "GET_RESOURCE_BY_ID", "FAILED"),
|
|
944
944
|
domain: ErrorDomain.STORAGE,
|
|
945
945
|
category: ErrorCategory.THIRD_PARTY,
|
|
946
946
|
details: { resourceId }
|
|
@@ -948,7 +948,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
948
948
|
error
|
|
949
949
|
);
|
|
950
950
|
this.logger?.error?.(mastraError.toString());
|
|
951
|
-
this.logger?.trackException(mastraError);
|
|
951
|
+
this.logger?.trackException?.(mastraError);
|
|
952
952
|
throw mastraError;
|
|
953
953
|
}
|
|
954
954
|
}
|
|
@@ -957,7 +957,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
957
957
|
tableName: TABLE_RESOURCES,
|
|
958
958
|
record: {
|
|
959
959
|
...resource,
|
|
960
|
-
metadata:
|
|
960
|
+
metadata: resource.metadata
|
|
961
961
|
}
|
|
962
962
|
});
|
|
963
963
|
return resource;
|
|
@@ -1007,7 +1007,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
1007
1007
|
} catch (error) {
|
|
1008
1008
|
const mastraError = new MastraError(
|
|
1009
1009
|
{
|
|
1010
|
-
id: "
|
|
1010
|
+
id: createStorageErrorId("MSSQL", "UPDATE_RESOURCE", "FAILED"),
|
|
1011
1011
|
domain: ErrorDomain.STORAGE,
|
|
1012
1012
|
category: ErrorCategory.THIRD_PARTY,
|
|
1013
1013
|
details: { resourceId }
|
|
@@ -1015,115 +1015,440 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
1015
1015
|
error
|
|
1016
1016
|
);
|
|
1017
1017
|
this.logger?.error?.(mastraError.toString());
|
|
1018
|
-
this.logger?.trackException(mastraError);
|
|
1018
|
+
this.logger?.trackException?.(mastraError);
|
|
1019
1019
|
throw mastraError;
|
|
1020
1020
|
}
|
|
1021
1021
|
}
|
|
1022
1022
|
};
|
|
1023
|
-
var
|
|
1023
|
+
var ObservabilityMSSQL = class extends ObservabilityStorage {
|
|
1024
1024
|
pool;
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
case "timestamp":
|
|
1033
|
-
return "DATETIME2(7)";
|
|
1034
|
-
case "uuid":
|
|
1035
|
-
return "UNIQUEIDENTIFIER";
|
|
1036
|
-
case "jsonb":
|
|
1037
|
-
return "NVARCHAR(MAX)";
|
|
1038
|
-
case "integer":
|
|
1039
|
-
return "INT";
|
|
1040
|
-
case "bigint":
|
|
1041
|
-
return "BIGINT";
|
|
1042
|
-
case "float":
|
|
1043
|
-
return "FLOAT";
|
|
1044
|
-
default:
|
|
1045
|
-
throw new MastraError({
|
|
1046
|
-
id: "MASTRA_STORAGE_MSSQL_STORE_TYPE_NOT_SUPPORTED",
|
|
1047
|
-
domain: ErrorDomain.STORAGE,
|
|
1048
|
-
category: ErrorCategory.THIRD_PARTY
|
|
1049
|
-
});
|
|
1050
|
-
}
|
|
1051
|
-
}
|
|
1052
|
-
constructor({ pool, schemaName }) {
|
|
1025
|
+
operations;
|
|
1026
|
+
schema;
|
|
1027
|
+
constructor({
|
|
1028
|
+
pool,
|
|
1029
|
+
operations,
|
|
1030
|
+
schema
|
|
1031
|
+
}) {
|
|
1053
1032
|
super();
|
|
1054
1033
|
this.pool = pool;
|
|
1055
|
-
this.
|
|
1056
|
-
|
|
1057
|
-
async hasColumn(table, column) {
|
|
1058
|
-
const schema = this.schemaName || "dbo";
|
|
1059
|
-
const request = this.pool.request();
|
|
1060
|
-
request.input("schema", schema);
|
|
1061
|
-
request.input("table", table);
|
|
1062
|
-
request.input("column", column);
|
|
1063
|
-
request.input("columnLower", column.toLowerCase());
|
|
1064
|
-
const result = await request.query(
|
|
1065
|
-
`SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = @table AND (COLUMN_NAME = @column OR COLUMN_NAME = @columnLower)`
|
|
1066
|
-
);
|
|
1067
|
-
return result.recordset.length > 0;
|
|
1034
|
+
this.operations = operations;
|
|
1035
|
+
this.schema = schema;
|
|
1068
1036
|
}
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
this.setupSchemaPromise = (async () => {
|
|
1075
|
-
try {
|
|
1076
|
-
const checkRequest = this.pool.request();
|
|
1077
|
-
checkRequest.input("schemaName", this.schemaName);
|
|
1078
|
-
const checkResult = await checkRequest.query(`
|
|
1079
|
-
SELECT 1 AS found FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = @schemaName
|
|
1080
|
-
`);
|
|
1081
|
-
const schemaExists = Array.isArray(checkResult.recordset) && checkResult.recordset.length > 0;
|
|
1082
|
-
if (!schemaExists) {
|
|
1083
|
-
try {
|
|
1084
|
-
await this.pool.request().query(`CREATE SCHEMA [${this.schemaName}]`);
|
|
1085
|
-
this.logger?.info?.(`Schema "${this.schemaName}" created successfully`);
|
|
1086
|
-
} catch (error) {
|
|
1087
|
-
this.logger?.error?.(`Failed to create schema "${this.schemaName}"`, { error });
|
|
1088
|
-
throw new Error(
|
|
1089
|
-
`Unable to create schema "${this.schemaName}". This requires CREATE privilege on the database. Either create the schema manually or grant CREATE privilege to the user.`
|
|
1090
|
-
);
|
|
1091
|
-
}
|
|
1092
|
-
}
|
|
1093
|
-
this.schemaSetupComplete = true;
|
|
1094
|
-
this.logger?.debug?.(`Schema "${this.schemaName}" is ready for use`);
|
|
1095
|
-
} catch (error) {
|
|
1096
|
-
this.schemaSetupComplete = void 0;
|
|
1097
|
-
this.setupSchemaPromise = null;
|
|
1098
|
-
throw error;
|
|
1099
|
-
} finally {
|
|
1100
|
-
this.setupSchemaPromise = null;
|
|
1101
|
-
}
|
|
1102
|
-
})();
|
|
1103
|
-
}
|
|
1104
|
-
await this.setupSchemaPromise;
|
|
1037
|
+
get tracingStrategy() {
|
|
1038
|
+
return {
|
|
1039
|
+
preferred: "batch-with-updates",
|
|
1040
|
+
supported: ["batch-with-updates", "insert-only"]
|
|
1041
|
+
};
|
|
1105
1042
|
}
|
|
1106
|
-
async
|
|
1043
|
+
async createSpan(span) {
|
|
1107
1044
|
try {
|
|
1108
|
-
const
|
|
1109
|
-
const
|
|
1110
|
-
const
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1045
|
+
const startedAt = span.startedAt instanceof Date ? span.startedAt.toISOString() : span.startedAt;
|
|
1046
|
+
const endedAt = span.endedAt instanceof Date ? span.endedAt.toISOString() : span.endedAt;
|
|
1047
|
+
const record = {
|
|
1048
|
+
...span,
|
|
1049
|
+
startedAt,
|
|
1050
|
+
endedAt
|
|
1051
|
+
// Note: createdAt/updatedAt will be set by default values
|
|
1052
|
+
};
|
|
1053
|
+
return this.operations.insert({ tableName: TABLE_SPANS, record });
|
|
1054
|
+
} catch (error) {
|
|
1055
|
+
throw new MastraError(
|
|
1056
|
+
{
|
|
1057
|
+
id: createStorageErrorId("MSSQL", "CREATE_SPAN", "FAILED"),
|
|
1058
|
+
domain: ErrorDomain.STORAGE,
|
|
1059
|
+
category: ErrorCategory.USER,
|
|
1060
|
+
details: {
|
|
1061
|
+
spanId: span.spanId,
|
|
1062
|
+
traceId: span.traceId,
|
|
1063
|
+
spanType: span.spanType,
|
|
1064
|
+
spanName: span.name
|
|
1065
|
+
}
|
|
1066
|
+
},
|
|
1067
|
+
error
|
|
1068
|
+
);
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
async getTrace(traceId) {
|
|
1072
|
+
try {
|
|
1073
|
+
const tableName = getTableName({
|
|
1074
|
+
indexName: TABLE_SPANS,
|
|
1075
|
+
schemaName: getSchemaName(this.schema)
|
|
1076
|
+
});
|
|
1077
|
+
const request = this.pool.request();
|
|
1078
|
+
request.input("traceId", traceId);
|
|
1079
|
+
const result = await request.query(
|
|
1080
|
+
`SELECT
|
|
1081
|
+
[traceId], [spanId], [parentSpanId], [name], [scope], [spanType],
|
|
1082
|
+
[attributes], [metadata], [links], [input], [output], [error], [isEvent],
|
|
1083
|
+
[startedAt], [endedAt], [createdAt], [updatedAt]
|
|
1084
|
+
FROM ${tableName}
|
|
1085
|
+
WHERE [traceId] = @traceId
|
|
1086
|
+
ORDER BY [startedAt] DESC`
|
|
1087
|
+
);
|
|
1088
|
+
if (!result.recordset || result.recordset.length === 0) {
|
|
1089
|
+
return null;
|
|
1090
|
+
}
|
|
1091
|
+
return {
|
|
1092
|
+
traceId,
|
|
1093
|
+
spans: result.recordset.map(
|
|
1094
|
+
(span) => transformFromSqlRow({
|
|
1095
|
+
tableName: TABLE_SPANS,
|
|
1096
|
+
sqlRow: span
|
|
1097
|
+
})
|
|
1098
|
+
)
|
|
1099
|
+
};
|
|
1100
|
+
} catch (error) {
|
|
1101
|
+
throw new MastraError(
|
|
1102
|
+
{
|
|
1103
|
+
id: createStorageErrorId("MSSQL", "GET_TRACE", "FAILED"),
|
|
1104
|
+
domain: ErrorDomain.STORAGE,
|
|
1105
|
+
category: ErrorCategory.USER,
|
|
1106
|
+
details: {
|
|
1107
|
+
traceId
|
|
1108
|
+
}
|
|
1109
|
+
},
|
|
1110
|
+
error
|
|
1111
|
+
);
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
async updateSpan({
|
|
1115
|
+
spanId,
|
|
1116
|
+
traceId,
|
|
1117
|
+
updates
|
|
1118
|
+
}) {
|
|
1119
|
+
try {
|
|
1120
|
+
const data = { ...updates };
|
|
1121
|
+
if (data.endedAt instanceof Date) {
|
|
1122
|
+
data.endedAt = data.endedAt.toISOString();
|
|
1123
|
+
}
|
|
1124
|
+
if (data.startedAt instanceof Date) {
|
|
1125
|
+
data.startedAt = data.startedAt.toISOString();
|
|
1126
|
+
}
|
|
1127
|
+
await this.operations.update({
|
|
1128
|
+
tableName: TABLE_SPANS,
|
|
1129
|
+
keys: { spanId, traceId },
|
|
1130
|
+
data
|
|
1131
|
+
});
|
|
1132
|
+
} catch (error) {
|
|
1133
|
+
throw new MastraError(
|
|
1134
|
+
{
|
|
1135
|
+
id: createStorageErrorId("MSSQL", "UPDATE_SPAN", "FAILED"),
|
|
1136
|
+
domain: ErrorDomain.STORAGE,
|
|
1137
|
+
category: ErrorCategory.USER,
|
|
1138
|
+
details: {
|
|
1139
|
+
spanId,
|
|
1140
|
+
traceId
|
|
1141
|
+
}
|
|
1142
|
+
},
|
|
1143
|
+
error
|
|
1144
|
+
);
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
async getTracesPaginated({
|
|
1148
|
+
filters,
|
|
1149
|
+
pagination
|
|
1150
|
+
}) {
|
|
1151
|
+
const page = pagination?.page ?? 0;
|
|
1152
|
+
const perPage = pagination?.perPage ?? 10;
|
|
1153
|
+
const { entityId, entityType, ...actualFilters } = filters || {};
|
|
1154
|
+
const filtersWithDateRange = {
|
|
1155
|
+
...actualFilters,
|
|
1156
|
+
...buildDateRangeFilter(pagination?.dateRange, "startedAt"),
|
|
1157
|
+
parentSpanId: null
|
|
1158
|
+
// Only get root spans for traces
|
|
1159
|
+
};
|
|
1160
|
+
const whereClause = prepareWhereClause(filtersWithDateRange);
|
|
1161
|
+
let actualWhereClause = whereClause.sql;
|
|
1162
|
+
const params = { ...whereClause.params };
|
|
1163
|
+
let currentParamIndex = Object.keys(params).length + 1;
|
|
1164
|
+
if (entityId && entityType) {
|
|
1165
|
+
let name = "";
|
|
1166
|
+
if (entityType === "workflow") {
|
|
1167
|
+
name = `workflow run: '${entityId}'`;
|
|
1168
|
+
} else if (entityType === "agent") {
|
|
1169
|
+
name = `agent run: '${entityId}'`;
|
|
1170
|
+
} else {
|
|
1171
|
+
const error = new MastraError({
|
|
1172
|
+
id: createStorageErrorId("MSSQL", "GET_TRACES_PAGINATED", "INVALID_ENTITY_TYPE"),
|
|
1173
|
+
domain: ErrorDomain.STORAGE,
|
|
1174
|
+
category: ErrorCategory.USER,
|
|
1175
|
+
details: {
|
|
1176
|
+
entityType
|
|
1177
|
+
},
|
|
1178
|
+
text: `Cannot filter by entity type: ${entityType}`
|
|
1179
|
+
});
|
|
1180
|
+
throw error;
|
|
1181
|
+
}
|
|
1182
|
+
const entityParam = `p${currentParamIndex++}`;
|
|
1183
|
+
if (actualWhereClause) {
|
|
1184
|
+
actualWhereClause += ` AND [name] = @${entityParam}`;
|
|
1185
|
+
} else {
|
|
1186
|
+
actualWhereClause = ` WHERE [name] = @${entityParam}`;
|
|
1187
|
+
}
|
|
1188
|
+
params[entityParam] = name;
|
|
1189
|
+
}
|
|
1190
|
+
const tableName = getTableName({
|
|
1191
|
+
indexName: TABLE_SPANS,
|
|
1192
|
+
schemaName: getSchemaName(this.schema)
|
|
1193
|
+
});
|
|
1194
|
+
try {
|
|
1195
|
+
const countRequest = this.pool.request();
|
|
1196
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
1197
|
+
countRequest.input(key, value);
|
|
1198
|
+
});
|
|
1199
|
+
const countResult = await countRequest.query(
|
|
1200
|
+
`SELECT COUNT(*) as count FROM ${tableName}${actualWhereClause}`
|
|
1201
|
+
);
|
|
1202
|
+
const total = countResult.recordset[0]?.count ?? 0;
|
|
1203
|
+
if (total === 0) {
|
|
1204
|
+
return {
|
|
1205
|
+
pagination: {
|
|
1206
|
+
total: 0,
|
|
1207
|
+
page,
|
|
1208
|
+
perPage,
|
|
1209
|
+
hasMore: false
|
|
1210
|
+
},
|
|
1211
|
+
spans: []
|
|
1212
|
+
};
|
|
1213
|
+
}
|
|
1214
|
+
const dataRequest = this.pool.request();
|
|
1215
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
1216
|
+
dataRequest.input(key, value);
|
|
1217
|
+
});
|
|
1218
|
+
dataRequest.input("offset", page * perPage);
|
|
1219
|
+
dataRequest.input("limit", perPage);
|
|
1220
|
+
const dataResult = await dataRequest.query(
|
|
1221
|
+
`SELECT * FROM ${tableName}${actualWhereClause} ORDER BY [startedAt] DESC OFFSET @offset ROWS FETCH NEXT @limit ROWS ONLY`
|
|
1222
|
+
);
|
|
1223
|
+
const spans = dataResult.recordset.map(
|
|
1224
|
+
(row) => transformFromSqlRow({
|
|
1225
|
+
tableName: TABLE_SPANS,
|
|
1226
|
+
sqlRow: row
|
|
1227
|
+
})
|
|
1228
|
+
);
|
|
1229
|
+
return {
|
|
1230
|
+
pagination: {
|
|
1231
|
+
total,
|
|
1232
|
+
page,
|
|
1233
|
+
perPage,
|
|
1234
|
+
hasMore: (page + 1) * perPage < total
|
|
1235
|
+
},
|
|
1236
|
+
spans
|
|
1237
|
+
};
|
|
1238
|
+
} catch (error) {
|
|
1239
|
+
throw new MastraError(
|
|
1240
|
+
{
|
|
1241
|
+
id: createStorageErrorId("MSSQL", "GET_TRACES_PAGINATED", "FAILED"),
|
|
1242
|
+
domain: ErrorDomain.STORAGE,
|
|
1243
|
+
category: ErrorCategory.USER
|
|
1244
|
+
},
|
|
1245
|
+
error
|
|
1246
|
+
);
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
async batchCreateSpans(args) {
|
|
1250
|
+
if (!args.records || args.records.length === 0) {
|
|
1251
|
+
return;
|
|
1252
|
+
}
|
|
1253
|
+
try {
|
|
1254
|
+
await this.operations.batchInsert({
|
|
1255
|
+
tableName: TABLE_SPANS,
|
|
1256
|
+
records: args.records.map((span) => ({
|
|
1257
|
+
...span,
|
|
1258
|
+
startedAt: span.startedAt instanceof Date ? span.startedAt.toISOString() : span.startedAt,
|
|
1259
|
+
endedAt: span.endedAt instanceof Date ? span.endedAt.toISOString() : span.endedAt
|
|
1260
|
+
}))
|
|
1261
|
+
});
|
|
1262
|
+
} catch (error) {
|
|
1263
|
+
throw new MastraError(
|
|
1264
|
+
{
|
|
1265
|
+
id: createStorageErrorId("MSSQL", "BATCH_CREATE_SPANS", "FAILED"),
|
|
1266
|
+
domain: ErrorDomain.STORAGE,
|
|
1267
|
+
category: ErrorCategory.USER,
|
|
1268
|
+
details: {
|
|
1269
|
+
count: args.records.length
|
|
1270
|
+
}
|
|
1271
|
+
},
|
|
1272
|
+
error
|
|
1273
|
+
);
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
async batchUpdateSpans(args) {
|
|
1277
|
+
if (!args.records || args.records.length === 0) {
|
|
1278
|
+
return;
|
|
1279
|
+
}
|
|
1280
|
+
try {
|
|
1281
|
+
const updates = args.records.map(({ traceId, spanId, updates: data }) => {
|
|
1282
|
+
const processedData = { ...data };
|
|
1283
|
+
if (processedData.endedAt instanceof Date) {
|
|
1284
|
+
processedData.endedAt = processedData.endedAt.toISOString();
|
|
1285
|
+
}
|
|
1286
|
+
if (processedData.startedAt instanceof Date) {
|
|
1287
|
+
processedData.startedAt = processedData.startedAt.toISOString();
|
|
1288
|
+
}
|
|
1289
|
+
return {
|
|
1290
|
+
keys: { spanId, traceId },
|
|
1291
|
+
data: processedData
|
|
1292
|
+
};
|
|
1293
|
+
});
|
|
1294
|
+
await this.operations.batchUpdate({
|
|
1295
|
+
tableName: TABLE_SPANS,
|
|
1296
|
+
updates
|
|
1297
|
+
});
|
|
1298
|
+
} catch (error) {
|
|
1299
|
+
throw new MastraError(
|
|
1300
|
+
{
|
|
1301
|
+
id: createStorageErrorId("MSSQL", "BATCH_UPDATE_SPANS", "FAILED"),
|
|
1302
|
+
domain: ErrorDomain.STORAGE,
|
|
1303
|
+
category: ErrorCategory.USER,
|
|
1304
|
+
details: {
|
|
1305
|
+
count: args.records.length
|
|
1306
|
+
}
|
|
1307
|
+
},
|
|
1308
|
+
error
|
|
1309
|
+
);
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
async batchDeleteTraces(args) {
|
|
1313
|
+
if (!args.traceIds || args.traceIds.length === 0) {
|
|
1314
|
+
return;
|
|
1315
|
+
}
|
|
1316
|
+
try {
|
|
1317
|
+
const keys = args.traceIds.map((traceId) => ({ traceId }));
|
|
1318
|
+
await this.operations.batchDelete({
|
|
1319
|
+
tableName: TABLE_SPANS,
|
|
1320
|
+
keys
|
|
1321
|
+
});
|
|
1322
|
+
} catch (error) {
|
|
1323
|
+
throw new MastraError(
|
|
1324
|
+
{
|
|
1325
|
+
id: createStorageErrorId("MSSQL", "BATCH_DELETE_TRACES", "FAILED"),
|
|
1326
|
+
domain: ErrorDomain.STORAGE,
|
|
1327
|
+
category: ErrorCategory.USER,
|
|
1328
|
+
details: {
|
|
1329
|
+
count: args.traceIds.length
|
|
1330
|
+
}
|
|
1331
|
+
},
|
|
1332
|
+
error
|
|
1333
|
+
);
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
};
|
|
1337
|
+
var StoreOperationsMSSQL = class extends StoreOperations {
|
|
1338
|
+
pool;
|
|
1339
|
+
schemaName;
|
|
1340
|
+
setupSchemaPromise = null;
|
|
1341
|
+
schemaSetupComplete = void 0;
|
|
1342
|
+
getSqlType(type, isPrimaryKey = false, useLargeStorage = false) {
|
|
1343
|
+
switch (type) {
|
|
1344
|
+
case "text":
|
|
1345
|
+
if (useLargeStorage) {
|
|
1346
|
+
return "NVARCHAR(MAX)";
|
|
1347
|
+
}
|
|
1348
|
+
return isPrimaryKey ? "NVARCHAR(255)" : "NVARCHAR(400)";
|
|
1349
|
+
case "timestamp":
|
|
1350
|
+
return "DATETIME2(7)";
|
|
1351
|
+
case "uuid":
|
|
1352
|
+
return "UNIQUEIDENTIFIER";
|
|
1353
|
+
case "jsonb":
|
|
1354
|
+
return "NVARCHAR(MAX)";
|
|
1355
|
+
case "integer":
|
|
1356
|
+
return "INT";
|
|
1357
|
+
case "bigint":
|
|
1358
|
+
return "BIGINT";
|
|
1359
|
+
case "float":
|
|
1360
|
+
return "FLOAT";
|
|
1361
|
+
case "boolean":
|
|
1362
|
+
return "BIT";
|
|
1363
|
+
default:
|
|
1364
|
+
throw new MastraError({
|
|
1365
|
+
id: createStorageErrorId("MSSQL", "TYPE", "NOT_SUPPORTED"),
|
|
1366
|
+
domain: ErrorDomain.STORAGE,
|
|
1367
|
+
category: ErrorCategory.THIRD_PARTY
|
|
1368
|
+
});
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
constructor({ pool, schemaName }) {
|
|
1372
|
+
super();
|
|
1373
|
+
this.pool = pool;
|
|
1374
|
+
this.schemaName = schemaName;
|
|
1375
|
+
}
|
|
1376
|
+
async hasColumn(table, column) {
|
|
1377
|
+
const schema = this.schemaName || "dbo";
|
|
1378
|
+
const request = this.pool.request();
|
|
1379
|
+
request.input("schema", schema);
|
|
1380
|
+
request.input("table", table);
|
|
1381
|
+
request.input("column", column);
|
|
1382
|
+
request.input("columnLower", column.toLowerCase());
|
|
1383
|
+
const result = await request.query(
|
|
1384
|
+
`SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = @table AND (COLUMN_NAME = @column OR COLUMN_NAME = @columnLower)`
|
|
1385
|
+
);
|
|
1386
|
+
return result.recordset.length > 0;
|
|
1387
|
+
}
|
|
1388
|
+
async setupSchema() {
|
|
1389
|
+
if (!this.schemaName || this.schemaSetupComplete) {
|
|
1390
|
+
return;
|
|
1391
|
+
}
|
|
1392
|
+
if (!this.setupSchemaPromise) {
|
|
1393
|
+
this.setupSchemaPromise = (async () => {
|
|
1394
|
+
try {
|
|
1395
|
+
const checkRequest = this.pool.request();
|
|
1396
|
+
checkRequest.input("schemaName", this.schemaName);
|
|
1397
|
+
const checkResult = await checkRequest.query(`
|
|
1398
|
+
SELECT 1 AS found FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = @schemaName
|
|
1399
|
+
`);
|
|
1400
|
+
const schemaExists = Array.isArray(checkResult.recordset) && checkResult.recordset.length > 0;
|
|
1401
|
+
if (!schemaExists) {
|
|
1402
|
+
try {
|
|
1403
|
+
await this.pool.request().query(`CREATE SCHEMA [${this.schemaName}]`);
|
|
1404
|
+
this.logger?.info?.(`Schema "${this.schemaName}" created successfully`);
|
|
1405
|
+
} catch (error) {
|
|
1406
|
+
this.logger?.error?.(`Failed to create schema "${this.schemaName}"`, { error });
|
|
1407
|
+
throw new Error(
|
|
1408
|
+
`Unable to create schema "${this.schemaName}". This requires CREATE privilege on the database. Either create the schema manually or grant CREATE privilege to the user.`
|
|
1409
|
+
);
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
this.schemaSetupComplete = true;
|
|
1413
|
+
this.logger?.debug?.(`Schema "${this.schemaName}" is ready for use`);
|
|
1414
|
+
} catch (error) {
|
|
1415
|
+
this.schemaSetupComplete = void 0;
|
|
1416
|
+
this.setupSchemaPromise = null;
|
|
1417
|
+
throw error;
|
|
1418
|
+
} finally {
|
|
1419
|
+
this.setupSchemaPromise = null;
|
|
1420
|
+
}
|
|
1421
|
+
})();
|
|
1422
|
+
}
|
|
1423
|
+
await this.setupSchemaPromise;
|
|
1424
|
+
}
|
|
1425
|
+
async insert({
|
|
1426
|
+
tableName,
|
|
1427
|
+
record,
|
|
1428
|
+
transaction
|
|
1429
|
+
}) {
|
|
1430
|
+
try {
|
|
1431
|
+
const columns = Object.keys(record);
|
|
1432
|
+
const parsedColumns = columns.map((col) => parseSqlIdentifier(col, "column name"));
|
|
1433
|
+
const paramNames = columns.map((_, i) => `@param${i}`);
|
|
1434
|
+
const insertSql = `INSERT INTO ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} (${parsedColumns.map((c) => `[${c}]`).join(", ")}) VALUES (${paramNames.join(", ")})`;
|
|
1435
|
+
const request = transaction ? transaction.request() : this.pool.request();
|
|
1436
|
+
columns.forEach((col, i) => {
|
|
1437
|
+
const value = record[col];
|
|
1438
|
+
const preparedValue = this.prepareValue(value, col, tableName);
|
|
1439
|
+
if (preparedValue instanceof Date) {
|
|
1440
|
+
request.input(`param${i}`, sql3.DateTime2, preparedValue);
|
|
1441
|
+
} else if (preparedValue === null || preparedValue === void 0) {
|
|
1442
|
+
request.input(`param${i}`, this.getMssqlType(tableName, col), null);
|
|
1118
1443
|
} else {
|
|
1119
|
-
request.input(`param${i}`,
|
|
1444
|
+
request.input(`param${i}`, preparedValue);
|
|
1120
1445
|
}
|
|
1121
1446
|
});
|
|
1122
1447
|
await request.query(insertSql);
|
|
1123
1448
|
} catch (error) {
|
|
1124
1449
|
throw new MastraError(
|
|
1125
1450
|
{
|
|
1126
|
-
id: "
|
|
1451
|
+
id: createStorageErrorId("MSSQL", "INSERT", "FAILED"),
|
|
1127
1452
|
domain: ErrorDomain.STORAGE,
|
|
1128
1453
|
category: ErrorCategory.THIRD_PARTY,
|
|
1129
1454
|
details: {
|
|
@@ -1140,7 +1465,7 @@ var StoreOperationsMSSQL = class extends StoreOperations {
|
|
|
1140
1465
|
try {
|
|
1141
1466
|
await this.pool.request().query(`TRUNCATE TABLE ${fullTableName}`);
|
|
1142
1467
|
} catch (truncateError) {
|
|
1143
|
-
if (truncateError
|
|
1468
|
+
if (truncateError?.number === 4712) {
|
|
1144
1469
|
await this.pool.request().query(`DELETE FROM ${fullTableName}`);
|
|
1145
1470
|
} else {
|
|
1146
1471
|
throw truncateError;
|
|
@@ -1149,7 +1474,7 @@ var StoreOperationsMSSQL = class extends StoreOperations {
|
|
|
1149
1474
|
} catch (error) {
|
|
1150
1475
|
throw new MastraError(
|
|
1151
1476
|
{
|
|
1152
|
-
id: "
|
|
1477
|
+
id: createStorageErrorId("MSSQL", "CLEAR_TABLE", "FAILED"),
|
|
1153
1478
|
domain: ErrorDomain.STORAGE,
|
|
1154
1479
|
category: ErrorCategory.THIRD_PARTY,
|
|
1155
1480
|
details: {
|
|
@@ -1163,9 +1488,11 @@ var StoreOperationsMSSQL = class extends StoreOperations {
|
|
|
1163
1488
|
getDefaultValue(type) {
|
|
1164
1489
|
switch (type) {
|
|
1165
1490
|
case "timestamp":
|
|
1166
|
-
return "DEFAULT
|
|
1491
|
+
return "DEFAULT SYSUTCDATETIME()";
|
|
1167
1492
|
case "jsonb":
|
|
1168
1493
|
return "DEFAULT N'{}'";
|
|
1494
|
+
case "boolean":
|
|
1495
|
+
return "DEFAULT 0";
|
|
1169
1496
|
default:
|
|
1170
1497
|
return super.getDefaultValue(type);
|
|
1171
1498
|
}
|
|
@@ -1176,13 +1503,29 @@ var StoreOperationsMSSQL = class extends StoreOperations {
|
|
|
1176
1503
|
}) {
|
|
1177
1504
|
try {
|
|
1178
1505
|
const uniqueConstraintColumns = tableName === TABLE_WORKFLOW_SNAPSHOT ? ["workflow_name", "run_id"] : [];
|
|
1506
|
+
const largeDataColumns = [
|
|
1507
|
+
"workingMemory",
|
|
1508
|
+
"snapshot",
|
|
1509
|
+
"metadata",
|
|
1510
|
+
"content",
|
|
1511
|
+
// messages.content - can be very long conversation content
|
|
1512
|
+
"input",
|
|
1513
|
+
// evals.input - test input data
|
|
1514
|
+
"output",
|
|
1515
|
+
// evals.output - test output data
|
|
1516
|
+
"instructions",
|
|
1517
|
+
// evals.instructions - evaluation instructions
|
|
1518
|
+
"other"
|
|
1519
|
+
// traces.other - additional trace data
|
|
1520
|
+
];
|
|
1179
1521
|
const columns = Object.entries(schema).map(([name, def]) => {
|
|
1180
1522
|
const parsedName = parseSqlIdentifier(name, "column name");
|
|
1181
1523
|
const constraints = [];
|
|
1182
1524
|
if (def.primaryKey) constraints.push("PRIMARY KEY");
|
|
1183
1525
|
if (!def.nullable) constraints.push("NOT NULL");
|
|
1184
1526
|
const isIndexed = !!def.primaryKey || uniqueConstraintColumns.includes(name);
|
|
1185
|
-
|
|
1527
|
+
const useLargeStorage = largeDataColumns.includes(name);
|
|
1528
|
+
return `[${parsedName}] ${this.getSqlType(def.type, isIndexed, useLargeStorage)} ${constraints.join(" ")}`.trim();
|
|
1186
1529
|
}).join(",\n");
|
|
1187
1530
|
if (this.schemaName) {
|
|
1188
1531
|
await this.setupSchema();
|
|
@@ -1234,7 +1577,7 @@ ${columns}
|
|
|
1234
1577
|
} catch (error) {
|
|
1235
1578
|
throw new MastraError(
|
|
1236
1579
|
{
|
|
1237
|
-
id: "
|
|
1580
|
+
id: createStorageErrorId("MSSQL", "CREATE_TABLE", "FAILED"),
|
|
1238
1581
|
domain: ErrorDomain.STORAGE,
|
|
1239
1582
|
category: ErrorCategory.THIRD_PARTY,
|
|
1240
1583
|
details: {
|
|
@@ -1269,7 +1612,19 @@ ${columns}
|
|
|
1269
1612
|
const columnExists = Array.isArray(checkResult.recordset) && checkResult.recordset.length > 0;
|
|
1270
1613
|
if (!columnExists) {
|
|
1271
1614
|
const columnDef = schema[columnName];
|
|
1272
|
-
const
|
|
1615
|
+
const largeDataColumns = [
|
|
1616
|
+
"workingMemory",
|
|
1617
|
+
"snapshot",
|
|
1618
|
+
"metadata",
|
|
1619
|
+
"content",
|
|
1620
|
+
"input",
|
|
1621
|
+
"output",
|
|
1622
|
+
"instructions",
|
|
1623
|
+
"other"
|
|
1624
|
+
];
|
|
1625
|
+
const useLargeStorage = largeDataColumns.includes(columnName);
|
|
1626
|
+
const isIndexed = !!columnDef.primaryKey;
|
|
1627
|
+
const sqlType = this.getSqlType(columnDef.type, isIndexed, useLargeStorage);
|
|
1273
1628
|
const nullable = columnDef.nullable === false ? "NOT NULL" : "";
|
|
1274
1629
|
const defaultValue = columnDef.nullable === false ? this.getDefaultValue(columnDef.type) : "";
|
|
1275
1630
|
const parsedColumnName = parseSqlIdentifier(columnName, "column name");
|
|
@@ -1282,7 +1637,7 @@ ${columns}
|
|
|
1282
1637
|
} catch (error) {
|
|
1283
1638
|
throw new MastraError(
|
|
1284
1639
|
{
|
|
1285
|
-
id: "
|
|
1640
|
+
id: createStorageErrorId("MSSQL", "ALTER_TABLE", "FAILED"),
|
|
1286
1641
|
domain: ErrorDomain.STORAGE,
|
|
1287
1642
|
category: ErrorCategory.THIRD_PARTY,
|
|
1288
1643
|
details: {
|
|
@@ -1297,13 +1652,17 @@ ${columns}
|
|
|
1297
1652
|
try {
|
|
1298
1653
|
const keyEntries = Object.entries(keys).map(([key, value]) => [parseSqlIdentifier(key, "column name"), value]);
|
|
1299
1654
|
const conditions = keyEntries.map(([key], i) => `[${key}] = @param${i}`).join(" AND ");
|
|
1300
|
-
const
|
|
1301
|
-
const sql7 = `SELECT * FROM ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} WHERE ${conditions}`;
|
|
1655
|
+
const sql5 = `SELECT * FROM ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} WHERE ${conditions}`;
|
|
1302
1656
|
const request = this.pool.request();
|
|
1303
|
-
|
|
1304
|
-
|
|
1657
|
+
keyEntries.forEach(([key, value], i) => {
|
|
1658
|
+
const preparedValue = this.prepareValue(value, key, tableName);
|
|
1659
|
+
if (preparedValue === null || preparedValue === void 0) {
|
|
1660
|
+
request.input(`param${i}`, this.getMssqlType(tableName, key), null);
|
|
1661
|
+
} else {
|
|
1662
|
+
request.input(`param${i}`, preparedValue);
|
|
1663
|
+
}
|
|
1305
1664
|
});
|
|
1306
|
-
const resultSet = await request.query(
|
|
1665
|
+
const resultSet = await request.query(sql5);
|
|
1307
1666
|
const result = resultSet.recordset[0] || null;
|
|
1308
1667
|
if (!result) {
|
|
1309
1668
|
return null;
|
|
@@ -1319,7 +1678,7 @@ ${columns}
|
|
|
1319
1678
|
} catch (error) {
|
|
1320
1679
|
throw new MastraError(
|
|
1321
1680
|
{
|
|
1322
|
-
id: "
|
|
1681
|
+
id: createStorageErrorId("MSSQL", "LOAD", "FAILED"),
|
|
1323
1682
|
domain: ErrorDomain.STORAGE,
|
|
1324
1683
|
category: ErrorCategory.THIRD_PARTY,
|
|
1325
1684
|
details: {
|
|
@@ -1335,14 +1694,14 @@ ${columns}
|
|
|
1335
1694
|
try {
|
|
1336
1695
|
await transaction.begin();
|
|
1337
1696
|
for (const record of records) {
|
|
1338
|
-
await this.insert({ tableName, record });
|
|
1697
|
+
await this.insert({ tableName, record, transaction });
|
|
1339
1698
|
}
|
|
1340
1699
|
await transaction.commit();
|
|
1341
1700
|
} catch (error) {
|
|
1342
1701
|
await transaction.rollback();
|
|
1343
1702
|
throw new MastraError(
|
|
1344
1703
|
{
|
|
1345
|
-
id: "
|
|
1704
|
+
id: createStorageErrorId("MSSQL", "BATCH_INSERT", "FAILED"),
|
|
1346
1705
|
domain: ErrorDomain.STORAGE,
|
|
1347
1706
|
category: ErrorCategory.THIRD_PARTY,
|
|
1348
1707
|
details: {
|
|
@@ -1361,7 +1720,7 @@ ${columns}
|
|
|
1361
1720
|
} catch (error) {
|
|
1362
1721
|
throw new MastraError(
|
|
1363
1722
|
{
|
|
1364
|
-
id: "
|
|
1723
|
+
id: createStorageErrorId("MSSQL", "DROP_TABLE", "FAILED"),
|
|
1365
1724
|
domain: ErrorDomain.STORAGE,
|
|
1366
1725
|
category: ErrorCategory.THIRD_PARTY,
|
|
1367
1726
|
details: {
|
|
@@ -1372,29 +1731,568 @@ ${columns}
|
|
|
1372
1731
|
);
|
|
1373
1732
|
}
|
|
1374
1733
|
}
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1734
|
+
/**
|
|
1735
|
+
* Prepares a value for database operations, handling Date objects and JSON serialization
|
|
1736
|
+
*/
|
|
1737
|
+
prepareValue(value, columnName, tableName) {
|
|
1738
|
+
if (value === null || value === void 0) {
|
|
1739
|
+
return value;
|
|
1740
|
+
}
|
|
1741
|
+
if (value instanceof Date) {
|
|
1742
|
+
return value;
|
|
1743
|
+
}
|
|
1744
|
+
const schema = TABLE_SCHEMAS[tableName];
|
|
1745
|
+
const columnSchema = schema?.[columnName];
|
|
1746
|
+
if (columnSchema?.type === "boolean") {
|
|
1747
|
+
return value ? 1 : 0;
|
|
1748
|
+
}
|
|
1749
|
+
if (columnSchema?.type === "jsonb") {
|
|
1750
|
+
if (typeof value === "string") {
|
|
1751
|
+
const trimmed = value.trim();
|
|
1752
|
+
if (trimmed.length > 0) {
|
|
1753
|
+
try {
|
|
1754
|
+
JSON.parse(trimmed);
|
|
1755
|
+
return trimmed;
|
|
1756
|
+
} catch {
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
return JSON.stringify(value);
|
|
1760
|
+
}
|
|
1761
|
+
if (typeof value === "bigint") {
|
|
1762
|
+
return value.toString();
|
|
1763
|
+
}
|
|
1764
|
+
return JSON.stringify(value);
|
|
1765
|
+
}
|
|
1766
|
+
if (typeof value === "object") {
|
|
1767
|
+
return JSON.stringify(value);
|
|
1768
|
+
}
|
|
1769
|
+
return value;
|
|
1381
1770
|
}
|
|
1382
|
-
|
|
1771
|
+
/**
|
|
1772
|
+
* Maps TABLE_SCHEMAS types to mssql param types (used when value is null)
|
|
1773
|
+
*/
|
|
1774
|
+
getMssqlType(tableName, columnName) {
|
|
1775
|
+
const col = TABLE_SCHEMAS[tableName]?.[columnName];
|
|
1776
|
+
switch (col?.type) {
|
|
1777
|
+
case "text":
|
|
1778
|
+
return sql3.NVarChar;
|
|
1779
|
+
case "timestamp":
|
|
1780
|
+
return sql3.DateTime2;
|
|
1781
|
+
case "uuid":
|
|
1782
|
+
return sql3.UniqueIdentifier;
|
|
1783
|
+
case "jsonb":
|
|
1784
|
+
return sql3.NVarChar;
|
|
1785
|
+
case "integer":
|
|
1786
|
+
return sql3.Int;
|
|
1787
|
+
case "bigint":
|
|
1788
|
+
return sql3.BigInt;
|
|
1789
|
+
case "float":
|
|
1790
|
+
return sql3.Float;
|
|
1791
|
+
case "boolean":
|
|
1792
|
+
return sql3.Bit;
|
|
1793
|
+
default:
|
|
1794
|
+
return sql3.NVarChar;
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
/**
|
|
1798
|
+
* Update a single record in the database
|
|
1799
|
+
*/
|
|
1800
|
+
async update({
|
|
1801
|
+
tableName,
|
|
1802
|
+
keys,
|
|
1803
|
+
data,
|
|
1804
|
+
transaction
|
|
1805
|
+
}) {
|
|
1806
|
+
try {
|
|
1807
|
+
if (!data || Object.keys(data).length === 0) {
|
|
1808
|
+
throw new MastraError({
|
|
1809
|
+
id: createStorageErrorId("MSSQL", "UPDATE", "EMPTY_DATA"),
|
|
1810
|
+
domain: ErrorDomain.STORAGE,
|
|
1811
|
+
category: ErrorCategory.USER,
|
|
1812
|
+
text: "Cannot update with empty data payload"
|
|
1813
|
+
});
|
|
1814
|
+
}
|
|
1815
|
+
if (!keys || Object.keys(keys).length === 0) {
|
|
1816
|
+
throw new MastraError({
|
|
1817
|
+
id: createStorageErrorId("MSSQL", "UPDATE", "EMPTY_KEYS"),
|
|
1818
|
+
domain: ErrorDomain.STORAGE,
|
|
1819
|
+
category: ErrorCategory.USER,
|
|
1820
|
+
text: "Cannot update without keys to identify records"
|
|
1821
|
+
});
|
|
1822
|
+
}
|
|
1823
|
+
const setClauses = [];
|
|
1824
|
+
const request = transaction ? transaction.request() : this.pool.request();
|
|
1825
|
+
let paramIndex = 0;
|
|
1826
|
+
Object.entries(data).forEach(([key, value]) => {
|
|
1827
|
+
const parsedKey = parseSqlIdentifier(key, "column name");
|
|
1828
|
+
const paramName = `set${paramIndex++}`;
|
|
1829
|
+
setClauses.push(`[${parsedKey}] = @${paramName}`);
|
|
1830
|
+
const preparedValue = this.prepareValue(value, key, tableName);
|
|
1831
|
+
if (preparedValue === null || preparedValue === void 0) {
|
|
1832
|
+
request.input(paramName, this.getMssqlType(tableName, key), null);
|
|
1833
|
+
} else {
|
|
1834
|
+
request.input(paramName, preparedValue);
|
|
1835
|
+
}
|
|
1836
|
+
});
|
|
1837
|
+
const whereConditions = [];
|
|
1838
|
+
Object.entries(keys).forEach(([key, value]) => {
|
|
1839
|
+
const parsedKey = parseSqlIdentifier(key, "column name");
|
|
1840
|
+
const paramName = `where${paramIndex++}`;
|
|
1841
|
+
whereConditions.push(`[${parsedKey}] = @${paramName}`);
|
|
1842
|
+
const preparedValue = this.prepareValue(value, key, tableName);
|
|
1843
|
+
if (preparedValue === null || preparedValue === void 0) {
|
|
1844
|
+
request.input(paramName, this.getMssqlType(tableName, key), null);
|
|
1845
|
+
} else {
|
|
1846
|
+
request.input(paramName, preparedValue);
|
|
1847
|
+
}
|
|
1848
|
+
});
|
|
1849
|
+
const tableName_ = getTableName({
|
|
1850
|
+
indexName: tableName,
|
|
1851
|
+
schemaName: getSchemaName(this.schemaName)
|
|
1852
|
+
});
|
|
1853
|
+
const updateSql = `UPDATE ${tableName_} SET ${setClauses.join(", ")} WHERE ${whereConditions.join(" AND ")}`;
|
|
1854
|
+
await request.query(updateSql);
|
|
1855
|
+
} catch (error) {
|
|
1856
|
+
throw new MastraError(
|
|
1857
|
+
{
|
|
1858
|
+
id: createStorageErrorId("MSSQL", "UPDATE", "FAILED"),
|
|
1859
|
+
domain: ErrorDomain.STORAGE,
|
|
1860
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1861
|
+
details: {
|
|
1862
|
+
tableName
|
|
1863
|
+
}
|
|
1864
|
+
},
|
|
1865
|
+
error
|
|
1866
|
+
);
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
/**
|
|
1870
|
+
* Update multiple records in a single batch transaction
|
|
1871
|
+
*/
|
|
1872
|
+
async batchUpdate({
|
|
1873
|
+
tableName,
|
|
1874
|
+
updates
|
|
1875
|
+
}) {
|
|
1876
|
+
const transaction = this.pool.transaction();
|
|
1877
|
+
try {
|
|
1878
|
+
await transaction.begin();
|
|
1879
|
+
for (const { keys, data } of updates) {
|
|
1880
|
+
await this.update({ tableName, keys, data, transaction });
|
|
1881
|
+
}
|
|
1882
|
+
await transaction.commit();
|
|
1883
|
+
} catch (error) {
|
|
1884
|
+
await transaction.rollback();
|
|
1885
|
+
throw new MastraError(
|
|
1886
|
+
{
|
|
1887
|
+
id: createStorageErrorId("MSSQL", "BATCH_UPDATE", "FAILED"),
|
|
1888
|
+
domain: ErrorDomain.STORAGE,
|
|
1889
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1890
|
+
details: {
|
|
1891
|
+
tableName,
|
|
1892
|
+
numberOfRecords: updates.length
|
|
1893
|
+
}
|
|
1894
|
+
},
|
|
1895
|
+
error
|
|
1896
|
+
);
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1899
|
+
/**
|
|
1900
|
+
* Delete multiple records by keys
|
|
1901
|
+
*/
|
|
1902
|
+
async batchDelete({ tableName, keys }) {
|
|
1903
|
+
if (keys.length === 0) {
|
|
1904
|
+
return;
|
|
1905
|
+
}
|
|
1906
|
+
const tableName_ = getTableName({
|
|
1907
|
+
indexName: tableName,
|
|
1908
|
+
schemaName: getSchemaName(this.schemaName)
|
|
1909
|
+
});
|
|
1910
|
+
const transaction = this.pool.transaction();
|
|
1911
|
+
try {
|
|
1912
|
+
await transaction.begin();
|
|
1913
|
+
for (const keySet of keys) {
|
|
1914
|
+
const conditions = [];
|
|
1915
|
+
const request = transaction.request();
|
|
1916
|
+
let paramIndex = 0;
|
|
1917
|
+
Object.entries(keySet).forEach(([key, value]) => {
|
|
1918
|
+
const parsedKey = parseSqlIdentifier(key, "column name");
|
|
1919
|
+
const paramName = `p${paramIndex++}`;
|
|
1920
|
+
conditions.push(`[${parsedKey}] = @${paramName}`);
|
|
1921
|
+
const preparedValue = this.prepareValue(value, key, tableName);
|
|
1922
|
+
if (preparedValue === null || preparedValue === void 0) {
|
|
1923
|
+
request.input(paramName, this.getMssqlType(tableName, key), null);
|
|
1924
|
+
} else {
|
|
1925
|
+
request.input(paramName, preparedValue);
|
|
1926
|
+
}
|
|
1927
|
+
});
|
|
1928
|
+
const deleteSql = `DELETE FROM ${tableName_} WHERE ${conditions.join(" AND ")}`;
|
|
1929
|
+
await request.query(deleteSql);
|
|
1930
|
+
}
|
|
1931
|
+
await transaction.commit();
|
|
1932
|
+
} catch (error) {
|
|
1933
|
+
await transaction.rollback();
|
|
1934
|
+
throw new MastraError(
|
|
1935
|
+
{
|
|
1936
|
+
id: createStorageErrorId("MSSQL", "BATCH_DELETE", "FAILED"),
|
|
1937
|
+
domain: ErrorDomain.STORAGE,
|
|
1938
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1939
|
+
details: {
|
|
1940
|
+
tableName,
|
|
1941
|
+
numberOfRecords: keys.length
|
|
1942
|
+
}
|
|
1943
|
+
},
|
|
1944
|
+
error
|
|
1945
|
+
);
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
/**
|
|
1949
|
+
* Create a new index on a table
|
|
1950
|
+
*/
|
|
1951
|
+
async createIndex(options) {
|
|
1952
|
+
try {
|
|
1953
|
+
const { name, table, columns, unique = false, where } = options;
|
|
1954
|
+
const schemaName = this.schemaName || "dbo";
|
|
1955
|
+
const fullTableName = getTableName({
|
|
1956
|
+
indexName: table,
|
|
1957
|
+
schemaName: getSchemaName(this.schemaName)
|
|
1958
|
+
});
|
|
1959
|
+
const indexNameSafe = parseSqlIdentifier(name, "index name");
|
|
1960
|
+
const checkRequest = this.pool.request();
|
|
1961
|
+
checkRequest.input("indexName", indexNameSafe);
|
|
1962
|
+
checkRequest.input("schemaName", schemaName);
|
|
1963
|
+
checkRequest.input("tableName", table);
|
|
1964
|
+
const indexExists = await checkRequest.query(`
|
|
1965
|
+
SELECT 1 as found
|
|
1966
|
+
FROM sys.indexes i
|
|
1967
|
+
INNER JOIN sys.tables t ON i.object_id = t.object_id
|
|
1968
|
+
INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
|
|
1969
|
+
WHERE i.name = @indexName
|
|
1970
|
+
AND s.name = @schemaName
|
|
1971
|
+
AND t.name = @tableName
|
|
1972
|
+
`);
|
|
1973
|
+
if (indexExists.recordset && indexExists.recordset.length > 0) {
|
|
1974
|
+
return;
|
|
1975
|
+
}
|
|
1976
|
+
const uniqueStr = unique ? "UNIQUE " : "";
|
|
1977
|
+
const columnsStr = columns.map((col) => {
|
|
1978
|
+
if (col.includes(" DESC") || col.includes(" ASC")) {
|
|
1979
|
+
const [colName, ...modifiers] = col.split(" ");
|
|
1980
|
+
if (!colName) {
|
|
1981
|
+
throw new Error(`Invalid column specification: ${col}`);
|
|
1982
|
+
}
|
|
1983
|
+
return `[${parseSqlIdentifier(colName, "column name")}] ${modifiers.join(" ")}`;
|
|
1984
|
+
}
|
|
1985
|
+
return `[${parseSqlIdentifier(col, "column name")}]`;
|
|
1986
|
+
}).join(", ");
|
|
1987
|
+
const whereStr = where ? ` WHERE ${where}` : "";
|
|
1988
|
+
const createIndexSql = `CREATE ${uniqueStr}INDEX [${indexNameSafe}] ON ${fullTableName} (${columnsStr})${whereStr}`;
|
|
1989
|
+
await this.pool.request().query(createIndexSql);
|
|
1990
|
+
} catch (error) {
|
|
1991
|
+
throw new MastraError(
|
|
1992
|
+
{
|
|
1993
|
+
id: createStorageErrorId("MSSQL", "INDEX_CREATE", "FAILED"),
|
|
1994
|
+
domain: ErrorDomain.STORAGE,
|
|
1995
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1996
|
+
details: {
|
|
1997
|
+
indexName: options.name,
|
|
1998
|
+
tableName: options.table
|
|
1999
|
+
}
|
|
2000
|
+
},
|
|
2001
|
+
error
|
|
2002
|
+
);
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
/**
|
|
2006
|
+
* Drop an existing index
|
|
2007
|
+
*/
|
|
2008
|
+
async dropIndex(indexName) {
|
|
2009
|
+
try {
|
|
2010
|
+
const schemaName = this.schemaName || "dbo";
|
|
2011
|
+
const indexNameSafe = parseSqlIdentifier(indexName, "index name");
|
|
2012
|
+
const checkRequest = this.pool.request();
|
|
2013
|
+
checkRequest.input("indexName", indexNameSafe);
|
|
2014
|
+
checkRequest.input("schemaName", schemaName);
|
|
2015
|
+
const result = await checkRequest.query(`
|
|
2016
|
+
SELECT t.name as table_name
|
|
2017
|
+
FROM sys.indexes i
|
|
2018
|
+
INNER JOIN sys.tables t ON i.object_id = t.object_id
|
|
2019
|
+
INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
|
|
2020
|
+
WHERE i.name = @indexName
|
|
2021
|
+
AND s.name = @schemaName
|
|
2022
|
+
`);
|
|
2023
|
+
if (!result.recordset || result.recordset.length === 0) {
|
|
2024
|
+
return;
|
|
2025
|
+
}
|
|
2026
|
+
if (result.recordset.length > 1) {
|
|
2027
|
+
const tables = result.recordset.map((r) => r.table_name).join(", ");
|
|
2028
|
+
throw new MastraError({
|
|
2029
|
+
id: createStorageErrorId("MSSQL", "INDEX", "AMBIGUOUS"),
|
|
2030
|
+
domain: ErrorDomain.STORAGE,
|
|
2031
|
+
category: ErrorCategory.USER,
|
|
2032
|
+
text: `Index "${indexNameSafe}" exists on multiple tables (${tables}) in schema "${schemaName}". Please drop indexes manually or ensure unique index names.`
|
|
2033
|
+
});
|
|
2034
|
+
}
|
|
2035
|
+
const tableName = result.recordset[0].table_name;
|
|
2036
|
+
const fullTableName = getTableName({
|
|
2037
|
+
indexName: tableName,
|
|
2038
|
+
schemaName: getSchemaName(this.schemaName)
|
|
2039
|
+
});
|
|
2040
|
+
const dropSql = `DROP INDEX [${indexNameSafe}] ON ${fullTableName}`;
|
|
2041
|
+
await this.pool.request().query(dropSql);
|
|
2042
|
+
} catch (error) {
|
|
2043
|
+
throw new MastraError(
|
|
2044
|
+
{
|
|
2045
|
+
id: createStorageErrorId("MSSQL", "INDEX_DROP", "FAILED"),
|
|
2046
|
+
domain: ErrorDomain.STORAGE,
|
|
2047
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2048
|
+
details: {
|
|
2049
|
+
indexName
|
|
2050
|
+
}
|
|
2051
|
+
},
|
|
2052
|
+
error
|
|
2053
|
+
);
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
/**
|
|
2057
|
+
* List indexes for a specific table or all tables
|
|
2058
|
+
*/
|
|
2059
|
+
async listIndexes(tableName) {
|
|
2060
|
+
try {
|
|
2061
|
+
const schemaName = this.schemaName || "dbo";
|
|
2062
|
+
let query;
|
|
2063
|
+
const request = this.pool.request();
|
|
2064
|
+
request.input("schemaName", schemaName);
|
|
2065
|
+
if (tableName) {
|
|
2066
|
+
query = `
|
|
2067
|
+
SELECT
|
|
2068
|
+
i.name as name,
|
|
2069
|
+
o.name as [table],
|
|
2070
|
+
i.is_unique as is_unique,
|
|
2071
|
+
CAST(SUM(s.used_page_count) * 8 / 1024.0 AS VARCHAR(50)) + ' MB' as size
|
|
2072
|
+
FROM sys.indexes i
|
|
2073
|
+
INNER JOIN sys.objects o ON i.object_id = o.object_id
|
|
2074
|
+
INNER JOIN sys.schemas sch ON o.schema_id = sch.schema_id
|
|
2075
|
+
LEFT JOIN sys.dm_db_partition_stats s ON i.object_id = s.object_id AND i.index_id = s.index_id
|
|
2076
|
+
WHERE sch.name = @schemaName
|
|
2077
|
+
AND o.name = @tableName
|
|
2078
|
+
AND i.name IS NOT NULL
|
|
2079
|
+
GROUP BY i.name, o.name, i.is_unique
|
|
2080
|
+
`;
|
|
2081
|
+
request.input("tableName", tableName);
|
|
2082
|
+
} else {
|
|
2083
|
+
query = `
|
|
2084
|
+
SELECT
|
|
2085
|
+
i.name as name,
|
|
2086
|
+
o.name as [table],
|
|
2087
|
+
i.is_unique as is_unique,
|
|
2088
|
+
CAST(SUM(s.used_page_count) * 8 / 1024.0 AS VARCHAR(50)) + ' MB' as size
|
|
2089
|
+
FROM sys.indexes i
|
|
2090
|
+
INNER JOIN sys.objects o ON i.object_id = o.object_id
|
|
2091
|
+
INNER JOIN sys.schemas sch ON o.schema_id = sch.schema_id
|
|
2092
|
+
LEFT JOIN sys.dm_db_partition_stats s ON i.object_id = s.object_id AND i.index_id = s.index_id
|
|
2093
|
+
WHERE sch.name = @schemaName
|
|
2094
|
+
AND i.name IS NOT NULL
|
|
2095
|
+
GROUP BY i.name, o.name, i.is_unique
|
|
2096
|
+
`;
|
|
2097
|
+
}
|
|
2098
|
+
const result = await request.query(query);
|
|
2099
|
+
const indexes = [];
|
|
2100
|
+
for (const row of result.recordset) {
|
|
2101
|
+
const colRequest = this.pool.request();
|
|
2102
|
+
colRequest.input("indexName", row.name);
|
|
2103
|
+
colRequest.input("schemaName", schemaName);
|
|
2104
|
+
const colResult = await colRequest.query(`
|
|
2105
|
+
SELECT c.name as column_name
|
|
2106
|
+
FROM sys.indexes i
|
|
2107
|
+
INNER JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
|
|
2108
|
+
INNER JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
|
|
2109
|
+
INNER JOIN sys.objects o ON i.object_id = o.object_id
|
|
2110
|
+
INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
|
|
2111
|
+
WHERE i.name = @indexName
|
|
2112
|
+
AND s.name = @schemaName
|
|
2113
|
+
ORDER BY ic.key_ordinal
|
|
2114
|
+
`);
|
|
2115
|
+
indexes.push({
|
|
2116
|
+
name: row.name,
|
|
2117
|
+
table: row.table,
|
|
2118
|
+
columns: colResult.recordset.map((c) => c.column_name),
|
|
2119
|
+
unique: row.is_unique || false,
|
|
2120
|
+
size: row.size || "0 MB",
|
|
2121
|
+
definition: ""
|
|
2122
|
+
// MSSQL doesn't store definition like PG
|
|
2123
|
+
});
|
|
2124
|
+
}
|
|
2125
|
+
return indexes;
|
|
2126
|
+
} catch (error) {
|
|
2127
|
+
throw new MastraError(
|
|
2128
|
+
{
|
|
2129
|
+
id: createStorageErrorId("MSSQL", "INDEX_LIST", "FAILED"),
|
|
2130
|
+
domain: ErrorDomain.STORAGE,
|
|
2131
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2132
|
+
details: tableName ? {
|
|
2133
|
+
tableName
|
|
2134
|
+
} : {}
|
|
2135
|
+
},
|
|
2136
|
+
error
|
|
2137
|
+
);
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2140
|
+
/**
|
|
2141
|
+
* Get detailed statistics for a specific index
|
|
2142
|
+
*/
|
|
2143
|
+
async describeIndex(indexName) {
|
|
2144
|
+
try {
|
|
2145
|
+
const schemaName = this.schemaName || "dbo";
|
|
2146
|
+
const request = this.pool.request();
|
|
2147
|
+
request.input("indexName", indexName);
|
|
2148
|
+
request.input("schemaName", schemaName);
|
|
2149
|
+
const query = `
|
|
2150
|
+
SELECT
|
|
2151
|
+
i.name as name,
|
|
2152
|
+
o.name as [table],
|
|
2153
|
+
i.is_unique as is_unique,
|
|
2154
|
+
CAST(SUM(s.used_page_count) * 8 / 1024.0 AS VARCHAR(50)) + ' MB' as size,
|
|
2155
|
+
i.type_desc as method,
|
|
2156
|
+
ISNULL(us.user_scans, 0) as scans,
|
|
2157
|
+
ISNULL(us.user_seeks + us.user_scans, 0) as tuples_read,
|
|
2158
|
+
ISNULL(us.user_lookups, 0) as tuples_fetched
|
|
2159
|
+
FROM sys.indexes i
|
|
2160
|
+
INNER JOIN sys.objects o ON i.object_id = o.object_id
|
|
2161
|
+
INNER JOIN sys.schemas sch ON o.schema_id = sch.schema_id
|
|
2162
|
+
LEFT JOIN sys.dm_db_partition_stats s ON i.object_id = s.object_id AND i.index_id = s.index_id
|
|
2163
|
+
LEFT JOIN sys.dm_db_index_usage_stats us ON i.object_id = us.object_id AND i.index_id = us.index_id
|
|
2164
|
+
WHERE i.name = @indexName
|
|
2165
|
+
AND sch.name = @schemaName
|
|
2166
|
+
GROUP BY i.name, o.name, i.is_unique, i.type_desc, us.user_seeks, us.user_scans, us.user_lookups
|
|
2167
|
+
`;
|
|
2168
|
+
const result = await request.query(query);
|
|
2169
|
+
if (!result.recordset || result.recordset.length === 0) {
|
|
2170
|
+
throw new Error(`Index "${indexName}" not found in schema "${schemaName}"`);
|
|
2171
|
+
}
|
|
2172
|
+
const row = result.recordset[0];
|
|
2173
|
+
const colRequest = this.pool.request();
|
|
2174
|
+
colRequest.input("indexName", indexName);
|
|
2175
|
+
colRequest.input("schemaName", schemaName);
|
|
2176
|
+
const colResult = await colRequest.query(`
|
|
2177
|
+
SELECT c.name as column_name
|
|
2178
|
+
FROM sys.indexes i
|
|
2179
|
+
INNER JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
|
|
2180
|
+
INNER JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
|
|
2181
|
+
INNER JOIN sys.objects o ON i.object_id = o.object_id
|
|
2182
|
+
INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
|
|
2183
|
+
WHERE i.name = @indexName
|
|
2184
|
+
AND s.name = @schemaName
|
|
2185
|
+
ORDER BY ic.key_ordinal
|
|
2186
|
+
`);
|
|
2187
|
+
return {
|
|
2188
|
+
name: row.name,
|
|
2189
|
+
table: row.table,
|
|
2190
|
+
columns: colResult.recordset.map((c) => c.column_name),
|
|
2191
|
+
unique: row.is_unique || false,
|
|
2192
|
+
size: row.size || "0 MB",
|
|
2193
|
+
definition: "",
|
|
2194
|
+
method: row.method?.toLowerCase() || "nonclustered",
|
|
2195
|
+
scans: Number(row.scans) || 0,
|
|
2196
|
+
tuples_read: Number(row.tuples_read) || 0,
|
|
2197
|
+
tuples_fetched: Number(row.tuples_fetched) || 0
|
|
2198
|
+
};
|
|
2199
|
+
} catch (error) {
|
|
2200
|
+
throw new MastraError(
|
|
2201
|
+
{
|
|
2202
|
+
id: createStorageErrorId("MSSQL", "INDEX_DESCRIBE", "FAILED"),
|
|
2203
|
+
domain: ErrorDomain.STORAGE,
|
|
2204
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2205
|
+
details: {
|
|
2206
|
+
indexName
|
|
2207
|
+
}
|
|
2208
|
+
},
|
|
2209
|
+
error
|
|
2210
|
+
);
|
|
2211
|
+
}
|
|
2212
|
+
}
|
|
2213
|
+
/**
|
|
2214
|
+
* Returns definitions for automatic performance indexes
|
|
2215
|
+
* IMPORTANT: Uses seq_id DESC instead of createdAt DESC for MSSQL due to millisecond accuracy limitations
|
|
2216
|
+
* NOTE: Using NVARCHAR(400) for text columns (800 bytes) leaves room for composite indexes
|
|
2217
|
+
*/
|
|
2218
|
+
getAutomaticIndexDefinitions() {
|
|
2219
|
+
const schemaPrefix = this.schemaName ? `${this.schemaName}_` : "";
|
|
2220
|
+
return [
|
|
2221
|
+
// Composite indexes for optimal filtering + sorting performance
|
|
2222
|
+
// NVARCHAR(400) = 800 bytes, plus BIGINT (8 bytes) = 808 bytes total (under 900-byte limit)
|
|
2223
|
+
{
|
|
2224
|
+
name: `${schemaPrefix}mastra_threads_resourceid_seqid_idx`,
|
|
2225
|
+
table: TABLE_THREADS,
|
|
2226
|
+
columns: ["resourceId", "seq_id DESC"]
|
|
2227
|
+
},
|
|
2228
|
+
{
|
|
2229
|
+
name: `${schemaPrefix}mastra_messages_thread_id_seqid_idx`,
|
|
2230
|
+
table: TABLE_MESSAGES,
|
|
2231
|
+
columns: ["thread_id", "seq_id DESC"]
|
|
2232
|
+
},
|
|
2233
|
+
{
|
|
2234
|
+
name: `${schemaPrefix}mastra_traces_name_seqid_idx`,
|
|
2235
|
+
table: TABLE_TRACES,
|
|
2236
|
+
columns: ["name", "seq_id DESC"]
|
|
2237
|
+
},
|
|
2238
|
+
{
|
|
2239
|
+
name: `${schemaPrefix}mastra_scores_trace_id_span_id_seqid_idx`,
|
|
2240
|
+
table: TABLE_SCORERS,
|
|
2241
|
+
columns: ["traceId", "spanId", "seq_id DESC"]
|
|
2242
|
+
},
|
|
2243
|
+
// Spans indexes for optimal trace querying
|
|
2244
|
+
{
|
|
2245
|
+
name: `${schemaPrefix}mastra_ai_spans_traceid_startedat_idx`,
|
|
2246
|
+
table: TABLE_SPANS,
|
|
2247
|
+
columns: ["traceId", "startedAt DESC"]
|
|
2248
|
+
},
|
|
2249
|
+
{
|
|
2250
|
+
name: `${schemaPrefix}mastra_ai_spans_parentspanid_startedat_idx`,
|
|
2251
|
+
table: TABLE_SPANS,
|
|
2252
|
+
columns: ["parentSpanId", "startedAt DESC"]
|
|
2253
|
+
},
|
|
2254
|
+
{
|
|
2255
|
+
name: `${schemaPrefix}mastra_ai_spans_name_idx`,
|
|
2256
|
+
table: TABLE_SPANS,
|
|
2257
|
+
columns: ["name"]
|
|
2258
|
+
},
|
|
2259
|
+
{
|
|
2260
|
+
name: `${schemaPrefix}mastra_ai_spans_spantype_startedat_idx`,
|
|
2261
|
+
table: TABLE_SPANS,
|
|
2262
|
+
columns: ["spanType", "startedAt DESC"]
|
|
2263
|
+
}
|
|
2264
|
+
];
|
|
2265
|
+
}
|
|
2266
|
+
/**
|
|
2267
|
+
* Creates automatic indexes for optimal query performance
|
|
2268
|
+
* Uses getAutomaticIndexDefinitions() to determine which indexes to create
|
|
2269
|
+
*/
|
|
2270
|
+
async createAutomaticIndexes() {
|
|
2271
|
+
try {
|
|
2272
|
+
const indexes = this.getAutomaticIndexDefinitions();
|
|
2273
|
+
for (const indexOptions of indexes) {
|
|
2274
|
+
try {
|
|
2275
|
+
await this.createIndex(indexOptions);
|
|
2276
|
+
} catch (error) {
|
|
2277
|
+
this.logger?.warn?.(`Failed to create index ${indexOptions.name}:`, error);
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
2280
|
+
} catch (error) {
|
|
2281
|
+
throw new MastraError(
|
|
2282
|
+
{
|
|
2283
|
+
id: createStorageErrorId("MSSQL", "CREATE_PERFORMANCE_INDEXES", "FAILED"),
|
|
2284
|
+
domain: ErrorDomain.STORAGE,
|
|
2285
|
+
category: ErrorCategory.THIRD_PARTY
|
|
2286
|
+
},
|
|
2287
|
+
error
|
|
2288
|
+
);
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
};
|
|
1383
2292
|
function transformScoreRow(row) {
|
|
1384
|
-
return {
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
scorer: parseJSON(row.scorer),
|
|
1388
|
-
preprocessStepResult: parseJSON(row.preprocessStepResult),
|
|
1389
|
-
analyzeStepResult: parseJSON(row.analyzeStepResult),
|
|
1390
|
-
metadata: parseJSON(row.metadata),
|
|
1391
|
-
output: parseJSON(row.output),
|
|
1392
|
-
additionalContext: parseJSON(row.additionalContext),
|
|
1393
|
-
runtimeContext: parseJSON(row.runtimeContext),
|
|
1394
|
-
entity: parseJSON(row.entity),
|
|
1395
|
-
createdAt: row.createdAt,
|
|
1396
|
-
updatedAt: row.updatedAt
|
|
1397
|
-
};
|
|
2293
|
+
return transformScoreRow$1(row, {
|
|
2294
|
+
convertTimestamps: true
|
|
2295
|
+
});
|
|
1398
2296
|
}
|
|
1399
2297
|
var ScoresMSSQL = class extends ScoresStorage {
|
|
1400
2298
|
pool;
|
|
@@ -1424,7 +2322,7 @@ var ScoresMSSQL = class extends ScoresStorage {
|
|
|
1424
2322
|
} catch (error) {
|
|
1425
2323
|
throw new MastraError(
|
|
1426
2324
|
{
|
|
1427
|
-
id: "
|
|
2325
|
+
id: createStorageErrorId("MSSQL", "GET_SCORE_BY_ID", "FAILED"),
|
|
1428
2326
|
domain: ErrorDomain.STORAGE,
|
|
1429
2327
|
category: ErrorCategory.THIRD_PARTY,
|
|
1430
2328
|
details: { id }
|
|
@@ -1432,10 +2330,31 @@ var ScoresMSSQL = class extends ScoresStorage {
|
|
|
1432
2330
|
error
|
|
1433
2331
|
);
|
|
1434
2332
|
}
|
|
1435
|
-
}
|
|
1436
|
-
async saveScore(score) {
|
|
2333
|
+
}
|
|
2334
|
+
async saveScore(score) {
|
|
2335
|
+
let validatedScore;
|
|
2336
|
+
try {
|
|
2337
|
+
validatedScore = saveScorePayloadSchema.parse(score);
|
|
2338
|
+
} catch (error) {
|
|
2339
|
+
throw new MastraError(
|
|
2340
|
+
{
|
|
2341
|
+
id: createStorageErrorId("MSSQL", "SAVE_SCORE", "VALIDATION_FAILED"),
|
|
2342
|
+
domain: ErrorDomain.STORAGE,
|
|
2343
|
+
category: ErrorCategory.USER,
|
|
2344
|
+
details: {
|
|
2345
|
+
scorer: score.scorer?.id ?? "unknown",
|
|
2346
|
+
entityId: score.entityId ?? "unknown",
|
|
2347
|
+
entityType: score.entityType ?? "unknown",
|
|
2348
|
+
traceId: score.traceId ?? "",
|
|
2349
|
+
spanId: score.spanId ?? ""
|
|
2350
|
+
}
|
|
2351
|
+
},
|
|
2352
|
+
error
|
|
2353
|
+
);
|
|
2354
|
+
}
|
|
1437
2355
|
try {
|
|
1438
|
-
const scoreId =
|
|
2356
|
+
const scoreId = randomUUID();
|
|
2357
|
+
const now = /* @__PURE__ */ new Date();
|
|
1439
2358
|
const {
|
|
1440
2359
|
scorer,
|
|
1441
2360
|
preprocessStepResult,
|
|
@@ -1444,34 +2363,33 @@ var ScoresMSSQL = class extends ScoresStorage {
|
|
|
1444
2363
|
input,
|
|
1445
2364
|
output,
|
|
1446
2365
|
additionalContext,
|
|
1447
|
-
|
|
2366
|
+
requestContext,
|
|
1448
2367
|
entity,
|
|
1449
2368
|
...rest
|
|
1450
|
-
} =
|
|
2369
|
+
} = validatedScore;
|
|
1451
2370
|
await this.operations.insert({
|
|
1452
2371
|
tableName: TABLE_SCORERS,
|
|
1453
2372
|
record: {
|
|
1454
2373
|
id: scoreId,
|
|
1455
2374
|
...rest,
|
|
1456
|
-
input:
|
|
1457
|
-
output:
|
|
1458
|
-
preprocessStepResult: preprocessStepResult
|
|
1459
|
-
analyzeStepResult: analyzeStepResult
|
|
1460
|
-
metadata: metadata
|
|
1461
|
-
additionalContext: additionalContext
|
|
1462
|
-
|
|
1463
|
-
entity: entity
|
|
1464
|
-
scorer: scorer
|
|
1465
|
-
createdAt:
|
|
1466
|
-
updatedAt:
|
|
2375
|
+
input: input || "",
|
|
2376
|
+
output: output || "",
|
|
2377
|
+
preprocessStepResult: preprocessStepResult || null,
|
|
2378
|
+
analyzeStepResult: analyzeStepResult || null,
|
|
2379
|
+
metadata: metadata || null,
|
|
2380
|
+
additionalContext: additionalContext || null,
|
|
2381
|
+
requestContext: requestContext || null,
|
|
2382
|
+
entity: entity || null,
|
|
2383
|
+
scorer: scorer || null,
|
|
2384
|
+
createdAt: now.toISOString(),
|
|
2385
|
+
updatedAt: now.toISOString()
|
|
1467
2386
|
}
|
|
1468
2387
|
});
|
|
1469
|
-
|
|
1470
|
-
return { score: scoreFromDb };
|
|
2388
|
+
return { score: { ...validatedScore, id: scoreId, createdAt: now, updatedAt: now } };
|
|
1471
2389
|
} catch (error) {
|
|
1472
2390
|
throw new MastraError(
|
|
1473
2391
|
{
|
|
1474
|
-
id: "
|
|
2392
|
+
id: createStorageErrorId("MSSQL", "SAVE_SCORE", "FAILED"),
|
|
1475
2393
|
domain: ErrorDomain.STORAGE,
|
|
1476
2394
|
category: ErrorCategory.THIRD_PARTY
|
|
1477
2395
|
},
|
|
@@ -1479,48 +2397,77 @@ var ScoresMSSQL = class extends ScoresStorage {
|
|
|
1479
2397
|
);
|
|
1480
2398
|
}
|
|
1481
2399
|
}
|
|
1482
|
-
async
|
|
2400
|
+
async listScoresByScorerId({
|
|
1483
2401
|
scorerId,
|
|
1484
|
-
pagination
|
|
2402
|
+
pagination,
|
|
2403
|
+
entityId,
|
|
2404
|
+
entityType,
|
|
2405
|
+
source
|
|
1485
2406
|
}) {
|
|
1486
2407
|
try {
|
|
1487
|
-
const
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
2408
|
+
const conditions = ["[scorerId] = @p1"];
|
|
2409
|
+
const params = { p1: scorerId };
|
|
2410
|
+
let paramIndex = 2;
|
|
2411
|
+
if (entityId) {
|
|
2412
|
+
conditions.push(`[entityId] = @p${paramIndex}`);
|
|
2413
|
+
params[`p${paramIndex}`] = entityId;
|
|
2414
|
+
paramIndex++;
|
|
2415
|
+
}
|
|
2416
|
+
if (entityType) {
|
|
2417
|
+
conditions.push(`[entityType] = @p${paramIndex}`);
|
|
2418
|
+
params[`p${paramIndex}`] = entityType;
|
|
2419
|
+
paramIndex++;
|
|
2420
|
+
}
|
|
2421
|
+
if (source) {
|
|
2422
|
+
conditions.push(`[source] = @p${paramIndex}`);
|
|
2423
|
+
params[`p${paramIndex}`] = source;
|
|
2424
|
+
paramIndex++;
|
|
2425
|
+
}
|
|
2426
|
+
const whereClause = conditions.join(" AND ");
|
|
2427
|
+
const tableName = getTableName({ indexName: TABLE_SCORERS, schemaName: getSchemaName(this.schema) });
|
|
2428
|
+
const countRequest = this.pool.request();
|
|
2429
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
2430
|
+
countRequest.input(key, value);
|
|
2431
|
+
});
|
|
2432
|
+
const totalResult = await countRequest.query(`SELECT COUNT(*) as count FROM ${tableName} WHERE ${whereClause}`);
|
|
1492
2433
|
const total = totalResult.recordset[0]?.count || 0;
|
|
2434
|
+
const { page, perPage: perPageInput } = pagination;
|
|
1493
2435
|
if (total === 0) {
|
|
1494
2436
|
return {
|
|
1495
2437
|
pagination: {
|
|
1496
2438
|
total: 0,
|
|
1497
|
-
page
|
|
1498
|
-
perPage:
|
|
2439
|
+
page,
|
|
2440
|
+
perPage: perPageInput,
|
|
1499
2441
|
hasMore: false
|
|
1500
2442
|
},
|
|
1501
2443
|
scores: []
|
|
1502
2444
|
};
|
|
1503
2445
|
}
|
|
2446
|
+
const perPage = normalizePerPage(perPageInput, 100);
|
|
2447
|
+
const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
2448
|
+
const limitValue = perPageInput === false ? total : perPage;
|
|
2449
|
+
const end = perPageInput === false ? total : start + perPage;
|
|
1504
2450
|
const dataRequest = this.pool.request();
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
2451
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
2452
|
+
dataRequest.input(key, value);
|
|
2453
|
+
});
|
|
2454
|
+
dataRequest.input("perPage", limitValue);
|
|
2455
|
+
dataRequest.input("offset", start);
|
|
2456
|
+
const dataQuery = `SELECT * FROM ${tableName} WHERE ${whereClause} ORDER BY [createdAt] DESC OFFSET @offset ROWS FETCH NEXT @perPage ROWS ONLY`;
|
|
2457
|
+
const result = await dataRequest.query(dataQuery);
|
|
1511
2458
|
return {
|
|
1512
2459
|
pagination: {
|
|
1513
2460
|
total: Number(total),
|
|
1514
|
-
page
|
|
1515
|
-
perPage:
|
|
1516
|
-
hasMore:
|
|
2461
|
+
page,
|
|
2462
|
+
perPage: perPageForResponse,
|
|
2463
|
+
hasMore: end < total
|
|
1517
2464
|
},
|
|
1518
2465
|
scores: result.recordset.map((row) => transformScoreRow(row))
|
|
1519
2466
|
};
|
|
1520
2467
|
} catch (error) {
|
|
1521
2468
|
throw new MastraError(
|
|
1522
2469
|
{
|
|
1523
|
-
id: "
|
|
2470
|
+
id: createStorageErrorId("MSSQL", "LIST_SCORES_BY_SCORER_ID", "FAILED"),
|
|
1524
2471
|
domain: ErrorDomain.STORAGE,
|
|
1525
2472
|
category: ErrorCategory.THIRD_PARTY,
|
|
1526
2473
|
details: { scorerId }
|
|
@@ -1529,7 +2476,7 @@ var ScoresMSSQL = class extends ScoresStorage {
|
|
|
1529
2476
|
);
|
|
1530
2477
|
}
|
|
1531
2478
|
}
|
|
1532
|
-
async
|
|
2479
|
+
async listScoresByRunId({
|
|
1533
2480
|
runId,
|
|
1534
2481
|
pagination
|
|
1535
2482
|
}) {
|
|
@@ -1540,37 +2487,42 @@ var ScoresMSSQL = class extends ScoresStorage {
|
|
|
1540
2487
|
`SELECT COUNT(*) as count FROM ${getTableName({ indexName: TABLE_SCORERS, schemaName: getSchemaName(this.schema) })} WHERE [runId] = @p1`
|
|
1541
2488
|
);
|
|
1542
2489
|
const total = totalResult.recordset[0]?.count || 0;
|
|
2490
|
+
const { page, perPage: perPageInput } = pagination;
|
|
1543
2491
|
if (total === 0) {
|
|
1544
2492
|
return {
|
|
1545
2493
|
pagination: {
|
|
1546
2494
|
total: 0,
|
|
1547
|
-
page
|
|
1548
|
-
perPage:
|
|
2495
|
+
page,
|
|
2496
|
+
perPage: perPageInput,
|
|
1549
2497
|
hasMore: false
|
|
1550
2498
|
},
|
|
1551
2499
|
scores: []
|
|
1552
2500
|
};
|
|
1553
2501
|
}
|
|
2502
|
+
const perPage = normalizePerPage(perPageInput, 100);
|
|
2503
|
+
const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
2504
|
+
const limitValue = perPageInput === false ? total : perPage;
|
|
2505
|
+
const end = perPageInput === false ? total : start + perPage;
|
|
1554
2506
|
const dataRequest = this.pool.request();
|
|
1555
2507
|
dataRequest.input("p1", runId);
|
|
1556
|
-
dataRequest.input("p2",
|
|
1557
|
-
dataRequest.input("p3",
|
|
2508
|
+
dataRequest.input("p2", limitValue);
|
|
2509
|
+
dataRequest.input("p3", start);
|
|
1558
2510
|
const result = await dataRequest.query(
|
|
1559
2511
|
`SELECT * FROM ${getTableName({ indexName: TABLE_SCORERS, schemaName: getSchemaName(this.schema) })} WHERE [runId] = @p1 ORDER BY [createdAt] DESC OFFSET @p3 ROWS FETCH NEXT @p2 ROWS ONLY`
|
|
1560
2512
|
);
|
|
1561
2513
|
return {
|
|
1562
2514
|
pagination: {
|
|
1563
2515
|
total: Number(total),
|
|
1564
|
-
page
|
|
1565
|
-
perPage:
|
|
1566
|
-
hasMore:
|
|
2516
|
+
page,
|
|
2517
|
+
perPage: perPageForResponse,
|
|
2518
|
+
hasMore: end < total
|
|
1567
2519
|
},
|
|
1568
2520
|
scores: result.recordset.map((row) => transformScoreRow(row))
|
|
1569
2521
|
};
|
|
1570
2522
|
} catch (error) {
|
|
1571
2523
|
throw new MastraError(
|
|
1572
2524
|
{
|
|
1573
|
-
id: "
|
|
2525
|
+
id: createStorageErrorId("MSSQL", "LIST_SCORES_BY_RUN_ID", "FAILED"),
|
|
1574
2526
|
domain: ErrorDomain.STORAGE,
|
|
1575
2527
|
category: ErrorCategory.THIRD_PARTY,
|
|
1576
2528
|
details: { runId }
|
|
@@ -1579,7 +2531,7 @@ var ScoresMSSQL = class extends ScoresStorage {
|
|
|
1579
2531
|
);
|
|
1580
2532
|
}
|
|
1581
2533
|
}
|
|
1582
|
-
async
|
|
2534
|
+
async listScoresByEntityId({
|
|
1583
2535
|
entityId,
|
|
1584
2536
|
entityType,
|
|
1585
2537
|
pagination
|
|
@@ -1592,38 +2544,43 @@ var ScoresMSSQL = class extends ScoresStorage {
|
|
|
1592
2544
|
`SELECT COUNT(*) as count FROM ${getTableName({ indexName: TABLE_SCORERS, schemaName: getSchemaName(this.schema) })} WHERE [entityId] = @p1 AND [entityType] = @p2`
|
|
1593
2545
|
);
|
|
1594
2546
|
const total = totalResult.recordset[0]?.count || 0;
|
|
2547
|
+
const { page, perPage: perPageInput } = pagination;
|
|
2548
|
+
const perPage = normalizePerPage(perPageInput, 100);
|
|
2549
|
+
const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
1595
2550
|
if (total === 0) {
|
|
1596
2551
|
return {
|
|
1597
2552
|
pagination: {
|
|
1598
2553
|
total: 0,
|
|
1599
|
-
page
|
|
1600
|
-
perPage:
|
|
2554
|
+
page,
|
|
2555
|
+
perPage: perPageForResponse,
|
|
1601
2556
|
hasMore: false
|
|
1602
2557
|
},
|
|
1603
2558
|
scores: []
|
|
1604
2559
|
};
|
|
1605
2560
|
}
|
|
2561
|
+
const limitValue = perPageInput === false ? total : perPage;
|
|
2562
|
+
const end = perPageInput === false ? total : start + perPage;
|
|
1606
2563
|
const dataRequest = this.pool.request();
|
|
1607
2564
|
dataRequest.input("p1", entityId);
|
|
1608
2565
|
dataRequest.input("p2", entityType);
|
|
1609
|
-
dataRequest.input("p3",
|
|
1610
|
-
dataRequest.input("p4",
|
|
2566
|
+
dataRequest.input("p3", limitValue);
|
|
2567
|
+
dataRequest.input("p4", start);
|
|
1611
2568
|
const result = await dataRequest.query(
|
|
1612
2569
|
`SELECT * FROM ${getTableName({ indexName: TABLE_SCORERS, schemaName: getSchemaName(this.schema) })} WHERE [entityId] = @p1 AND [entityType] = @p2 ORDER BY [createdAt] DESC OFFSET @p4 ROWS FETCH NEXT @p3 ROWS ONLY`
|
|
1613
2570
|
);
|
|
1614
2571
|
return {
|
|
1615
2572
|
pagination: {
|
|
1616
2573
|
total: Number(total),
|
|
1617
|
-
page
|
|
1618
|
-
perPage:
|
|
1619
|
-
hasMore:
|
|
2574
|
+
page,
|
|
2575
|
+
perPage: perPageForResponse,
|
|
2576
|
+
hasMore: end < total
|
|
1620
2577
|
},
|
|
1621
2578
|
scores: result.recordset.map((row) => transformScoreRow(row))
|
|
1622
2579
|
};
|
|
1623
2580
|
} catch (error) {
|
|
1624
2581
|
throw new MastraError(
|
|
1625
2582
|
{
|
|
1626
|
-
id: "
|
|
2583
|
+
id: createStorageErrorId("MSSQL", "LIST_SCORES_BY_ENTITY_ID", "FAILED"),
|
|
1627
2584
|
domain: ErrorDomain.STORAGE,
|
|
1628
2585
|
category: ErrorCategory.THIRD_PARTY,
|
|
1629
2586
|
details: { entityId, entityType }
|
|
@@ -1632,8 +2589,66 @@ var ScoresMSSQL = class extends ScoresStorage {
|
|
|
1632
2589
|
);
|
|
1633
2590
|
}
|
|
1634
2591
|
}
|
|
2592
|
+
async listScoresBySpan({
|
|
2593
|
+
traceId,
|
|
2594
|
+
spanId,
|
|
2595
|
+
pagination
|
|
2596
|
+
}) {
|
|
2597
|
+
try {
|
|
2598
|
+
const request = this.pool.request();
|
|
2599
|
+
request.input("p1", traceId);
|
|
2600
|
+
request.input("p2", spanId);
|
|
2601
|
+
const totalResult = await request.query(
|
|
2602
|
+
`SELECT COUNT(*) as count FROM ${getTableName({ indexName: TABLE_SCORERS, schemaName: getSchemaName(this.schema) })} WHERE [traceId] = @p1 AND [spanId] = @p2`
|
|
2603
|
+
);
|
|
2604
|
+
const total = totalResult.recordset[0]?.count || 0;
|
|
2605
|
+
const { page, perPage: perPageInput } = pagination;
|
|
2606
|
+
const perPage = normalizePerPage(perPageInput, 100);
|
|
2607
|
+
const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
2608
|
+
if (total === 0) {
|
|
2609
|
+
return {
|
|
2610
|
+
pagination: {
|
|
2611
|
+
total: 0,
|
|
2612
|
+
page,
|
|
2613
|
+
perPage: perPageForResponse,
|
|
2614
|
+
hasMore: false
|
|
2615
|
+
},
|
|
2616
|
+
scores: []
|
|
2617
|
+
};
|
|
2618
|
+
}
|
|
2619
|
+
const limitValue = perPageInput === false ? total : perPage;
|
|
2620
|
+
const end = perPageInput === false ? total : start + perPage;
|
|
2621
|
+
const dataRequest = this.pool.request();
|
|
2622
|
+
dataRequest.input("p1", traceId);
|
|
2623
|
+
dataRequest.input("p2", spanId);
|
|
2624
|
+
dataRequest.input("p3", limitValue);
|
|
2625
|
+
dataRequest.input("p4", start);
|
|
2626
|
+
const result = await dataRequest.query(
|
|
2627
|
+
`SELECT * FROM ${getTableName({ indexName: TABLE_SCORERS, schemaName: getSchemaName(this.schema) })} WHERE [traceId] = @p1 AND [spanId] = @p2 ORDER BY [createdAt] DESC OFFSET @p4 ROWS FETCH NEXT @p3 ROWS ONLY`
|
|
2628
|
+
);
|
|
2629
|
+
return {
|
|
2630
|
+
pagination: {
|
|
2631
|
+
total: Number(total),
|
|
2632
|
+
page,
|
|
2633
|
+
perPage: perPageForResponse,
|
|
2634
|
+
hasMore: end < total
|
|
2635
|
+
},
|
|
2636
|
+
scores: result.recordset.map((row) => transformScoreRow(row))
|
|
2637
|
+
};
|
|
2638
|
+
} catch (error) {
|
|
2639
|
+
throw new MastraError(
|
|
2640
|
+
{
|
|
2641
|
+
id: createStorageErrorId("MSSQL", "LIST_SCORES_BY_SPAN", "FAILED"),
|
|
2642
|
+
domain: ErrorDomain.STORAGE,
|
|
2643
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2644
|
+
details: { traceId, spanId }
|
|
2645
|
+
},
|
|
2646
|
+
error
|
|
2647
|
+
);
|
|
2648
|
+
}
|
|
2649
|
+
}
|
|
1635
2650
|
};
|
|
1636
|
-
var
|
|
2651
|
+
var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
1637
2652
|
pool;
|
|
1638
2653
|
operations;
|
|
1639
2654
|
schema;
|
|
@@ -1647,194 +2662,170 @@ var TracesMSSQL = class extends TracesStorage {
|
|
|
1647
2662
|
this.operations = operations;
|
|
1648
2663
|
this.schema = schema;
|
|
1649
2664
|
}
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
if (
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
2665
|
+
parseWorkflowRun(row) {
|
|
2666
|
+
let parsedSnapshot = row.snapshot;
|
|
2667
|
+
if (typeof parsedSnapshot === "string") {
|
|
2668
|
+
try {
|
|
2669
|
+
parsedSnapshot = JSON.parse(row.snapshot);
|
|
2670
|
+
} catch (e) {
|
|
2671
|
+
this.logger?.warn?.(`Failed to parse snapshot for workflow ${row.workflow_name}:`, e);
|
|
2672
|
+
}
|
|
1657
2673
|
}
|
|
1658
|
-
|
|
1659
|
-
|
|
2674
|
+
return {
|
|
2675
|
+
workflowName: row.workflow_name,
|
|
2676
|
+
runId: row.run_id,
|
|
2677
|
+
snapshot: parsedSnapshot,
|
|
2678
|
+
createdAt: row.createdAt,
|
|
2679
|
+
updatedAt: row.updatedAt,
|
|
2680
|
+
resourceId: row.resourceId
|
|
2681
|
+
};
|
|
1660
2682
|
}
|
|
1661
|
-
async
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
const
|
|
1669
|
-
|
|
1670
|
-
if (name) {
|
|
1671
|
-
const paramName = `p${paramIndex++}`;
|
|
1672
|
-
conditions.push(`[name] LIKE @${paramName}`);
|
|
1673
|
-
paramMap[paramName] = `${name}%`;
|
|
1674
|
-
}
|
|
1675
|
-
if (scope) {
|
|
1676
|
-
const paramName = `p${paramIndex++}`;
|
|
1677
|
-
conditions.push(`[scope] = @${paramName}`);
|
|
1678
|
-
paramMap[paramName] = scope;
|
|
1679
|
-
}
|
|
1680
|
-
if (attributes) {
|
|
1681
|
-
Object.entries(attributes).forEach(([key, value]) => {
|
|
1682
|
-
const parsedKey = parseFieldKey(key);
|
|
1683
|
-
const paramName = `p${paramIndex++}`;
|
|
1684
|
-
conditions.push(`JSON_VALUE([attributes], '$.${parsedKey}') = @${paramName}`);
|
|
1685
|
-
paramMap[paramName] = value;
|
|
1686
|
-
});
|
|
1687
|
-
}
|
|
1688
|
-
if (filters) {
|
|
1689
|
-
Object.entries(filters).forEach(([key, value]) => {
|
|
1690
|
-
const parsedKey = parseFieldKey(key);
|
|
1691
|
-
const paramName = `p${paramIndex++}`;
|
|
1692
|
-
conditions.push(`[${parsedKey}] = @${paramName}`);
|
|
1693
|
-
paramMap[paramName] = value;
|
|
1694
|
-
});
|
|
1695
|
-
}
|
|
1696
|
-
if (fromDate instanceof Date && !isNaN(fromDate.getTime())) {
|
|
1697
|
-
const paramName = `p${paramIndex++}`;
|
|
1698
|
-
conditions.push(`[createdAt] >= @${paramName}`);
|
|
1699
|
-
paramMap[paramName] = fromDate.toISOString();
|
|
1700
|
-
}
|
|
1701
|
-
if (toDate instanceof Date && !isNaN(toDate.getTime())) {
|
|
1702
|
-
const paramName = `p${paramIndex++}`;
|
|
1703
|
-
conditions.push(`[createdAt] <= @${paramName}`);
|
|
1704
|
-
paramMap[paramName] = toDate.toISOString();
|
|
1705
|
-
}
|
|
1706
|
-
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
1707
|
-
const countQuery = `SELECT COUNT(*) as total FROM ${getTableName({ indexName: TABLE_TRACES, schemaName: getSchemaName(this.schema) })} ${whereClause}`;
|
|
1708
|
-
let total = 0;
|
|
2683
|
+
async updateWorkflowResults({
|
|
2684
|
+
workflowName,
|
|
2685
|
+
runId,
|
|
2686
|
+
stepId,
|
|
2687
|
+
result,
|
|
2688
|
+
requestContext
|
|
2689
|
+
}) {
|
|
2690
|
+
const table = getTableName({ indexName: TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName(this.schema) });
|
|
2691
|
+
const transaction = this.pool.transaction();
|
|
1709
2692
|
try {
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
2693
|
+
await transaction.begin();
|
|
2694
|
+
const selectRequest = new sql3.Request(transaction);
|
|
2695
|
+
selectRequest.input("workflow_name", workflowName);
|
|
2696
|
+
selectRequest.input("run_id", runId);
|
|
2697
|
+
const existingSnapshotResult = await selectRequest.query(
|
|
2698
|
+
`SELECT snapshot FROM ${table} WITH (UPDLOCK, HOLDLOCK) WHERE workflow_name = @workflow_name AND run_id = @run_id`
|
|
2699
|
+
);
|
|
2700
|
+
let snapshot;
|
|
2701
|
+
if (!existingSnapshotResult.recordset || existingSnapshotResult.recordset.length === 0) {
|
|
2702
|
+
snapshot = {
|
|
2703
|
+
context: {},
|
|
2704
|
+
activePaths: [],
|
|
2705
|
+
activeStepsPath: {},
|
|
2706
|
+
timestamp: Date.now(),
|
|
2707
|
+
suspendedPaths: {},
|
|
2708
|
+
resumeLabels: {},
|
|
2709
|
+
serializedStepGraph: [],
|
|
2710
|
+
status: "pending",
|
|
2711
|
+
value: {},
|
|
2712
|
+
waitingPaths: {},
|
|
2713
|
+
runId,
|
|
2714
|
+
requestContext: {}
|
|
2715
|
+
};
|
|
2716
|
+
} else {
|
|
2717
|
+
const existingSnapshot = existingSnapshotResult.recordset[0].snapshot;
|
|
2718
|
+
snapshot = typeof existingSnapshot === "string" ? JSON.parse(existingSnapshot) : existingSnapshot;
|
|
2719
|
+
}
|
|
2720
|
+
snapshot.context[stepId] = result;
|
|
2721
|
+
snapshot.requestContext = { ...snapshot.requestContext, ...requestContext };
|
|
2722
|
+
const upsertReq = new sql3.Request(transaction);
|
|
2723
|
+
upsertReq.input("workflow_name", workflowName);
|
|
2724
|
+
upsertReq.input("run_id", runId);
|
|
2725
|
+
upsertReq.input("snapshot", JSON.stringify(snapshot));
|
|
2726
|
+
upsertReq.input("createdAt", sql3.DateTime2, /* @__PURE__ */ new Date());
|
|
2727
|
+
upsertReq.input("updatedAt", sql3.DateTime2, /* @__PURE__ */ new Date());
|
|
2728
|
+
await upsertReq.query(
|
|
2729
|
+
`MERGE ${table} AS target
|
|
2730
|
+
USING (SELECT @workflow_name AS workflow_name, @run_id AS run_id) AS src
|
|
2731
|
+
ON target.workflow_name = src.workflow_name AND target.run_id = src.run_id
|
|
2732
|
+
WHEN MATCHED THEN UPDATE SET snapshot = @snapshot, [updatedAt] = @updatedAt
|
|
2733
|
+
WHEN NOT MATCHED THEN INSERT (workflow_name, run_id, snapshot, [createdAt], [updatedAt])
|
|
2734
|
+
VALUES (@workflow_name, @run_id, @snapshot, @createdAt, @updatedAt);`
|
|
2735
|
+
);
|
|
2736
|
+
await transaction.commit();
|
|
2737
|
+
return snapshot.context;
|
|
1720
2738
|
} catch (error) {
|
|
2739
|
+
try {
|
|
2740
|
+
await transaction.rollback();
|
|
2741
|
+
} catch {
|
|
2742
|
+
}
|
|
1721
2743
|
throw new MastraError(
|
|
1722
2744
|
{
|
|
1723
|
-
id: "
|
|
2745
|
+
id: createStorageErrorId("MSSQL", "UPDATE_WORKFLOW_RESULTS", "FAILED"),
|
|
1724
2746
|
domain: ErrorDomain.STORAGE,
|
|
1725
2747
|
category: ErrorCategory.THIRD_PARTY,
|
|
1726
2748
|
details: {
|
|
1727
|
-
|
|
1728
|
-
|
|
2749
|
+
workflowName,
|
|
2750
|
+
runId,
|
|
2751
|
+
stepId
|
|
1729
2752
|
}
|
|
1730
2753
|
},
|
|
1731
2754
|
error
|
|
1732
2755
|
);
|
|
1733
2756
|
}
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
}
|
|
1743
|
-
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`;
|
|
1744
|
-
const dataRequest = this.pool.request();
|
|
1745
|
-
Object.entries(paramMap).forEach(([key, value]) => {
|
|
1746
|
-
if (value instanceof Date) {
|
|
1747
|
-
dataRequest.input(key, sql2.DateTime, value);
|
|
1748
|
-
} else {
|
|
1749
|
-
dataRequest.input(key, value);
|
|
1750
|
-
}
|
|
1751
|
-
});
|
|
1752
|
-
dataRequest.input("offset", currentOffset);
|
|
1753
|
-
dataRequest.input("limit", perPage);
|
|
2757
|
+
}
|
|
2758
|
+
async updateWorkflowState({
|
|
2759
|
+
workflowName,
|
|
2760
|
+
runId,
|
|
2761
|
+
opts
|
|
2762
|
+
}) {
|
|
2763
|
+
const table = getTableName({ indexName: TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName(this.schema) });
|
|
2764
|
+
const transaction = this.pool.transaction();
|
|
1754
2765
|
try {
|
|
1755
|
-
|
|
1756
|
-
const
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
2766
|
+
await transaction.begin();
|
|
2767
|
+
const selectRequest = new sql3.Request(transaction);
|
|
2768
|
+
selectRequest.input("workflow_name", workflowName);
|
|
2769
|
+
selectRequest.input("run_id", runId);
|
|
2770
|
+
const existingSnapshotResult = await selectRequest.query(
|
|
2771
|
+
`SELECT snapshot FROM ${table} WITH (UPDLOCK, HOLDLOCK) WHERE workflow_name = @workflow_name AND run_id = @run_id`
|
|
2772
|
+
);
|
|
2773
|
+
if (!existingSnapshotResult.recordset || existingSnapshotResult.recordset.length === 0) {
|
|
2774
|
+
await transaction.rollback();
|
|
2775
|
+
return void 0;
|
|
2776
|
+
}
|
|
2777
|
+
const existingSnapshot = existingSnapshotResult.recordset[0].snapshot;
|
|
2778
|
+
const snapshot = typeof existingSnapshot === "string" ? JSON.parse(existingSnapshot) : existingSnapshot;
|
|
2779
|
+
if (!snapshot || !snapshot?.context) {
|
|
2780
|
+
await transaction.rollback();
|
|
2781
|
+
throw new MastraError(
|
|
2782
|
+
{
|
|
2783
|
+
id: createStorageErrorId("MSSQL", "UPDATE_WORKFLOW_STATE", "SNAPSHOT_NOT_FOUND"),
|
|
2784
|
+
domain: ErrorDomain.STORAGE,
|
|
2785
|
+
category: ErrorCategory.SYSTEM,
|
|
2786
|
+
details: {
|
|
2787
|
+
workflowName,
|
|
2788
|
+
runId
|
|
2789
|
+
}
|
|
2790
|
+
},
|
|
2791
|
+
new Error(`Snapshot not found for runId ${runId}`)
|
|
2792
|
+
);
|
|
2793
|
+
}
|
|
2794
|
+
const updatedSnapshot = { ...snapshot, ...opts };
|
|
2795
|
+
const updateRequest = new sql3.Request(transaction);
|
|
2796
|
+
updateRequest.input("snapshot", JSON.stringify(updatedSnapshot));
|
|
2797
|
+
updateRequest.input("workflow_name", workflowName);
|
|
2798
|
+
updateRequest.input("run_id", runId);
|
|
2799
|
+
updateRequest.input("updatedAt", sql3.DateTime2, /* @__PURE__ */ new Date());
|
|
2800
|
+
await updateRequest.query(
|
|
2801
|
+
`UPDATE ${table} SET snapshot = @snapshot, [updatedAt] = @updatedAt WHERE workflow_name = @workflow_name AND run_id = @run_id`
|
|
2802
|
+
);
|
|
2803
|
+
await transaction.commit();
|
|
2804
|
+
return updatedSnapshot;
|
|
1780
2805
|
} catch (error) {
|
|
2806
|
+
try {
|
|
2807
|
+
await transaction.rollback();
|
|
2808
|
+
} catch {
|
|
2809
|
+
}
|
|
2810
|
+
if (error instanceof MastraError) throw error;
|
|
1781
2811
|
throw new MastraError(
|
|
1782
2812
|
{
|
|
1783
|
-
id: "
|
|
2813
|
+
id: createStorageErrorId("MSSQL", "UPDATE_WORKFLOW_STATE", "FAILED"),
|
|
1784
2814
|
domain: ErrorDomain.STORAGE,
|
|
1785
2815
|
category: ErrorCategory.THIRD_PARTY,
|
|
1786
2816
|
details: {
|
|
1787
|
-
|
|
1788
|
-
|
|
2817
|
+
workflowName,
|
|
2818
|
+
runId
|
|
1789
2819
|
}
|
|
1790
2820
|
},
|
|
1791
2821
|
error
|
|
1792
2822
|
);
|
|
1793
2823
|
}
|
|
1794
2824
|
}
|
|
1795
|
-
async batchTraceInsert({ records }) {
|
|
1796
|
-
this.logger.debug("Batch inserting traces", { count: records.length });
|
|
1797
|
-
await this.operations.batchInsert({
|
|
1798
|
-
tableName: TABLE_TRACES,
|
|
1799
|
-
records
|
|
1800
|
-
});
|
|
1801
|
-
}
|
|
1802
|
-
};
|
|
1803
|
-
function parseWorkflowRun(row) {
|
|
1804
|
-
let parsedSnapshot = row.snapshot;
|
|
1805
|
-
if (typeof parsedSnapshot === "string") {
|
|
1806
|
-
try {
|
|
1807
|
-
parsedSnapshot = JSON.parse(row.snapshot);
|
|
1808
|
-
} catch (e) {
|
|
1809
|
-
console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
|
|
1810
|
-
}
|
|
1811
|
-
}
|
|
1812
|
-
return {
|
|
1813
|
-
workflowName: row.workflow_name,
|
|
1814
|
-
runId: row.run_id,
|
|
1815
|
-
snapshot: parsedSnapshot,
|
|
1816
|
-
createdAt: row.createdAt,
|
|
1817
|
-
updatedAt: row.updatedAt,
|
|
1818
|
-
resourceId: row.resourceId
|
|
1819
|
-
};
|
|
1820
|
-
}
|
|
1821
|
-
var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
1822
|
-
pool;
|
|
1823
|
-
operations;
|
|
1824
|
-
schema;
|
|
1825
|
-
constructor({
|
|
1826
|
-
pool,
|
|
1827
|
-
operations,
|
|
1828
|
-
schema
|
|
1829
|
-
}) {
|
|
1830
|
-
super();
|
|
1831
|
-
this.pool = pool;
|
|
1832
|
-
this.operations = operations;
|
|
1833
|
-
this.schema = schema;
|
|
1834
|
-
}
|
|
1835
2825
|
async persistWorkflowSnapshot({
|
|
1836
2826
|
workflowName,
|
|
1837
2827
|
runId,
|
|
2828
|
+
resourceId,
|
|
1838
2829
|
snapshot
|
|
1839
2830
|
}) {
|
|
1840
2831
|
const table = getTableName({ indexName: TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName(this.schema) });
|
|
@@ -1843,22 +2834,24 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
|
1843
2834
|
const request = this.pool.request();
|
|
1844
2835
|
request.input("workflow_name", workflowName);
|
|
1845
2836
|
request.input("run_id", runId);
|
|
2837
|
+
request.input("resourceId", resourceId);
|
|
1846
2838
|
request.input("snapshot", JSON.stringify(snapshot));
|
|
1847
|
-
request.input("createdAt",
|
|
1848
|
-
request.input("updatedAt",
|
|
2839
|
+
request.input("createdAt", sql3.DateTime2, new Date(now));
|
|
2840
|
+
request.input("updatedAt", sql3.DateTime2, new Date(now));
|
|
1849
2841
|
const mergeSql = `MERGE INTO ${table} AS target
|
|
1850
2842
|
USING (SELECT @workflow_name AS workflow_name, @run_id AS run_id) AS src
|
|
1851
2843
|
ON target.workflow_name = src.workflow_name AND target.run_id = src.run_id
|
|
1852
2844
|
WHEN MATCHED THEN UPDATE SET
|
|
2845
|
+
resourceId = @resourceId,
|
|
1853
2846
|
snapshot = @snapshot,
|
|
1854
2847
|
[updatedAt] = @updatedAt
|
|
1855
|
-
WHEN NOT MATCHED THEN INSERT (workflow_name, run_id, snapshot, [createdAt], [updatedAt])
|
|
1856
|
-
VALUES (@workflow_name, @run_id, @snapshot, @createdAt, @updatedAt);`;
|
|
2848
|
+
WHEN NOT MATCHED THEN INSERT (workflow_name, run_id, resourceId, snapshot, [createdAt], [updatedAt])
|
|
2849
|
+
VALUES (@workflow_name, @run_id, @resourceId, @snapshot, @createdAt, @updatedAt);`;
|
|
1857
2850
|
await request.query(mergeSql);
|
|
1858
2851
|
} catch (error) {
|
|
1859
2852
|
throw new MastraError(
|
|
1860
2853
|
{
|
|
1861
|
-
id: "
|
|
2854
|
+
id: createStorageErrorId("MSSQL", "PERSIST_WORKFLOW_SNAPSHOT", "FAILED"),
|
|
1862
2855
|
domain: ErrorDomain.STORAGE,
|
|
1863
2856
|
category: ErrorCategory.THIRD_PARTY,
|
|
1864
2857
|
details: {
|
|
@@ -1889,7 +2882,7 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
|
1889
2882
|
} catch (error) {
|
|
1890
2883
|
throw new MastraError(
|
|
1891
2884
|
{
|
|
1892
|
-
id: "
|
|
2885
|
+
id: createStorageErrorId("MSSQL", "LOAD_WORKFLOW_SNAPSHOT", "FAILED"),
|
|
1893
2886
|
domain: ErrorDomain.STORAGE,
|
|
1894
2887
|
category: ErrorCategory.THIRD_PARTY,
|
|
1895
2888
|
details: {
|
|
@@ -1925,11 +2918,11 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
|
1925
2918
|
if (!result.recordset || result.recordset.length === 0) {
|
|
1926
2919
|
return null;
|
|
1927
2920
|
}
|
|
1928
|
-
return parseWorkflowRun(result.recordset[0]);
|
|
2921
|
+
return this.parseWorkflowRun(result.recordset[0]);
|
|
1929
2922
|
} catch (error) {
|
|
1930
2923
|
throw new MastraError(
|
|
1931
2924
|
{
|
|
1932
|
-
id: "
|
|
2925
|
+
id: createStorageErrorId("MSSQL", "GET_WORKFLOW_RUN_BY_ID", "FAILED"),
|
|
1933
2926
|
domain: ErrorDomain.STORAGE,
|
|
1934
2927
|
category: ErrorCategory.THIRD_PARTY,
|
|
1935
2928
|
details: {
|
|
@@ -1941,13 +2934,43 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
|
1941
2934
|
);
|
|
1942
2935
|
}
|
|
1943
2936
|
}
|
|
1944
|
-
async
|
|
2937
|
+
async deleteWorkflowRunById({ runId, workflowName }) {
|
|
2938
|
+
const table = getTableName({ indexName: TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName(this.schema) });
|
|
2939
|
+
const transaction = this.pool.transaction();
|
|
2940
|
+
try {
|
|
2941
|
+
await transaction.begin();
|
|
2942
|
+
const deleteRequest = new sql3.Request(transaction);
|
|
2943
|
+
deleteRequest.input("workflow_name", workflowName);
|
|
2944
|
+
deleteRequest.input("run_id", runId);
|
|
2945
|
+
await deleteRequest.query(`DELETE FROM ${table} WHERE workflow_name = @workflow_name AND run_id = @run_id`);
|
|
2946
|
+
await transaction.commit();
|
|
2947
|
+
} catch (error) {
|
|
2948
|
+
try {
|
|
2949
|
+
await transaction.rollback();
|
|
2950
|
+
} catch {
|
|
2951
|
+
}
|
|
2952
|
+
throw new MastraError(
|
|
2953
|
+
{
|
|
2954
|
+
id: createStorageErrorId("MSSQL", "DELETE_WORKFLOW_RUN_BY_ID", "FAILED"),
|
|
2955
|
+
domain: ErrorDomain.STORAGE,
|
|
2956
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2957
|
+
details: {
|
|
2958
|
+
runId,
|
|
2959
|
+
workflowName
|
|
2960
|
+
}
|
|
2961
|
+
},
|
|
2962
|
+
error
|
|
2963
|
+
);
|
|
2964
|
+
}
|
|
2965
|
+
}
|
|
2966
|
+
async listWorkflowRuns({
|
|
1945
2967
|
workflowName,
|
|
1946
2968
|
fromDate,
|
|
1947
2969
|
toDate,
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
resourceId
|
|
2970
|
+
page,
|
|
2971
|
+
perPage,
|
|
2972
|
+
resourceId,
|
|
2973
|
+
status
|
|
1951
2974
|
} = {}) {
|
|
1952
2975
|
try {
|
|
1953
2976
|
const conditions = [];
|
|
@@ -1956,13 +2979,17 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
|
1956
2979
|
conditions.push(`[workflow_name] = @workflowName`);
|
|
1957
2980
|
paramMap["workflowName"] = workflowName;
|
|
1958
2981
|
}
|
|
2982
|
+
if (status) {
|
|
2983
|
+
conditions.push(`JSON_VALUE([snapshot], '$.status') = @status`);
|
|
2984
|
+
paramMap["status"] = status;
|
|
2985
|
+
}
|
|
1959
2986
|
if (resourceId) {
|
|
1960
2987
|
const hasResourceId = await this.operations.hasColumn(TABLE_WORKFLOW_SNAPSHOT, "resourceId");
|
|
1961
2988
|
if (hasResourceId) {
|
|
1962
2989
|
conditions.push(`[resourceId] = @resourceId`);
|
|
1963
2990
|
paramMap["resourceId"] = resourceId;
|
|
1964
2991
|
} else {
|
|
1965
|
-
|
|
2992
|
+
this.logger?.warn?.(`[${TABLE_WORKFLOW_SNAPSHOT}] resourceId column not found. Skipping resourceId filter.`);
|
|
1966
2993
|
}
|
|
1967
2994
|
}
|
|
1968
2995
|
if (fromDate instanceof Date && !isNaN(fromDate.getTime())) {
|
|
@@ -1979,29 +3006,32 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
|
1979
3006
|
const request = this.pool.request();
|
|
1980
3007
|
Object.entries(paramMap).forEach(([key, value]) => {
|
|
1981
3008
|
if (value instanceof Date) {
|
|
1982
|
-
request.input(key,
|
|
3009
|
+
request.input(key, sql3.DateTime, value);
|
|
1983
3010
|
} else {
|
|
1984
3011
|
request.input(key, value);
|
|
1985
3012
|
}
|
|
1986
3013
|
});
|
|
1987
|
-
|
|
3014
|
+
const usePagination = typeof perPage === "number" && typeof page === "number";
|
|
3015
|
+
if (usePagination) {
|
|
1988
3016
|
const countQuery = `SELECT COUNT(*) as count FROM ${tableName} ${whereClause}`;
|
|
1989
3017
|
const countResult = await request.query(countQuery);
|
|
1990
3018
|
total = Number(countResult.recordset[0]?.count || 0);
|
|
1991
3019
|
}
|
|
1992
3020
|
let query = `SELECT * FROM ${tableName} ${whereClause} ORDER BY [seq_id] DESC`;
|
|
1993
|
-
if (
|
|
1994
|
-
|
|
1995
|
-
|
|
3021
|
+
if (usePagination) {
|
|
3022
|
+
const normalizedPerPage = normalizePerPage(perPage, Number.MAX_SAFE_INTEGER);
|
|
3023
|
+
const offset = page * normalizedPerPage;
|
|
3024
|
+
query += ` OFFSET @offset ROWS FETCH NEXT @perPage ROWS ONLY`;
|
|
3025
|
+
request.input("perPage", normalizedPerPage);
|
|
1996
3026
|
request.input("offset", offset);
|
|
1997
3027
|
}
|
|
1998
3028
|
const result = await request.query(query);
|
|
1999
|
-
const runs = (result.recordset || []).map((row) => parseWorkflowRun(row));
|
|
3029
|
+
const runs = (result.recordset || []).map((row) => this.parseWorkflowRun(row));
|
|
2000
3030
|
return { runs, total: total || runs.length };
|
|
2001
3031
|
} catch (error) {
|
|
2002
3032
|
throw new MastraError(
|
|
2003
3033
|
{
|
|
2004
|
-
id: "
|
|
3034
|
+
id: createStorageErrorId("MSSQL", "LIST_WORKFLOW_RUNS", "FAILED"),
|
|
2005
3035
|
domain: ErrorDomain.STORAGE,
|
|
2006
3036
|
category: ErrorCategory.THIRD_PARTY,
|
|
2007
3037
|
details: {
|
|
@@ -2021,7 +3051,10 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2021
3051
|
isConnected = null;
|
|
2022
3052
|
stores;
|
|
2023
3053
|
constructor(config) {
|
|
2024
|
-
|
|
3054
|
+
if (!config.id || typeof config.id !== "string" || config.id.trim() === "") {
|
|
3055
|
+
throw new Error("MSSQLStore: id must be provided and cannot be empty.");
|
|
3056
|
+
}
|
|
3057
|
+
super({ id: config.id, name: "MSSQLStore", disableInit: config.disableInit });
|
|
2025
3058
|
try {
|
|
2026
3059
|
if ("connectionString" in config) {
|
|
2027
3060
|
if (!config.connectionString || typeof config.connectionString !== "string" || config.connectionString.trim() === "") {
|
|
@@ -2036,7 +3069,7 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2036
3069
|
}
|
|
2037
3070
|
}
|
|
2038
3071
|
this.schema = config.schemaName || "dbo";
|
|
2039
|
-
this.pool = "connectionString" in config ? new
|
|
3072
|
+
this.pool = "connectionString" in config ? new sql3.ConnectionPool(config.connectionString) : new sql3.ConnectionPool({
|
|
2040
3073
|
server: config.server,
|
|
2041
3074
|
database: config.database,
|
|
2042
3075
|
user: config.user,
|
|
@@ -2044,24 +3077,22 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2044
3077
|
port: config.port,
|
|
2045
3078
|
options: config.options || { encrypt: true, trustServerCertificate: true }
|
|
2046
3079
|
});
|
|
2047
|
-
const legacyEvals = new LegacyEvalsMSSQL({ pool: this.pool, schema: this.schema });
|
|
2048
3080
|
const operations = new StoreOperationsMSSQL({ pool: this.pool, schemaName: this.schema });
|
|
2049
3081
|
const scores = new ScoresMSSQL({ pool: this.pool, operations, schema: this.schema });
|
|
2050
|
-
const traces = new TracesMSSQL({ pool: this.pool, operations, schema: this.schema });
|
|
2051
3082
|
const workflows = new WorkflowsMSSQL({ pool: this.pool, operations, schema: this.schema });
|
|
2052
3083
|
const memory = new MemoryMSSQL({ pool: this.pool, schema: this.schema, operations });
|
|
3084
|
+
const observability = new ObservabilityMSSQL({ pool: this.pool, operations, schema: this.schema });
|
|
2053
3085
|
this.stores = {
|
|
2054
3086
|
operations,
|
|
2055
3087
|
scores,
|
|
2056
|
-
traces,
|
|
2057
3088
|
workflows,
|
|
2058
|
-
|
|
2059
|
-
|
|
3089
|
+
memory,
|
|
3090
|
+
observability
|
|
2060
3091
|
};
|
|
2061
3092
|
} catch (e) {
|
|
2062
3093
|
throw new MastraError(
|
|
2063
3094
|
{
|
|
2064
|
-
id: "
|
|
3095
|
+
id: createStorageErrorId("MSSQL", "INITIALIZATION", "FAILED"),
|
|
2065
3096
|
domain: ErrorDomain.STORAGE,
|
|
2066
3097
|
category: ErrorCategory.USER
|
|
2067
3098
|
},
|
|
@@ -2076,11 +3107,16 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2076
3107
|
try {
|
|
2077
3108
|
await this.isConnected;
|
|
2078
3109
|
await super.init();
|
|
3110
|
+
try {
|
|
3111
|
+
await this.stores.operations.createAutomaticIndexes();
|
|
3112
|
+
} catch (indexError) {
|
|
3113
|
+
this.logger?.warn?.("Failed to create indexes:", indexError);
|
|
3114
|
+
}
|
|
2079
3115
|
} catch (error) {
|
|
2080
3116
|
this.isConnected = null;
|
|
2081
3117
|
throw new MastraError(
|
|
2082
3118
|
{
|
|
2083
|
-
id: "
|
|
3119
|
+
id: createStorageErrorId("MSSQL", "INIT", "FAILED"),
|
|
2084
3120
|
domain: ErrorDomain.STORAGE,
|
|
2085
3121
|
category: ErrorCategory.THIRD_PARTY
|
|
2086
3122
|
},
|
|
@@ -2102,28 +3138,12 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2102
3138
|
resourceWorkingMemory: true,
|
|
2103
3139
|
hasColumn: true,
|
|
2104
3140
|
createTable: true,
|
|
2105
|
-
deleteMessages: true
|
|
3141
|
+
deleteMessages: true,
|
|
3142
|
+
listScoresBySpan: true,
|
|
3143
|
+
observabilityInstance: true,
|
|
3144
|
+
indexManagement: true
|
|
2106
3145
|
};
|
|
2107
3146
|
}
|
|
2108
|
-
/** @deprecated use getEvals instead */
|
|
2109
|
-
async getEvalsByAgentName(agentName, type) {
|
|
2110
|
-
return this.stores.legacyEvals.getEvalsByAgentName(agentName, type);
|
|
2111
|
-
}
|
|
2112
|
-
async getEvals(options = {}) {
|
|
2113
|
-
return this.stores.legacyEvals.getEvals(options);
|
|
2114
|
-
}
|
|
2115
|
-
/**
|
|
2116
|
-
* @deprecated use getTracesPaginated instead
|
|
2117
|
-
*/
|
|
2118
|
-
async getTraces(args) {
|
|
2119
|
-
return this.stores.traces.getTraces(args);
|
|
2120
|
-
}
|
|
2121
|
-
async getTracesPaginated(args) {
|
|
2122
|
-
return this.stores.traces.getTracesPaginated(args);
|
|
2123
|
-
}
|
|
2124
|
-
async batchTraceInsert({ records }) {
|
|
2125
|
-
return this.stores.traces.batchTraceInsert({ records });
|
|
2126
|
-
}
|
|
2127
3147
|
async createTable({
|
|
2128
3148
|
tableName,
|
|
2129
3149
|
schema
|
|
@@ -2158,15 +3178,6 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2158
3178
|
async getThreadById({ threadId }) {
|
|
2159
3179
|
return this.stores.memory.getThreadById({ threadId });
|
|
2160
3180
|
}
|
|
2161
|
-
/**
|
|
2162
|
-
* @deprecated use getThreadsByResourceIdPaginated instead
|
|
2163
|
-
*/
|
|
2164
|
-
async getThreadsByResourceId(args) {
|
|
2165
|
-
return this.stores.memory.getThreadsByResourceId(args);
|
|
2166
|
-
}
|
|
2167
|
-
async getThreadsByResourceIdPaginated(args) {
|
|
2168
|
-
return this.stores.memory.getThreadsByResourceIdPaginated(args);
|
|
2169
|
-
}
|
|
2170
3181
|
async saveThread({ thread }) {
|
|
2171
3182
|
return this.stores.memory.saveThread({ thread });
|
|
2172
3183
|
}
|
|
@@ -2180,11 +3191,8 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2180
3191
|
async deleteThread({ threadId }) {
|
|
2181
3192
|
return this.stores.memory.deleteThread({ threadId });
|
|
2182
3193
|
}
|
|
2183
|
-
async
|
|
2184
|
-
return this.stores.memory.
|
|
2185
|
-
}
|
|
2186
|
-
async getMessagesPaginated(args) {
|
|
2187
|
-
return this.stores.memory.getMessagesPaginated(args);
|
|
3194
|
+
async listMessagesById({ messageIds }) {
|
|
3195
|
+
return this.stores.memory.listMessagesById({ messageIds });
|
|
2188
3196
|
}
|
|
2189
3197
|
async saveMessages(args) {
|
|
2190
3198
|
return this.stores.memory.saveMessages(args);
|
|
@@ -2213,12 +3221,29 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2213
3221
|
/**
|
|
2214
3222
|
* Workflows
|
|
2215
3223
|
*/
|
|
3224
|
+
async updateWorkflowResults({
|
|
3225
|
+
workflowName,
|
|
3226
|
+
runId,
|
|
3227
|
+
stepId,
|
|
3228
|
+
result,
|
|
3229
|
+
requestContext
|
|
3230
|
+
}) {
|
|
3231
|
+
return this.stores.workflows.updateWorkflowResults({ workflowName, runId, stepId, result, requestContext });
|
|
3232
|
+
}
|
|
3233
|
+
async updateWorkflowState({
|
|
3234
|
+
workflowName,
|
|
3235
|
+
runId,
|
|
3236
|
+
opts
|
|
3237
|
+
}) {
|
|
3238
|
+
return this.stores.workflows.updateWorkflowState({ workflowName, runId, opts });
|
|
3239
|
+
}
|
|
2216
3240
|
async persistWorkflowSnapshot({
|
|
2217
3241
|
workflowName,
|
|
2218
3242
|
runId,
|
|
3243
|
+
resourceId,
|
|
2219
3244
|
snapshot
|
|
2220
3245
|
}) {
|
|
2221
|
-
return this.stores.workflows.persistWorkflowSnapshot({ workflowName, runId, snapshot });
|
|
3246
|
+
return this.stores.workflows.persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot });
|
|
2222
3247
|
}
|
|
2223
3248
|
async loadWorkflowSnapshot({
|
|
2224
3249
|
workflowName,
|
|
@@ -2226,15 +3251,8 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2226
3251
|
}) {
|
|
2227
3252
|
return this.stores.workflows.loadWorkflowSnapshot({ workflowName, runId });
|
|
2228
3253
|
}
|
|
2229
|
-
async
|
|
2230
|
-
|
|
2231
|
-
fromDate,
|
|
2232
|
-
toDate,
|
|
2233
|
-
limit,
|
|
2234
|
-
offset,
|
|
2235
|
-
resourceId
|
|
2236
|
-
} = {}) {
|
|
2237
|
-
return this.stores.workflows.getWorkflowRuns({ workflowName, fromDate, toDate, limit, offset, resourceId });
|
|
3254
|
+
async listWorkflowRuns(args = {}) {
|
|
3255
|
+
return this.stores.workflows.listWorkflowRuns(args);
|
|
2238
3256
|
}
|
|
2239
3257
|
async getWorkflowRunById({
|
|
2240
3258
|
runId,
|
|
@@ -2242,41 +3260,114 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2242
3260
|
}) {
|
|
2243
3261
|
return this.stores.workflows.getWorkflowRunById({ runId, workflowName });
|
|
2244
3262
|
}
|
|
3263
|
+
async deleteWorkflowRunById({ runId, workflowName }) {
|
|
3264
|
+
return this.stores.workflows.deleteWorkflowRunById({ runId, workflowName });
|
|
3265
|
+
}
|
|
2245
3266
|
async close() {
|
|
2246
3267
|
await this.pool.close();
|
|
2247
3268
|
}
|
|
3269
|
+
/**
|
|
3270
|
+
* Index Management
|
|
3271
|
+
*/
|
|
3272
|
+
async createIndex(options) {
|
|
3273
|
+
return this.stores.operations.createIndex(options);
|
|
3274
|
+
}
|
|
3275
|
+
async listIndexes(tableName) {
|
|
3276
|
+
return this.stores.operations.listIndexes(tableName);
|
|
3277
|
+
}
|
|
3278
|
+
async describeIndex(indexName) {
|
|
3279
|
+
return this.stores.operations.describeIndex(indexName);
|
|
3280
|
+
}
|
|
3281
|
+
async dropIndex(indexName) {
|
|
3282
|
+
return this.stores.operations.dropIndex(indexName);
|
|
3283
|
+
}
|
|
3284
|
+
/**
|
|
3285
|
+
* Tracing / Observability
|
|
3286
|
+
*/
|
|
3287
|
+
getObservabilityStore() {
|
|
3288
|
+
if (!this.stores.observability) {
|
|
3289
|
+
throw new MastraError({
|
|
3290
|
+
id: createStorageErrorId("MSSQL", "OBSERVABILITY", "NOT_INITIALIZED"),
|
|
3291
|
+
domain: ErrorDomain.STORAGE,
|
|
3292
|
+
category: ErrorCategory.SYSTEM,
|
|
3293
|
+
text: "Observability storage is not initialized"
|
|
3294
|
+
});
|
|
3295
|
+
}
|
|
3296
|
+
return this.stores.observability;
|
|
3297
|
+
}
|
|
3298
|
+
async createSpan(span) {
|
|
3299
|
+
return this.getObservabilityStore().createSpan(span);
|
|
3300
|
+
}
|
|
3301
|
+
async updateSpan({
|
|
3302
|
+
spanId,
|
|
3303
|
+
traceId,
|
|
3304
|
+
updates
|
|
3305
|
+
}) {
|
|
3306
|
+
return this.getObservabilityStore().updateSpan({ spanId, traceId, updates });
|
|
3307
|
+
}
|
|
3308
|
+
async getTrace(traceId) {
|
|
3309
|
+
return this.getObservabilityStore().getTrace(traceId);
|
|
3310
|
+
}
|
|
3311
|
+
async getTracesPaginated(args) {
|
|
3312
|
+
return this.getObservabilityStore().getTracesPaginated(args);
|
|
3313
|
+
}
|
|
3314
|
+
async batchCreateSpans(args) {
|
|
3315
|
+
return this.getObservabilityStore().batchCreateSpans(args);
|
|
3316
|
+
}
|
|
3317
|
+
async batchUpdateSpans(args) {
|
|
3318
|
+
return this.getObservabilityStore().batchUpdateSpans(args);
|
|
3319
|
+
}
|
|
3320
|
+
async batchDeleteTraces(args) {
|
|
3321
|
+
return this.getObservabilityStore().batchDeleteTraces(args);
|
|
3322
|
+
}
|
|
2248
3323
|
/**
|
|
2249
3324
|
* Scorers
|
|
2250
3325
|
*/
|
|
2251
3326
|
async getScoreById({ id: _id }) {
|
|
2252
3327
|
return this.stores.scores.getScoreById({ id: _id });
|
|
2253
3328
|
}
|
|
2254
|
-
async
|
|
3329
|
+
async listScoresByScorerId({
|
|
2255
3330
|
scorerId: _scorerId,
|
|
2256
|
-
pagination: _pagination
|
|
3331
|
+
pagination: _pagination,
|
|
3332
|
+
entityId: _entityId,
|
|
3333
|
+
entityType: _entityType,
|
|
3334
|
+
source: _source
|
|
2257
3335
|
}) {
|
|
2258
|
-
return this.stores.scores.
|
|
3336
|
+
return this.stores.scores.listScoresByScorerId({
|
|
3337
|
+
scorerId: _scorerId,
|
|
3338
|
+
pagination: _pagination,
|
|
3339
|
+
entityId: _entityId,
|
|
3340
|
+
entityType: _entityType,
|
|
3341
|
+
source: _source
|
|
3342
|
+
});
|
|
2259
3343
|
}
|
|
2260
|
-
async saveScore(
|
|
2261
|
-
return this.stores.scores.saveScore(
|
|
3344
|
+
async saveScore(score) {
|
|
3345
|
+
return this.stores.scores.saveScore(score);
|
|
2262
3346
|
}
|
|
2263
|
-
async
|
|
3347
|
+
async listScoresByRunId({
|
|
2264
3348
|
runId: _runId,
|
|
2265
3349
|
pagination: _pagination
|
|
2266
3350
|
}) {
|
|
2267
|
-
return this.stores.scores.
|
|
3351
|
+
return this.stores.scores.listScoresByRunId({ runId: _runId, pagination: _pagination });
|
|
2268
3352
|
}
|
|
2269
|
-
async
|
|
3353
|
+
async listScoresByEntityId({
|
|
2270
3354
|
entityId: _entityId,
|
|
2271
3355
|
entityType: _entityType,
|
|
2272
3356
|
pagination: _pagination
|
|
2273
3357
|
}) {
|
|
2274
|
-
return this.stores.scores.
|
|
3358
|
+
return this.stores.scores.listScoresByEntityId({
|
|
2275
3359
|
entityId: _entityId,
|
|
2276
3360
|
entityType: _entityType,
|
|
2277
3361
|
pagination: _pagination
|
|
2278
3362
|
});
|
|
2279
3363
|
}
|
|
3364
|
+
async listScoresBySpan({
|
|
3365
|
+
traceId,
|
|
3366
|
+
spanId,
|
|
3367
|
+
pagination: _pagination
|
|
3368
|
+
}) {
|
|
3369
|
+
return this.stores.scores.listScoresBySpan({ traceId, spanId, pagination: _pagination });
|
|
3370
|
+
}
|
|
2280
3371
|
};
|
|
2281
3372
|
|
|
2282
3373
|
export { MSSQLStore };
|