@mastra/libsql 1.0.0-beta.8 → 1.0.0-beta.9
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 +194 -0
- package/dist/index.cjs +1911 -1780
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1912 -1781
- package/dist/index.js.map +1 -1
- package/dist/storage/db/index.d.ts +264 -0
- package/dist/storage/db/index.d.ts.map +1 -0
- package/dist/storage/{domains → db}/utils.d.ts +5 -12
- package/dist/storage/db/utils.d.ts.map +1 -0
- package/dist/storage/domains/agents/index.d.ts +5 -7
- package/dist/storage/domains/agents/index.d.ts.map +1 -1
- package/dist/storage/domains/memory/index.d.ts +5 -8
- package/dist/storage/domains/memory/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/index.d.ts +19 -27
- package/dist/storage/domains/observability/index.d.ts.map +1 -1
- package/dist/storage/domains/scores/index.d.ts +11 -26
- package/dist/storage/domains/scores/index.d.ts.map +1 -1
- package/dist/storage/domains/workflows/index.d.ts +9 -14
- package/dist/storage/domains/workflows/index.d.ts.map +1 -1
- package/dist/storage/index.d.ts +20 -187
- package/dist/storage/index.d.ts.map +1 -1
- package/package.json +4 -4
- package/dist/storage/domains/operations/index.d.ts +0 -110
- package/dist/storage/domains/operations/index.d.ts.map +0 -1
- package/dist/storage/domains/utils.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { createClient } from '@libsql/client';
|
|
2
2
|
import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
|
|
3
|
-
import { createVectorErrorId, MastraStorage,
|
|
3
|
+
import { createVectorErrorId, MastraStorage, ScoresStorage, SCORERS_SCHEMA, TABLE_SCORERS, normalizePerPage, calculatePagination, createStorageErrorId, transformScoreRow, WorkflowsStorage, TABLE_SCHEMAS, TABLE_WORKFLOW_SNAPSHOT, MemoryStorage, TABLE_THREADS, TABLE_MESSAGES, TABLE_RESOURCES, ObservabilityStorage, SPAN_SCHEMA, TABLE_SPANS, listTracesArgsSchema, AgentsStorage, AGENTS_SCHEMA, TABLE_AGENTS, getSqlType, TraceStatus, safelyParseJSON } from '@mastra/core/storage';
|
|
4
4
|
import { parseSqlIdentifier, parseFieldKey } from '@mastra/core/utils';
|
|
5
5
|
import { MastraVector } from '@mastra/core/vector';
|
|
6
6
|
import { BaseFilterTranslator } from '@mastra/core/vector/filter';
|
|
7
|
+
import { MastraBase } from '@mastra/core/base';
|
|
7
8
|
import { MessageList } from '@mastra/core/agent';
|
|
8
9
|
import { saveScorePayloadSchema } from '@mastra/core/evals';
|
|
9
10
|
|
|
@@ -1092,1807 +1093,2134 @@ var LibSQLVector = class extends MastraVector {
|
|
|
1092
1093
|
});
|
|
1093
1094
|
}
|
|
1094
1095
|
};
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
}
|
|
1110
|
-
|
|
1111
|
-
|
|
1096
|
+
function isLockError(error) {
|
|
1097
|
+
return error.code === "SQLITE_BUSY" || error.code === "SQLITE_LOCKED" || error.message?.toLowerCase().includes("database is locked") || error.message?.toLowerCase().includes("database table is locked") || error.message?.toLowerCase().includes("table is locked") || error.constructor.name === "SqliteError" && error.message?.toLowerCase().includes("locked");
|
|
1098
|
+
}
|
|
1099
|
+
function createExecuteWriteOperationWithRetry({
|
|
1100
|
+
logger,
|
|
1101
|
+
maxRetries,
|
|
1102
|
+
initialBackoffMs
|
|
1103
|
+
}) {
|
|
1104
|
+
return async function executeWriteOperationWithRetry(operationFn, operationDescription) {
|
|
1105
|
+
let attempts = 0;
|
|
1106
|
+
let backoff = initialBackoffMs;
|
|
1107
|
+
while (attempts < maxRetries) {
|
|
1108
|
+
try {
|
|
1109
|
+
return await operationFn();
|
|
1110
|
+
} catch (error) {
|
|
1111
|
+
logger.debug(`LibSQLStore: Error caught in retry loop for ${operationDescription}`, {
|
|
1112
|
+
errorType: error.constructor.name,
|
|
1113
|
+
errorCode: error.code,
|
|
1114
|
+
errorMessage: error.message,
|
|
1115
|
+
attempts,
|
|
1116
|
+
maxRetries
|
|
1117
|
+
});
|
|
1118
|
+
if (isLockError(error)) {
|
|
1119
|
+
attempts++;
|
|
1120
|
+
if (attempts >= maxRetries) {
|
|
1121
|
+
logger.error(
|
|
1122
|
+
`LibSQLStore: Operation failed after ${maxRetries} attempts due to database lock: ${error.message}`,
|
|
1123
|
+
{ error, attempts, maxRetries }
|
|
1124
|
+
);
|
|
1125
|
+
throw error;
|
|
1126
|
+
}
|
|
1127
|
+
logger.warn(
|
|
1128
|
+
`LibSQLStore: Attempt ${attempts} failed due to database lock during ${operationDescription}. Retrying in ${backoff}ms...`,
|
|
1129
|
+
{ errorMessage: error.message, attempts, backoff, maxRetries }
|
|
1130
|
+
);
|
|
1131
|
+
await new Promise((resolve) => setTimeout(resolve, backoff));
|
|
1132
|
+
backoff *= 2;
|
|
1133
|
+
} else {
|
|
1134
|
+
logger.error(`LibSQLStore: Non-lock error during ${operationDescription}, not retrying`, { error });
|
|
1135
|
+
throw error;
|
|
1136
|
+
}
|
|
1112
1137
|
}
|
|
1113
|
-
throw new MastraError(
|
|
1114
|
-
{
|
|
1115
|
-
id: createStorageErrorId("LIBSQL", "PARSE_JSON", "INVALID_JSON"),
|
|
1116
|
-
domain: ErrorDomain.STORAGE,
|
|
1117
|
-
category: ErrorCategory.SYSTEM,
|
|
1118
|
-
text: `Failed to parse JSON${fieldName ? ` for field "${fieldName}"` : ""}: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
1119
|
-
details
|
|
1120
|
-
},
|
|
1121
|
-
error
|
|
1122
|
-
);
|
|
1123
1138
|
}
|
|
1139
|
+
throw new Error(`LibSQLStore: Unexpected exit from retry loop for ${operationDescription}`);
|
|
1140
|
+
};
|
|
1141
|
+
}
|
|
1142
|
+
function prepareStatement({ tableName, record }) {
|
|
1143
|
+
const parsedTableName = parseSqlIdentifier(tableName, "table name");
|
|
1144
|
+
const columns = Object.keys(record).map((col) => parseSqlIdentifier(col, "column name"));
|
|
1145
|
+
const values = Object.values(record).map((v) => {
|
|
1146
|
+
if (typeof v === `undefined` || v === null) {
|
|
1147
|
+
return null;
|
|
1148
|
+
}
|
|
1149
|
+
if (v instanceof Date) {
|
|
1150
|
+
return v.toISOString();
|
|
1151
|
+
}
|
|
1152
|
+
return typeof v === "object" ? JSON.stringify(v) : v;
|
|
1153
|
+
});
|
|
1154
|
+
const placeholders = values.map(() => "?").join(", ");
|
|
1155
|
+
return {
|
|
1156
|
+
sql: `INSERT OR REPLACE INTO ${parsedTableName} (${columns.join(", ")}) VALUES (${placeholders})`,
|
|
1157
|
+
args: values
|
|
1158
|
+
};
|
|
1159
|
+
}
|
|
1160
|
+
function prepareUpdateStatement({
|
|
1161
|
+
tableName,
|
|
1162
|
+
updates,
|
|
1163
|
+
keys
|
|
1164
|
+
}) {
|
|
1165
|
+
const parsedTableName = parseSqlIdentifier(tableName, "table name");
|
|
1166
|
+
const schema = TABLE_SCHEMAS[tableName];
|
|
1167
|
+
const updateColumns = Object.keys(updates).map((col) => parseSqlIdentifier(col, "column name"));
|
|
1168
|
+
const updateValues = Object.values(updates).map(transformToSqlValue);
|
|
1169
|
+
const setClause = updateColumns.map((col) => `${col} = ?`).join(", ");
|
|
1170
|
+
const whereClause = prepareWhereClause(keys, schema);
|
|
1171
|
+
return {
|
|
1172
|
+
sql: `UPDATE ${parsedTableName} SET ${setClause}${whereClause.sql}`,
|
|
1173
|
+
args: [...updateValues, ...whereClause.args]
|
|
1174
|
+
};
|
|
1175
|
+
}
|
|
1176
|
+
function transformToSqlValue(value) {
|
|
1177
|
+
if (typeof value === "undefined" || value === null) {
|
|
1178
|
+
return null;
|
|
1124
1179
|
}
|
|
1125
|
-
|
|
1126
|
-
return
|
|
1127
|
-
id: row.id,
|
|
1128
|
-
name: row.name,
|
|
1129
|
-
description: row.description,
|
|
1130
|
-
instructions: row.instructions,
|
|
1131
|
-
model: this.parseJson(row.model, "model"),
|
|
1132
|
-
tools: this.parseJson(row.tools, "tools"),
|
|
1133
|
-
defaultOptions: this.parseJson(row.defaultOptions, "defaultOptions"),
|
|
1134
|
-
workflows: this.parseJson(row.workflows, "workflows"),
|
|
1135
|
-
agents: this.parseJson(row.agents, "agents"),
|
|
1136
|
-
inputProcessors: this.parseJson(row.inputProcessors, "inputProcessors"),
|
|
1137
|
-
outputProcessors: this.parseJson(row.outputProcessors, "outputProcessors"),
|
|
1138
|
-
memory: this.parseJson(row.memory, "memory"),
|
|
1139
|
-
scorers: this.parseJson(row.scorers, "scorers"),
|
|
1140
|
-
metadata: this.parseJson(row.metadata, "metadata"),
|
|
1141
|
-
createdAt: new Date(row.createdAt),
|
|
1142
|
-
updatedAt: new Date(row.updatedAt)
|
|
1143
|
-
};
|
|
1180
|
+
if (value instanceof Date) {
|
|
1181
|
+
return value.toISOString();
|
|
1144
1182
|
}
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
},
|
|
1163
|
-
error
|
|
1164
|
-
);
|
|
1183
|
+
return typeof value === "object" ? JSON.stringify(value) : value;
|
|
1184
|
+
}
|
|
1185
|
+
function prepareDeleteStatement({ tableName, keys }) {
|
|
1186
|
+
const parsedTableName = parseSqlIdentifier(tableName, "table name");
|
|
1187
|
+
const whereClause = prepareWhereClause(keys, TABLE_SCHEMAS[tableName]);
|
|
1188
|
+
return {
|
|
1189
|
+
sql: `DELETE FROM ${parsedTableName}${whereClause.sql}`,
|
|
1190
|
+
args: whereClause.args
|
|
1191
|
+
};
|
|
1192
|
+
}
|
|
1193
|
+
function prepareWhereClause(filters, schema) {
|
|
1194
|
+
const conditions = [];
|
|
1195
|
+
const args = [];
|
|
1196
|
+
for (const [columnName, filterValue] of Object.entries(filters)) {
|
|
1197
|
+
const column = schema[columnName];
|
|
1198
|
+
if (!column) {
|
|
1199
|
+
throw new Error(`Unknown column: ${columnName}`);
|
|
1165
1200
|
}
|
|
1201
|
+
const parsedColumn = parseSqlIdentifier(columnName, "column name");
|
|
1202
|
+
const result = buildCondition2(parsedColumn, filterValue);
|
|
1203
|
+
conditions.push(result.condition);
|
|
1204
|
+
args.push(...result.args);
|
|
1166
1205
|
}
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
agent.id,
|
|
1176
|
-
agent.name,
|
|
1177
|
-
agent.description ?? null,
|
|
1178
|
-
agent.instructions,
|
|
1179
|
-
JSON.stringify(agent.model),
|
|
1180
|
-
agent.tools ? JSON.stringify(agent.tools) : null,
|
|
1181
|
-
agent.defaultOptions ? JSON.stringify(agent.defaultOptions) : null,
|
|
1182
|
-
agent.workflows ? JSON.stringify(agent.workflows) : null,
|
|
1183
|
-
agent.agents ? JSON.stringify(agent.agents) : null,
|
|
1184
|
-
agent.inputProcessors ? JSON.stringify(agent.inputProcessors) : null,
|
|
1185
|
-
agent.outputProcessors ? JSON.stringify(agent.outputProcessors) : null,
|
|
1186
|
-
agent.memory ? JSON.stringify(agent.memory) : null,
|
|
1187
|
-
agent.scorers ? JSON.stringify(agent.scorers) : null,
|
|
1188
|
-
agent.metadata ? JSON.stringify(agent.metadata) : null,
|
|
1189
|
-
nowIso,
|
|
1190
|
-
nowIso
|
|
1191
|
-
]
|
|
1192
|
-
});
|
|
1193
|
-
return {
|
|
1194
|
-
...agent,
|
|
1195
|
-
createdAt: now,
|
|
1196
|
-
updatedAt: now
|
|
1197
|
-
};
|
|
1198
|
-
} catch (error) {
|
|
1199
|
-
throw new MastraError(
|
|
1200
|
-
{
|
|
1201
|
-
id: createStorageErrorId("LIBSQL", "CREATE_AGENT", "FAILED"),
|
|
1202
|
-
domain: ErrorDomain.STORAGE,
|
|
1203
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
1204
|
-
details: { agentId: agent.id }
|
|
1205
|
-
},
|
|
1206
|
-
error
|
|
1207
|
-
);
|
|
1208
|
-
}
|
|
1206
|
+
return {
|
|
1207
|
+
sql: conditions.length > 0 ? ` WHERE ${conditions.join(" AND ")}` : "",
|
|
1208
|
+
args
|
|
1209
|
+
};
|
|
1210
|
+
}
|
|
1211
|
+
function buildCondition2(columnName, filterValue) {
|
|
1212
|
+
if (filterValue === null) {
|
|
1213
|
+
return { condition: `${columnName} IS NULL`, args: [] };
|
|
1209
1214
|
}
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
const existingAgent = await this.getAgentById({ id });
|
|
1213
|
-
if (!existingAgent) {
|
|
1214
|
-
throw new MastraError({
|
|
1215
|
-
id: createStorageErrorId("LIBSQL", "UPDATE_AGENT", "NOT_FOUND"),
|
|
1216
|
-
domain: ErrorDomain.STORAGE,
|
|
1217
|
-
category: ErrorCategory.USER,
|
|
1218
|
-
text: `Agent ${id} not found`,
|
|
1219
|
-
details: { agentId: id }
|
|
1220
|
-
});
|
|
1221
|
-
}
|
|
1222
|
-
const setClauses = [];
|
|
1223
|
-
const args = [];
|
|
1224
|
-
if (updates.name !== void 0) {
|
|
1225
|
-
setClauses.push("name = ?");
|
|
1226
|
-
args.push(updates.name);
|
|
1227
|
-
}
|
|
1228
|
-
if (updates.description !== void 0) {
|
|
1229
|
-
setClauses.push("description = ?");
|
|
1230
|
-
args.push(updates.description);
|
|
1231
|
-
}
|
|
1232
|
-
if (updates.instructions !== void 0) {
|
|
1233
|
-
setClauses.push("instructions = ?");
|
|
1234
|
-
args.push(updates.instructions);
|
|
1235
|
-
}
|
|
1236
|
-
if (updates.model !== void 0) {
|
|
1237
|
-
setClauses.push("model = ?");
|
|
1238
|
-
args.push(JSON.stringify(updates.model));
|
|
1239
|
-
}
|
|
1240
|
-
if (updates.tools !== void 0) {
|
|
1241
|
-
setClauses.push("tools = ?");
|
|
1242
|
-
args.push(JSON.stringify(updates.tools));
|
|
1243
|
-
}
|
|
1244
|
-
if (updates.defaultOptions !== void 0) {
|
|
1245
|
-
setClauses.push('"defaultOptions" = ?');
|
|
1246
|
-
args.push(JSON.stringify(updates.defaultOptions));
|
|
1247
|
-
}
|
|
1248
|
-
if (updates.workflows !== void 0) {
|
|
1249
|
-
setClauses.push("workflows = ?");
|
|
1250
|
-
args.push(JSON.stringify(updates.workflows));
|
|
1251
|
-
}
|
|
1252
|
-
if (updates.agents !== void 0) {
|
|
1253
|
-
setClauses.push("agents = ?");
|
|
1254
|
-
args.push(JSON.stringify(updates.agents));
|
|
1255
|
-
}
|
|
1256
|
-
if (updates.inputProcessors !== void 0) {
|
|
1257
|
-
setClauses.push('"inputProcessors" = ?');
|
|
1258
|
-
args.push(JSON.stringify(updates.inputProcessors));
|
|
1259
|
-
}
|
|
1260
|
-
if (updates.outputProcessors !== void 0) {
|
|
1261
|
-
setClauses.push('"outputProcessors" = ?');
|
|
1262
|
-
args.push(JSON.stringify(updates.outputProcessors));
|
|
1263
|
-
}
|
|
1264
|
-
if (updates.memory !== void 0) {
|
|
1265
|
-
setClauses.push("memory = ?");
|
|
1266
|
-
args.push(JSON.stringify(updates.memory));
|
|
1267
|
-
}
|
|
1268
|
-
if (updates.scorers !== void 0) {
|
|
1269
|
-
setClauses.push("scorers = ?");
|
|
1270
|
-
args.push(JSON.stringify(updates.scorers));
|
|
1271
|
-
}
|
|
1272
|
-
if (updates.metadata !== void 0) {
|
|
1273
|
-
const mergedMetadata = { ...existingAgent.metadata, ...updates.metadata };
|
|
1274
|
-
setClauses.push("metadata = ?");
|
|
1275
|
-
args.push(JSON.stringify(mergedMetadata));
|
|
1276
|
-
}
|
|
1277
|
-
const now = /* @__PURE__ */ new Date();
|
|
1278
|
-
setClauses.push('"updatedAt" = ?');
|
|
1279
|
-
args.push(now.toISOString());
|
|
1280
|
-
args.push(id);
|
|
1281
|
-
if (setClauses.length > 1) {
|
|
1282
|
-
await this.client.execute({
|
|
1283
|
-
sql: `UPDATE "${TABLE_AGENTS}" SET ${setClauses.join(", ")} WHERE id = ?`,
|
|
1284
|
-
args
|
|
1285
|
-
});
|
|
1286
|
-
}
|
|
1287
|
-
const updatedAgent = await this.getAgentById({ id });
|
|
1288
|
-
if (!updatedAgent) {
|
|
1289
|
-
throw new MastraError({
|
|
1290
|
-
id: createStorageErrorId("LIBSQL", "UPDATE_AGENT", "NOT_FOUND_AFTER_UPDATE"),
|
|
1291
|
-
domain: ErrorDomain.STORAGE,
|
|
1292
|
-
category: ErrorCategory.SYSTEM,
|
|
1293
|
-
text: `Agent ${id} not found after update`,
|
|
1294
|
-
details: { agentId: id }
|
|
1295
|
-
});
|
|
1296
|
-
}
|
|
1297
|
-
return updatedAgent;
|
|
1298
|
-
} catch (error) {
|
|
1299
|
-
if (error instanceof MastraError) {
|
|
1300
|
-
throw error;
|
|
1301
|
-
}
|
|
1302
|
-
throw new MastraError(
|
|
1303
|
-
{
|
|
1304
|
-
id: createStorageErrorId("LIBSQL", "UPDATE_AGENT", "FAILED"),
|
|
1305
|
-
domain: ErrorDomain.STORAGE,
|
|
1306
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
1307
|
-
details: { agentId: id }
|
|
1308
|
-
},
|
|
1309
|
-
error
|
|
1310
|
-
);
|
|
1311
|
-
}
|
|
1215
|
+
if (typeof filterValue === "object" && filterValue !== null && ("startAt" in filterValue || "endAt" in filterValue)) {
|
|
1216
|
+
return buildDateRangeCondition(columnName, filterValue);
|
|
1312
1217
|
}
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
1325
|
-
details: { agentId: id }
|
|
1326
|
-
},
|
|
1327
|
-
error
|
|
1328
|
-
);
|
|
1329
|
-
}
|
|
1218
|
+
return {
|
|
1219
|
+
condition: `${columnName} = ?`,
|
|
1220
|
+
args: [transformToSqlValue(filterValue)]
|
|
1221
|
+
};
|
|
1222
|
+
}
|
|
1223
|
+
function buildDateRangeCondition(columnName, range) {
|
|
1224
|
+
const conditions = [];
|
|
1225
|
+
const args = [];
|
|
1226
|
+
if (range.startAt !== void 0) {
|
|
1227
|
+
conditions.push(`${columnName} >= ?`);
|
|
1228
|
+
args.push(transformToSqlValue(range.startAt));
|
|
1330
1229
|
}
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1230
|
+
if (range.endAt !== void 0) {
|
|
1231
|
+
conditions.push(`${columnName} <= ?`);
|
|
1232
|
+
args.push(transformToSqlValue(range.endAt));
|
|
1233
|
+
}
|
|
1234
|
+
if (conditions.length === 0) {
|
|
1235
|
+
throw new Error("Date range must specify at least startAt or endAt");
|
|
1236
|
+
}
|
|
1237
|
+
return {
|
|
1238
|
+
condition: conditions.join(" AND "),
|
|
1239
|
+
args
|
|
1240
|
+
};
|
|
1241
|
+
}
|
|
1242
|
+
function transformFromSqlRow({
|
|
1243
|
+
tableName,
|
|
1244
|
+
sqlRow
|
|
1245
|
+
}) {
|
|
1246
|
+
const result = {};
|
|
1247
|
+
const jsonColumns = new Set(
|
|
1248
|
+
Object.keys(TABLE_SCHEMAS[tableName]).filter((key) => TABLE_SCHEMAS[tableName][key].type === "jsonb").map((key) => key)
|
|
1249
|
+
);
|
|
1250
|
+
const dateColumns = new Set(
|
|
1251
|
+
Object.keys(TABLE_SCHEMAS[tableName]).filter((key) => TABLE_SCHEMAS[tableName][key].type === "timestamp").map((key) => key)
|
|
1252
|
+
);
|
|
1253
|
+
for (const [key, value] of Object.entries(sqlRow)) {
|
|
1254
|
+
if (value === null || value === void 0) {
|
|
1255
|
+
result[key] = value;
|
|
1256
|
+
continue;
|
|
1344
1257
|
}
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
const total = Number(countResult.rows?.[0]?.count ?? 0);
|
|
1353
|
-
if (total === 0) {
|
|
1354
|
-
return {
|
|
1355
|
-
agents: [],
|
|
1356
|
-
total: 0,
|
|
1357
|
-
page,
|
|
1358
|
-
perPage: perPageForResponse,
|
|
1359
|
-
hasMore: false
|
|
1360
|
-
};
|
|
1361
|
-
}
|
|
1362
|
-
const limitValue = perPageInput === false ? total : perPage;
|
|
1363
|
-
const dataResult = await this.client.execute({
|
|
1364
|
-
sql: `SELECT * FROM "${TABLE_AGENTS}" ORDER BY "${field}" ${direction} LIMIT ? OFFSET ?`,
|
|
1365
|
-
args: [limitValue, offset]
|
|
1366
|
-
});
|
|
1367
|
-
const agents = (dataResult.rows || []).map((row) => this.parseRow(row));
|
|
1368
|
-
return {
|
|
1369
|
-
agents,
|
|
1370
|
-
total,
|
|
1371
|
-
page,
|
|
1372
|
-
perPage: perPageForResponse,
|
|
1373
|
-
hasMore: perPageInput === false ? false : offset + perPage < total
|
|
1374
|
-
};
|
|
1375
|
-
} catch (error) {
|
|
1376
|
-
throw new MastraError(
|
|
1377
|
-
{
|
|
1378
|
-
id: createStorageErrorId("LIBSQL", "LIST_AGENTS", "FAILED"),
|
|
1379
|
-
domain: ErrorDomain.STORAGE,
|
|
1380
|
-
category: ErrorCategory.THIRD_PARTY
|
|
1381
|
-
},
|
|
1382
|
-
error
|
|
1383
|
-
);
|
|
1258
|
+
if (dateColumns.has(key) && typeof value === "string") {
|
|
1259
|
+
result[key] = new Date(value);
|
|
1260
|
+
continue;
|
|
1261
|
+
}
|
|
1262
|
+
if (jsonColumns.has(key) && typeof value === "string") {
|
|
1263
|
+
result[key] = safelyParseJSON(value);
|
|
1264
|
+
continue;
|
|
1384
1265
|
}
|
|
1266
|
+
result[key] = value;
|
|
1385
1267
|
}
|
|
1386
|
-
|
|
1387
|
-
|
|
1268
|
+
return result;
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
// src/storage/db/index.ts
|
|
1272
|
+
function resolveClient(config) {
|
|
1273
|
+
if ("client" in config) {
|
|
1274
|
+
return config.client;
|
|
1275
|
+
}
|
|
1276
|
+
return createClient({
|
|
1277
|
+
url: config.url,
|
|
1278
|
+
...config.authToken ? { authToken: config.authToken } : {}
|
|
1279
|
+
});
|
|
1280
|
+
}
|
|
1281
|
+
var LibSQLDB = class extends MastraBase {
|
|
1388
1282
|
client;
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1283
|
+
maxRetries;
|
|
1284
|
+
initialBackoffMs;
|
|
1285
|
+
executeWriteOperationWithRetry;
|
|
1286
|
+
constructor({
|
|
1287
|
+
client,
|
|
1288
|
+
maxRetries,
|
|
1289
|
+
initialBackoffMs
|
|
1290
|
+
}) {
|
|
1291
|
+
super({
|
|
1292
|
+
component: "STORAGE",
|
|
1293
|
+
name: "LIBSQL_DB_LAYER"
|
|
1294
|
+
});
|
|
1392
1295
|
this.client = client;
|
|
1393
|
-
this.
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
}
|
|
1400
|
-
}
|
|
1401
|
-
const result = {
|
|
1402
|
-
id: row.id,
|
|
1403
|
-
content,
|
|
1404
|
-
role: row.role,
|
|
1405
|
-
createdAt: new Date(row.createdAt),
|
|
1406
|
-
threadId: row.thread_id,
|
|
1407
|
-
resourceId: row.resourceId
|
|
1408
|
-
};
|
|
1409
|
-
if (row.type && row.type !== `v2`) result.type = row.type;
|
|
1410
|
-
return result;
|
|
1296
|
+
this.maxRetries = maxRetries ?? 5;
|
|
1297
|
+
this.initialBackoffMs = initialBackoffMs ?? 100;
|
|
1298
|
+
this.executeWriteOperationWithRetry = createExecuteWriteOperationWithRetry({
|
|
1299
|
+
logger: this.logger,
|
|
1300
|
+
maxRetries: this.maxRetries,
|
|
1301
|
+
initialBackoffMs: this.initialBackoffMs
|
|
1302
|
+
});
|
|
1411
1303
|
}
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
),
|
|
1424
|
-
numbered_messages AS (
|
|
1425
|
-
SELECT
|
|
1426
|
-
id, content, role, type, "createdAt", thread_id, "resourceId",
|
|
1427
|
-
ROW_NUMBER() OVER (ORDER BY "createdAt" ASC) as row_num
|
|
1428
|
-
FROM "${TABLE_MESSAGES}"
|
|
1429
|
-
WHERE thread_id = (SELECT thread_id FROM target_thread)
|
|
1430
|
-
),
|
|
1431
|
-
target_positions AS (
|
|
1432
|
-
SELECT row_num as target_pos
|
|
1433
|
-
FROM numbered_messages
|
|
1434
|
-
WHERE id = ?
|
|
1435
|
-
)
|
|
1436
|
-
SELECT DISTINCT m.*
|
|
1437
|
-
FROM numbered_messages m
|
|
1438
|
-
CROSS JOIN target_positions t
|
|
1439
|
-
WHERE m.row_num BETWEEN (t.target_pos - ?) AND (t.target_pos + ?)
|
|
1440
|
-
)
|
|
1441
|
-
`
|
|
1442
|
-
// Keep ASC for final sorting after fetching context
|
|
1443
|
-
);
|
|
1444
|
-
params.push(id, id, withPreviousMessages, withNextMessages);
|
|
1445
|
-
}
|
|
1446
|
-
const finalQuery = unionQueries.join(" UNION ALL ") + ' ORDER BY "createdAt" ASC';
|
|
1447
|
-
const includedResult = await this.client.execute({ sql: finalQuery, args: params });
|
|
1448
|
-
const includedRows = includedResult.rows?.map((row) => this.parseRow(row));
|
|
1449
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1450
|
-
const dedupedRows = includedRows.filter((row) => {
|
|
1451
|
-
if (seen.has(row.id)) return false;
|
|
1452
|
-
seen.add(row.id);
|
|
1453
|
-
return true;
|
|
1304
|
+
/**
|
|
1305
|
+
* Checks if a column exists in the specified table.
|
|
1306
|
+
*
|
|
1307
|
+
* @param table - The name of the table to check
|
|
1308
|
+
* @param column - The name of the column to look for
|
|
1309
|
+
* @returns `true` if the column exists in the table, `false` otherwise
|
|
1310
|
+
*/
|
|
1311
|
+
async hasColumn(table, column) {
|
|
1312
|
+
const sanitizedTable = parseSqlIdentifier(table, "table name");
|
|
1313
|
+
const result = await this.client.execute({
|
|
1314
|
+
sql: `PRAGMA table_info("${sanitizedTable}")`
|
|
1454
1315
|
});
|
|
1455
|
-
return
|
|
1316
|
+
return result.rows?.some((row) => row.name === column);
|
|
1317
|
+
}
|
|
1318
|
+
/**
|
|
1319
|
+
* Internal insert implementation without retry logic.
|
|
1320
|
+
*/
|
|
1321
|
+
async doInsert({
|
|
1322
|
+
tableName,
|
|
1323
|
+
record
|
|
1324
|
+
}) {
|
|
1325
|
+
await this.client.execute(
|
|
1326
|
+
prepareStatement({
|
|
1327
|
+
tableName,
|
|
1328
|
+
record
|
|
1329
|
+
})
|
|
1330
|
+
);
|
|
1331
|
+
}
|
|
1332
|
+
/**
|
|
1333
|
+
* Inserts or replaces a record in the specified table with automatic retry on lock errors.
|
|
1334
|
+
*
|
|
1335
|
+
* @param args - The insert arguments
|
|
1336
|
+
* @param args.tableName - The name of the table to insert into
|
|
1337
|
+
* @param args.record - The record to insert (key-value pairs)
|
|
1338
|
+
*/
|
|
1339
|
+
insert(args) {
|
|
1340
|
+
return this.executeWriteOperationWithRetry(() => this.doInsert(args), `insert into table ${args.tableName}`);
|
|
1456
1341
|
}
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1342
|
+
/**
|
|
1343
|
+
* Internal update implementation without retry logic.
|
|
1344
|
+
*/
|
|
1345
|
+
async doUpdate({
|
|
1346
|
+
tableName,
|
|
1347
|
+
keys,
|
|
1348
|
+
data
|
|
1349
|
+
}) {
|
|
1350
|
+
await this.client.execute(prepareUpdateStatement({ tableName, updates: data, keys }));
|
|
1351
|
+
}
|
|
1352
|
+
/**
|
|
1353
|
+
* Updates a record in the specified table with automatic retry on lock errors.
|
|
1354
|
+
*
|
|
1355
|
+
* @param args - The update arguments
|
|
1356
|
+
* @param args.tableName - The name of the table to update
|
|
1357
|
+
* @param args.keys - The key(s) identifying the record to update
|
|
1358
|
+
* @param args.data - The fields to update (key-value pairs)
|
|
1359
|
+
*/
|
|
1360
|
+
update(args) {
|
|
1361
|
+
return this.executeWriteOperationWithRetry(() => this.doUpdate(args), `update table ${args.tableName}`);
|
|
1362
|
+
}
|
|
1363
|
+
/**
|
|
1364
|
+
* Internal batch insert implementation without retry logic.
|
|
1365
|
+
*/
|
|
1366
|
+
async doBatchInsert({
|
|
1367
|
+
tableName,
|
|
1368
|
+
records
|
|
1369
|
+
}) {
|
|
1370
|
+
if (records.length === 0) return;
|
|
1371
|
+
const batchStatements = records.map((r) => prepareStatement({ tableName, record: r }));
|
|
1372
|
+
await this.client.batch(batchStatements, "write");
|
|
1373
|
+
}
|
|
1374
|
+
/**
|
|
1375
|
+
* Inserts multiple records in a single batch transaction with automatic retry on lock errors.
|
|
1376
|
+
*
|
|
1377
|
+
* @param args - The batch insert arguments
|
|
1378
|
+
* @param args.tableName - The name of the table to insert into
|
|
1379
|
+
* @param args.records - Array of records to insert
|
|
1380
|
+
* @throws {MastraError} When the batch insert fails after retries
|
|
1381
|
+
*/
|
|
1382
|
+
async batchInsert(args) {
|
|
1383
|
+
return this.executeWriteOperationWithRetry(
|
|
1384
|
+
() => this.doBatchInsert(args),
|
|
1385
|
+
`batch insert into table ${args.tableName}`
|
|
1386
|
+
).catch((error) => {
|
|
1478
1387
|
throw new MastraError(
|
|
1479
1388
|
{
|
|
1480
|
-
id: createStorageErrorId("LIBSQL", "
|
|
1389
|
+
id: createStorageErrorId("LIBSQL", "BATCH_INSERT", "FAILED"),
|
|
1481
1390
|
domain: ErrorDomain.STORAGE,
|
|
1482
1391
|
category: ErrorCategory.THIRD_PARTY,
|
|
1483
|
-
details: {
|
|
1392
|
+
details: {
|
|
1393
|
+
tableName: args.tableName
|
|
1394
|
+
}
|
|
1484
1395
|
},
|
|
1485
1396
|
error
|
|
1486
1397
|
);
|
|
1487
|
-
}
|
|
1398
|
+
});
|
|
1488
1399
|
}
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1400
|
+
/**
|
|
1401
|
+
* Internal batch update implementation without retry logic.
|
|
1402
|
+
* Each record can be updated based on single or composite keys.
|
|
1403
|
+
*/
|
|
1404
|
+
async doBatchUpdate({
|
|
1405
|
+
tableName,
|
|
1406
|
+
updates
|
|
1407
|
+
}) {
|
|
1408
|
+
if (updates.length === 0) return;
|
|
1409
|
+
const batchStatements = updates.map(
|
|
1410
|
+
({ keys, data }) => prepareUpdateStatement({
|
|
1411
|
+
tableName,
|
|
1412
|
+
updates: data,
|
|
1413
|
+
keys
|
|
1414
|
+
})
|
|
1415
|
+
);
|
|
1416
|
+
await this.client.batch(batchStatements, "write");
|
|
1417
|
+
}
|
|
1418
|
+
/**
|
|
1419
|
+
* Updates multiple records in a single batch transaction with automatic retry on lock errors.
|
|
1420
|
+
* Each record can be updated based on single or composite keys.
|
|
1421
|
+
*
|
|
1422
|
+
* @param args - The batch update arguments
|
|
1423
|
+
* @param args.tableName - The name of the table to update
|
|
1424
|
+
* @param args.updates - Array of update operations, each containing keys and data
|
|
1425
|
+
* @throws {MastraError} When the batch update fails after retries
|
|
1426
|
+
*/
|
|
1427
|
+
async batchUpdate(args) {
|
|
1428
|
+
return this.executeWriteOperationWithRetry(
|
|
1429
|
+
() => this.doBatchUpdate(args),
|
|
1430
|
+
`batch update in table ${args.tableName}`
|
|
1431
|
+
).catch((error) => {
|
|
1493
1432
|
throw new MastraError(
|
|
1494
1433
|
{
|
|
1495
|
-
id: createStorageErrorId("LIBSQL", "
|
|
1434
|
+
id: createStorageErrorId("LIBSQL", "BATCH_UPDATE", "FAILED"),
|
|
1496
1435
|
domain: ErrorDomain.STORAGE,
|
|
1497
1436
|
category: ErrorCategory.THIRD_PARTY,
|
|
1498
|
-
details: {
|
|
1437
|
+
details: {
|
|
1438
|
+
tableName: args.tableName
|
|
1439
|
+
}
|
|
1499
1440
|
},
|
|
1500
|
-
|
|
1441
|
+
error
|
|
1501
1442
|
);
|
|
1502
|
-
}
|
|
1503
|
-
|
|
1443
|
+
});
|
|
1444
|
+
}
|
|
1445
|
+
/**
|
|
1446
|
+
* Internal batch delete implementation without retry logic.
|
|
1447
|
+
* Each record can be deleted based on single or composite keys.
|
|
1448
|
+
*/
|
|
1449
|
+
async doBatchDelete({
|
|
1450
|
+
tableName,
|
|
1451
|
+
keys
|
|
1452
|
+
}) {
|
|
1453
|
+
if (keys.length === 0) return;
|
|
1454
|
+
const batchStatements = keys.map(
|
|
1455
|
+
(keyObj) => prepareDeleteStatement({
|
|
1456
|
+
tableName,
|
|
1457
|
+
keys: keyObj
|
|
1458
|
+
})
|
|
1459
|
+
);
|
|
1460
|
+
await this.client.batch(batchStatements, "write");
|
|
1461
|
+
}
|
|
1462
|
+
/**
|
|
1463
|
+
* Deletes multiple records in a single batch transaction with automatic retry on lock errors.
|
|
1464
|
+
* Each record can be deleted based on single or composite keys.
|
|
1465
|
+
*
|
|
1466
|
+
* @param args - The batch delete arguments
|
|
1467
|
+
* @param args.tableName - The name of the table to delete from
|
|
1468
|
+
* @param args.keys - Array of key objects identifying records to delete
|
|
1469
|
+
* @throws {MastraError} When the batch delete fails after retries
|
|
1470
|
+
*/
|
|
1471
|
+
async batchDelete({
|
|
1472
|
+
tableName,
|
|
1473
|
+
keys
|
|
1474
|
+
}) {
|
|
1475
|
+
return this.executeWriteOperationWithRetry(
|
|
1476
|
+
() => this.doBatchDelete({ tableName, keys }),
|
|
1477
|
+
`batch delete from table ${tableName}`
|
|
1478
|
+
).catch((error) => {
|
|
1504
1479
|
throw new MastraError(
|
|
1505
1480
|
{
|
|
1506
|
-
id: createStorageErrorId("LIBSQL", "
|
|
1507
|
-
domain: ErrorDomain.STORAGE,
|
|
1508
|
-
category: ErrorCategory.USER,
|
|
1509
|
-
details: { page }
|
|
1510
|
-
},
|
|
1511
|
-
new Error("page must be >= 0")
|
|
1512
|
-
);
|
|
1513
|
-
}
|
|
1514
|
-
const perPage = normalizePerPage(perPageInput, 40);
|
|
1515
|
-
const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
1516
|
-
try {
|
|
1517
|
-
const { field, direction } = this.parseOrderBy(orderBy, "ASC");
|
|
1518
|
-
const orderByStatement = `ORDER BY "${field}" ${direction}`;
|
|
1519
|
-
const threadPlaceholders = threadIds.map(() => "?").join(", ");
|
|
1520
|
-
const conditions = [`thread_id IN (${threadPlaceholders})`];
|
|
1521
|
-
const queryParams = [...threadIds];
|
|
1522
|
-
if (resourceId) {
|
|
1523
|
-
conditions.push(`"resourceId" = ?`);
|
|
1524
|
-
queryParams.push(resourceId);
|
|
1525
|
-
}
|
|
1526
|
-
if (filter?.dateRange?.start) {
|
|
1527
|
-
conditions.push(`"createdAt" >= ?`);
|
|
1528
|
-
queryParams.push(
|
|
1529
|
-
filter.dateRange.start instanceof Date ? filter.dateRange.start.toISOString() : filter.dateRange.start
|
|
1530
|
-
);
|
|
1531
|
-
}
|
|
1532
|
-
if (filter?.dateRange?.end) {
|
|
1533
|
-
conditions.push(`"createdAt" <= ?`);
|
|
1534
|
-
queryParams.push(
|
|
1535
|
-
filter.dateRange.end instanceof Date ? filter.dateRange.end.toISOString() : filter.dateRange.end
|
|
1536
|
-
);
|
|
1537
|
-
}
|
|
1538
|
-
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
1539
|
-
const countResult = await this.client.execute({
|
|
1540
|
-
sql: `SELECT COUNT(*) as count FROM ${TABLE_MESSAGES} ${whereClause}`,
|
|
1541
|
-
args: queryParams
|
|
1542
|
-
});
|
|
1543
|
-
const total = Number(countResult.rows?.[0]?.count ?? 0);
|
|
1544
|
-
const limitValue = perPageInput === false ? total : perPage;
|
|
1545
|
-
const dataResult = await this.client.execute({
|
|
1546
|
-
sql: `SELECT id, content, role, type, "createdAt", "resourceId", "thread_id" FROM ${TABLE_MESSAGES} ${whereClause} ${orderByStatement} LIMIT ? OFFSET ?`,
|
|
1547
|
-
args: [...queryParams, limitValue, offset]
|
|
1548
|
-
});
|
|
1549
|
-
const messages = (dataResult.rows || []).map((row) => this.parseRow(row));
|
|
1550
|
-
if (total === 0 && messages.length === 0 && (!include || include.length === 0)) {
|
|
1551
|
-
return {
|
|
1552
|
-
messages: [],
|
|
1553
|
-
total: 0,
|
|
1554
|
-
page,
|
|
1555
|
-
perPage: perPageForResponse,
|
|
1556
|
-
hasMore: false
|
|
1557
|
-
};
|
|
1558
|
-
}
|
|
1559
|
-
const messageIds = new Set(messages.map((m) => m.id));
|
|
1560
|
-
if (include && include.length > 0) {
|
|
1561
|
-
const includeMessages = await this._getIncludedMessages({ include });
|
|
1562
|
-
if (includeMessages) {
|
|
1563
|
-
for (const includeMsg of includeMessages) {
|
|
1564
|
-
if (!messageIds.has(includeMsg.id)) {
|
|
1565
|
-
messages.push(includeMsg);
|
|
1566
|
-
messageIds.add(includeMsg.id);
|
|
1567
|
-
}
|
|
1568
|
-
}
|
|
1569
|
-
}
|
|
1570
|
-
}
|
|
1571
|
-
const list = new MessageList().add(messages, "memory");
|
|
1572
|
-
let finalMessages = list.get.all.db();
|
|
1573
|
-
finalMessages = finalMessages.sort((a, b) => {
|
|
1574
|
-
const isDateField = field === "createdAt" || field === "updatedAt";
|
|
1575
|
-
const aValue = isDateField ? new Date(a[field]).getTime() : a[field];
|
|
1576
|
-
const bValue = isDateField ? new Date(b[field]).getTime() : b[field];
|
|
1577
|
-
if (typeof aValue === "number" && typeof bValue === "number") {
|
|
1578
|
-
return direction === "ASC" ? aValue - bValue : bValue - aValue;
|
|
1579
|
-
}
|
|
1580
|
-
return direction === "ASC" ? String(aValue).localeCompare(String(bValue)) : String(bValue).localeCompare(String(aValue));
|
|
1581
|
-
});
|
|
1582
|
-
const threadIdSet = new Set(threadIds);
|
|
1583
|
-
const returnedThreadMessageIds = new Set(
|
|
1584
|
-
finalMessages.filter((m) => m.threadId && threadIdSet.has(m.threadId)).map((m) => m.id)
|
|
1585
|
-
);
|
|
1586
|
-
const allThreadMessagesReturned = returnedThreadMessageIds.size >= total;
|
|
1587
|
-
const hasMore = perPageInput !== false && !allThreadMessagesReturned && offset + perPage < total;
|
|
1588
|
-
return {
|
|
1589
|
-
messages: finalMessages,
|
|
1590
|
-
total,
|
|
1591
|
-
page,
|
|
1592
|
-
perPage: perPageForResponse,
|
|
1593
|
-
hasMore
|
|
1594
|
-
};
|
|
1595
|
-
} catch (error) {
|
|
1596
|
-
const mastraError = new MastraError(
|
|
1597
|
-
{
|
|
1598
|
-
id: createStorageErrorId("LIBSQL", "LIST_MESSAGES", "FAILED"),
|
|
1481
|
+
id: createStorageErrorId("LIBSQL", "BATCH_DELETE", "FAILED"),
|
|
1599
1482
|
domain: ErrorDomain.STORAGE,
|
|
1600
1483
|
category: ErrorCategory.THIRD_PARTY,
|
|
1601
1484
|
details: {
|
|
1602
|
-
|
|
1603
|
-
resourceId: resourceId ?? ""
|
|
1485
|
+
tableName
|
|
1604
1486
|
}
|
|
1605
1487
|
},
|
|
1606
1488
|
error
|
|
1607
1489
|
);
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1490
|
+
});
|
|
1491
|
+
}
|
|
1492
|
+
/**
|
|
1493
|
+
* Internal single-record delete implementation without retry logic.
|
|
1494
|
+
*/
|
|
1495
|
+
async doDelete({ tableName, keys }) {
|
|
1496
|
+
await this.client.execute(prepareDeleteStatement({ tableName, keys }));
|
|
1497
|
+
}
|
|
1498
|
+
/**
|
|
1499
|
+
* Deletes a single record from the specified table with automatic retry on lock errors.
|
|
1500
|
+
*
|
|
1501
|
+
* @param args - The delete arguments
|
|
1502
|
+
* @param args.tableName - The name of the table to delete from
|
|
1503
|
+
* @param args.keys - The key(s) identifying the record to delete
|
|
1504
|
+
* @throws {MastraError} When the delete fails after retries
|
|
1505
|
+
*/
|
|
1506
|
+
async delete(args) {
|
|
1507
|
+
return this.executeWriteOperationWithRetry(() => this.doDelete(args), `delete from table ${args.tableName}`).catch(
|
|
1508
|
+
(error) => {
|
|
1509
|
+
throw new MastraError(
|
|
1510
|
+
{
|
|
1511
|
+
id: createStorageErrorId("LIBSQL", "DELETE", "FAILED"),
|
|
1512
|
+
domain: ErrorDomain.STORAGE,
|
|
1513
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1514
|
+
details: {
|
|
1515
|
+
tableName: args.tableName
|
|
1516
|
+
}
|
|
1517
|
+
},
|
|
1518
|
+
error
|
|
1519
|
+
);
|
|
1520
|
+
}
|
|
1521
|
+
);
|
|
1522
|
+
}
|
|
1523
|
+
/**
|
|
1524
|
+
* Selects a single record from the specified table by key(s).
|
|
1525
|
+
* Returns the most recently created record if multiple matches exist.
|
|
1526
|
+
* Automatically parses JSON string values back to objects/arrays.
|
|
1527
|
+
*
|
|
1528
|
+
* @typeParam R - The expected return type of the record
|
|
1529
|
+
* @param args - The select arguments
|
|
1530
|
+
* @param args.tableName - The name of the table to select from
|
|
1531
|
+
* @param args.keys - The key(s) identifying the record to select
|
|
1532
|
+
* @returns The matching record or `null` if not found
|
|
1533
|
+
*/
|
|
1534
|
+
async select({ tableName, keys }) {
|
|
1535
|
+
const parsedTableName = parseSqlIdentifier(tableName, "table name");
|
|
1536
|
+
const parsedKeys = Object.keys(keys).map((key) => parseSqlIdentifier(key, "column name"));
|
|
1537
|
+
const conditions = parsedKeys.map((key) => `${key} = ?`).join(" AND ");
|
|
1538
|
+
const values = Object.values(keys);
|
|
1539
|
+
const result = await this.client.execute({
|
|
1540
|
+
sql: `SELECT * FROM ${parsedTableName} WHERE ${conditions} ORDER BY createdAt DESC LIMIT 1`,
|
|
1541
|
+
args: values
|
|
1542
|
+
});
|
|
1543
|
+
if (!result.rows || result.rows.length === 0) {
|
|
1544
|
+
return null;
|
|
1545
|
+
}
|
|
1546
|
+
const row = result.rows[0];
|
|
1547
|
+
const parsed = Object.fromEntries(
|
|
1548
|
+
Object.entries(row || {}).map(([k, v]) => {
|
|
1549
|
+
try {
|
|
1550
|
+
return [k, typeof v === "string" ? v.startsWith("{") || v.startsWith("[") ? JSON.parse(v) : v : v];
|
|
1551
|
+
} catch {
|
|
1552
|
+
return [k, v];
|
|
1553
|
+
}
|
|
1554
|
+
})
|
|
1555
|
+
);
|
|
1556
|
+
return parsed;
|
|
1557
|
+
}
|
|
1558
|
+
/**
|
|
1559
|
+
* Selects multiple records from the specified table with optional filtering, ordering, and pagination.
|
|
1560
|
+
*
|
|
1561
|
+
* @typeParam R - The expected return type of each record
|
|
1562
|
+
* @param args - The select arguments
|
|
1563
|
+
* @param args.tableName - The name of the table to select from
|
|
1564
|
+
* @param args.whereClause - Optional WHERE clause with SQL string and arguments
|
|
1565
|
+
* @param args.orderBy - Optional ORDER BY clause (e.g., "createdAt DESC")
|
|
1566
|
+
* @param args.offset - Optional offset for pagination
|
|
1567
|
+
* @param args.limit - Optional limit for pagination
|
|
1568
|
+
* @param args.args - Optional additional query arguments
|
|
1569
|
+
* @returns Array of matching records
|
|
1570
|
+
*/
|
|
1571
|
+
async selectMany({
|
|
1572
|
+
tableName,
|
|
1573
|
+
whereClause,
|
|
1574
|
+
orderBy,
|
|
1575
|
+
offset,
|
|
1576
|
+
limit,
|
|
1577
|
+
args
|
|
1578
|
+
}) {
|
|
1579
|
+
const parsedTableName = parseSqlIdentifier(tableName, "table name");
|
|
1580
|
+
let statement = `SELECT * FROM ${parsedTableName}`;
|
|
1581
|
+
if (whereClause?.sql) {
|
|
1582
|
+
statement += ` ${whereClause.sql}`;
|
|
1583
|
+
}
|
|
1584
|
+
if (orderBy) {
|
|
1585
|
+
statement += ` ORDER BY ${orderBy}`;
|
|
1586
|
+
}
|
|
1587
|
+
if (limit) {
|
|
1588
|
+
statement += ` LIMIT ${limit}`;
|
|
1589
|
+
}
|
|
1590
|
+
if (offset) {
|
|
1591
|
+
statement += ` OFFSET ${offset}`;
|
|
1592
|
+
}
|
|
1593
|
+
const result = await this.client.execute({
|
|
1594
|
+
sql: statement,
|
|
1595
|
+
args: [...whereClause?.args ?? [], ...args ?? []]
|
|
1596
|
+
});
|
|
1597
|
+
return result.rows;
|
|
1598
|
+
}
|
|
1599
|
+
/**
|
|
1600
|
+
* Returns the total count of records matching the optional WHERE clause.
|
|
1601
|
+
*
|
|
1602
|
+
* @param args - The count arguments
|
|
1603
|
+
* @param args.tableName - The name of the table to count from
|
|
1604
|
+
* @param args.whereClause - Optional WHERE clause with SQL string and arguments
|
|
1605
|
+
* @returns The total count of matching records
|
|
1606
|
+
*/
|
|
1607
|
+
async selectTotalCount({
|
|
1608
|
+
tableName,
|
|
1609
|
+
whereClause
|
|
1610
|
+
}) {
|
|
1611
|
+
const parsedTableName = parseSqlIdentifier(tableName, "table name");
|
|
1612
|
+
const statement = `SELECT COUNT(*) as count FROM ${parsedTableName} ${whereClause ? `${whereClause.sql}` : ""}`;
|
|
1613
|
+
const result = await this.client.execute({
|
|
1614
|
+
sql: statement,
|
|
1615
|
+
args: whereClause?.args ?? []
|
|
1616
|
+
});
|
|
1617
|
+
if (!result.rows || result.rows.length === 0) {
|
|
1618
|
+
return 0;
|
|
1617
1619
|
}
|
|
1620
|
+
return result.rows[0]?.count ?? 0;
|
|
1618
1621
|
}
|
|
1619
|
-
|
|
1620
|
-
|
|
1622
|
+
/**
|
|
1623
|
+
* Maps a storage column type to its SQLite equivalent.
|
|
1624
|
+
*/
|
|
1625
|
+
getSqlType(type) {
|
|
1626
|
+
switch (type) {
|
|
1627
|
+
case "bigint":
|
|
1628
|
+
return "INTEGER";
|
|
1629
|
+
// SQLite uses INTEGER for all integer sizes
|
|
1630
|
+
case "timestamp":
|
|
1631
|
+
return "TEXT";
|
|
1632
|
+
// Store timestamps as ISO strings in SQLite
|
|
1633
|
+
case "float":
|
|
1634
|
+
return "REAL";
|
|
1635
|
+
// SQLite's floating point type
|
|
1636
|
+
case "boolean":
|
|
1637
|
+
return "INTEGER";
|
|
1638
|
+
// SQLite uses 0/1 for booleans
|
|
1639
|
+
case "jsonb":
|
|
1640
|
+
return "TEXT";
|
|
1641
|
+
// Store JSON as TEXT in SQLite
|
|
1642
|
+
default:
|
|
1643
|
+
return getSqlType(type);
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
/**
|
|
1647
|
+
* Creates a table if it doesn't exist based on the provided schema.
|
|
1648
|
+
*
|
|
1649
|
+
* @param args - The create table arguments
|
|
1650
|
+
* @param args.tableName - The name of the table to create
|
|
1651
|
+
* @param args.schema - The schema definition for the table columns
|
|
1652
|
+
*/
|
|
1653
|
+
async createTable({
|
|
1654
|
+
tableName,
|
|
1655
|
+
schema
|
|
1656
|
+
}) {
|
|
1621
1657
|
try {
|
|
1622
|
-
const
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
if (!message.threadId) {
|
|
1629
|
-
throw new Error(
|
|
1630
|
-
`Expected to find a threadId for message, but couldn't find one. An unexpected error has occurred.`
|
|
1631
|
-
);
|
|
1632
|
-
}
|
|
1633
|
-
if (!message.resourceId) {
|
|
1634
|
-
throw new Error(
|
|
1635
|
-
`Expected to find a resourceId for message, but couldn't find one. An unexpected error has occurred.`
|
|
1636
|
-
);
|
|
1637
|
-
}
|
|
1638
|
-
return {
|
|
1639
|
-
sql: `INSERT INTO "${TABLE_MESSAGES}" (id, thread_id, content, role, type, "createdAt", "resourceId")
|
|
1640
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
1641
|
-
ON CONFLICT(id) DO UPDATE SET
|
|
1642
|
-
thread_id=excluded.thread_id,
|
|
1643
|
-
content=excluded.content,
|
|
1644
|
-
role=excluded.role,
|
|
1645
|
-
type=excluded.type,
|
|
1646
|
-
"resourceId"=excluded."resourceId"
|
|
1647
|
-
`,
|
|
1648
|
-
args: [
|
|
1649
|
-
message.id,
|
|
1650
|
-
message.threadId,
|
|
1651
|
-
typeof message.content === "object" ? JSON.stringify(message.content) : message.content,
|
|
1652
|
-
message.role,
|
|
1653
|
-
message.type || "v2",
|
|
1654
|
-
time instanceof Date ? time.toISOString() : time,
|
|
1655
|
-
message.resourceId
|
|
1656
|
-
]
|
|
1657
|
-
};
|
|
1658
|
-
});
|
|
1659
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1660
|
-
batchStatements.push({
|
|
1661
|
-
sql: `UPDATE "${TABLE_THREADS}" SET "updatedAt" = ? WHERE id = ?`,
|
|
1662
|
-
args: [now, threadId]
|
|
1658
|
+
const parsedTableName = parseSqlIdentifier(tableName, "table name");
|
|
1659
|
+
const columnDefinitions = Object.entries(schema).map(([colName, colDef]) => {
|
|
1660
|
+
const type = this.getSqlType(colDef.type);
|
|
1661
|
+
const nullable = colDef.nullable === false ? "NOT NULL" : "";
|
|
1662
|
+
const primaryKey = colDef.primaryKey ? "PRIMARY KEY" : "";
|
|
1663
|
+
return `"${colName}" ${type} ${nullable} ${primaryKey}`.trim();
|
|
1663
1664
|
});
|
|
1664
|
-
const
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1665
|
+
const tableConstraints = [];
|
|
1666
|
+
if (tableName === TABLE_WORKFLOW_SNAPSHOT) {
|
|
1667
|
+
tableConstraints.push("UNIQUE (workflow_name, run_id)");
|
|
1668
|
+
}
|
|
1669
|
+
const allDefinitions = [...columnDefinitions, ...tableConstraints].join(",\n ");
|
|
1670
|
+
const sql = `CREATE TABLE IF NOT EXISTS ${parsedTableName} (
|
|
1671
|
+
${allDefinitions}
|
|
1672
|
+
)`;
|
|
1673
|
+
await this.client.execute(sql);
|
|
1674
|
+
this.logger.debug(`LibSQLDB: Created table ${tableName}`);
|
|
1675
|
+
if (tableName === TABLE_SPANS) {
|
|
1676
|
+
await this.migrateSpansTable();
|
|
1675
1677
|
}
|
|
1676
|
-
const list = new MessageList().add(messages, "memory");
|
|
1677
|
-
return { messages: list.get.all.db() };
|
|
1678
1678
|
} catch (error) {
|
|
1679
1679
|
throw new MastraError(
|
|
1680
1680
|
{
|
|
1681
|
-
id: createStorageErrorId("LIBSQL", "
|
|
1681
|
+
id: createStorageErrorId("LIBSQL", "CREATE_TABLE", "FAILED"),
|
|
1682
1682
|
domain: ErrorDomain.STORAGE,
|
|
1683
|
-
category: ErrorCategory.THIRD_PARTY
|
|
1683
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1684
|
+
details: { tableName }
|
|
1684
1685
|
},
|
|
1685
1686
|
error
|
|
1686
1687
|
);
|
|
1687
1688
|
}
|
|
1688
1689
|
}
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
const batchStatements = [];
|
|
1704
|
-
const threadIdsToUpdate = /* @__PURE__ */ new Set();
|
|
1705
|
-
const columnMapping = {
|
|
1706
|
-
threadId: "thread_id"
|
|
1707
|
-
};
|
|
1708
|
-
for (const existingMessage of existingMessages) {
|
|
1709
|
-
const updatePayload = messages.find((m) => m.id === existingMessage.id);
|
|
1710
|
-
if (!updatePayload) continue;
|
|
1711
|
-
const { id, ...fieldsToUpdate } = updatePayload;
|
|
1712
|
-
if (Object.keys(fieldsToUpdate).length === 0) continue;
|
|
1713
|
-
threadIdsToUpdate.add(existingMessage.threadId);
|
|
1714
|
-
if (updatePayload.threadId && updatePayload.threadId !== existingMessage.threadId) {
|
|
1715
|
-
threadIdsToUpdate.add(updatePayload.threadId);
|
|
1716
|
-
}
|
|
1717
|
-
const setClauses = [];
|
|
1718
|
-
const args = [];
|
|
1719
|
-
const updatableFields = { ...fieldsToUpdate };
|
|
1720
|
-
if (updatableFields.content) {
|
|
1721
|
-
const newContent = {
|
|
1722
|
-
...existingMessage.content,
|
|
1723
|
-
...updatableFields.content,
|
|
1724
|
-
// Deep merge metadata if it exists on both
|
|
1725
|
-
...existingMessage.content?.metadata && updatableFields.content.metadata ? {
|
|
1726
|
-
metadata: {
|
|
1727
|
-
...existingMessage.content.metadata,
|
|
1728
|
-
...updatableFields.content.metadata
|
|
1729
|
-
}
|
|
1730
|
-
} : {}
|
|
1731
|
-
};
|
|
1732
|
-
setClauses.push(`${parseSqlIdentifier("content", "column name")} = ?`);
|
|
1733
|
-
args.push(JSON.stringify(newContent));
|
|
1734
|
-
delete updatableFields.content;
|
|
1735
|
-
}
|
|
1736
|
-
for (const key in updatableFields) {
|
|
1737
|
-
if (Object.prototype.hasOwnProperty.call(updatableFields, key)) {
|
|
1738
|
-
const dbKey = columnMapping[key] || key;
|
|
1739
|
-
setClauses.push(`${parseSqlIdentifier(dbKey, "column name")} = ?`);
|
|
1740
|
-
let value = updatableFields[key];
|
|
1741
|
-
if (typeof value === "object" && value !== null) {
|
|
1742
|
-
value = JSON.stringify(value);
|
|
1743
|
-
}
|
|
1744
|
-
args.push(value);
|
|
1690
|
+
/**
|
|
1691
|
+
* Migrates the spans table schema from OLD_SPAN_SCHEMA to current SPAN_SCHEMA.
|
|
1692
|
+
* This adds new columns that don't exist in old schema.
|
|
1693
|
+
*/
|
|
1694
|
+
async migrateSpansTable() {
|
|
1695
|
+
const schema = TABLE_SCHEMAS[TABLE_SPANS];
|
|
1696
|
+
try {
|
|
1697
|
+
for (const [columnName, columnDef] of Object.entries(schema)) {
|
|
1698
|
+
const columnExists = await this.hasColumn(TABLE_SPANS, columnName);
|
|
1699
|
+
if (!columnExists) {
|
|
1700
|
+
const sqlType = this.getSqlType(columnDef.type);
|
|
1701
|
+
const alterSql = `ALTER TABLE "${TABLE_SPANS}" ADD COLUMN "${columnName}" ${sqlType}`;
|
|
1702
|
+
await this.client.execute(alterSql);
|
|
1703
|
+
this.logger.debug(`LibSQLDB: Added column '${columnName}' to ${TABLE_SPANS}`);
|
|
1745
1704
|
}
|
|
1746
1705
|
}
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
batchStatements.push({ sql, args });
|
|
1751
|
-
}
|
|
1752
|
-
if (batchStatements.length === 0) {
|
|
1753
|
-
return existingMessages;
|
|
1754
|
-
}
|
|
1755
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1756
|
-
for (const threadId of threadIdsToUpdate) {
|
|
1757
|
-
if (threadId) {
|
|
1758
|
-
batchStatements.push({
|
|
1759
|
-
sql: `UPDATE ${TABLE_THREADS} SET updatedAt = ? WHERE id = ?`,
|
|
1760
|
-
args: [now, threadId]
|
|
1761
|
-
});
|
|
1762
|
-
}
|
|
1706
|
+
this.logger.info(`LibSQLDB: Migration completed for ${TABLE_SPANS}`);
|
|
1707
|
+
} catch (error) {
|
|
1708
|
+
this.logger.warn(`LibSQLDB: Failed to migrate spans table ${TABLE_SPANS}:`, error);
|
|
1763
1709
|
}
|
|
1764
|
-
await this.client.batch(batchStatements, "write");
|
|
1765
|
-
const updatedResult = await this.client.execute({ sql: selectSql, args: messageIds });
|
|
1766
|
-
return updatedResult.rows.map((row) => this.parseRow(row));
|
|
1767
1710
|
}
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1711
|
+
/**
|
|
1712
|
+
* Gets a default value for a column type (used when adding NOT NULL columns).
|
|
1713
|
+
*/
|
|
1714
|
+
getDefaultValue(type) {
|
|
1715
|
+
switch (type) {
|
|
1716
|
+
case "text":
|
|
1717
|
+
case "uuid":
|
|
1718
|
+
return "DEFAULT ''";
|
|
1719
|
+
case "integer":
|
|
1720
|
+
case "bigint":
|
|
1721
|
+
case "float":
|
|
1722
|
+
return "DEFAULT 0";
|
|
1723
|
+
case "boolean":
|
|
1724
|
+
return "DEFAULT 0";
|
|
1725
|
+
case "jsonb":
|
|
1726
|
+
return "DEFAULT '{}'";
|
|
1727
|
+
case "timestamp":
|
|
1728
|
+
return "DEFAULT CURRENT_TIMESTAMP";
|
|
1729
|
+
default:
|
|
1730
|
+
return "DEFAULT ''";
|
|
1771
1731
|
}
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1732
|
+
}
|
|
1733
|
+
/**
|
|
1734
|
+
* Alters an existing table to add missing columns.
|
|
1735
|
+
* Used for schema migrations when new columns are added.
|
|
1736
|
+
*
|
|
1737
|
+
* @param args - The alter table arguments
|
|
1738
|
+
* @param args.tableName - The name of the table to alter
|
|
1739
|
+
* @param args.schema - The full schema definition for the table
|
|
1740
|
+
* @param args.ifNotExists - Array of column names to add if they don't exist
|
|
1741
|
+
*/
|
|
1742
|
+
async alterTable({
|
|
1743
|
+
tableName,
|
|
1744
|
+
schema,
|
|
1745
|
+
ifNotExists
|
|
1746
|
+
}) {
|
|
1747
|
+
const parsedTableName = parseSqlIdentifier(tableName, "table name");
|
|
1748
|
+
try {
|
|
1749
|
+
const tableInfo = await this.client.execute({
|
|
1750
|
+
sql: `PRAGMA table_info("${parsedTableName}")`
|
|
1751
|
+
});
|
|
1752
|
+
const existingColumns = new Set((tableInfo.rows || []).map((row) => row.name?.toLowerCase()));
|
|
1753
|
+
for (const columnName of ifNotExists) {
|
|
1754
|
+
if (!existingColumns.has(columnName.toLowerCase()) && schema[columnName]) {
|
|
1755
|
+
const columnDef = schema[columnName];
|
|
1756
|
+
const sqlType = this.getSqlType(columnDef.type);
|
|
1757
|
+
const defaultValue = this.getDefaultValue(columnDef.type);
|
|
1758
|
+
const alterSql = `ALTER TABLE ${parsedTableName} ADD COLUMN "${columnName}" ${sqlType} ${defaultValue}`;
|
|
1759
|
+
await this.client.execute(alterSql);
|
|
1760
|
+
this.logger.debug(`LibSQLDB: Added column ${columnName} to table ${tableName}`);
|
|
1800
1761
|
}
|
|
1801
|
-
await tx.commit();
|
|
1802
|
-
} catch (error) {
|
|
1803
|
-
await tx.rollback();
|
|
1804
|
-
throw error;
|
|
1805
1762
|
}
|
|
1806
1763
|
} catch (error) {
|
|
1807
1764
|
throw new MastraError(
|
|
1808
1765
|
{
|
|
1809
|
-
id: createStorageErrorId("LIBSQL", "
|
|
1766
|
+
id: createStorageErrorId("LIBSQL", "ALTER_TABLE", "FAILED"),
|
|
1810
1767
|
domain: ErrorDomain.STORAGE,
|
|
1811
1768
|
category: ErrorCategory.THIRD_PARTY,
|
|
1812
|
-
details: {
|
|
1769
|
+
details: { tableName }
|
|
1813
1770
|
},
|
|
1814
1771
|
error
|
|
1815
1772
|
);
|
|
1816
1773
|
}
|
|
1817
1774
|
}
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1775
|
+
/**
|
|
1776
|
+
* Deletes all records from the specified table.
|
|
1777
|
+
* Errors are logged but not thrown.
|
|
1778
|
+
*
|
|
1779
|
+
* @param args - The delete arguments
|
|
1780
|
+
* @param args.tableName - The name of the table to clear
|
|
1781
|
+
*/
|
|
1782
|
+
async deleteData({ tableName }) {
|
|
1783
|
+
const parsedTableName = parseSqlIdentifier(tableName, "table name");
|
|
1784
|
+
try {
|
|
1785
|
+
await this.client.execute(`DELETE FROM ${parsedTableName}`);
|
|
1786
|
+
} catch (e) {
|
|
1787
|
+
const mastraError = new MastraError(
|
|
1788
|
+
{
|
|
1789
|
+
id: createStorageErrorId("LIBSQL", "CLEAR_TABLE", "FAILED"),
|
|
1790
|
+
domain: ErrorDomain.STORAGE,
|
|
1791
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1792
|
+
details: {
|
|
1793
|
+
tableName
|
|
1794
|
+
}
|
|
1795
|
+
},
|
|
1796
|
+
e
|
|
1797
|
+
);
|
|
1798
|
+
this.logger?.trackException?.(mastraError);
|
|
1799
|
+
this.logger?.error?.(mastraError.toString());
|
|
1825
1800
|
}
|
|
1826
|
-
return {
|
|
1827
|
-
...result,
|
|
1828
|
-
// Ensure workingMemory is always returned as a string, even if auto-parsed as JSON
|
|
1829
|
-
workingMemory: result.workingMemory && typeof result.workingMemory === "object" ? JSON.stringify(result.workingMemory) : result.workingMemory,
|
|
1830
|
-
metadata: typeof result.metadata === "string" ? JSON.parse(result.metadata) : result.metadata,
|
|
1831
|
-
createdAt: new Date(result.createdAt),
|
|
1832
|
-
updatedAt: new Date(result.updatedAt)
|
|
1833
|
-
};
|
|
1834
1801
|
}
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1802
|
+
};
|
|
1803
|
+
|
|
1804
|
+
// src/storage/domains/agents/index.ts
|
|
1805
|
+
var AgentsLibSQL = class extends AgentsStorage {
|
|
1806
|
+
#db;
|
|
1807
|
+
constructor(config) {
|
|
1808
|
+
super();
|
|
1809
|
+
const client = resolveClient(config);
|
|
1810
|
+
this.#db = new LibSQLDB({ client, maxRetries: config.maxRetries, initialBackoffMs: config.initialBackoffMs });
|
|
1844
1811
|
}
|
|
1845
|
-
async
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1812
|
+
async init() {
|
|
1813
|
+
await this.#db.createTable({ tableName: TABLE_AGENTS, schema: AGENTS_SCHEMA });
|
|
1814
|
+
}
|
|
1815
|
+
async dangerouslyClearAll() {
|
|
1816
|
+
await this.#db.deleteData({ tableName: TABLE_AGENTS });
|
|
1817
|
+
}
|
|
1818
|
+
parseJson(value, fieldName) {
|
|
1819
|
+
if (!value) return void 0;
|
|
1820
|
+
if (typeof value !== "string") return value;
|
|
1821
|
+
try {
|
|
1822
|
+
return JSON.parse(value);
|
|
1823
|
+
} catch (error) {
|
|
1824
|
+
const details = {
|
|
1825
|
+
value: value.length > 100 ? value.substring(0, 100) + "..." : value
|
|
1858
1826
|
};
|
|
1859
|
-
|
|
1827
|
+
if (fieldName) {
|
|
1828
|
+
details.field = fieldName;
|
|
1829
|
+
}
|
|
1830
|
+
throw new MastraError(
|
|
1831
|
+
{
|
|
1832
|
+
id: createStorageErrorId("LIBSQL", "PARSE_JSON", "INVALID_JSON"),
|
|
1833
|
+
domain: ErrorDomain.STORAGE,
|
|
1834
|
+
category: ErrorCategory.SYSTEM,
|
|
1835
|
+
text: `Failed to parse JSON${fieldName ? ` for field "${fieldName}"` : ""}: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
1836
|
+
details
|
|
1837
|
+
},
|
|
1838
|
+
error
|
|
1839
|
+
);
|
|
1860
1840
|
}
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1841
|
+
}
|
|
1842
|
+
parseRow(row) {
|
|
1843
|
+
return {
|
|
1844
|
+
id: row.id,
|
|
1845
|
+
name: row.name,
|
|
1846
|
+
description: row.description,
|
|
1847
|
+
instructions: row.instructions,
|
|
1848
|
+
model: this.parseJson(row.model, "model"),
|
|
1849
|
+
tools: this.parseJson(row.tools, "tools"),
|
|
1850
|
+
defaultOptions: this.parseJson(row.defaultOptions, "defaultOptions"),
|
|
1851
|
+
workflows: this.parseJson(row.workflows, "workflows"),
|
|
1852
|
+
agents: this.parseJson(row.agents, "agents"),
|
|
1853
|
+
inputProcessors: this.parseJson(row.inputProcessors, "inputProcessors"),
|
|
1854
|
+
outputProcessors: this.parseJson(row.outputProcessors, "outputProcessors"),
|
|
1855
|
+
memory: this.parseJson(row.memory, "memory"),
|
|
1856
|
+
scorers: this.parseJson(row.scorers, "scorers"),
|
|
1857
|
+
metadata: this.parseJson(row.metadata, "metadata"),
|
|
1858
|
+
createdAt: new Date(row.createdAt),
|
|
1859
|
+
updatedAt: new Date(row.updatedAt)
|
|
1869
1860
|
};
|
|
1870
|
-
const updates = [];
|
|
1871
|
-
const values = [];
|
|
1872
|
-
if (workingMemory !== void 0) {
|
|
1873
|
-
updates.push("workingMemory = ?");
|
|
1874
|
-
values.push(workingMemory);
|
|
1875
|
-
}
|
|
1876
|
-
if (metadata) {
|
|
1877
|
-
updates.push("metadata = ?");
|
|
1878
|
-
values.push(JSON.stringify(updatedResource.metadata));
|
|
1879
|
-
}
|
|
1880
|
-
updates.push("updatedAt = ?");
|
|
1881
|
-
values.push(updatedResource.updatedAt.toISOString());
|
|
1882
|
-
values.push(resourceId);
|
|
1883
|
-
await this.client.execute({
|
|
1884
|
-
sql: `UPDATE ${TABLE_RESOURCES} SET ${updates.join(", ")} WHERE id = ?`,
|
|
1885
|
-
args: values
|
|
1886
|
-
});
|
|
1887
|
-
return updatedResource;
|
|
1888
1861
|
}
|
|
1889
|
-
async
|
|
1862
|
+
async getAgentById({ id }) {
|
|
1890
1863
|
try {
|
|
1891
|
-
const result = await this.
|
|
1892
|
-
tableName:
|
|
1893
|
-
keys: { id
|
|
1864
|
+
const result = await this.#db.select({
|
|
1865
|
+
tableName: TABLE_AGENTS,
|
|
1866
|
+
keys: { id }
|
|
1894
1867
|
});
|
|
1895
|
-
|
|
1896
|
-
return null;
|
|
1897
|
-
}
|
|
1898
|
-
return {
|
|
1899
|
-
...result,
|
|
1900
|
-
metadata: typeof result.metadata === "string" ? JSON.parse(result.metadata) : result.metadata,
|
|
1901
|
-
createdAt: new Date(result.createdAt),
|
|
1902
|
-
updatedAt: new Date(result.updatedAt)
|
|
1903
|
-
};
|
|
1868
|
+
return result ? this.parseRow(result) : null;
|
|
1904
1869
|
} catch (error) {
|
|
1905
1870
|
throw new MastraError(
|
|
1906
1871
|
{
|
|
1907
|
-
id: createStorageErrorId("LIBSQL", "
|
|
1872
|
+
id: createStorageErrorId("LIBSQL", "GET_AGENT_BY_ID", "FAILED"),
|
|
1908
1873
|
domain: ErrorDomain.STORAGE,
|
|
1909
1874
|
category: ErrorCategory.THIRD_PARTY,
|
|
1910
|
-
details: {
|
|
1875
|
+
details: { agentId: id }
|
|
1911
1876
|
},
|
|
1912
1877
|
error
|
|
1913
1878
|
);
|
|
1914
1879
|
}
|
|
1915
1880
|
}
|
|
1916
|
-
async
|
|
1917
|
-
|
|
1918
|
-
|
|
1881
|
+
async createAgent({ agent }) {
|
|
1882
|
+
try {
|
|
1883
|
+
const now = /* @__PURE__ */ new Date();
|
|
1884
|
+
await this.#db.insert({
|
|
1885
|
+
tableName: TABLE_AGENTS,
|
|
1886
|
+
record: {
|
|
1887
|
+
id: agent.id,
|
|
1888
|
+
name: agent.name,
|
|
1889
|
+
description: agent.description ?? null,
|
|
1890
|
+
instructions: agent.instructions,
|
|
1891
|
+
model: agent.model,
|
|
1892
|
+
tools: agent.tools ?? null,
|
|
1893
|
+
defaultOptions: agent.defaultOptions ?? null,
|
|
1894
|
+
workflows: agent.workflows ?? null,
|
|
1895
|
+
agents: agent.agents ?? null,
|
|
1896
|
+
inputProcessors: agent.inputProcessors ?? null,
|
|
1897
|
+
outputProcessors: agent.outputProcessors ?? null,
|
|
1898
|
+
memory: agent.memory ?? null,
|
|
1899
|
+
scorers: agent.scorers ?? null,
|
|
1900
|
+
metadata: agent.metadata ?? null,
|
|
1901
|
+
createdAt: now,
|
|
1902
|
+
updatedAt: now
|
|
1903
|
+
}
|
|
1904
|
+
});
|
|
1905
|
+
return {
|
|
1906
|
+
...agent,
|
|
1907
|
+
createdAt: now,
|
|
1908
|
+
updatedAt: now
|
|
1909
|
+
};
|
|
1910
|
+
} catch (error) {
|
|
1919
1911
|
throw new MastraError(
|
|
1920
1912
|
{
|
|
1921
|
-
id: createStorageErrorId("LIBSQL", "
|
|
1913
|
+
id: createStorageErrorId("LIBSQL", "CREATE_AGENT", "FAILED"),
|
|
1922
1914
|
domain: ErrorDomain.STORAGE,
|
|
1923
|
-
category: ErrorCategory.
|
|
1924
|
-
details: {
|
|
1915
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1916
|
+
details: { agentId: agent.id }
|
|
1925
1917
|
},
|
|
1926
|
-
|
|
1918
|
+
error
|
|
1927
1919
|
);
|
|
1928
1920
|
}
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
const { field, direction } = this.parseOrderBy(orderBy);
|
|
1921
|
+
}
|
|
1922
|
+
async updateAgent({ id, ...updates }) {
|
|
1932
1923
|
try {
|
|
1933
|
-
const
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
// Convert string to Date
|
|
1943
|
-
metadata: typeof row.metadata === "string" ? JSON.parse(row.metadata) : row.metadata
|
|
1944
|
-
});
|
|
1945
|
-
const countResult = await this.client.execute({
|
|
1946
|
-
sql: `SELECT COUNT(*) as count ${baseQuery}`,
|
|
1947
|
-
args: queryParams
|
|
1948
|
-
});
|
|
1949
|
-
const total = Number(countResult.rows?.[0]?.count ?? 0);
|
|
1950
|
-
if (total === 0) {
|
|
1951
|
-
return {
|
|
1952
|
-
threads: [],
|
|
1953
|
-
total: 0,
|
|
1954
|
-
page,
|
|
1955
|
-
perPage: perPageForResponse,
|
|
1956
|
-
hasMore: false
|
|
1957
|
-
};
|
|
1924
|
+
const existingAgent = await this.getAgentById({ id });
|
|
1925
|
+
if (!existingAgent) {
|
|
1926
|
+
throw new MastraError({
|
|
1927
|
+
id: createStorageErrorId("LIBSQL", "UPDATE_AGENT", "NOT_FOUND"),
|
|
1928
|
+
domain: ErrorDomain.STORAGE,
|
|
1929
|
+
category: ErrorCategory.USER,
|
|
1930
|
+
text: `Agent ${id} not found`,
|
|
1931
|
+
details: { agentId: id }
|
|
1932
|
+
});
|
|
1958
1933
|
}
|
|
1959
|
-
const
|
|
1960
|
-
|
|
1961
|
-
sql: `SELECT * ${baseQuery} ORDER BY "${field}" ${direction} LIMIT ? OFFSET ?`,
|
|
1962
|
-
args: [...queryParams, limitValue, offset]
|
|
1963
|
-
});
|
|
1964
|
-
const threads = (dataResult.rows || []).map(mapRowToStorageThreadType);
|
|
1965
|
-
return {
|
|
1966
|
-
threads,
|
|
1967
|
-
total,
|
|
1968
|
-
page,
|
|
1969
|
-
perPage: perPageForResponse,
|
|
1970
|
-
hasMore: perPageInput === false ? false : offset + perPage < total
|
|
1934
|
+
const data = {
|
|
1935
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1971
1936
|
};
|
|
1937
|
+
if (updates.name !== void 0) data.name = updates.name;
|
|
1938
|
+
if (updates.description !== void 0) data.description = updates.description;
|
|
1939
|
+
if (updates.instructions !== void 0) data.instructions = updates.instructions;
|
|
1940
|
+
if (updates.model !== void 0) data.model = updates.model;
|
|
1941
|
+
if (updates.tools !== void 0) data.tools = updates.tools;
|
|
1942
|
+
if (updates.defaultOptions !== void 0) data.defaultOptions = updates.defaultOptions;
|
|
1943
|
+
if (updates.workflows !== void 0) data.workflows = updates.workflows;
|
|
1944
|
+
if (updates.agents !== void 0) data.agents = updates.agents;
|
|
1945
|
+
if (updates.inputProcessors !== void 0) data.inputProcessors = updates.inputProcessors;
|
|
1946
|
+
if (updates.outputProcessors !== void 0) data.outputProcessors = updates.outputProcessors;
|
|
1947
|
+
if (updates.memory !== void 0) data.memory = updates.memory;
|
|
1948
|
+
if (updates.scorers !== void 0) data.scorers = updates.scorers;
|
|
1949
|
+
if (updates.metadata !== void 0) {
|
|
1950
|
+
data.metadata = { ...existingAgent.metadata, ...updates.metadata };
|
|
1951
|
+
}
|
|
1952
|
+
if (Object.keys(data).length > 1) {
|
|
1953
|
+
await this.#db.update({
|
|
1954
|
+
tableName: TABLE_AGENTS,
|
|
1955
|
+
keys: { id },
|
|
1956
|
+
data
|
|
1957
|
+
});
|
|
1958
|
+
}
|
|
1959
|
+
const updatedAgent = await this.getAgentById({ id });
|
|
1960
|
+
if (!updatedAgent) {
|
|
1961
|
+
throw new MastraError({
|
|
1962
|
+
id: createStorageErrorId("LIBSQL", "UPDATE_AGENT", "NOT_FOUND_AFTER_UPDATE"),
|
|
1963
|
+
domain: ErrorDomain.STORAGE,
|
|
1964
|
+
category: ErrorCategory.SYSTEM,
|
|
1965
|
+
text: `Agent ${id} not found after update`,
|
|
1966
|
+
details: { agentId: id }
|
|
1967
|
+
});
|
|
1968
|
+
}
|
|
1969
|
+
return updatedAgent;
|
|
1972
1970
|
} catch (error) {
|
|
1973
|
-
|
|
1971
|
+
if (error instanceof MastraError) {
|
|
1972
|
+
throw error;
|
|
1973
|
+
}
|
|
1974
|
+
throw new MastraError(
|
|
1974
1975
|
{
|
|
1975
|
-
id: createStorageErrorId("LIBSQL", "
|
|
1976
|
+
id: createStorageErrorId("LIBSQL", "UPDATE_AGENT", "FAILED"),
|
|
1976
1977
|
domain: ErrorDomain.STORAGE,
|
|
1977
1978
|
category: ErrorCategory.THIRD_PARTY,
|
|
1978
|
-
details: {
|
|
1979
|
+
details: { agentId: id }
|
|
1979
1980
|
},
|
|
1980
1981
|
error
|
|
1981
1982
|
);
|
|
1982
|
-
this.logger?.trackException?.(mastraError);
|
|
1983
|
-
this.logger?.error?.(mastraError.toString());
|
|
1984
|
-
return {
|
|
1985
|
-
threads: [],
|
|
1986
|
-
total: 0,
|
|
1987
|
-
page,
|
|
1988
|
-
perPage: perPageForResponse,
|
|
1989
|
-
hasMore: false
|
|
1990
|
-
};
|
|
1991
1983
|
}
|
|
1992
1984
|
}
|
|
1993
|
-
async
|
|
1985
|
+
async deleteAgent({ id }) {
|
|
1994
1986
|
try {
|
|
1995
|
-
await this.
|
|
1996
|
-
tableName:
|
|
1997
|
-
|
|
1998
|
-
...thread,
|
|
1999
|
-
metadata: JSON.stringify(thread.metadata)
|
|
2000
|
-
}
|
|
1987
|
+
await this.#db.delete({
|
|
1988
|
+
tableName: TABLE_AGENTS,
|
|
1989
|
+
keys: { id }
|
|
2001
1990
|
});
|
|
2002
|
-
return thread;
|
|
2003
1991
|
} catch (error) {
|
|
2004
|
-
|
|
1992
|
+
throw new MastraError(
|
|
2005
1993
|
{
|
|
2006
|
-
id: createStorageErrorId("LIBSQL", "
|
|
1994
|
+
id: createStorageErrorId("LIBSQL", "DELETE_AGENT", "FAILED"),
|
|
2007
1995
|
domain: ErrorDomain.STORAGE,
|
|
2008
1996
|
category: ErrorCategory.THIRD_PARTY,
|
|
2009
|
-
details: {
|
|
1997
|
+
details: { agentId: id }
|
|
2010
1998
|
},
|
|
2011
1999
|
error
|
|
2012
2000
|
);
|
|
2013
|
-
this.logger?.trackException?.(mastraError);
|
|
2014
|
-
this.logger?.error?.(mastraError.toString());
|
|
2015
|
-
throw mastraError;
|
|
2016
2001
|
}
|
|
2017
2002
|
}
|
|
2018
|
-
async
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
}) {
|
|
2023
|
-
const thread = await this.getThreadById({ threadId: id });
|
|
2024
|
-
if (!thread) {
|
|
2025
|
-
throw new MastraError({
|
|
2026
|
-
id: createStorageErrorId("LIBSQL", "UPDATE_THREAD", "NOT_FOUND"),
|
|
2027
|
-
domain: ErrorDomain.STORAGE,
|
|
2028
|
-
category: ErrorCategory.USER,
|
|
2029
|
-
text: `Thread ${id} not found`,
|
|
2030
|
-
details: {
|
|
2031
|
-
status: 404,
|
|
2032
|
-
threadId: id
|
|
2033
|
-
}
|
|
2034
|
-
});
|
|
2035
|
-
}
|
|
2036
|
-
const updatedThread = {
|
|
2037
|
-
...thread,
|
|
2038
|
-
title,
|
|
2039
|
-
metadata: {
|
|
2040
|
-
...thread.metadata,
|
|
2041
|
-
...metadata
|
|
2042
|
-
}
|
|
2043
|
-
};
|
|
2044
|
-
try {
|
|
2045
|
-
await this.client.execute({
|
|
2046
|
-
sql: `UPDATE ${TABLE_THREADS} SET title = ?, metadata = ? WHERE id = ?`,
|
|
2047
|
-
args: [title, JSON.stringify(updatedThread.metadata), id]
|
|
2048
|
-
});
|
|
2049
|
-
return updatedThread;
|
|
2050
|
-
} catch (error) {
|
|
2003
|
+
async listAgents(args) {
|
|
2004
|
+
const { page = 0, perPage: perPageInput, orderBy } = args || {};
|
|
2005
|
+
const { field, direction } = this.parseOrderBy(orderBy);
|
|
2006
|
+
if (page < 0) {
|
|
2051
2007
|
throw new MastraError(
|
|
2052
2008
|
{
|
|
2053
|
-
id: createStorageErrorId("LIBSQL", "
|
|
2009
|
+
id: createStorageErrorId("LIBSQL", "LIST_AGENTS", "INVALID_PAGE"),
|
|
2054
2010
|
domain: ErrorDomain.STORAGE,
|
|
2055
|
-
category: ErrorCategory.
|
|
2056
|
-
|
|
2057
|
-
details: { threadId: id }
|
|
2011
|
+
category: ErrorCategory.USER,
|
|
2012
|
+
details: { page }
|
|
2058
2013
|
},
|
|
2059
|
-
|
|
2014
|
+
new Error("page must be >= 0")
|
|
2060
2015
|
);
|
|
2061
2016
|
}
|
|
2062
|
-
|
|
2063
|
-
|
|
2017
|
+
const perPage = normalizePerPage(perPageInput, 100);
|
|
2018
|
+
const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
2064
2019
|
try {
|
|
2065
|
-
await this.
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2020
|
+
const total = await this.#db.selectTotalCount({ tableName: TABLE_AGENTS });
|
|
2021
|
+
if (total === 0) {
|
|
2022
|
+
return {
|
|
2023
|
+
agents: [],
|
|
2024
|
+
total: 0,
|
|
2025
|
+
page,
|
|
2026
|
+
perPage: perPageForResponse,
|
|
2027
|
+
hasMore: false
|
|
2028
|
+
};
|
|
2029
|
+
}
|
|
2030
|
+
const limitValue = perPageInput === false ? total : perPage;
|
|
2031
|
+
const rows = await this.#db.selectMany({
|
|
2032
|
+
tableName: TABLE_AGENTS,
|
|
2033
|
+
orderBy: `"${field}" ${direction}`,
|
|
2034
|
+
limit: limitValue,
|
|
2035
|
+
offset
|
|
2072
2036
|
});
|
|
2037
|
+
const agents = rows.map((row) => this.parseRow(row));
|
|
2038
|
+
return {
|
|
2039
|
+
agents,
|
|
2040
|
+
total,
|
|
2041
|
+
page,
|
|
2042
|
+
perPage: perPageForResponse,
|
|
2043
|
+
hasMore: perPageInput === false ? false : offset + perPage < total
|
|
2044
|
+
};
|
|
2073
2045
|
} catch (error) {
|
|
2074
2046
|
throw new MastraError(
|
|
2075
2047
|
{
|
|
2076
|
-
id: createStorageErrorId("LIBSQL", "
|
|
2048
|
+
id: createStorageErrorId("LIBSQL", "LIST_AGENTS", "FAILED"),
|
|
2077
2049
|
domain: ErrorDomain.STORAGE,
|
|
2078
|
-
category: ErrorCategory.THIRD_PARTY
|
|
2079
|
-
details: { threadId }
|
|
2050
|
+
category: ErrorCategory.THIRD_PARTY
|
|
2080
2051
|
},
|
|
2081
2052
|
error
|
|
2082
2053
|
);
|
|
2083
2054
|
}
|
|
2084
2055
|
}
|
|
2085
2056
|
};
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
} else {
|
|
2105
|
-
logger.error(`LibSQLStore: Error during ${operationDescription} after ${retries} retries: ${error}`);
|
|
2106
|
-
throw error;
|
|
2107
|
-
}
|
|
2108
|
-
}
|
|
2109
|
-
}
|
|
2110
|
-
};
|
|
2111
|
-
}
|
|
2112
|
-
function prepareStatement({ tableName, record }) {
|
|
2113
|
-
const parsedTableName = parseSqlIdentifier(tableName, "table name");
|
|
2114
|
-
const columns = Object.keys(record).map((col) => parseSqlIdentifier(col, "column name"));
|
|
2115
|
-
const values = Object.values(record).map((v) => {
|
|
2116
|
-
if (typeof v === `undefined` || v === null) {
|
|
2117
|
-
return null;
|
|
2118
|
-
}
|
|
2119
|
-
if (v instanceof Date) {
|
|
2120
|
-
return v.toISOString();
|
|
2121
|
-
}
|
|
2122
|
-
return typeof v === "object" ? JSON.stringify(v) : v;
|
|
2123
|
-
});
|
|
2124
|
-
const placeholders = values.map(() => "?").join(", ");
|
|
2125
|
-
return {
|
|
2126
|
-
sql: `INSERT OR REPLACE INTO ${parsedTableName} (${columns.join(", ")}) VALUES (${placeholders})`,
|
|
2127
|
-
args: values
|
|
2128
|
-
};
|
|
2129
|
-
}
|
|
2130
|
-
function prepareUpdateStatement({
|
|
2131
|
-
tableName,
|
|
2132
|
-
updates,
|
|
2133
|
-
keys
|
|
2134
|
-
}) {
|
|
2135
|
-
const parsedTableName = parseSqlIdentifier(tableName, "table name");
|
|
2136
|
-
const schema = TABLE_SCHEMAS[tableName];
|
|
2137
|
-
const updateColumns = Object.keys(updates).map((col) => parseSqlIdentifier(col, "column name"));
|
|
2138
|
-
const updateValues = Object.values(updates).map(transformToSqlValue);
|
|
2139
|
-
const setClause = updateColumns.map((col) => `${col} = ?`).join(", ");
|
|
2140
|
-
const whereClause = prepareWhereClause(keys, schema);
|
|
2141
|
-
return {
|
|
2142
|
-
sql: `UPDATE ${parsedTableName} SET ${setClause}${whereClause.sql}`,
|
|
2143
|
-
args: [...updateValues, ...whereClause.args]
|
|
2144
|
-
};
|
|
2145
|
-
}
|
|
2146
|
-
function transformToSqlValue(value) {
|
|
2147
|
-
if (typeof value === "undefined" || value === null) {
|
|
2148
|
-
return null;
|
|
2057
|
+
var MemoryLibSQL = class extends MemoryStorage {
|
|
2058
|
+
#client;
|
|
2059
|
+
#db;
|
|
2060
|
+
constructor(config) {
|
|
2061
|
+
super();
|
|
2062
|
+
const client = resolveClient(config);
|
|
2063
|
+
this.#client = client;
|
|
2064
|
+
this.#db = new LibSQLDB({ client, maxRetries: config.maxRetries, initialBackoffMs: config.initialBackoffMs });
|
|
2065
|
+
}
|
|
2066
|
+
async init() {
|
|
2067
|
+
await this.#db.createTable({ tableName: TABLE_THREADS, schema: TABLE_SCHEMAS[TABLE_THREADS] });
|
|
2068
|
+
await this.#db.createTable({ tableName: TABLE_MESSAGES, schema: TABLE_SCHEMAS[TABLE_MESSAGES] });
|
|
2069
|
+
await this.#db.createTable({ tableName: TABLE_RESOURCES, schema: TABLE_SCHEMAS[TABLE_RESOURCES] });
|
|
2070
|
+
await this.#db.alterTable({
|
|
2071
|
+
tableName: TABLE_MESSAGES,
|
|
2072
|
+
schema: TABLE_SCHEMAS[TABLE_MESSAGES],
|
|
2073
|
+
ifNotExists: ["resourceId"]
|
|
2074
|
+
});
|
|
2149
2075
|
}
|
|
2150
|
-
|
|
2151
|
-
|
|
2076
|
+
async dangerouslyClearAll() {
|
|
2077
|
+
await this.#db.deleteData({ tableName: TABLE_MESSAGES });
|
|
2078
|
+
await this.#db.deleteData({ tableName: TABLE_THREADS });
|
|
2079
|
+
await this.#db.deleteData({ tableName: TABLE_RESOURCES });
|
|
2152
2080
|
}
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
return {
|
|
2159
|
-
sql: `DELETE FROM ${parsedTableName}${whereClause.sql}`,
|
|
2160
|
-
args: whereClause.args
|
|
2161
|
-
};
|
|
2162
|
-
}
|
|
2163
|
-
function prepareWhereClause(filters, schema) {
|
|
2164
|
-
const conditions = [];
|
|
2165
|
-
const args = [];
|
|
2166
|
-
for (const [columnName, filterValue] of Object.entries(filters)) {
|
|
2167
|
-
const column = schema[columnName];
|
|
2168
|
-
if (!column) {
|
|
2169
|
-
throw new Error(`Unknown column: ${columnName}`);
|
|
2081
|
+
parseRow(row) {
|
|
2082
|
+
let content = row.content;
|
|
2083
|
+
try {
|
|
2084
|
+
content = JSON.parse(row.content);
|
|
2085
|
+
} catch {
|
|
2170
2086
|
}
|
|
2171
|
-
const
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
function buildCondition2(columnName, filterValue) {
|
|
2182
|
-
if (filterValue === null) {
|
|
2183
|
-
return { condition: `${columnName} IS NULL`, args: [] };
|
|
2184
|
-
}
|
|
2185
|
-
if (typeof filterValue === "object" && filterValue !== null && ("startAt" in filterValue || "endAt" in filterValue)) {
|
|
2186
|
-
return buildDateRangeCondition(columnName, filterValue);
|
|
2187
|
-
}
|
|
2188
|
-
return {
|
|
2189
|
-
condition: `${columnName} = ?`,
|
|
2190
|
-
args: [transformToSqlValue(filterValue)]
|
|
2191
|
-
};
|
|
2192
|
-
}
|
|
2193
|
-
function buildDateRangeCondition(columnName, range) {
|
|
2194
|
-
const conditions = [];
|
|
2195
|
-
const args = [];
|
|
2196
|
-
if (range.startAt !== void 0) {
|
|
2197
|
-
conditions.push(`${columnName} >= ?`);
|
|
2198
|
-
args.push(transformToSqlValue(range.startAt));
|
|
2199
|
-
}
|
|
2200
|
-
if (range.endAt !== void 0) {
|
|
2201
|
-
conditions.push(`${columnName} <= ?`);
|
|
2202
|
-
args.push(transformToSqlValue(range.endAt));
|
|
2203
|
-
}
|
|
2204
|
-
if (conditions.length === 0) {
|
|
2205
|
-
throw new Error("Date range must specify at least startAt or endAt");
|
|
2206
|
-
}
|
|
2207
|
-
return {
|
|
2208
|
-
condition: conditions.join(" AND "),
|
|
2209
|
-
args
|
|
2210
|
-
};
|
|
2211
|
-
}
|
|
2212
|
-
function buildDateRangeFilter(dateRange, columnName = "createdAt") {
|
|
2213
|
-
if (!dateRange?.start && !dateRange?.end) {
|
|
2214
|
-
return {};
|
|
2215
|
-
}
|
|
2216
|
-
const filter = {};
|
|
2217
|
-
if (dateRange.start) {
|
|
2218
|
-
filter.startAt = new Date(dateRange.start).toISOString();
|
|
2087
|
+
const result = {
|
|
2088
|
+
id: row.id,
|
|
2089
|
+
content,
|
|
2090
|
+
role: row.role,
|
|
2091
|
+
createdAt: new Date(row.createdAt),
|
|
2092
|
+
threadId: row.thread_id,
|
|
2093
|
+
resourceId: row.resourceId
|
|
2094
|
+
};
|
|
2095
|
+
if (row.type && row.type !== `v2`) result.type = row.type;
|
|
2096
|
+
return result;
|
|
2219
2097
|
}
|
|
2220
|
-
|
|
2221
|
-
|
|
2098
|
+
async _getIncludedMessages({ include }) {
|
|
2099
|
+
if (!include || include.length === 0) return null;
|
|
2100
|
+
const unionQueries = [];
|
|
2101
|
+
const params = [];
|
|
2102
|
+
for (const inc of include) {
|
|
2103
|
+
const { id, withPreviousMessages = 0, withNextMessages = 0 } = inc;
|
|
2104
|
+
unionQueries.push(
|
|
2105
|
+
`
|
|
2106
|
+
SELECT * FROM (
|
|
2107
|
+
WITH target_thread AS (
|
|
2108
|
+
SELECT thread_id FROM "${TABLE_MESSAGES}" WHERE id = ?
|
|
2109
|
+
),
|
|
2110
|
+
numbered_messages AS (
|
|
2111
|
+
SELECT
|
|
2112
|
+
id, content, role, type, "createdAt", thread_id, "resourceId",
|
|
2113
|
+
ROW_NUMBER() OVER (ORDER BY "createdAt" ASC) as row_num
|
|
2114
|
+
FROM "${TABLE_MESSAGES}"
|
|
2115
|
+
WHERE thread_id = (SELECT thread_id FROM target_thread)
|
|
2116
|
+
),
|
|
2117
|
+
target_positions AS (
|
|
2118
|
+
SELECT row_num as target_pos
|
|
2119
|
+
FROM numbered_messages
|
|
2120
|
+
WHERE id = ?
|
|
2121
|
+
)
|
|
2122
|
+
SELECT DISTINCT m.*
|
|
2123
|
+
FROM numbered_messages m
|
|
2124
|
+
CROSS JOIN target_positions t
|
|
2125
|
+
WHERE m.row_num BETWEEN (t.target_pos - ?) AND (t.target_pos + ?)
|
|
2126
|
+
)
|
|
2127
|
+
`
|
|
2128
|
+
// Keep ASC for final sorting after fetching context
|
|
2129
|
+
);
|
|
2130
|
+
params.push(id, id, withPreviousMessages, withNextMessages);
|
|
2131
|
+
}
|
|
2132
|
+
const finalQuery = unionQueries.join(" UNION ALL ") + ' ORDER BY "createdAt" ASC';
|
|
2133
|
+
const includedResult = await this.#client.execute({ sql: finalQuery, args: params });
|
|
2134
|
+
const includedRows = includedResult.rows?.map((row) => this.parseRow(row));
|
|
2135
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2136
|
+
const dedupedRows = includedRows.filter((row) => {
|
|
2137
|
+
if (seen.has(row.id)) return false;
|
|
2138
|
+
seen.add(row.id);
|
|
2139
|
+
return true;
|
|
2140
|
+
});
|
|
2141
|
+
return dedupedRows;
|
|
2222
2142
|
}
|
|
2223
|
-
|
|
2224
|
-
}
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2143
|
+
async listMessagesById({ messageIds }) {
|
|
2144
|
+
if (messageIds.length === 0) return { messages: [] };
|
|
2145
|
+
try {
|
|
2146
|
+
const sql = `
|
|
2147
|
+
SELECT
|
|
2148
|
+
id,
|
|
2149
|
+
content,
|
|
2150
|
+
role,
|
|
2151
|
+
type,
|
|
2152
|
+
"createdAt",
|
|
2153
|
+
thread_id,
|
|
2154
|
+
"resourceId"
|
|
2155
|
+
FROM "${TABLE_MESSAGES}"
|
|
2156
|
+
WHERE id IN (${messageIds.map(() => "?").join(", ")})
|
|
2157
|
+
ORDER BY "createdAt" DESC
|
|
2158
|
+
`;
|
|
2159
|
+
const result = await this.#client.execute({ sql, args: messageIds });
|
|
2160
|
+
if (!result.rows) return { messages: [] };
|
|
2161
|
+
const list = new MessageList().add(result.rows.map(this.parseRow), "memory");
|
|
2162
|
+
return { messages: list.get.all.db() };
|
|
2163
|
+
} catch (error) {
|
|
2164
|
+
throw new MastraError(
|
|
2165
|
+
{
|
|
2166
|
+
id: createStorageErrorId("LIBSQL", "LIST_MESSAGES_BY_ID", "FAILED"),
|
|
2167
|
+
domain: ErrorDomain.STORAGE,
|
|
2168
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2169
|
+
details: { messageIds: JSON.stringify(messageIds) }
|
|
2170
|
+
},
|
|
2171
|
+
error
|
|
2172
|
+
);
|
|
2240
2173
|
}
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2174
|
+
}
|
|
2175
|
+
async listMessages(args) {
|
|
2176
|
+
const { threadId, resourceId, include, filter, perPage: perPageInput, page = 0, orderBy } = args;
|
|
2177
|
+
const threadIds = Array.isArray(threadId) ? threadId : [threadId];
|
|
2178
|
+
if (threadIds.length === 0 || threadIds.some((id) => !id.trim())) {
|
|
2179
|
+
throw new MastraError(
|
|
2180
|
+
{
|
|
2181
|
+
id: createStorageErrorId("LIBSQL", "LIST_MESSAGES", "INVALID_THREAD_ID"),
|
|
2182
|
+
domain: ErrorDomain.STORAGE,
|
|
2183
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2184
|
+
details: { threadId: Array.isArray(threadId) ? threadId.join(",") : threadId }
|
|
2185
|
+
},
|
|
2186
|
+
new Error("threadId must be a non-empty string or array of non-empty strings")
|
|
2187
|
+
);
|
|
2244
2188
|
}
|
|
2245
|
-
if (
|
|
2246
|
-
|
|
2247
|
-
|
|
2189
|
+
if (page < 0) {
|
|
2190
|
+
throw new MastraError(
|
|
2191
|
+
{
|
|
2192
|
+
id: createStorageErrorId("LIBSQL", "LIST_MESSAGES", "INVALID_PAGE"),
|
|
2193
|
+
domain: ErrorDomain.STORAGE,
|
|
2194
|
+
category: ErrorCategory.USER,
|
|
2195
|
+
details: { page }
|
|
2196
|
+
},
|
|
2197
|
+
new Error("page must be >= 0")
|
|
2198
|
+
);
|
|
2248
2199
|
}
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
return result;
|
|
2252
|
-
}
|
|
2253
|
-
|
|
2254
|
-
// src/storage/domains/observability/index.ts
|
|
2255
|
-
var ObservabilityLibSQL = class extends ObservabilityStorage {
|
|
2256
|
-
operations;
|
|
2257
|
-
constructor({ operations }) {
|
|
2258
|
-
super();
|
|
2259
|
-
this.operations = operations;
|
|
2260
|
-
}
|
|
2261
|
-
async createSpan(span) {
|
|
2200
|
+
const perPage = normalizePerPage(perPageInput, 40);
|
|
2201
|
+
const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
2262
2202
|
try {
|
|
2263
|
-
const
|
|
2264
|
-
const
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2203
|
+
const { field, direction } = this.parseOrderBy(orderBy, "ASC");
|
|
2204
|
+
const orderByStatement = `ORDER BY "${field}" ${direction}`;
|
|
2205
|
+
const threadPlaceholders = threadIds.map(() => "?").join(", ");
|
|
2206
|
+
const conditions = [`thread_id IN (${threadPlaceholders})`];
|
|
2207
|
+
const queryParams = [...threadIds];
|
|
2208
|
+
if (resourceId) {
|
|
2209
|
+
conditions.push(`"resourceId" = ?`);
|
|
2210
|
+
queryParams.push(resourceId);
|
|
2211
|
+
}
|
|
2212
|
+
if (filter?.dateRange?.start) {
|
|
2213
|
+
conditions.push(`"createdAt" >= ?`);
|
|
2214
|
+
queryParams.push(
|
|
2215
|
+
filter.dateRange.start instanceof Date ? filter.dateRange.start.toISOString() : filter.dateRange.start
|
|
2216
|
+
);
|
|
2217
|
+
}
|
|
2218
|
+
if (filter?.dateRange?.end) {
|
|
2219
|
+
conditions.push(`"createdAt" <= ?`);
|
|
2220
|
+
queryParams.push(
|
|
2221
|
+
filter.dateRange.end instanceof Date ? filter.dateRange.end.toISOString() : filter.dateRange.end
|
|
2222
|
+
);
|
|
2223
|
+
}
|
|
2224
|
+
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
2225
|
+
const countResult = await this.#client.execute({
|
|
2226
|
+
sql: `SELECT COUNT(*) as count FROM ${TABLE_MESSAGES} ${whereClause}`,
|
|
2227
|
+
args: queryParams
|
|
2228
|
+
});
|
|
2229
|
+
const total = Number(countResult.rows?.[0]?.count ?? 0);
|
|
2230
|
+
const limitValue = perPageInput === false ? total : perPage;
|
|
2231
|
+
const dataResult = await this.#client.execute({
|
|
2232
|
+
sql: `SELECT id, content, role, type, "createdAt", "resourceId", "thread_id" FROM ${TABLE_MESSAGES} ${whereClause} ${orderByStatement} LIMIT ? OFFSET ?`,
|
|
2233
|
+
args: [...queryParams, limitValue, offset]
|
|
2234
|
+
});
|
|
2235
|
+
const messages = (dataResult.rows || []).map((row) => this.parseRow(row));
|
|
2236
|
+
if (total === 0 && messages.length === 0 && (!include || include.length === 0)) {
|
|
2237
|
+
return {
|
|
2238
|
+
messages: [],
|
|
2239
|
+
total: 0,
|
|
2240
|
+
page,
|
|
2241
|
+
perPage: perPageForResponse,
|
|
2242
|
+
hasMore: false
|
|
2243
|
+
};
|
|
2244
|
+
}
|
|
2245
|
+
const messageIds = new Set(messages.map((m) => m.id));
|
|
2246
|
+
if (include && include.length > 0) {
|
|
2247
|
+
const includeMessages = await this._getIncludedMessages({ include });
|
|
2248
|
+
if (includeMessages) {
|
|
2249
|
+
for (const includeMsg of includeMessages) {
|
|
2250
|
+
if (!messageIds.has(includeMsg.id)) {
|
|
2251
|
+
messages.push(includeMsg);
|
|
2252
|
+
messageIds.add(includeMsg.id);
|
|
2253
|
+
}
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2257
|
+
const list = new MessageList().add(messages, "memory");
|
|
2258
|
+
let finalMessages = list.get.all.db();
|
|
2259
|
+
finalMessages = finalMessages.sort((a, b) => {
|
|
2260
|
+
const isDateField = field === "createdAt" || field === "updatedAt";
|
|
2261
|
+
const aValue = isDateField ? new Date(a[field]).getTime() : a[field];
|
|
2262
|
+
const bValue = isDateField ? new Date(b[field]).getTime() : b[field];
|
|
2263
|
+
if (typeof aValue === "number" && typeof bValue === "number") {
|
|
2264
|
+
return direction === "ASC" ? aValue - bValue : bValue - aValue;
|
|
2265
|
+
}
|
|
2266
|
+
return direction === "ASC" ? String(aValue).localeCompare(String(bValue)) : String(bValue).localeCompare(String(aValue));
|
|
2267
|
+
});
|
|
2268
|
+
const threadIdSet = new Set(threadIds);
|
|
2269
|
+
const returnedThreadMessageIds = new Set(
|
|
2270
|
+
finalMessages.filter((m) => m.threadId && threadIdSet.has(m.threadId)).map((m) => m.id)
|
|
2271
|
+
);
|
|
2272
|
+
const allThreadMessagesReturned = returnedThreadMessageIds.size >= total;
|
|
2273
|
+
const hasMore = perPageInput !== false && !allThreadMessagesReturned && offset + perPage < total;
|
|
2274
|
+
return {
|
|
2275
|
+
messages: finalMessages,
|
|
2276
|
+
total,
|
|
2277
|
+
page,
|
|
2278
|
+
perPage: perPageForResponse,
|
|
2279
|
+
hasMore
|
|
2268
2280
|
};
|
|
2269
|
-
return this.operations.insert({ tableName: TABLE_SPANS, record });
|
|
2270
2281
|
} catch (error) {
|
|
2271
|
-
|
|
2282
|
+
const mastraError = new MastraError(
|
|
2272
2283
|
{
|
|
2273
|
-
id: createStorageErrorId("LIBSQL", "
|
|
2284
|
+
id: createStorageErrorId("LIBSQL", "LIST_MESSAGES", "FAILED"),
|
|
2274
2285
|
domain: ErrorDomain.STORAGE,
|
|
2275
|
-
category: ErrorCategory.
|
|
2286
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2276
2287
|
details: {
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
spanType: span.spanType,
|
|
2280
|
-
spanName: span.name
|
|
2288
|
+
threadId: Array.isArray(threadId) ? threadId.join(",") : threadId,
|
|
2289
|
+
resourceId: resourceId ?? ""
|
|
2281
2290
|
}
|
|
2282
2291
|
},
|
|
2283
2292
|
error
|
|
2284
2293
|
);
|
|
2294
|
+
this.logger?.error?.(mastraError.toString());
|
|
2295
|
+
this.logger?.trackException?.(mastraError);
|
|
2296
|
+
return {
|
|
2297
|
+
messages: [],
|
|
2298
|
+
total: 0,
|
|
2299
|
+
page,
|
|
2300
|
+
perPage: perPageForResponse,
|
|
2301
|
+
hasMore: false
|
|
2302
|
+
};
|
|
2285
2303
|
}
|
|
2286
2304
|
}
|
|
2287
|
-
async
|
|
2305
|
+
async saveMessages({ messages }) {
|
|
2306
|
+
if (messages.length === 0) return { messages };
|
|
2288
2307
|
try {
|
|
2289
|
-
const
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2308
|
+
const threadId = messages[0]?.threadId;
|
|
2309
|
+
if (!threadId) {
|
|
2310
|
+
throw new Error("Thread ID is required");
|
|
2311
|
+
}
|
|
2312
|
+
const batchStatements = messages.map((message) => {
|
|
2313
|
+
const time = message.createdAt || /* @__PURE__ */ new Date();
|
|
2314
|
+
if (!message.threadId) {
|
|
2315
|
+
throw new Error(
|
|
2316
|
+
`Expected to find a threadId for message, but couldn't find one. An unexpected error has occurred.`
|
|
2317
|
+
);
|
|
2318
|
+
}
|
|
2319
|
+
if (!message.resourceId) {
|
|
2320
|
+
throw new Error(
|
|
2321
|
+
`Expected to find a resourceId for message, but couldn't find one. An unexpected error has occurred.`
|
|
2322
|
+
);
|
|
2323
|
+
}
|
|
2324
|
+
return {
|
|
2325
|
+
sql: `INSERT INTO "${TABLE_MESSAGES}" (id, thread_id, content, role, type, "createdAt", "resourceId")
|
|
2326
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
2327
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
2328
|
+
thread_id=excluded.thread_id,
|
|
2329
|
+
content=excluded.content,
|
|
2330
|
+
role=excluded.role,
|
|
2331
|
+
type=excluded.type,
|
|
2332
|
+
"resourceId"=excluded."resourceId"
|
|
2333
|
+
`,
|
|
2334
|
+
args: [
|
|
2335
|
+
message.id,
|
|
2336
|
+
message.threadId,
|
|
2337
|
+
typeof message.content === "object" ? JSON.stringify(message.content) : message.content,
|
|
2338
|
+
message.role,
|
|
2339
|
+
message.type || "v2",
|
|
2340
|
+
time instanceof Date ? time.toISOString() : time,
|
|
2341
|
+
message.resourceId
|
|
2342
|
+
]
|
|
2343
|
+
};
|
|
2293
2344
|
});
|
|
2294
|
-
|
|
2295
|
-
|
|
2345
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2346
|
+
batchStatements.push({
|
|
2347
|
+
sql: `UPDATE "${TABLE_THREADS}" SET "updatedAt" = ? WHERE id = ?`,
|
|
2348
|
+
args: [now, threadId]
|
|
2349
|
+
});
|
|
2350
|
+
const BATCH_SIZE = 50;
|
|
2351
|
+
const messageStatements = batchStatements.slice(0, -1);
|
|
2352
|
+
const threadUpdateStatement = batchStatements[batchStatements.length - 1];
|
|
2353
|
+
for (let i = 0; i < messageStatements.length; i += BATCH_SIZE) {
|
|
2354
|
+
const batch = messageStatements.slice(i, i + BATCH_SIZE);
|
|
2355
|
+
if (batch.length > 0) {
|
|
2356
|
+
await this.#client.batch(batch, "write");
|
|
2357
|
+
}
|
|
2296
2358
|
}
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2359
|
+
if (threadUpdateStatement) {
|
|
2360
|
+
await this.#client.execute(threadUpdateStatement);
|
|
2361
|
+
}
|
|
2362
|
+
const list = new MessageList().add(messages, "memory");
|
|
2363
|
+
return { messages: list.get.all.db() };
|
|
2301
2364
|
} catch (error) {
|
|
2302
2365
|
throw new MastraError(
|
|
2303
2366
|
{
|
|
2304
|
-
id: createStorageErrorId("LIBSQL", "
|
|
2367
|
+
id: createStorageErrorId("LIBSQL", "SAVE_MESSAGES", "FAILED"),
|
|
2305
2368
|
domain: ErrorDomain.STORAGE,
|
|
2306
|
-
category: ErrorCategory.
|
|
2307
|
-
details: {
|
|
2308
|
-
traceId
|
|
2309
|
-
}
|
|
2369
|
+
category: ErrorCategory.THIRD_PARTY
|
|
2310
2370
|
},
|
|
2311
2371
|
error
|
|
2312
2372
|
);
|
|
2313
2373
|
}
|
|
2314
2374
|
}
|
|
2315
|
-
async
|
|
2316
|
-
|
|
2317
|
-
traceId,
|
|
2318
|
-
updates
|
|
2375
|
+
async updateMessages({
|
|
2376
|
+
messages
|
|
2319
2377
|
}) {
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
tableName: TABLE_SPANS,
|
|
2323
|
-
keys: { spanId, traceId },
|
|
2324
|
-
data: { ...updates, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }
|
|
2325
|
-
});
|
|
2326
|
-
} catch (error) {
|
|
2327
|
-
throw new MastraError(
|
|
2328
|
-
{
|
|
2329
|
-
id: createStorageErrorId("LIBSQL", "UPDATE_SPAN", "FAILED"),
|
|
2330
|
-
domain: ErrorDomain.STORAGE,
|
|
2331
|
-
category: ErrorCategory.USER,
|
|
2332
|
-
details: {
|
|
2333
|
-
spanId,
|
|
2334
|
-
traceId
|
|
2335
|
-
}
|
|
2336
|
-
},
|
|
2337
|
-
error
|
|
2338
|
-
);
|
|
2378
|
+
if (messages.length === 0) {
|
|
2379
|
+
return [];
|
|
2339
2380
|
}
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
const
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2381
|
+
const messageIds = messages.map((m) => m.id);
|
|
2382
|
+
const placeholders = messageIds.map(() => "?").join(",");
|
|
2383
|
+
const selectSql = `SELECT * FROM ${TABLE_MESSAGES} WHERE id IN (${placeholders})`;
|
|
2384
|
+
const existingResult = await this.#client.execute({ sql: selectSql, args: messageIds });
|
|
2385
|
+
const existingMessages = existingResult.rows.map((row) => this.parseRow(row));
|
|
2386
|
+
if (existingMessages.length === 0) {
|
|
2387
|
+
return [];
|
|
2388
|
+
}
|
|
2389
|
+
const batchStatements = [];
|
|
2390
|
+
const threadIdsToUpdate = /* @__PURE__ */ new Set();
|
|
2391
|
+
const columnMapping = {
|
|
2392
|
+
threadId: "thread_id"
|
|
2352
2393
|
};
|
|
2353
|
-
const
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
const
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
name = `agent run: '${entityId}'`;
|
|
2362
|
-
} else {
|
|
2363
|
-
const error = new MastraError({
|
|
2364
|
-
id: createStorageErrorId("LIBSQL", "GET_TRACES_PAGINATED", "INVALID_ENTITY_TYPE"),
|
|
2365
|
-
domain: ErrorDomain.STORAGE,
|
|
2366
|
-
category: ErrorCategory.USER,
|
|
2367
|
-
details: {
|
|
2368
|
-
entityType
|
|
2369
|
-
},
|
|
2370
|
-
text: `Cannot filter by entity type: ${entityType}`
|
|
2371
|
-
});
|
|
2372
|
-
this.logger?.trackException(error);
|
|
2373
|
-
throw error;
|
|
2394
|
+
for (const existingMessage of existingMessages) {
|
|
2395
|
+
const updatePayload = messages.find((m) => m.id === existingMessage.id);
|
|
2396
|
+
if (!updatePayload) continue;
|
|
2397
|
+
const { id, ...fieldsToUpdate } = updatePayload;
|
|
2398
|
+
if (Object.keys(fieldsToUpdate).length === 0) continue;
|
|
2399
|
+
threadIdsToUpdate.add(existingMessage.threadId);
|
|
2400
|
+
if (updatePayload.threadId && updatePayload.threadId !== existingMessage.threadId) {
|
|
2401
|
+
threadIdsToUpdate.add(updatePayload.threadId);
|
|
2374
2402
|
}
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2403
|
+
const setClauses = [];
|
|
2404
|
+
const args = [];
|
|
2405
|
+
const updatableFields = { ...fieldsToUpdate };
|
|
2406
|
+
if (updatableFields.content) {
|
|
2407
|
+
const newContent = {
|
|
2408
|
+
...existingMessage.content,
|
|
2409
|
+
...updatableFields.content,
|
|
2410
|
+
// Deep merge metadata if it exists on both
|
|
2411
|
+
...existingMessage.content?.metadata && updatableFields.content.metadata ? {
|
|
2412
|
+
metadata: {
|
|
2413
|
+
...existingMessage.content.metadata,
|
|
2414
|
+
...updatableFields.content.metadata
|
|
2415
|
+
}
|
|
2416
|
+
} : {}
|
|
2417
|
+
};
|
|
2418
|
+
setClauses.push(`${parseSqlIdentifier("content", "column name")} = ?`);
|
|
2419
|
+
args.push(JSON.stringify(newContent));
|
|
2420
|
+
delete updatableFields.content;
|
|
2421
|
+
}
|
|
2422
|
+
for (const key in updatableFields) {
|
|
2423
|
+
if (Object.prototype.hasOwnProperty.call(updatableFields, key)) {
|
|
2424
|
+
const dbKey = columnMapping[key] || key;
|
|
2425
|
+
setClauses.push(`${parseSqlIdentifier(dbKey, "column name")} = ?`);
|
|
2426
|
+
let value = updatableFields[key];
|
|
2427
|
+
if (typeof value === "object" && value !== null) {
|
|
2428
|
+
value = JSON.stringify(value);
|
|
2429
|
+
}
|
|
2430
|
+
args.push(value);
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
if (setClauses.length === 0) continue;
|
|
2434
|
+
args.push(id);
|
|
2435
|
+
const sql = `UPDATE ${TABLE_MESSAGES} SET ${setClauses.join(", ")} WHERE id = ?`;
|
|
2436
|
+
batchStatements.push({ sql, args });
|
|
2437
|
+
}
|
|
2438
|
+
if (batchStatements.length === 0) {
|
|
2439
|
+
return existingMessages;
|
|
2440
|
+
}
|
|
2441
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2442
|
+
for (const threadId of threadIdsToUpdate) {
|
|
2443
|
+
if (threadId) {
|
|
2444
|
+
batchStatements.push({
|
|
2445
|
+
sql: `UPDATE ${TABLE_THREADS} SET updatedAt = ? WHERE id = ?`,
|
|
2446
|
+
args: [now, threadId]
|
|
2447
|
+
});
|
|
2380
2448
|
}
|
|
2381
2449
|
}
|
|
2382
|
-
|
|
2383
|
-
|
|
2450
|
+
await this.#client.batch(batchStatements, "write");
|
|
2451
|
+
const updatedResult = await this.#client.execute({ sql: selectSql, args: messageIds });
|
|
2452
|
+
return updatedResult.rows.map((row) => this.parseRow(row));
|
|
2453
|
+
}
|
|
2454
|
+
async deleteMessages(messageIds) {
|
|
2455
|
+
if (!messageIds || messageIds.length === 0) {
|
|
2456
|
+
return;
|
|
2457
|
+
}
|
|
2384
2458
|
try {
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2459
|
+
const BATCH_SIZE = 100;
|
|
2460
|
+
const threadIds = /* @__PURE__ */ new Set();
|
|
2461
|
+
const tx = await this.#client.transaction("write");
|
|
2462
|
+
try {
|
|
2463
|
+
for (let i = 0; i < messageIds.length; i += BATCH_SIZE) {
|
|
2464
|
+
const batch = messageIds.slice(i, i + BATCH_SIZE);
|
|
2465
|
+
const placeholders = batch.map(() => "?").join(",");
|
|
2466
|
+
const result = await tx.execute({
|
|
2467
|
+
sql: `SELECT DISTINCT thread_id FROM "${TABLE_MESSAGES}" WHERE id IN (${placeholders})`,
|
|
2468
|
+
args: batch
|
|
2469
|
+
});
|
|
2470
|
+
result.rows?.forEach((row) => {
|
|
2471
|
+
if (row.thread_id) threadIds.add(row.thread_id);
|
|
2472
|
+
});
|
|
2473
|
+
await tx.execute({
|
|
2474
|
+
sql: `DELETE FROM "${TABLE_MESSAGES}" WHERE id IN (${placeholders})`,
|
|
2475
|
+
args: batch
|
|
2476
|
+
});
|
|
2477
|
+
}
|
|
2478
|
+
if (threadIds.size > 0) {
|
|
2479
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2480
|
+
for (const threadId of threadIds) {
|
|
2481
|
+
await tx.execute({
|
|
2482
|
+
sql: `UPDATE "${TABLE_THREADS}" SET "updatedAt" = ? WHERE id = ?`,
|
|
2483
|
+
args: [now, threadId]
|
|
2484
|
+
});
|
|
2485
|
+
}
|
|
2486
|
+
}
|
|
2487
|
+
await tx.commit();
|
|
2488
|
+
} catch (error) {
|
|
2489
|
+
await tx.rollback();
|
|
2490
|
+
throw error;
|
|
2491
|
+
}
|
|
2389
2492
|
} catch (error) {
|
|
2390
2493
|
throw new MastraError(
|
|
2391
2494
|
{
|
|
2392
|
-
id: createStorageErrorId("LIBSQL", "
|
|
2495
|
+
id: createStorageErrorId("LIBSQL", "DELETE_MESSAGES", "FAILED"),
|
|
2393
2496
|
domain: ErrorDomain.STORAGE,
|
|
2394
|
-
category: ErrorCategory.
|
|
2497
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2498
|
+
details: { messageIds: messageIds.join(", ") }
|
|
2395
2499
|
},
|
|
2396
2500
|
error
|
|
2397
2501
|
);
|
|
2398
2502
|
}
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2503
|
+
}
|
|
2504
|
+
async getResourceById({ resourceId }) {
|
|
2505
|
+
const result = await this.#db.select({
|
|
2506
|
+
tableName: TABLE_RESOURCES,
|
|
2507
|
+
keys: { id: resourceId }
|
|
2508
|
+
});
|
|
2509
|
+
if (!result) {
|
|
2510
|
+
return null;
|
|
2511
|
+
}
|
|
2512
|
+
return {
|
|
2513
|
+
...result,
|
|
2514
|
+
// Ensure workingMemory is always returned as a string, even if auto-parsed as JSON
|
|
2515
|
+
workingMemory: result.workingMemory && typeof result.workingMemory === "object" ? JSON.stringify(result.workingMemory) : result.workingMemory,
|
|
2516
|
+
metadata: typeof result.metadata === "string" ? JSON.parse(result.metadata) : result.metadata,
|
|
2517
|
+
createdAt: new Date(result.createdAt),
|
|
2518
|
+
updatedAt: new Date(result.updatedAt)
|
|
2519
|
+
};
|
|
2520
|
+
}
|
|
2521
|
+
async saveResource({ resource }) {
|
|
2522
|
+
await this.#db.insert({
|
|
2523
|
+
tableName: TABLE_RESOURCES,
|
|
2524
|
+
record: {
|
|
2525
|
+
...resource,
|
|
2526
|
+
metadata: JSON.stringify(resource.metadata)
|
|
2527
|
+
}
|
|
2528
|
+
});
|
|
2529
|
+
return resource;
|
|
2530
|
+
}
|
|
2531
|
+
async updateResource({
|
|
2532
|
+
resourceId,
|
|
2533
|
+
workingMemory,
|
|
2534
|
+
metadata
|
|
2535
|
+
}) {
|
|
2536
|
+
const existingResource = await this.getResourceById({ resourceId });
|
|
2537
|
+
if (!existingResource) {
|
|
2538
|
+
const newResource = {
|
|
2539
|
+
id: resourceId,
|
|
2540
|
+
workingMemory,
|
|
2541
|
+
metadata: metadata || {},
|
|
2542
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
2543
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
2408
2544
|
};
|
|
2545
|
+
return this.saveResource({ resource: newResource });
|
|
2546
|
+
}
|
|
2547
|
+
const updatedResource = {
|
|
2548
|
+
...existingResource,
|
|
2549
|
+
workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
|
|
2550
|
+
metadata: {
|
|
2551
|
+
...existingResource.metadata,
|
|
2552
|
+
...metadata
|
|
2553
|
+
},
|
|
2554
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
2555
|
+
};
|
|
2556
|
+
const updates = [];
|
|
2557
|
+
const values = [];
|
|
2558
|
+
if (workingMemory !== void 0) {
|
|
2559
|
+
updates.push("workingMemory = ?");
|
|
2560
|
+
values.push(workingMemory);
|
|
2561
|
+
}
|
|
2562
|
+
if (metadata) {
|
|
2563
|
+
updates.push("metadata = ?");
|
|
2564
|
+
values.push(JSON.stringify(updatedResource.metadata));
|
|
2409
2565
|
}
|
|
2566
|
+
updates.push("updatedAt = ?");
|
|
2567
|
+
values.push(updatedResource.updatedAt.toISOString());
|
|
2568
|
+
values.push(resourceId);
|
|
2569
|
+
await this.#client.execute({
|
|
2570
|
+
sql: `UPDATE ${TABLE_RESOURCES} SET ${updates.join(", ")} WHERE id = ?`,
|
|
2571
|
+
args: values
|
|
2572
|
+
});
|
|
2573
|
+
return updatedResource;
|
|
2574
|
+
}
|
|
2575
|
+
async getThreadById({ threadId }) {
|
|
2410
2576
|
try {
|
|
2411
|
-
const
|
|
2412
|
-
tableName:
|
|
2413
|
-
|
|
2414
|
-
sql: actualWhereClause,
|
|
2415
|
-
args: whereClause.args
|
|
2416
|
-
},
|
|
2417
|
-
orderBy,
|
|
2418
|
-
offset: page * perPage,
|
|
2419
|
-
limit: perPage
|
|
2577
|
+
const result = await this.#db.select({
|
|
2578
|
+
tableName: TABLE_THREADS,
|
|
2579
|
+
keys: { id: threadId }
|
|
2420
2580
|
});
|
|
2581
|
+
if (!result) {
|
|
2582
|
+
return null;
|
|
2583
|
+
}
|
|
2421
2584
|
return {
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
hasMore: spans.length === perPage
|
|
2427
|
-
},
|
|
2428
|
-
spans: spans.map((span) => transformFromSqlRow({ tableName: TABLE_SPANS, sqlRow: span }))
|
|
2585
|
+
...result,
|
|
2586
|
+
metadata: typeof result.metadata === "string" ? JSON.parse(result.metadata) : result.metadata,
|
|
2587
|
+
createdAt: new Date(result.createdAt),
|
|
2588
|
+
updatedAt: new Date(result.updatedAt)
|
|
2429
2589
|
};
|
|
2430
2590
|
} catch (error) {
|
|
2431
2591
|
throw new MastraError(
|
|
2432
2592
|
{
|
|
2433
|
-
id: createStorageErrorId("LIBSQL", "
|
|
2593
|
+
id: createStorageErrorId("LIBSQL", "GET_THREAD_BY_ID", "FAILED"),
|
|
2434
2594
|
domain: ErrorDomain.STORAGE,
|
|
2435
|
-
category: ErrorCategory.
|
|
2595
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2596
|
+
details: { threadId }
|
|
2436
2597
|
},
|
|
2437
2598
|
error
|
|
2438
2599
|
);
|
|
2439
2600
|
}
|
|
2440
2601
|
}
|
|
2441
|
-
async
|
|
2602
|
+
async listThreadsByResourceId(args) {
|
|
2603
|
+
const { resourceId, page = 0, perPage: perPageInput, orderBy } = args;
|
|
2604
|
+
if (page < 0) {
|
|
2605
|
+
throw new MastraError(
|
|
2606
|
+
{
|
|
2607
|
+
id: createStorageErrorId("LIBSQL", "LIST_THREADS_BY_RESOURCE_ID", "INVALID_PAGE"),
|
|
2608
|
+
domain: ErrorDomain.STORAGE,
|
|
2609
|
+
category: ErrorCategory.USER,
|
|
2610
|
+
details: { page }
|
|
2611
|
+
},
|
|
2612
|
+
new Error("page must be >= 0")
|
|
2613
|
+
);
|
|
2614
|
+
}
|
|
2615
|
+
const perPage = normalizePerPage(perPageInput, 100);
|
|
2616
|
+
const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
2617
|
+
const { field, direction } = this.parseOrderBy(orderBy);
|
|
2442
2618
|
try {
|
|
2443
|
-
const
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2619
|
+
const baseQuery = `FROM ${TABLE_THREADS} WHERE resourceId = ?`;
|
|
2620
|
+
const queryParams = [resourceId];
|
|
2621
|
+
const mapRowToStorageThreadType = (row) => ({
|
|
2622
|
+
id: row.id,
|
|
2623
|
+
resourceId: row.resourceId,
|
|
2624
|
+
title: row.title,
|
|
2625
|
+
createdAt: new Date(row.createdAt),
|
|
2626
|
+
// Convert string to Date
|
|
2627
|
+
updatedAt: new Date(row.updatedAt),
|
|
2628
|
+
// Convert string to Date
|
|
2629
|
+
metadata: typeof row.metadata === "string" ? JSON.parse(row.metadata) : row.metadata
|
|
2630
|
+
});
|
|
2631
|
+
const countResult = await this.#client.execute({
|
|
2632
|
+
sql: `SELECT COUNT(*) as count ${baseQuery}`,
|
|
2633
|
+
args: queryParams
|
|
2634
|
+
});
|
|
2635
|
+
const total = Number(countResult.rows?.[0]?.count ?? 0);
|
|
2636
|
+
if (total === 0) {
|
|
2637
|
+
return {
|
|
2638
|
+
threads: [],
|
|
2639
|
+
total: 0,
|
|
2640
|
+
page,
|
|
2641
|
+
perPage: perPageForResponse,
|
|
2642
|
+
hasMore: false
|
|
2643
|
+
};
|
|
2644
|
+
}
|
|
2645
|
+
const limitValue = perPageInput === false ? total : perPage;
|
|
2646
|
+
const dataResult = await this.#client.execute({
|
|
2647
|
+
sql: `SELECT * ${baseQuery} ORDER BY "${field}" ${direction} LIMIT ? OFFSET ?`,
|
|
2648
|
+
args: [...queryParams, limitValue, offset]
|
|
2451
2649
|
});
|
|
2650
|
+
const threads = (dataResult.rows || []).map(mapRowToStorageThreadType);
|
|
2651
|
+
return {
|
|
2652
|
+
threads,
|
|
2653
|
+
total,
|
|
2654
|
+
page,
|
|
2655
|
+
perPage: perPageForResponse,
|
|
2656
|
+
hasMore: perPageInput === false ? false : offset + perPage < total
|
|
2657
|
+
};
|
|
2452
2658
|
} catch (error) {
|
|
2453
|
-
|
|
2659
|
+
const mastraError = new MastraError(
|
|
2454
2660
|
{
|
|
2455
|
-
id: createStorageErrorId("LIBSQL", "
|
|
2661
|
+
id: createStorageErrorId("LIBSQL", "LIST_THREADS_BY_RESOURCE_ID", "FAILED"),
|
|
2456
2662
|
domain: ErrorDomain.STORAGE,
|
|
2457
|
-
category: ErrorCategory.
|
|
2663
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2664
|
+
details: { resourceId }
|
|
2458
2665
|
},
|
|
2459
2666
|
error
|
|
2460
2667
|
);
|
|
2668
|
+
this.logger?.trackException?.(mastraError);
|
|
2669
|
+
this.logger?.error?.(mastraError.toString());
|
|
2670
|
+
return {
|
|
2671
|
+
threads: [],
|
|
2672
|
+
total: 0,
|
|
2673
|
+
page,
|
|
2674
|
+
perPage: perPageForResponse,
|
|
2675
|
+
hasMore: false
|
|
2676
|
+
};
|
|
2461
2677
|
}
|
|
2462
2678
|
}
|
|
2463
|
-
async
|
|
2679
|
+
async saveThread({ thread }) {
|
|
2464
2680
|
try {
|
|
2465
|
-
|
|
2466
|
-
tableName:
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
}
|
|
2681
|
+
await this.#db.insert({
|
|
2682
|
+
tableName: TABLE_THREADS,
|
|
2683
|
+
record: {
|
|
2684
|
+
...thread,
|
|
2685
|
+
metadata: JSON.stringify(thread.metadata)
|
|
2686
|
+
}
|
|
2471
2687
|
});
|
|
2688
|
+
return thread;
|
|
2472
2689
|
} catch (error) {
|
|
2473
|
-
|
|
2690
|
+
const mastraError = new MastraError(
|
|
2474
2691
|
{
|
|
2475
|
-
id: createStorageErrorId("LIBSQL", "
|
|
2692
|
+
id: createStorageErrorId("LIBSQL", "SAVE_THREAD", "FAILED"),
|
|
2476
2693
|
domain: ErrorDomain.STORAGE,
|
|
2477
|
-
category: ErrorCategory.
|
|
2694
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2695
|
+
details: { threadId: thread.id }
|
|
2478
2696
|
},
|
|
2479
2697
|
error
|
|
2480
2698
|
);
|
|
2699
|
+
this.logger?.trackException?.(mastraError);
|
|
2700
|
+
this.logger?.error?.(mastraError.toString());
|
|
2701
|
+
throw mastraError;
|
|
2481
2702
|
}
|
|
2482
2703
|
}
|
|
2483
|
-
async
|
|
2704
|
+
async updateThread({
|
|
2705
|
+
id,
|
|
2706
|
+
title,
|
|
2707
|
+
metadata
|
|
2708
|
+
}) {
|
|
2709
|
+
const thread = await this.getThreadById({ threadId: id });
|
|
2710
|
+
if (!thread) {
|
|
2711
|
+
throw new MastraError({
|
|
2712
|
+
id: createStorageErrorId("LIBSQL", "UPDATE_THREAD", "NOT_FOUND"),
|
|
2713
|
+
domain: ErrorDomain.STORAGE,
|
|
2714
|
+
category: ErrorCategory.USER,
|
|
2715
|
+
text: `Thread ${id} not found`,
|
|
2716
|
+
details: {
|
|
2717
|
+
status: 404,
|
|
2718
|
+
threadId: id
|
|
2719
|
+
}
|
|
2720
|
+
});
|
|
2721
|
+
}
|
|
2722
|
+
const updatedThread = {
|
|
2723
|
+
...thread,
|
|
2724
|
+
title,
|
|
2725
|
+
metadata: {
|
|
2726
|
+
...thread.metadata,
|
|
2727
|
+
...metadata
|
|
2728
|
+
}
|
|
2729
|
+
};
|
|
2484
2730
|
try {
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
keys
|
|
2731
|
+
await this.#client.execute({
|
|
2732
|
+
sql: `UPDATE ${TABLE_THREADS} SET title = ?, metadata = ? WHERE id = ?`,
|
|
2733
|
+
args: [title, JSON.stringify(updatedThread.metadata), id]
|
|
2489
2734
|
});
|
|
2735
|
+
return updatedThread;
|
|
2490
2736
|
} catch (error) {
|
|
2491
2737
|
throw new MastraError(
|
|
2492
2738
|
{
|
|
2493
|
-
id: createStorageErrorId("LIBSQL", "
|
|
2739
|
+
id: createStorageErrorId("LIBSQL", "UPDATE_THREAD", "FAILED"),
|
|
2494
2740
|
domain: ErrorDomain.STORAGE,
|
|
2495
|
-
category: ErrorCategory.
|
|
2741
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2742
|
+
text: `Failed to update thread ${id}`,
|
|
2743
|
+
details: { threadId: id }
|
|
2496
2744
|
},
|
|
2497
2745
|
error
|
|
2498
2746
|
);
|
|
2499
2747
|
}
|
|
2500
2748
|
}
|
|
2501
|
-
}
|
|
2502
|
-
var StoreOperationsLibSQL = class extends StoreOperations {
|
|
2503
|
-
client;
|
|
2504
|
-
/**
|
|
2505
|
-
* Maximum number of retries for write operations if an SQLITE_BUSY error occurs.
|
|
2506
|
-
* @default 5
|
|
2507
|
-
*/
|
|
2508
|
-
maxRetries;
|
|
2509
|
-
/**
|
|
2510
|
-
* Initial backoff time in milliseconds for retrying write operations on SQLITE_BUSY.
|
|
2511
|
-
* The backoff time will double with each retry (exponential backoff).
|
|
2512
|
-
* @default 100
|
|
2513
|
-
*/
|
|
2514
|
-
initialBackoffMs;
|
|
2515
|
-
constructor({
|
|
2516
|
-
client,
|
|
2517
|
-
maxRetries,
|
|
2518
|
-
initialBackoffMs
|
|
2519
|
-
}) {
|
|
2520
|
-
super();
|
|
2521
|
-
this.client = client;
|
|
2522
|
-
this.maxRetries = maxRetries ?? 5;
|
|
2523
|
-
this.initialBackoffMs = initialBackoffMs ?? 100;
|
|
2524
|
-
}
|
|
2525
|
-
async hasColumn(table, column) {
|
|
2526
|
-
const result = await this.client.execute({
|
|
2527
|
-
sql: `PRAGMA table_info(${table})`
|
|
2528
|
-
});
|
|
2529
|
-
return (await result.rows)?.some((row) => row.name === column);
|
|
2530
|
-
}
|
|
2531
|
-
getCreateTableSQL(tableName, schema) {
|
|
2532
|
-
const parsedTableName = parseSqlIdentifier(tableName, "table name");
|
|
2533
|
-
const columns = Object.entries(schema).map(([name, col]) => {
|
|
2534
|
-
const parsedColumnName = parseSqlIdentifier(name, "column name");
|
|
2535
|
-
const type = this.getSqlType(col.type);
|
|
2536
|
-
const nullable = col.nullable ? "" : "NOT NULL";
|
|
2537
|
-
const primaryKey = col.primaryKey ? "PRIMARY KEY" : "";
|
|
2538
|
-
return `${parsedColumnName} ${type} ${nullable} ${primaryKey}`.trim();
|
|
2539
|
-
});
|
|
2540
|
-
if (tableName === TABLE_WORKFLOW_SNAPSHOT) {
|
|
2541
|
-
const stmnt = `CREATE TABLE IF NOT EXISTS ${parsedTableName} (
|
|
2542
|
-
${columns.join(",\n")},
|
|
2543
|
-
PRIMARY KEY (workflow_name, run_id)
|
|
2544
|
-
)`;
|
|
2545
|
-
return stmnt;
|
|
2546
|
-
}
|
|
2547
|
-
if (tableName === TABLE_SPANS) {
|
|
2548
|
-
const stmnt = `CREATE TABLE IF NOT EXISTS ${parsedTableName} (
|
|
2549
|
-
${columns.join(",\n")},
|
|
2550
|
-
PRIMARY KEY (traceId, spanId)
|
|
2551
|
-
)`;
|
|
2552
|
-
return stmnt;
|
|
2553
|
-
}
|
|
2554
|
-
return `CREATE TABLE IF NOT EXISTS ${parsedTableName} (${columns.join(", ")})`;
|
|
2555
|
-
}
|
|
2556
|
-
async createTable({
|
|
2557
|
-
tableName,
|
|
2558
|
-
schema
|
|
2559
|
-
}) {
|
|
2749
|
+
async deleteThread({ threadId }) {
|
|
2560
2750
|
try {
|
|
2561
|
-
this.
|
|
2562
|
-
|
|
2563
|
-
|
|
2751
|
+
await this.#client.execute({
|
|
2752
|
+
sql: `DELETE FROM ${TABLE_MESSAGES} WHERE thread_id = ?`,
|
|
2753
|
+
args: [threadId]
|
|
2754
|
+
});
|
|
2755
|
+
await this.#client.execute({
|
|
2756
|
+
sql: `DELETE FROM ${TABLE_THREADS} WHERE id = ?`,
|
|
2757
|
+
args: [threadId]
|
|
2758
|
+
});
|
|
2564
2759
|
} catch (error) {
|
|
2565
2760
|
throw new MastraError(
|
|
2566
2761
|
{
|
|
2567
|
-
id: createStorageErrorId("LIBSQL", "
|
|
2762
|
+
id: createStorageErrorId("LIBSQL", "DELETE_THREAD", "FAILED"),
|
|
2568
2763
|
domain: ErrorDomain.STORAGE,
|
|
2569
2764
|
category: ErrorCategory.THIRD_PARTY,
|
|
2570
|
-
details: {
|
|
2571
|
-
tableName
|
|
2572
|
-
}
|
|
2765
|
+
details: { threadId }
|
|
2573
2766
|
},
|
|
2574
2767
|
error
|
|
2575
|
-
);
|
|
2576
|
-
}
|
|
2577
|
-
}
|
|
2578
|
-
getSqlType(type) {
|
|
2579
|
-
switch (type) {
|
|
2580
|
-
case "bigint":
|
|
2581
|
-
return "INTEGER";
|
|
2582
|
-
// SQLite uses INTEGER for all integer sizes
|
|
2583
|
-
case "timestamp":
|
|
2584
|
-
return "TEXT";
|
|
2585
|
-
// Store timestamps as ISO strings in SQLite
|
|
2586
|
-
// jsonb falls through to base class which returns 'JSONB'
|
|
2587
|
-
// SQLite's flexible type system treats JSONB as TEXT affinity
|
|
2588
|
-
default:
|
|
2589
|
-
return super.getSqlType(type);
|
|
2590
|
-
}
|
|
2591
|
-
}
|
|
2592
|
-
async doInsert({
|
|
2593
|
-
tableName,
|
|
2594
|
-
record
|
|
2595
|
-
}) {
|
|
2596
|
-
await this.client.execute(
|
|
2597
|
-
prepareStatement({
|
|
2598
|
-
tableName,
|
|
2599
|
-
record
|
|
2600
|
-
})
|
|
2601
|
-
);
|
|
2602
|
-
}
|
|
2603
|
-
insert(args) {
|
|
2604
|
-
const executeWriteOperationWithRetry = createExecuteWriteOperationWithRetry({
|
|
2605
|
-
logger: this.logger,
|
|
2606
|
-
maxRetries: this.maxRetries,
|
|
2607
|
-
initialBackoffMs: this.initialBackoffMs
|
|
2608
|
-
});
|
|
2609
|
-
return executeWriteOperationWithRetry(() => this.doInsert(args), `insert into table ${args.tableName}`);
|
|
2610
|
-
}
|
|
2611
|
-
async load({ tableName, keys }) {
|
|
2612
|
-
const parsedTableName = parseSqlIdentifier(tableName, "table name");
|
|
2613
|
-
const parsedKeys = Object.keys(keys).map((key) => parseSqlIdentifier(key, "column name"));
|
|
2614
|
-
const conditions = parsedKeys.map((key) => `${key} = ?`).join(" AND ");
|
|
2615
|
-
const values = Object.values(keys);
|
|
2616
|
-
const result = await this.client.execute({
|
|
2617
|
-
sql: `SELECT * FROM ${parsedTableName} WHERE ${conditions} ORDER BY createdAt DESC LIMIT 1`,
|
|
2618
|
-
args: values
|
|
2619
|
-
});
|
|
2620
|
-
if (!result.rows || result.rows.length === 0) {
|
|
2621
|
-
return null;
|
|
2622
|
-
}
|
|
2623
|
-
const row = result.rows[0];
|
|
2624
|
-
const parsed = Object.fromEntries(
|
|
2625
|
-
Object.entries(row || {}).map(([k, v]) => {
|
|
2626
|
-
try {
|
|
2627
|
-
return [k, typeof v === "string" ? v.startsWith("{") || v.startsWith("[") ? JSON.parse(v) : v : v];
|
|
2628
|
-
} catch {
|
|
2629
|
-
return [k, v];
|
|
2630
|
-
}
|
|
2631
|
-
})
|
|
2632
|
-
);
|
|
2633
|
-
return parsed;
|
|
2634
|
-
}
|
|
2635
|
-
async loadMany({
|
|
2636
|
-
tableName,
|
|
2637
|
-
whereClause,
|
|
2638
|
-
orderBy,
|
|
2639
|
-
offset,
|
|
2640
|
-
limit,
|
|
2641
|
-
args
|
|
2642
|
-
}) {
|
|
2643
|
-
const parsedTableName = parseSqlIdentifier(tableName, "table name");
|
|
2644
|
-
let statement = `SELECT * FROM ${parsedTableName}`;
|
|
2645
|
-
if (whereClause?.sql) {
|
|
2646
|
-
statement += `${whereClause.sql}`;
|
|
2647
|
-
}
|
|
2648
|
-
if (orderBy) {
|
|
2649
|
-
statement += ` ORDER BY ${orderBy}`;
|
|
2650
|
-
}
|
|
2651
|
-
if (limit) {
|
|
2652
|
-
statement += ` LIMIT ${limit}`;
|
|
2653
|
-
}
|
|
2654
|
-
if (offset) {
|
|
2655
|
-
statement += ` OFFSET ${offset}`;
|
|
2768
|
+
);
|
|
2656
2769
|
}
|
|
2657
|
-
const result = await this.client.execute({
|
|
2658
|
-
sql: statement,
|
|
2659
|
-
args: [...whereClause?.args ?? [], ...args ?? []]
|
|
2660
|
-
});
|
|
2661
|
-
return result.rows;
|
|
2662
2770
|
}
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
const
|
|
2669
|
-
|
|
2670
|
-
sql: statement,
|
|
2671
|
-
args: whereClause?.args ?? []
|
|
2672
|
-
});
|
|
2673
|
-
if (!result.rows || result.rows.length === 0) {
|
|
2674
|
-
return 0;
|
|
2675
|
-
}
|
|
2676
|
-
return result.rows[0]?.count ?? 0;
|
|
2771
|
+
};
|
|
2772
|
+
var ObservabilityLibSQL = class extends ObservabilityStorage {
|
|
2773
|
+
#db;
|
|
2774
|
+
constructor(config) {
|
|
2775
|
+
super();
|
|
2776
|
+
const client = resolveClient(config);
|
|
2777
|
+
this.#db = new LibSQLDB({ client, maxRetries: config.maxRetries, initialBackoffMs: config.initialBackoffMs });
|
|
2677
2778
|
}
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
logger: this.logger,
|
|
2681
|
-
maxRetries: this.maxRetries,
|
|
2682
|
-
initialBackoffMs: this.initialBackoffMs
|
|
2683
|
-
});
|
|
2684
|
-
return executeWriteOperationWithRetry(() => this.executeUpdate(args), `update table ${args.tableName}`);
|
|
2779
|
+
async init() {
|
|
2780
|
+
await this.#db.createTable({ tableName: TABLE_SPANS, schema: SPAN_SCHEMA });
|
|
2685
2781
|
}
|
|
2686
|
-
async
|
|
2687
|
-
tableName
|
|
2688
|
-
keys,
|
|
2689
|
-
data
|
|
2690
|
-
}) {
|
|
2691
|
-
await this.client.execute(prepareUpdateStatement({ tableName, updates: data, keys }));
|
|
2782
|
+
async dangerouslyClearAll() {
|
|
2783
|
+
await this.#db.deleteData({ tableName: TABLE_SPANS });
|
|
2692
2784
|
}
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
const batchStatements = records.map((r) => prepareStatement({ tableName, record: r }));
|
|
2699
|
-
await this.client.batch(batchStatements, "write");
|
|
2785
|
+
get tracingStrategy() {
|
|
2786
|
+
return {
|
|
2787
|
+
preferred: "batch-with-updates",
|
|
2788
|
+
supported: ["batch-with-updates", "insert-only"]
|
|
2789
|
+
};
|
|
2700
2790
|
}
|
|
2701
|
-
|
|
2702
|
-
const
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2791
|
+
async createSpan(args) {
|
|
2792
|
+
const { span } = args;
|
|
2793
|
+
try {
|
|
2794
|
+
const startedAt = span.startedAt instanceof Date ? span.startedAt.toISOString() : span.startedAt;
|
|
2795
|
+
const endedAt = span.endedAt instanceof Date ? span.endedAt.toISOString() : span.endedAt;
|
|
2796
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2797
|
+
const record = {
|
|
2798
|
+
...span,
|
|
2799
|
+
startedAt,
|
|
2800
|
+
endedAt,
|
|
2801
|
+
createdAt: now,
|
|
2802
|
+
updatedAt: now
|
|
2803
|
+
};
|
|
2804
|
+
return this.#db.insert({ tableName: TABLE_SPANS, record });
|
|
2805
|
+
} catch (error) {
|
|
2711
2806
|
throw new MastraError(
|
|
2712
2807
|
{
|
|
2713
|
-
id: createStorageErrorId("LIBSQL", "
|
|
2808
|
+
id: createStorageErrorId("LIBSQL", "CREATE_SPAN", "FAILED"),
|
|
2714
2809
|
domain: ErrorDomain.STORAGE,
|
|
2715
|
-
category: ErrorCategory.
|
|
2810
|
+
category: ErrorCategory.USER,
|
|
2716
2811
|
details: {
|
|
2717
|
-
|
|
2812
|
+
spanId: span.spanId,
|
|
2813
|
+
traceId: span.traceId,
|
|
2814
|
+
spanType: span.spanType,
|
|
2815
|
+
name: span.name
|
|
2718
2816
|
}
|
|
2719
2817
|
},
|
|
2720
2818
|
error
|
|
2721
2819
|
);
|
|
2722
|
-
}
|
|
2820
|
+
}
|
|
2723
2821
|
}
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2822
|
+
async getSpan(args) {
|
|
2823
|
+
const { traceId, spanId } = args;
|
|
2824
|
+
try {
|
|
2825
|
+
const rows = await this.#db.selectMany({
|
|
2826
|
+
tableName: TABLE_SPANS,
|
|
2827
|
+
whereClause: { sql: " WHERE traceId = ? AND spanId = ?", args: [traceId, spanId] },
|
|
2828
|
+
limit: 1
|
|
2829
|
+
});
|
|
2830
|
+
if (!rows || rows.length === 0) {
|
|
2831
|
+
return null;
|
|
2832
|
+
}
|
|
2833
|
+
return {
|
|
2834
|
+
span: transformFromSqlRow({ tableName: TABLE_SPANS, sqlRow: rows[0] })
|
|
2835
|
+
};
|
|
2836
|
+
} catch (error) {
|
|
2737
2837
|
throw new MastraError(
|
|
2738
2838
|
{
|
|
2739
|
-
id: createStorageErrorId("LIBSQL", "
|
|
2839
|
+
id: createStorageErrorId("LIBSQL", "GET_SPAN", "FAILED"),
|
|
2740
2840
|
domain: ErrorDomain.STORAGE,
|
|
2741
|
-
category: ErrorCategory.
|
|
2742
|
-
details: {
|
|
2743
|
-
tableName: args.tableName
|
|
2744
|
-
}
|
|
2841
|
+
category: ErrorCategory.USER,
|
|
2842
|
+
details: { traceId, spanId }
|
|
2745
2843
|
},
|
|
2746
2844
|
error
|
|
2747
2845
|
);
|
|
2748
|
-
}
|
|
2846
|
+
}
|
|
2749
2847
|
}
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
}
|
|
2764
|
-
)
|
|
2765
|
-
|
|
2848
|
+
async getRootSpan(args) {
|
|
2849
|
+
const { traceId } = args;
|
|
2850
|
+
try {
|
|
2851
|
+
const rows = await this.#db.selectMany({
|
|
2852
|
+
tableName: TABLE_SPANS,
|
|
2853
|
+
whereClause: { sql: " WHERE traceId = ? AND parentSpanId IS NULL", args: [traceId] },
|
|
2854
|
+
limit: 1
|
|
2855
|
+
});
|
|
2856
|
+
if (!rows || rows.length === 0) {
|
|
2857
|
+
return null;
|
|
2858
|
+
}
|
|
2859
|
+
return {
|
|
2860
|
+
span: transformFromSqlRow({ tableName: TABLE_SPANS, sqlRow: rows[0] })
|
|
2861
|
+
};
|
|
2862
|
+
} catch (error) {
|
|
2863
|
+
throw new MastraError(
|
|
2864
|
+
{
|
|
2865
|
+
id: createStorageErrorId("LIBSQL", "GET_ROOT_SPAN", "FAILED"),
|
|
2866
|
+
domain: ErrorDomain.STORAGE,
|
|
2867
|
+
category: ErrorCategory.USER,
|
|
2868
|
+
details: { traceId }
|
|
2869
|
+
},
|
|
2870
|
+
error
|
|
2871
|
+
);
|
|
2872
|
+
}
|
|
2766
2873
|
}
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2874
|
+
async getTrace(args) {
|
|
2875
|
+
const { traceId } = args;
|
|
2876
|
+
try {
|
|
2877
|
+
const spans = await this.#db.selectMany({
|
|
2878
|
+
tableName: TABLE_SPANS,
|
|
2879
|
+
whereClause: { sql: " WHERE traceId = ?", args: [traceId] },
|
|
2880
|
+
orderBy: "startedAt ASC"
|
|
2881
|
+
});
|
|
2882
|
+
if (!spans || spans.length === 0) {
|
|
2883
|
+
return null;
|
|
2884
|
+
}
|
|
2885
|
+
return {
|
|
2886
|
+
traceId,
|
|
2887
|
+
spans: spans.map((span) => transformFromSqlRow({ tableName: TABLE_SPANS, sqlRow: span }))
|
|
2888
|
+
};
|
|
2889
|
+
} catch (error) {
|
|
2780
2890
|
throw new MastraError(
|
|
2781
2891
|
{
|
|
2782
|
-
id: createStorageErrorId("LIBSQL", "
|
|
2892
|
+
id: createStorageErrorId("LIBSQL", "GET_TRACE", "FAILED"),
|
|
2783
2893
|
domain: ErrorDomain.STORAGE,
|
|
2784
|
-
category: ErrorCategory.
|
|
2894
|
+
category: ErrorCategory.USER,
|
|
2785
2895
|
details: {
|
|
2786
|
-
|
|
2896
|
+
traceId
|
|
2787
2897
|
}
|
|
2788
2898
|
},
|
|
2789
2899
|
error
|
|
2790
2900
|
);
|
|
2791
|
-
}
|
|
2901
|
+
}
|
|
2792
2902
|
}
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2903
|
+
async updateSpan(args) {
|
|
2904
|
+
const { traceId, spanId, updates } = args;
|
|
2905
|
+
try {
|
|
2906
|
+
const data = { ...updates };
|
|
2907
|
+
if (data.endedAt instanceof Date) {
|
|
2908
|
+
data.endedAt = data.endedAt.toISOString();
|
|
2909
|
+
}
|
|
2910
|
+
if (data.startedAt instanceof Date) {
|
|
2911
|
+
data.startedAt = data.startedAt.toISOString();
|
|
2912
|
+
}
|
|
2913
|
+
data.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2914
|
+
await this.#db.update({
|
|
2915
|
+
tableName: TABLE_SPANS,
|
|
2916
|
+
keys: { spanId, traceId },
|
|
2917
|
+
data
|
|
2918
|
+
});
|
|
2919
|
+
} catch (error) {
|
|
2920
|
+
throw new MastraError(
|
|
2921
|
+
{
|
|
2922
|
+
id: createStorageErrorId("LIBSQL", "UPDATE_SPAN", "FAILED"),
|
|
2923
|
+
domain: ErrorDomain.STORAGE,
|
|
2924
|
+
category: ErrorCategory.USER,
|
|
2925
|
+
details: {
|
|
2926
|
+
spanId,
|
|
2927
|
+
traceId
|
|
2928
|
+
}
|
|
2929
|
+
},
|
|
2930
|
+
error
|
|
2931
|
+
);
|
|
2932
|
+
}
|
|
2808
2933
|
}
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
* @param ifNotExists Array of column names to add if they don't exist
|
|
2814
|
-
*/
|
|
2815
|
-
async alterTable({
|
|
2816
|
-
tableName,
|
|
2817
|
-
schema,
|
|
2818
|
-
ifNotExists
|
|
2819
|
-
}) {
|
|
2820
|
-
const parsedTableName = parseSqlIdentifier(tableName, "table name");
|
|
2934
|
+
async listTraces(args) {
|
|
2935
|
+
const { filters, pagination, orderBy } = listTracesArgsSchema.parse(args);
|
|
2936
|
+
const { page, perPage } = pagination;
|
|
2937
|
+
const tableName = parseSqlIdentifier(TABLE_SPANS, "table name");
|
|
2821
2938
|
try {
|
|
2822
|
-
const
|
|
2823
|
-
const
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2939
|
+
const conditions = ["parentSpanId IS NULL"];
|
|
2940
|
+
const queryArgs = [];
|
|
2941
|
+
if (filters) {
|
|
2942
|
+
if (filters.startedAt?.start) {
|
|
2943
|
+
conditions.push(`startedAt >= ?`);
|
|
2944
|
+
queryArgs.push(filters.startedAt.start.toISOString());
|
|
2945
|
+
}
|
|
2946
|
+
if (filters.startedAt?.end) {
|
|
2947
|
+
conditions.push(`startedAt <= ?`);
|
|
2948
|
+
queryArgs.push(filters.startedAt.end.toISOString());
|
|
2949
|
+
}
|
|
2950
|
+
if (filters.endedAt?.start) {
|
|
2951
|
+
conditions.push(`endedAt >= ?`);
|
|
2952
|
+
queryArgs.push(filters.endedAt.start.toISOString());
|
|
2953
|
+
}
|
|
2954
|
+
if (filters.endedAt?.end) {
|
|
2955
|
+
conditions.push(`endedAt <= ?`);
|
|
2956
|
+
queryArgs.push(filters.endedAt.end.toISOString());
|
|
2957
|
+
}
|
|
2958
|
+
if (filters.spanType !== void 0) {
|
|
2959
|
+
conditions.push(`spanType = ?`);
|
|
2960
|
+
queryArgs.push(filters.spanType);
|
|
2961
|
+
}
|
|
2962
|
+
if (filters.entityType !== void 0) {
|
|
2963
|
+
conditions.push(`entityType = ?`);
|
|
2964
|
+
queryArgs.push(filters.entityType);
|
|
2965
|
+
}
|
|
2966
|
+
if (filters.entityId !== void 0) {
|
|
2967
|
+
conditions.push(`entityId = ?`);
|
|
2968
|
+
queryArgs.push(filters.entityId);
|
|
2969
|
+
}
|
|
2970
|
+
if (filters.entityName !== void 0) {
|
|
2971
|
+
conditions.push(`entityName = ?`);
|
|
2972
|
+
queryArgs.push(filters.entityName);
|
|
2973
|
+
}
|
|
2974
|
+
if (filters.userId !== void 0) {
|
|
2975
|
+
conditions.push(`userId = ?`);
|
|
2976
|
+
queryArgs.push(filters.userId);
|
|
2977
|
+
}
|
|
2978
|
+
if (filters.organizationId !== void 0) {
|
|
2979
|
+
conditions.push(`organizationId = ?`);
|
|
2980
|
+
queryArgs.push(filters.organizationId);
|
|
2981
|
+
}
|
|
2982
|
+
if (filters.resourceId !== void 0) {
|
|
2983
|
+
conditions.push(`resourceId = ?`);
|
|
2984
|
+
queryArgs.push(filters.resourceId);
|
|
2985
|
+
}
|
|
2986
|
+
if (filters.runId !== void 0) {
|
|
2987
|
+
conditions.push(`runId = ?`);
|
|
2988
|
+
queryArgs.push(filters.runId);
|
|
2989
|
+
}
|
|
2990
|
+
if (filters.sessionId !== void 0) {
|
|
2991
|
+
conditions.push(`sessionId = ?`);
|
|
2992
|
+
queryArgs.push(filters.sessionId);
|
|
2993
|
+
}
|
|
2994
|
+
if (filters.threadId !== void 0) {
|
|
2995
|
+
conditions.push(`threadId = ?`);
|
|
2996
|
+
queryArgs.push(filters.threadId);
|
|
2997
|
+
}
|
|
2998
|
+
if (filters.requestId !== void 0) {
|
|
2999
|
+
conditions.push(`requestId = ?`);
|
|
3000
|
+
queryArgs.push(filters.requestId);
|
|
2834
3001
|
}
|
|
3002
|
+
if (filters.environment !== void 0) {
|
|
3003
|
+
conditions.push(`environment = ?`);
|
|
3004
|
+
queryArgs.push(filters.environment);
|
|
3005
|
+
}
|
|
3006
|
+
if (filters.source !== void 0) {
|
|
3007
|
+
conditions.push(`source = ?`);
|
|
3008
|
+
queryArgs.push(filters.source);
|
|
3009
|
+
}
|
|
3010
|
+
if (filters.serviceName !== void 0) {
|
|
3011
|
+
conditions.push(`serviceName = ?`);
|
|
3012
|
+
queryArgs.push(filters.serviceName);
|
|
3013
|
+
}
|
|
3014
|
+
if (filters.scope != null) {
|
|
3015
|
+
for (const [key, value] of Object.entries(filters.scope)) {
|
|
3016
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(key)) {
|
|
3017
|
+
throw new MastraError({
|
|
3018
|
+
id: createStorageErrorId("LIBSQL", "LIST_TRACES", "INVALID_FILTER_KEY"),
|
|
3019
|
+
domain: ErrorDomain.STORAGE,
|
|
3020
|
+
category: ErrorCategory.USER,
|
|
3021
|
+
details: { key }
|
|
3022
|
+
});
|
|
3023
|
+
}
|
|
3024
|
+
conditions.push(`json_extract(scope, '$.${key}') = ?`);
|
|
3025
|
+
queryArgs.push(typeof value === "string" ? value : JSON.stringify(value));
|
|
3026
|
+
}
|
|
3027
|
+
}
|
|
3028
|
+
if (filters.metadata != null) {
|
|
3029
|
+
for (const [key, value] of Object.entries(filters.metadata)) {
|
|
3030
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(key)) {
|
|
3031
|
+
throw new MastraError({
|
|
3032
|
+
id: createStorageErrorId("LIBSQL", "LIST_TRACES", "INVALID_FILTER_KEY"),
|
|
3033
|
+
domain: ErrorDomain.STORAGE,
|
|
3034
|
+
category: ErrorCategory.USER,
|
|
3035
|
+
details: { key }
|
|
3036
|
+
});
|
|
3037
|
+
}
|
|
3038
|
+
conditions.push(`json_extract(metadata, '$.${key}') = ?`);
|
|
3039
|
+
queryArgs.push(typeof value === "string" ? value : JSON.stringify(value));
|
|
3040
|
+
}
|
|
3041
|
+
}
|
|
3042
|
+
if (filters.tags != null && filters.tags.length > 0) {
|
|
3043
|
+
for (const tag of filters.tags) {
|
|
3044
|
+
conditions.push(`EXISTS (SELECT 1 FROM json_each(${tableName}.tags) WHERE value = ?)`);
|
|
3045
|
+
queryArgs.push(tag);
|
|
3046
|
+
}
|
|
3047
|
+
}
|
|
3048
|
+
if (filters.status !== void 0) {
|
|
3049
|
+
switch (filters.status) {
|
|
3050
|
+
case TraceStatus.ERROR:
|
|
3051
|
+
conditions.push(`error IS NOT NULL`);
|
|
3052
|
+
break;
|
|
3053
|
+
case TraceStatus.RUNNING:
|
|
3054
|
+
conditions.push(`endedAt IS NULL AND error IS NULL`);
|
|
3055
|
+
break;
|
|
3056
|
+
case TraceStatus.SUCCESS:
|
|
3057
|
+
conditions.push(`endedAt IS NOT NULL AND error IS NULL`);
|
|
3058
|
+
break;
|
|
3059
|
+
}
|
|
3060
|
+
}
|
|
3061
|
+
if (filters.hasChildError !== void 0) {
|
|
3062
|
+
if (filters.hasChildError) {
|
|
3063
|
+
conditions.push(`EXISTS (
|
|
3064
|
+
SELECT 1 FROM ${tableName} c
|
|
3065
|
+
WHERE c.traceId = ${tableName}.traceId AND c.error IS NOT NULL
|
|
3066
|
+
)`);
|
|
3067
|
+
} else {
|
|
3068
|
+
conditions.push(`NOT EXISTS (
|
|
3069
|
+
SELECT 1 FROM ${tableName} c
|
|
3070
|
+
WHERE c.traceId = ${tableName}.traceId AND c.error IS NOT NULL
|
|
3071
|
+
)`);
|
|
3072
|
+
}
|
|
3073
|
+
}
|
|
3074
|
+
}
|
|
3075
|
+
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
3076
|
+
const sortField = orderBy.field;
|
|
3077
|
+
const sortDirection = orderBy.direction;
|
|
3078
|
+
let orderByClause;
|
|
3079
|
+
if (sortField === "endedAt") {
|
|
3080
|
+
orderByClause = sortDirection === "DESC" ? `CASE WHEN ${sortField} IS NULL THEN 0 ELSE 1 END, ${sortField} DESC` : `CASE WHEN ${sortField} IS NULL THEN 1 ELSE 0 END, ${sortField} ASC`;
|
|
3081
|
+
} else {
|
|
3082
|
+
orderByClause = `${sortField} ${sortDirection}`;
|
|
3083
|
+
}
|
|
3084
|
+
const count = await this.#db.selectTotalCount({
|
|
3085
|
+
tableName: TABLE_SPANS,
|
|
3086
|
+
whereClause: { sql: whereClause, args: queryArgs }
|
|
3087
|
+
});
|
|
3088
|
+
if (count === 0) {
|
|
3089
|
+
return {
|
|
3090
|
+
pagination: {
|
|
3091
|
+
total: 0,
|
|
3092
|
+
page,
|
|
3093
|
+
perPage,
|
|
3094
|
+
hasMore: false
|
|
3095
|
+
},
|
|
3096
|
+
spans: []
|
|
3097
|
+
};
|
|
2835
3098
|
}
|
|
3099
|
+
const spans = await this.#db.selectMany({
|
|
3100
|
+
tableName: TABLE_SPANS,
|
|
3101
|
+
whereClause: { sql: whereClause, args: queryArgs },
|
|
3102
|
+
orderBy: orderByClause,
|
|
3103
|
+
offset: page * perPage,
|
|
3104
|
+
limit: perPage
|
|
3105
|
+
});
|
|
3106
|
+
return {
|
|
3107
|
+
pagination: {
|
|
3108
|
+
total: count,
|
|
3109
|
+
page,
|
|
3110
|
+
perPage,
|
|
3111
|
+
hasMore: (page + 1) * perPage < count
|
|
3112
|
+
},
|
|
3113
|
+
spans: spans.map((span) => transformFromSqlRow({ tableName: TABLE_SPANS, sqlRow: span }))
|
|
3114
|
+
};
|
|
3115
|
+
} catch (error) {
|
|
3116
|
+
throw new MastraError(
|
|
3117
|
+
{
|
|
3118
|
+
id: createStorageErrorId("LIBSQL", "LIST_TRACES", "FAILED"),
|
|
3119
|
+
domain: ErrorDomain.STORAGE,
|
|
3120
|
+
category: ErrorCategory.USER
|
|
3121
|
+
},
|
|
3122
|
+
error
|
|
3123
|
+
);
|
|
3124
|
+
}
|
|
3125
|
+
}
|
|
3126
|
+
async batchCreateSpans(args) {
|
|
3127
|
+
try {
|
|
3128
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3129
|
+
const records = args.records.map((record) => {
|
|
3130
|
+
const startedAt = record.startedAt instanceof Date ? record.startedAt.toISOString() : record.startedAt;
|
|
3131
|
+
const endedAt = record.endedAt instanceof Date ? record.endedAt.toISOString() : record.endedAt;
|
|
3132
|
+
return {
|
|
3133
|
+
...record,
|
|
3134
|
+
startedAt,
|
|
3135
|
+
endedAt,
|
|
3136
|
+
createdAt: now,
|
|
3137
|
+
updatedAt: now
|
|
3138
|
+
};
|
|
3139
|
+
});
|
|
3140
|
+
return this.#db.batchInsert({
|
|
3141
|
+
tableName: TABLE_SPANS,
|
|
3142
|
+
records
|
|
3143
|
+
});
|
|
2836
3144
|
} catch (error) {
|
|
2837
3145
|
throw new MastraError(
|
|
2838
3146
|
{
|
|
2839
|
-
id: createStorageErrorId("LIBSQL", "
|
|
3147
|
+
id: createStorageErrorId("LIBSQL", "BATCH_CREATE_SPANS", "FAILED"),
|
|
2840
3148
|
domain: ErrorDomain.STORAGE,
|
|
2841
|
-
category: ErrorCategory.
|
|
2842
|
-
details: {
|
|
2843
|
-
tableName
|
|
2844
|
-
}
|
|
3149
|
+
category: ErrorCategory.USER
|
|
2845
3150
|
},
|
|
2846
3151
|
error
|
|
2847
3152
|
);
|
|
2848
3153
|
}
|
|
2849
3154
|
}
|
|
2850
|
-
async
|
|
2851
|
-
const
|
|
3155
|
+
async batchUpdateSpans(args) {
|
|
3156
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2852
3157
|
try {
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
3158
|
+
return this.#db.batchUpdate({
|
|
3159
|
+
tableName: TABLE_SPANS,
|
|
3160
|
+
updates: args.records.map((record) => {
|
|
3161
|
+
const data = { ...record.updates };
|
|
3162
|
+
if (data.endedAt instanceof Date) {
|
|
3163
|
+
data.endedAt = data.endedAt.toISOString();
|
|
3164
|
+
}
|
|
3165
|
+
if (data.startedAt instanceof Date) {
|
|
3166
|
+
data.startedAt = data.startedAt.toISOString();
|
|
3167
|
+
}
|
|
3168
|
+
data.updatedAt = now;
|
|
3169
|
+
return {
|
|
3170
|
+
keys: { spanId: record.spanId, traceId: record.traceId },
|
|
3171
|
+
data
|
|
3172
|
+
};
|
|
3173
|
+
})
|
|
3174
|
+
});
|
|
3175
|
+
} catch (error) {
|
|
3176
|
+
throw new MastraError(
|
|
2856
3177
|
{
|
|
2857
|
-
id: createStorageErrorId("LIBSQL", "
|
|
3178
|
+
id: createStorageErrorId("LIBSQL", "BATCH_UPDATE_SPANS", "FAILED"),
|
|
2858
3179
|
domain: ErrorDomain.STORAGE,
|
|
2859
|
-
category: ErrorCategory.
|
|
2860
|
-
details: {
|
|
2861
|
-
tableName
|
|
2862
|
-
}
|
|
3180
|
+
category: ErrorCategory.USER
|
|
2863
3181
|
},
|
|
2864
|
-
|
|
3182
|
+
error
|
|
2865
3183
|
);
|
|
2866
|
-
this.logger?.trackException?.(mastraError);
|
|
2867
|
-
this.logger?.error?.(mastraError.toString());
|
|
2868
3184
|
}
|
|
2869
3185
|
}
|
|
2870
|
-
async
|
|
2871
|
-
const parsedTableName = parseSqlIdentifier(tableName, "table name");
|
|
3186
|
+
async batchDeleteTraces(args) {
|
|
2872
3187
|
try {
|
|
2873
|
-
|
|
2874
|
-
|
|
3188
|
+
const keys = args.traceIds.map((traceId) => ({ traceId }));
|
|
3189
|
+
return this.#db.batchDelete({
|
|
3190
|
+
tableName: TABLE_SPANS,
|
|
3191
|
+
keys
|
|
3192
|
+
});
|
|
3193
|
+
} catch (error) {
|
|
2875
3194
|
throw new MastraError(
|
|
2876
3195
|
{
|
|
2877
|
-
id: createStorageErrorId("LIBSQL", "
|
|
3196
|
+
id: createStorageErrorId("LIBSQL", "BATCH_DELETE_TRACES", "FAILED"),
|
|
2878
3197
|
domain: ErrorDomain.STORAGE,
|
|
2879
|
-
category: ErrorCategory.
|
|
2880
|
-
details: {
|
|
2881
|
-
tableName
|
|
2882
|
-
}
|
|
3198
|
+
category: ErrorCategory.USER
|
|
2883
3199
|
},
|
|
2884
|
-
|
|
3200
|
+
error
|
|
2885
3201
|
);
|
|
2886
3202
|
}
|
|
2887
3203
|
}
|
|
2888
3204
|
};
|
|
2889
3205
|
var ScoresLibSQL = class extends ScoresStorage {
|
|
2890
|
-
|
|
2891
|
-
client;
|
|
2892
|
-
constructor(
|
|
3206
|
+
#db;
|
|
3207
|
+
#client;
|
|
3208
|
+
constructor(config) {
|
|
2893
3209
|
super();
|
|
2894
|
-
|
|
2895
|
-
this
|
|
3210
|
+
const client = resolveClient(config);
|
|
3211
|
+
this.#client = client;
|
|
3212
|
+
this.#db = new LibSQLDB({ client, maxRetries: config.maxRetries, initialBackoffMs: config.initialBackoffMs });
|
|
3213
|
+
}
|
|
3214
|
+
async init() {
|
|
3215
|
+
await this.#db.createTable({ tableName: TABLE_SCORERS, schema: SCORERS_SCHEMA });
|
|
3216
|
+
await this.#db.alterTable({
|
|
3217
|
+
tableName: TABLE_SCORERS,
|
|
3218
|
+
schema: SCORERS_SCHEMA,
|
|
3219
|
+
ifNotExists: ["spanId", "requestContext"]
|
|
3220
|
+
});
|
|
3221
|
+
}
|
|
3222
|
+
async dangerouslyClearAll() {
|
|
3223
|
+
await this.#db.deleteData({ tableName: TABLE_SCORERS });
|
|
2896
3224
|
}
|
|
2897
3225
|
async listScoresByRunId({
|
|
2898
3226
|
runId,
|
|
@@ -2900,7 +3228,7 @@ var ScoresLibSQL = class extends ScoresStorage {
|
|
|
2900
3228
|
}) {
|
|
2901
3229
|
try {
|
|
2902
3230
|
const { page, perPage: perPageInput } = pagination;
|
|
2903
|
-
const countResult = await this
|
|
3231
|
+
const countResult = await this.#client.execute({
|
|
2904
3232
|
sql: `SELECT COUNT(*) as count FROM ${TABLE_SCORERS} WHERE runId = ?`,
|
|
2905
3233
|
args: [runId]
|
|
2906
3234
|
});
|
|
@@ -2920,7 +3248,7 @@ var ScoresLibSQL = class extends ScoresStorage {
|
|
|
2920
3248
|
const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
2921
3249
|
const limitValue = perPageInput === false ? total : perPage;
|
|
2922
3250
|
const end = perPageInput === false ? total : start + perPage;
|
|
2923
|
-
const result = await this
|
|
3251
|
+
const result = await this.#client.execute({
|
|
2924
3252
|
sql: `SELECT * FROM ${TABLE_SCORERS} WHERE runId = ? ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
|
|
2925
3253
|
args: [runId, limitValue, start]
|
|
2926
3254
|
});
|
|
@@ -2973,7 +3301,7 @@ var ScoresLibSQL = class extends ScoresStorage {
|
|
|
2973
3301
|
queryParams.push(source);
|
|
2974
3302
|
}
|
|
2975
3303
|
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
2976
|
-
const countResult = await this
|
|
3304
|
+
const countResult = await this.#client.execute({
|
|
2977
3305
|
sql: `SELECT COUNT(*) as count FROM ${TABLE_SCORERS} ${whereClause}`,
|
|
2978
3306
|
args: queryParams
|
|
2979
3307
|
});
|
|
@@ -2993,7 +3321,7 @@ var ScoresLibSQL = class extends ScoresStorage {
|
|
|
2993
3321
|
const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
2994
3322
|
const limitValue = perPageInput === false ? total : perPage;
|
|
2995
3323
|
const end = perPageInput === false ? total : start + perPage;
|
|
2996
|
-
const result = await this
|
|
3324
|
+
const result = await this.#client.execute({
|
|
2997
3325
|
sql: `SELECT * FROM ${TABLE_SCORERS} ${whereClause} ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
|
|
2998
3326
|
args: [...queryParams, limitValue, start]
|
|
2999
3327
|
});
|
|
@@ -3028,7 +3356,7 @@ var ScoresLibSQL = class extends ScoresStorage {
|
|
|
3028
3356
|
});
|
|
3029
3357
|
}
|
|
3030
3358
|
async getScoreById({ id }) {
|
|
3031
|
-
const result = await this
|
|
3359
|
+
const result = await this.#client.execute({
|
|
3032
3360
|
sql: `SELECT * FROM ${TABLE_SCORERS} WHERE id = ?`,
|
|
3033
3361
|
args: [id]
|
|
3034
3362
|
});
|
|
@@ -3045,7 +3373,7 @@ var ScoresLibSQL = class extends ScoresStorage {
|
|
|
3045
3373
|
domain: ErrorDomain.STORAGE,
|
|
3046
3374
|
category: ErrorCategory.USER,
|
|
3047
3375
|
details: {
|
|
3048
|
-
scorer: score.scorer?.id ?? "unknown",
|
|
3376
|
+
scorer: typeof score.scorer?.id === "string" ? score.scorer.id : String(score.scorer?.id ?? "unknown"),
|
|
3049
3377
|
entityId: score.entityId ?? "unknown",
|
|
3050
3378
|
entityType: score.entityType ?? "unknown",
|
|
3051
3379
|
traceId: score.traceId ?? "",
|
|
@@ -3058,7 +3386,7 @@ var ScoresLibSQL = class extends ScoresStorage {
|
|
|
3058
3386
|
try {
|
|
3059
3387
|
const id = crypto.randomUUID();
|
|
3060
3388
|
const now = /* @__PURE__ */ new Date();
|
|
3061
|
-
await this.
|
|
3389
|
+
await this.#db.insert({
|
|
3062
3390
|
tableName: TABLE_SCORERS,
|
|
3063
3391
|
record: {
|
|
3064
3392
|
...parsedScore,
|
|
@@ -3086,7 +3414,7 @@ var ScoresLibSQL = class extends ScoresStorage {
|
|
|
3086
3414
|
}) {
|
|
3087
3415
|
try {
|
|
3088
3416
|
const { page, perPage: perPageInput } = pagination;
|
|
3089
|
-
const countResult = await this
|
|
3417
|
+
const countResult = await this.#client.execute({
|
|
3090
3418
|
sql: `SELECT COUNT(*) as count FROM ${TABLE_SCORERS} WHERE entityId = ? AND entityType = ?`,
|
|
3091
3419
|
args: [entityId, entityType]
|
|
3092
3420
|
});
|
|
@@ -3106,7 +3434,7 @@ var ScoresLibSQL = class extends ScoresStorage {
|
|
|
3106
3434
|
const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
3107
3435
|
const limitValue = perPageInput === false ? total : perPage;
|
|
3108
3436
|
const end = perPageInput === false ? total : start + perPage;
|
|
3109
|
-
const result = await this
|
|
3437
|
+
const result = await this.#client.execute({
|
|
3110
3438
|
sql: `SELECT * FROM ${TABLE_SCORERS} WHERE entityId = ? AND entityType = ? ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
|
|
3111
3439
|
args: [entityId, entityType, limitValue, start]
|
|
3112
3440
|
});
|
|
@@ -3140,14 +3468,14 @@ var ScoresLibSQL = class extends ScoresStorage {
|
|
|
3140
3468
|
const { page, perPage: perPageInput } = pagination;
|
|
3141
3469
|
const perPage = normalizePerPage(perPageInput, 100);
|
|
3142
3470
|
const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
3143
|
-
const countSQLResult = await this
|
|
3471
|
+
const countSQLResult = await this.#client.execute({
|
|
3144
3472
|
sql: `SELECT COUNT(*) as count FROM ${TABLE_SCORERS} WHERE traceId = ? AND spanId = ?`,
|
|
3145
3473
|
args: [traceId, spanId]
|
|
3146
3474
|
});
|
|
3147
3475
|
const total = Number(countSQLResult.rows?.[0]?.count ?? 0);
|
|
3148
3476
|
const limitValue = perPageInput === false ? total : perPage;
|
|
3149
3477
|
const end = perPageInput === false ? total : start + perPage;
|
|
3150
|
-
const result = await this
|
|
3478
|
+
const result = await this.#client.execute({
|
|
3151
3479
|
sql: `SELECT * FROM ${TABLE_SCORERS} WHERE traceId = ? AND spanId = ? ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
|
|
3152
3480
|
args: [traceId, spanId, limitValue, start]
|
|
3153
3481
|
});
|
|
@@ -3192,37 +3520,49 @@ function parseWorkflowRun(row) {
|
|
|
3192
3520
|
};
|
|
3193
3521
|
}
|
|
3194
3522
|
var WorkflowsLibSQL = class extends WorkflowsStorage {
|
|
3195
|
-
|
|
3196
|
-
client;
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
constructor({
|
|
3200
|
-
operations,
|
|
3201
|
-
client,
|
|
3202
|
-
maxRetries = 5,
|
|
3203
|
-
initialBackoffMs = 500
|
|
3204
|
-
}) {
|
|
3523
|
+
#db;
|
|
3524
|
+
#client;
|
|
3525
|
+
executeWithRetry;
|
|
3526
|
+
constructor(config) {
|
|
3205
3527
|
super();
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
this
|
|
3528
|
+
const client = resolveClient(config);
|
|
3529
|
+
const maxRetries = config.maxRetries ?? 5;
|
|
3530
|
+
const initialBackoffMs = config.initialBackoffMs ?? 500;
|
|
3531
|
+
this.#client = client;
|
|
3532
|
+
this.#db = new LibSQLDB({ client, maxRetries, initialBackoffMs });
|
|
3533
|
+
this.executeWithRetry = createExecuteWriteOperationWithRetry({
|
|
3534
|
+
logger: this.logger,
|
|
3535
|
+
maxRetries,
|
|
3536
|
+
initialBackoffMs
|
|
3537
|
+
});
|
|
3210
3538
|
this.setupPragmaSettings().catch(
|
|
3211
3539
|
(err) => this.logger.warn("LibSQL Workflows: Failed to setup PRAGMA settings.", err)
|
|
3212
3540
|
);
|
|
3213
3541
|
}
|
|
3542
|
+
async init() {
|
|
3543
|
+
const schema = TABLE_SCHEMAS[TABLE_WORKFLOW_SNAPSHOT];
|
|
3544
|
+
await this.#db.createTable({ tableName: TABLE_WORKFLOW_SNAPSHOT, schema });
|
|
3545
|
+
await this.#db.alterTable({
|
|
3546
|
+
tableName: TABLE_WORKFLOW_SNAPSHOT,
|
|
3547
|
+
schema,
|
|
3548
|
+
ifNotExists: ["resourceId"]
|
|
3549
|
+
});
|
|
3550
|
+
}
|
|
3551
|
+
async dangerouslyClearAll() {
|
|
3552
|
+
await this.#db.deleteData({ tableName: TABLE_WORKFLOW_SNAPSHOT });
|
|
3553
|
+
}
|
|
3214
3554
|
async setupPragmaSettings() {
|
|
3215
3555
|
try {
|
|
3216
|
-
await this
|
|
3556
|
+
await this.#client.execute("PRAGMA busy_timeout = 10000;");
|
|
3217
3557
|
this.logger.debug("LibSQL Workflows: PRAGMA busy_timeout=10000 set.");
|
|
3218
3558
|
try {
|
|
3219
|
-
await this
|
|
3559
|
+
await this.#client.execute("PRAGMA journal_mode = WAL;");
|
|
3220
3560
|
this.logger.debug("LibSQL Workflows: PRAGMA journal_mode=WAL set.");
|
|
3221
3561
|
} catch {
|
|
3222
3562
|
this.logger.debug("LibSQL Workflows: WAL mode not supported, using default journal mode.");
|
|
3223
3563
|
}
|
|
3224
3564
|
try {
|
|
3225
|
-
await this
|
|
3565
|
+
await this.#client.execute("PRAGMA synchronous = NORMAL;");
|
|
3226
3566
|
this.logger.debug("LibSQL Workflows: PRAGMA synchronous=NORMAL set.");
|
|
3227
3567
|
} catch {
|
|
3228
3568
|
this.logger.debug("LibSQL Workflows: Failed to set synchronous mode.");
|
|
@@ -3231,44 +3571,6 @@ var WorkflowsLibSQL = class extends WorkflowsStorage {
|
|
|
3231
3571
|
this.logger.warn("LibSQL Workflows: Failed to set PRAGMA settings.", err);
|
|
3232
3572
|
}
|
|
3233
3573
|
}
|
|
3234
|
-
async executeWithRetry(operation) {
|
|
3235
|
-
let attempts = 0;
|
|
3236
|
-
let backoff = this.initialBackoffMs;
|
|
3237
|
-
while (attempts < this.maxRetries) {
|
|
3238
|
-
try {
|
|
3239
|
-
return await operation();
|
|
3240
|
-
} catch (error) {
|
|
3241
|
-
this.logger.debug("LibSQL Workflows: Error caught in retry loop", {
|
|
3242
|
-
errorType: error.constructor.name,
|
|
3243
|
-
errorCode: error.code,
|
|
3244
|
-
errorMessage: error.message,
|
|
3245
|
-
attempts,
|
|
3246
|
-
maxRetries: this.maxRetries
|
|
3247
|
-
});
|
|
3248
|
-
const isLockError = error.code === "SQLITE_BUSY" || error.code === "SQLITE_LOCKED" || error.message?.toLowerCase().includes("database is locked") || error.message?.toLowerCase().includes("database table is locked") || error.message?.toLowerCase().includes("table is locked") || error.constructor.name === "SqliteError" && error.message?.toLowerCase().includes("locked");
|
|
3249
|
-
if (isLockError) {
|
|
3250
|
-
attempts++;
|
|
3251
|
-
if (attempts >= this.maxRetries) {
|
|
3252
|
-
this.logger.error(
|
|
3253
|
-
`LibSQL Workflows: Operation failed after ${this.maxRetries} attempts due to database lock: ${error.message}`,
|
|
3254
|
-
{ error, attempts, maxRetries: this.maxRetries }
|
|
3255
|
-
);
|
|
3256
|
-
throw error;
|
|
3257
|
-
}
|
|
3258
|
-
this.logger.warn(
|
|
3259
|
-
`LibSQL Workflows: Attempt ${attempts} failed due to database lock. Retrying in ${backoff}ms...`,
|
|
3260
|
-
{ errorMessage: error.message, attempts, backoff, maxRetries: this.maxRetries }
|
|
3261
|
-
);
|
|
3262
|
-
await new Promise((resolve) => setTimeout(resolve, backoff));
|
|
3263
|
-
backoff *= 2;
|
|
3264
|
-
} else {
|
|
3265
|
-
this.logger.error("LibSQL Workflows: Non-lock error occurred, not retrying", { error });
|
|
3266
|
-
throw error;
|
|
3267
|
-
}
|
|
3268
|
-
}
|
|
3269
|
-
}
|
|
3270
|
-
throw new Error("LibSQL Workflows: Max retries reached, but no error was re-thrown from the loop.");
|
|
3271
|
-
}
|
|
3272
3574
|
async updateWorkflowResults({
|
|
3273
3575
|
workflowName,
|
|
3274
3576
|
runId,
|
|
@@ -3277,7 +3579,7 @@ var WorkflowsLibSQL = class extends WorkflowsStorage {
|
|
|
3277
3579
|
requestContext
|
|
3278
3580
|
}) {
|
|
3279
3581
|
return this.executeWithRetry(async () => {
|
|
3280
|
-
const tx = await this
|
|
3582
|
+
const tx = await this.#client.transaction("write");
|
|
3281
3583
|
try {
|
|
3282
3584
|
const existingSnapshotResult = await tx.execute({
|
|
3283
3585
|
sql: `SELECT snapshot FROM ${TABLE_WORKFLOW_SNAPSHOT} WHERE workflow_name = ? AND run_id = ?`,
|
|
@@ -3317,7 +3619,7 @@ var WorkflowsLibSQL = class extends WorkflowsStorage {
|
|
|
3317
3619
|
}
|
|
3318
3620
|
throw error;
|
|
3319
3621
|
}
|
|
3320
|
-
});
|
|
3622
|
+
}, "updateWorkflowResults");
|
|
3321
3623
|
}
|
|
3322
3624
|
async updateWorkflowState({
|
|
3323
3625
|
workflowName,
|
|
@@ -3325,7 +3627,7 @@ var WorkflowsLibSQL = class extends WorkflowsStorage {
|
|
|
3325
3627
|
opts
|
|
3326
3628
|
}) {
|
|
3327
3629
|
return this.executeWithRetry(async () => {
|
|
3328
|
-
const tx = await this
|
|
3630
|
+
const tx = await this.#client.transaction("write");
|
|
3329
3631
|
try {
|
|
3330
3632
|
const existingSnapshotResult = await tx.execute({
|
|
3331
3633
|
sql: `SELECT snapshot FROM ${TABLE_WORKFLOW_SNAPSHOT} WHERE workflow_name = ? AND run_id = ?`,
|
|
@@ -3354,24 +3656,27 @@ var WorkflowsLibSQL = class extends WorkflowsStorage {
|
|
|
3354
3656
|
}
|
|
3355
3657
|
throw error;
|
|
3356
3658
|
}
|
|
3357
|
-
});
|
|
3659
|
+
}, "updateWorkflowState");
|
|
3358
3660
|
}
|
|
3359
3661
|
async persistWorkflowSnapshot({
|
|
3360
3662
|
workflowName,
|
|
3361
3663
|
runId,
|
|
3362
3664
|
resourceId,
|
|
3363
|
-
snapshot
|
|
3665
|
+
snapshot,
|
|
3666
|
+
createdAt,
|
|
3667
|
+
updatedAt
|
|
3364
3668
|
}) {
|
|
3669
|
+
const now = /* @__PURE__ */ new Date();
|
|
3365
3670
|
const data = {
|
|
3366
3671
|
workflow_name: workflowName,
|
|
3367
3672
|
run_id: runId,
|
|
3368
3673
|
resourceId,
|
|
3369
3674
|
snapshot,
|
|
3370
|
-
createdAt:
|
|
3371
|
-
updatedAt:
|
|
3675
|
+
createdAt: createdAt ?? now,
|
|
3676
|
+
updatedAt: updatedAt ?? now
|
|
3372
3677
|
};
|
|
3373
3678
|
this.logger.debug("Persisting workflow snapshot", { workflowName, runId, data });
|
|
3374
|
-
await this.
|
|
3679
|
+
await this.#db.insert({
|
|
3375
3680
|
tableName: TABLE_WORKFLOW_SNAPSHOT,
|
|
3376
3681
|
record: data
|
|
3377
3682
|
});
|
|
@@ -3381,7 +3686,7 @@ var WorkflowsLibSQL = class extends WorkflowsStorage {
|
|
|
3381
3686
|
runId
|
|
3382
3687
|
}) {
|
|
3383
3688
|
this.logger.debug("Loading workflow snapshot", { workflowName, runId });
|
|
3384
|
-
const d = await this.
|
|
3689
|
+
const d = await this.#db.select({
|
|
3385
3690
|
tableName: TABLE_WORKFLOW_SNAPSHOT,
|
|
3386
3691
|
keys: { workflow_name: workflowName, run_id: runId }
|
|
3387
3692
|
});
|
|
@@ -3403,7 +3708,7 @@ var WorkflowsLibSQL = class extends WorkflowsStorage {
|
|
|
3403
3708
|
}
|
|
3404
3709
|
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
3405
3710
|
try {
|
|
3406
|
-
const result = await this
|
|
3711
|
+
const result = await this.#client.execute({
|
|
3407
3712
|
sql: `SELECT * FROM ${TABLE_WORKFLOW_SNAPSHOT} ${whereClause} ORDER BY createdAt DESC LIMIT 1`,
|
|
3408
3713
|
args
|
|
3409
3714
|
});
|
|
@@ -3423,22 +3728,24 @@ var WorkflowsLibSQL = class extends WorkflowsStorage {
|
|
|
3423
3728
|
}
|
|
3424
3729
|
}
|
|
3425
3730
|
async deleteWorkflowRunById({ runId, workflowName }) {
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3731
|
+
return this.executeWithRetry(async () => {
|
|
3732
|
+
try {
|
|
3733
|
+
await this.#client.execute({
|
|
3734
|
+
sql: `DELETE FROM ${TABLE_WORKFLOW_SNAPSHOT} WHERE workflow_name = ? AND run_id = ?`,
|
|
3735
|
+
args: [workflowName, runId]
|
|
3736
|
+
});
|
|
3737
|
+
} catch (error) {
|
|
3738
|
+
throw new MastraError(
|
|
3739
|
+
{
|
|
3740
|
+
id: createStorageErrorId("LIBSQL", "DELETE_WORKFLOW_RUN_BY_ID", "FAILED"),
|
|
3741
|
+
domain: ErrorDomain.STORAGE,
|
|
3742
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
3743
|
+
details: { runId, workflowName }
|
|
3744
|
+
},
|
|
3745
|
+
error
|
|
3746
|
+
);
|
|
3747
|
+
}
|
|
3748
|
+
}, "deleteWorkflowRunById");
|
|
3442
3749
|
}
|
|
3443
3750
|
async listWorkflowRuns({
|
|
3444
3751
|
workflowName,
|
|
@@ -3469,7 +3776,7 @@ var WorkflowsLibSQL = class extends WorkflowsStorage {
|
|
|
3469
3776
|
args.push(toDate.toISOString());
|
|
3470
3777
|
}
|
|
3471
3778
|
if (resourceId) {
|
|
3472
|
-
const hasResourceId = await this.
|
|
3779
|
+
const hasResourceId = await this.#db.hasColumn(TABLE_WORKFLOW_SNAPSHOT, "resourceId");
|
|
3473
3780
|
if (hasResourceId) {
|
|
3474
3781
|
conditions.push("resourceId = ?");
|
|
3475
3782
|
args.push(resourceId);
|
|
@@ -3481,7 +3788,7 @@ var WorkflowsLibSQL = class extends WorkflowsStorage {
|
|
|
3481
3788
|
let total = 0;
|
|
3482
3789
|
const usePagination = typeof perPage === "number" && typeof page === "number";
|
|
3483
3790
|
if (usePagination) {
|
|
3484
|
-
const countResult = await this
|
|
3791
|
+
const countResult = await this.#client.execute({
|
|
3485
3792
|
sql: `SELECT COUNT(*) as count FROM ${TABLE_WORKFLOW_SNAPSHOT} ${whereClause}`,
|
|
3486
3793
|
args
|
|
3487
3794
|
});
|
|
@@ -3489,7 +3796,7 @@ var WorkflowsLibSQL = class extends WorkflowsStorage {
|
|
|
3489
3796
|
}
|
|
3490
3797
|
const normalizedPerPage = usePagination ? normalizePerPage(perPage, Number.MAX_SAFE_INTEGER) : 0;
|
|
3491
3798
|
const offset = usePagination ? page * normalizedPerPage : 0;
|
|
3492
|
-
const result = await this
|
|
3799
|
+
const result = await this.#client.execute({
|
|
3493
3800
|
sql: `SELECT * FROM ${TABLE_WORKFLOW_SNAPSHOT} ${whereClause} ORDER BY createdAt DESC${usePagination ? ` LIMIT ? OFFSET ?` : ""}`,
|
|
3494
3801
|
args: usePagination ? [...args, normalizedPerPage, offset] : args
|
|
3495
3802
|
});
|
|
@@ -3536,18 +3843,17 @@ var LibSQLStore = class extends MastraStorage {
|
|
|
3536
3843
|
} else {
|
|
3537
3844
|
this.client = config.client;
|
|
3538
3845
|
}
|
|
3539
|
-
const
|
|
3846
|
+
const domainConfig = {
|
|
3540
3847
|
client: this.client,
|
|
3541
3848
|
maxRetries: this.maxRetries,
|
|
3542
3849
|
initialBackoffMs: this.initialBackoffMs
|
|
3543
|
-
}
|
|
3544
|
-
const scores = new ScoresLibSQL(
|
|
3545
|
-
const workflows = new WorkflowsLibSQL(
|
|
3546
|
-
const memory = new MemoryLibSQL(
|
|
3547
|
-
const observability = new ObservabilityLibSQL(
|
|
3548
|
-
const agents = new AgentsLibSQL(
|
|
3850
|
+
};
|
|
3851
|
+
const scores = new ScoresLibSQL(domainConfig);
|
|
3852
|
+
const workflows = new WorkflowsLibSQL(domainConfig);
|
|
3853
|
+
const memory = new MemoryLibSQL(domainConfig);
|
|
3854
|
+
const observability = new ObservabilityLibSQL(domainConfig);
|
|
3855
|
+
const agents = new AgentsLibSQL(domainConfig);
|
|
3549
3856
|
this.stores = {
|
|
3550
|
-
operations,
|
|
3551
3857
|
scores,
|
|
3552
3858
|
workflows,
|
|
3553
3859
|
memory,
|
|
@@ -3562,187 +3868,12 @@ var LibSQLStore = class extends MastraStorage {
|
|
|
3562
3868
|
hasColumn: true,
|
|
3563
3869
|
createTable: true,
|
|
3564
3870
|
deleteMessages: true,
|
|
3565
|
-
|
|
3871
|
+
observability: true,
|
|
3872
|
+
indexManagement: false,
|
|
3566
3873
|
listScoresBySpan: true,
|
|
3567
3874
|
agents: true
|
|
3568
3875
|
};
|
|
3569
3876
|
}
|
|
3570
|
-
async createTable({
|
|
3571
|
-
tableName,
|
|
3572
|
-
schema
|
|
3573
|
-
}) {
|
|
3574
|
-
await this.stores.operations.createTable({ tableName, schema });
|
|
3575
|
-
}
|
|
3576
|
-
/**
|
|
3577
|
-
* Alters table schema to add columns if they don't exist
|
|
3578
|
-
* @param tableName Name of the table
|
|
3579
|
-
* @param schema Schema of the table
|
|
3580
|
-
* @param ifNotExists Array of column names to add if they don't exist
|
|
3581
|
-
*/
|
|
3582
|
-
async alterTable({
|
|
3583
|
-
tableName,
|
|
3584
|
-
schema,
|
|
3585
|
-
ifNotExists
|
|
3586
|
-
}) {
|
|
3587
|
-
await this.stores.operations.alterTable({ tableName, schema, ifNotExists });
|
|
3588
|
-
}
|
|
3589
|
-
async clearTable({ tableName }) {
|
|
3590
|
-
await this.stores.operations.clearTable({ tableName });
|
|
3591
|
-
}
|
|
3592
|
-
async dropTable({ tableName }) {
|
|
3593
|
-
await this.stores.operations.dropTable({ tableName });
|
|
3594
|
-
}
|
|
3595
|
-
insert(args) {
|
|
3596
|
-
return this.stores.operations.insert(args);
|
|
3597
|
-
}
|
|
3598
|
-
batchInsert(args) {
|
|
3599
|
-
return this.stores.operations.batchInsert(args);
|
|
3600
|
-
}
|
|
3601
|
-
async load({ tableName, keys }) {
|
|
3602
|
-
return this.stores.operations.load({ tableName, keys });
|
|
3603
|
-
}
|
|
3604
|
-
async getThreadById({ threadId }) {
|
|
3605
|
-
return this.stores.memory.getThreadById({ threadId });
|
|
3606
|
-
}
|
|
3607
|
-
async saveThread({ thread }) {
|
|
3608
|
-
return this.stores.memory.saveThread({ thread });
|
|
3609
|
-
}
|
|
3610
|
-
async updateThread({
|
|
3611
|
-
id,
|
|
3612
|
-
title,
|
|
3613
|
-
metadata
|
|
3614
|
-
}) {
|
|
3615
|
-
return this.stores.memory.updateThread({ id, title, metadata });
|
|
3616
|
-
}
|
|
3617
|
-
async deleteThread({ threadId }) {
|
|
3618
|
-
return this.stores.memory.deleteThread({ threadId });
|
|
3619
|
-
}
|
|
3620
|
-
async listMessagesById({ messageIds }) {
|
|
3621
|
-
return this.stores.memory.listMessagesById({ messageIds });
|
|
3622
|
-
}
|
|
3623
|
-
async saveMessages(args) {
|
|
3624
|
-
const result = await this.stores.memory.saveMessages({ messages: args.messages });
|
|
3625
|
-
return { messages: result.messages };
|
|
3626
|
-
}
|
|
3627
|
-
async updateMessages({
|
|
3628
|
-
messages
|
|
3629
|
-
}) {
|
|
3630
|
-
return this.stores.memory.updateMessages({ messages });
|
|
3631
|
-
}
|
|
3632
|
-
async deleteMessages(messageIds) {
|
|
3633
|
-
return this.stores.memory.deleteMessages(messageIds);
|
|
3634
|
-
}
|
|
3635
|
-
async getScoreById({ id }) {
|
|
3636
|
-
return this.stores.scores.getScoreById({ id });
|
|
3637
|
-
}
|
|
3638
|
-
async saveScore(score) {
|
|
3639
|
-
return this.stores.scores.saveScore(score);
|
|
3640
|
-
}
|
|
3641
|
-
async listScoresByScorerId({
|
|
3642
|
-
scorerId,
|
|
3643
|
-
entityId,
|
|
3644
|
-
entityType,
|
|
3645
|
-
source,
|
|
3646
|
-
pagination
|
|
3647
|
-
}) {
|
|
3648
|
-
return this.stores.scores.listScoresByScorerId({ scorerId, entityId, entityType, source, pagination });
|
|
3649
|
-
}
|
|
3650
|
-
async listScoresByRunId({
|
|
3651
|
-
runId,
|
|
3652
|
-
pagination
|
|
3653
|
-
}) {
|
|
3654
|
-
return this.stores.scores.listScoresByRunId({ runId, pagination });
|
|
3655
|
-
}
|
|
3656
|
-
async listScoresByEntityId({
|
|
3657
|
-
entityId,
|
|
3658
|
-
entityType,
|
|
3659
|
-
pagination
|
|
3660
|
-
}) {
|
|
3661
|
-
return this.stores.scores.listScoresByEntityId({ entityId, entityType, pagination });
|
|
3662
|
-
}
|
|
3663
|
-
/**
|
|
3664
|
-
* WORKFLOWS
|
|
3665
|
-
*/
|
|
3666
|
-
async updateWorkflowResults({
|
|
3667
|
-
workflowName,
|
|
3668
|
-
runId,
|
|
3669
|
-
stepId,
|
|
3670
|
-
result,
|
|
3671
|
-
requestContext
|
|
3672
|
-
}) {
|
|
3673
|
-
return this.stores.workflows.updateWorkflowResults({ workflowName, runId, stepId, result, requestContext });
|
|
3674
|
-
}
|
|
3675
|
-
async updateWorkflowState({
|
|
3676
|
-
workflowName,
|
|
3677
|
-
runId,
|
|
3678
|
-
opts
|
|
3679
|
-
}) {
|
|
3680
|
-
return this.stores.workflows.updateWorkflowState({ workflowName, runId, opts });
|
|
3681
|
-
}
|
|
3682
|
-
async persistWorkflowSnapshot({
|
|
3683
|
-
workflowName,
|
|
3684
|
-
runId,
|
|
3685
|
-
resourceId,
|
|
3686
|
-
snapshot
|
|
3687
|
-
}) {
|
|
3688
|
-
return this.stores.workflows.persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot });
|
|
3689
|
-
}
|
|
3690
|
-
async loadWorkflowSnapshot({
|
|
3691
|
-
workflowName,
|
|
3692
|
-
runId
|
|
3693
|
-
}) {
|
|
3694
|
-
return this.stores.workflows.loadWorkflowSnapshot({ workflowName, runId });
|
|
3695
|
-
}
|
|
3696
|
-
async listWorkflowRuns(args = {}) {
|
|
3697
|
-
return this.stores.workflows.listWorkflowRuns(args);
|
|
3698
|
-
}
|
|
3699
|
-
async getWorkflowRunById({
|
|
3700
|
-
runId,
|
|
3701
|
-
workflowName
|
|
3702
|
-
}) {
|
|
3703
|
-
return this.stores.workflows.getWorkflowRunById({ runId, workflowName });
|
|
3704
|
-
}
|
|
3705
|
-
async deleteWorkflowRunById({ runId, workflowName }) {
|
|
3706
|
-
return this.stores.workflows.deleteWorkflowRunById({ runId, workflowName });
|
|
3707
|
-
}
|
|
3708
|
-
async getResourceById({ resourceId }) {
|
|
3709
|
-
return this.stores.memory.getResourceById({ resourceId });
|
|
3710
|
-
}
|
|
3711
|
-
async saveResource({ resource }) {
|
|
3712
|
-
return this.stores.memory.saveResource({ resource });
|
|
3713
|
-
}
|
|
3714
|
-
async updateResource({
|
|
3715
|
-
resourceId,
|
|
3716
|
-
workingMemory,
|
|
3717
|
-
metadata
|
|
3718
|
-
}) {
|
|
3719
|
-
return this.stores.memory.updateResource({ resourceId, workingMemory, metadata });
|
|
3720
|
-
}
|
|
3721
|
-
async createSpan(span) {
|
|
3722
|
-
return this.stores.observability.createSpan(span);
|
|
3723
|
-
}
|
|
3724
|
-
async updateSpan(params) {
|
|
3725
|
-
return this.stores.observability.updateSpan(params);
|
|
3726
|
-
}
|
|
3727
|
-
async getTrace(traceId) {
|
|
3728
|
-
return this.stores.observability.getTrace(traceId);
|
|
3729
|
-
}
|
|
3730
|
-
async getTracesPaginated(args) {
|
|
3731
|
-
return this.stores.observability.getTracesPaginated(args);
|
|
3732
|
-
}
|
|
3733
|
-
async listScoresBySpan({
|
|
3734
|
-
traceId,
|
|
3735
|
-
spanId,
|
|
3736
|
-
pagination
|
|
3737
|
-
}) {
|
|
3738
|
-
return this.stores.scores.listScoresBySpan({ traceId, spanId, pagination });
|
|
3739
|
-
}
|
|
3740
|
-
async batchCreateSpans(args) {
|
|
3741
|
-
return this.stores.observability.batchCreateSpans(args);
|
|
3742
|
-
}
|
|
3743
|
-
async batchUpdateSpans(args) {
|
|
3744
|
-
return this.stores.observability.batchUpdateSpans(args);
|
|
3745
|
-
}
|
|
3746
3877
|
};
|
|
3747
3878
|
|
|
3748
3879
|
// src/vector/prompt.ts
|