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