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