@mastra/libsql 0.11.0 → 0.11.1-alpha.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/.turbo/turbo-build.log +7 -7
- package/CHANGELOG.md +26 -0
- package/LICENSE.md +12 -4
- package/dist/_tsup-dts-rollup.d.cts +336 -30
- package/dist/_tsup-dts-rollup.d.ts +336 -30
- package/dist/index.cjs +1357 -847
- package/dist/index.js +1227 -717
- package/package.json +6 -6
- package/src/storage/domains/legacy-evals/index.ts +149 -0
- package/src/storage/domains/memory/index.ts +786 -0
- package/src/storage/domains/operations/index.ts +296 -0
- package/src/storage/domains/scores/index.ts +217 -0
- package/src/storage/domains/traces/index.ts +150 -0
- package/src/storage/domains/utils.ts +67 -0
- package/src/storage/domains/workflows/index.ts +198 -0
- package/src/storage/index.test.ts +2 -515
- package/src/storage/index.ts +186 -1338
package/dist/index.js
CHANGED
|
@@ -3,8 +3,9 @@ import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
|
|
|
3
3
|
import { parseSqlIdentifier, parseFieldKey } from '@mastra/core/utils';
|
|
4
4
|
import { MastraVector } from '@mastra/core/vector';
|
|
5
5
|
import { BaseFilterTranslator } from '@mastra/core/vector/filter';
|
|
6
|
+
import { MastraStorage, StoreOperations, TABLE_WORKFLOW_SNAPSHOT, ScoresStorage, TABLE_SCORERS, TracesStorage, TABLE_TRACES, safelyParseJSON, WorkflowsStorage, MemoryStorage, resolveMessageLimit, TABLE_MESSAGES, TABLE_THREADS, TABLE_RESOURCES, LegacyEvalsStorage, TABLE_EVALS } from '@mastra/core/storage';
|
|
7
|
+
import { parseSqlIdentifier as parseSqlIdentifier$1 } from '@mastra/core';
|
|
6
8
|
import { MessageList } from '@mastra/core/agent';
|
|
7
|
-
import { MastraStorage, TABLE_WORKFLOW_SNAPSHOT, TABLE_THREADS, TABLE_MESSAGES, TABLE_EVALS, TABLE_TRACES, TABLE_RESOURCES } from '@mastra/core/storage';
|
|
8
9
|
|
|
9
10
|
// src/vector/index.ts
|
|
10
11
|
var LibSQLFilterTranslator = class extends BaseFilterTranslator {
|
|
@@ -904,256 +905,542 @@ var LibSQLVector = class extends MastraVector {
|
|
|
904
905
|
});
|
|
905
906
|
}
|
|
906
907
|
};
|
|
907
|
-
function
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
908
|
+
function transformEvalRow(row) {
|
|
909
|
+
const resultValue = JSON.parse(row.result);
|
|
910
|
+
const testInfoValue = row.test_info ? JSON.parse(row.test_info) : void 0;
|
|
911
|
+
if (!resultValue || typeof resultValue !== "object" || !("score" in resultValue)) {
|
|
912
|
+
throw new Error(`Invalid MetricResult format: ${JSON.stringify(resultValue)}`);
|
|
912
913
|
}
|
|
914
|
+
return {
|
|
915
|
+
input: row.input,
|
|
916
|
+
output: row.output,
|
|
917
|
+
result: resultValue,
|
|
918
|
+
agentName: row.agent_name,
|
|
919
|
+
metricName: row.metric_name,
|
|
920
|
+
instructions: row.instructions,
|
|
921
|
+
testInfo: testInfoValue,
|
|
922
|
+
globalRunId: row.global_run_id,
|
|
923
|
+
runId: row.run_id,
|
|
924
|
+
createdAt: row.created_at
|
|
925
|
+
};
|
|
913
926
|
}
|
|
914
|
-
var
|
|
927
|
+
var LegacyEvalsLibSQL = class extends LegacyEvalsStorage {
|
|
915
928
|
client;
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
super({ name: `LibSQLStore` });
|
|
920
|
-
this.maxRetries = config.maxRetries ?? 5;
|
|
921
|
-
this.initialBackoffMs = config.initialBackoffMs ?? 100;
|
|
922
|
-
if (config.url.endsWith(":memory:")) {
|
|
923
|
-
this.shouldCacheInit = false;
|
|
924
|
-
}
|
|
925
|
-
this.client = createClient(config);
|
|
926
|
-
if (config.url.startsWith("file:") || config.url.includes(":memory:")) {
|
|
927
|
-
this.client.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));
|
|
928
|
-
this.client.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.", err));
|
|
929
|
-
}
|
|
930
|
-
}
|
|
931
|
-
get supports() {
|
|
932
|
-
return {
|
|
933
|
-
selectByIncludeResourceScope: true,
|
|
934
|
-
resourceWorkingMemory: true
|
|
935
|
-
};
|
|
929
|
+
constructor({ client }) {
|
|
930
|
+
super();
|
|
931
|
+
this.client = client;
|
|
936
932
|
}
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
const
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
return
|
|
947
|
-
})
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
933
|
+
/** @deprecated use getEvals instead */
|
|
934
|
+
async getEvalsByAgentName(agentName, type) {
|
|
935
|
+
try {
|
|
936
|
+
const baseQuery = `SELECT * FROM ${TABLE_EVALS} WHERE agent_name = ?`;
|
|
937
|
+
const typeCondition = type === "test" ? " AND test_info IS NOT NULL AND test_info->>'testPath' IS NOT NULL" : type === "live" ? " AND (test_info IS NULL OR test_info->>'testPath' IS NULL)" : "";
|
|
938
|
+
const result = await this.client.execute({
|
|
939
|
+
sql: `${baseQuery}${typeCondition} ORDER BY created_at DESC`,
|
|
940
|
+
args: [agentName]
|
|
941
|
+
});
|
|
942
|
+
return result.rows?.map((row) => transformEvalRow(row)) ?? [];
|
|
943
|
+
} catch (error) {
|
|
944
|
+
if (error instanceof Error && error.message.includes("no such table")) {
|
|
945
|
+
return [];
|
|
946
|
+
}
|
|
947
|
+
throw new MastraError(
|
|
948
|
+
{
|
|
949
|
+
id: "LIBSQL_STORE_GET_EVALS_BY_AGENT_NAME_FAILED",
|
|
950
|
+
domain: ErrorDomain.STORAGE,
|
|
951
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
952
|
+
details: { agentName }
|
|
953
|
+
},
|
|
954
|
+
error
|
|
955
|
+
);
|
|
954
956
|
}
|
|
955
|
-
return `CREATE TABLE IF NOT EXISTS ${parsedTableName} (${columns.join(", ")})`;
|
|
956
957
|
}
|
|
957
|
-
async
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
958
|
+
async getEvals(options = {}) {
|
|
959
|
+
const { agentName, type, page = 0, perPage = 100, dateRange } = options;
|
|
960
|
+
const fromDate = dateRange?.start;
|
|
961
|
+
const toDate = dateRange?.end;
|
|
962
|
+
const conditions = [];
|
|
963
|
+
const queryParams = [];
|
|
964
|
+
if (agentName) {
|
|
965
|
+
conditions.push(`agent_name = ?`);
|
|
966
|
+
queryParams.push(agentName);
|
|
967
|
+
}
|
|
968
|
+
if (type === "test") {
|
|
969
|
+
conditions.push(`(test_info IS NOT NULL AND json_extract(test_info, '$.testPath') IS NOT NULL)`);
|
|
970
|
+
} else if (type === "live") {
|
|
971
|
+
conditions.push(`(test_info IS NULL OR json_extract(test_info, '$.testPath') IS NULL)`);
|
|
972
|
+
}
|
|
973
|
+
if (fromDate) {
|
|
974
|
+
conditions.push(`created_at >= ?`);
|
|
975
|
+
queryParams.push(fromDate.toISOString());
|
|
976
|
+
}
|
|
977
|
+
if (toDate) {
|
|
978
|
+
conditions.push(`created_at <= ?`);
|
|
979
|
+
queryParams.push(toDate.toISOString());
|
|
980
|
+
}
|
|
981
|
+
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
961
982
|
try {
|
|
962
|
-
this.
|
|
963
|
-
|
|
964
|
-
|
|
983
|
+
const countResult = await this.client.execute({
|
|
984
|
+
sql: `SELECT COUNT(*) as count FROM ${TABLE_EVALS} ${whereClause}`,
|
|
985
|
+
args: queryParams
|
|
986
|
+
});
|
|
987
|
+
const total = Number(countResult.rows?.[0]?.count ?? 0);
|
|
988
|
+
const currentOffset = page * perPage;
|
|
989
|
+
const hasMore = currentOffset + perPage < total;
|
|
990
|
+
if (total === 0) {
|
|
991
|
+
return {
|
|
992
|
+
evals: [],
|
|
993
|
+
total: 0,
|
|
994
|
+
page,
|
|
995
|
+
perPage,
|
|
996
|
+
hasMore: false
|
|
997
|
+
};
|
|
998
|
+
}
|
|
999
|
+
const dataResult = await this.client.execute({
|
|
1000
|
+
sql: `SELECT * FROM ${TABLE_EVALS} ${whereClause} ORDER BY created_at DESC LIMIT ? OFFSET ?`,
|
|
1001
|
+
args: [...queryParams, perPage, currentOffset]
|
|
1002
|
+
});
|
|
1003
|
+
return {
|
|
1004
|
+
evals: dataResult.rows?.map((row) => transformEvalRow(row)) ?? [],
|
|
1005
|
+
total,
|
|
1006
|
+
page,
|
|
1007
|
+
perPage,
|
|
1008
|
+
hasMore
|
|
1009
|
+
};
|
|
965
1010
|
} catch (error) {
|
|
966
1011
|
throw new MastraError(
|
|
967
1012
|
{
|
|
968
|
-
id: "
|
|
1013
|
+
id: "LIBSQL_STORE_GET_EVALS_FAILED",
|
|
969
1014
|
domain: ErrorDomain.STORAGE,
|
|
970
|
-
category: ErrorCategory.THIRD_PARTY
|
|
971
|
-
details: {
|
|
972
|
-
tableName
|
|
973
|
-
}
|
|
1015
|
+
category: ErrorCategory.THIRD_PARTY
|
|
974
1016
|
},
|
|
975
1017
|
error
|
|
976
1018
|
);
|
|
977
1019
|
}
|
|
978
1020
|
}
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
1021
|
+
};
|
|
1022
|
+
var MemoryLibSQL = class extends MemoryStorage {
|
|
1023
|
+
client;
|
|
1024
|
+
operations;
|
|
1025
|
+
constructor({ client, operations }) {
|
|
1026
|
+
super();
|
|
1027
|
+
this.client = client;
|
|
1028
|
+
this.operations = operations;
|
|
1029
|
+
}
|
|
1030
|
+
parseRow(row) {
|
|
1031
|
+
let content = row.content;
|
|
1032
|
+
try {
|
|
1033
|
+
content = JSON.parse(row.content);
|
|
1034
|
+
} catch {
|
|
989
1035
|
}
|
|
1036
|
+
const result = {
|
|
1037
|
+
id: row.id,
|
|
1038
|
+
content,
|
|
1039
|
+
role: row.role,
|
|
1040
|
+
createdAt: new Date(row.createdAt),
|
|
1041
|
+
threadId: row.thread_id,
|
|
1042
|
+
resourceId: row.resourceId
|
|
1043
|
+
};
|
|
1044
|
+
if (row.type && row.type !== `v2`) result.type = row.type;
|
|
1045
|
+
return result;
|
|
990
1046
|
}
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1047
|
+
async _getIncludedMessages({
|
|
1048
|
+
threadId,
|
|
1049
|
+
selectBy
|
|
1050
|
+
}) {
|
|
1051
|
+
const include = selectBy?.include;
|
|
1052
|
+
if (!include) return null;
|
|
1053
|
+
const unionQueries = [];
|
|
1054
|
+
const params = [];
|
|
1055
|
+
for (const inc of include) {
|
|
1056
|
+
const { id, withPreviousMessages = 0, withNextMessages = 0 } = inc;
|
|
1057
|
+
const searchId = inc.threadId || threadId;
|
|
1058
|
+
unionQueries.push(
|
|
1059
|
+
`
|
|
1060
|
+
SELECT * FROM (
|
|
1061
|
+
WITH numbered_messages AS (
|
|
1062
|
+
SELECT
|
|
1063
|
+
id, content, role, type, "createdAt", thread_id, "resourceId",
|
|
1064
|
+
ROW_NUMBER() OVER (ORDER BY "createdAt" ASC) as row_num
|
|
1065
|
+
FROM "${TABLE_MESSAGES}"
|
|
1066
|
+
WHERE thread_id = ?
|
|
1067
|
+
),
|
|
1068
|
+
target_positions AS (
|
|
1069
|
+
SELECT row_num as target_pos
|
|
1070
|
+
FROM numbered_messages
|
|
1071
|
+
WHERE id = ?
|
|
1072
|
+
)
|
|
1073
|
+
SELECT DISTINCT m.*
|
|
1074
|
+
FROM numbered_messages m
|
|
1075
|
+
CROSS JOIN target_positions t
|
|
1076
|
+
WHERE m.row_num BETWEEN (t.target_pos - ?) AND (t.target_pos + ?)
|
|
1077
|
+
)
|
|
1078
|
+
`
|
|
1079
|
+
// Keep ASC for final sorting after fetching context
|
|
1080
|
+
);
|
|
1081
|
+
params.push(searchId, id, withPreviousMessages, withNextMessages);
|
|
1082
|
+
}
|
|
1083
|
+
const finalQuery = unionQueries.join(" UNION ALL ") + ' ORDER BY "createdAt" ASC';
|
|
1084
|
+
const includedResult = await this.client.execute({ sql: finalQuery, args: params });
|
|
1085
|
+
const includedRows = includedResult.rows?.map((row) => this.parseRow(row));
|
|
1086
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1087
|
+
const dedupedRows = includedRows.filter((row) => {
|
|
1088
|
+
if (seen.has(row.id)) return false;
|
|
1089
|
+
seen.add(row.id);
|
|
1090
|
+
return true;
|
|
1091
|
+
});
|
|
1092
|
+
return dedupedRows;
|
|
1093
|
+
}
|
|
1094
|
+
async getMessages({
|
|
1095
|
+
threadId,
|
|
1096
|
+
selectBy,
|
|
1097
|
+
format
|
|
1001
1098
|
}) {
|
|
1002
|
-
const parsedTableName = parseSqlIdentifier(tableName, "table name");
|
|
1003
1099
|
try {
|
|
1004
|
-
const
|
|
1005
|
-
const
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
if (
|
|
1009
|
-
|
|
1010
|
-
const sqlType = this.getSqlType(columnDef.type);
|
|
1011
|
-
const nullable = columnDef.nullable === false ? "NOT NULL" : "";
|
|
1012
|
-
const defaultValue = columnDef.nullable === false ? this.getDefaultValue(columnDef.type) : "";
|
|
1013
|
-
const alterSql = `ALTER TABLE ${parsedTableName} ADD COLUMN "${columnName}" ${sqlType} ${nullable} ${defaultValue}`.trim();
|
|
1014
|
-
await this.client.execute(alterSql);
|
|
1015
|
-
this.logger?.debug?.(`Added column ${columnName} to table ${parsedTableName}`);
|
|
1100
|
+
const messages = [];
|
|
1101
|
+
const limit = resolveMessageLimit({ last: selectBy?.last, defaultLimit: 40 });
|
|
1102
|
+
if (selectBy?.include?.length) {
|
|
1103
|
+
const includeMessages = await this._getIncludedMessages({ threadId, selectBy });
|
|
1104
|
+
if (includeMessages) {
|
|
1105
|
+
messages.push(...includeMessages);
|
|
1016
1106
|
}
|
|
1017
1107
|
}
|
|
1108
|
+
const excludeIds = messages.map((m) => m.id);
|
|
1109
|
+
const remainingSql = `
|
|
1110
|
+
SELECT
|
|
1111
|
+
id,
|
|
1112
|
+
content,
|
|
1113
|
+
role,
|
|
1114
|
+
type,
|
|
1115
|
+
"createdAt",
|
|
1116
|
+
thread_id,
|
|
1117
|
+
"resourceId"
|
|
1118
|
+
FROM "${TABLE_MESSAGES}"
|
|
1119
|
+
WHERE thread_id = ?
|
|
1120
|
+
${excludeIds.length ? `AND id NOT IN (${excludeIds.map(() => "?").join(", ")})` : ""}
|
|
1121
|
+
ORDER BY "createdAt" DESC
|
|
1122
|
+
LIMIT ?
|
|
1123
|
+
`;
|
|
1124
|
+
const remainingArgs = [threadId, ...excludeIds.length ? excludeIds : [], limit];
|
|
1125
|
+
const remainingResult = await this.client.execute({ sql: remainingSql, args: remainingArgs });
|
|
1126
|
+
if (remainingResult.rows) {
|
|
1127
|
+
messages.push(...remainingResult.rows.map((row) => this.parseRow(row)));
|
|
1128
|
+
}
|
|
1129
|
+
messages.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
|
|
1130
|
+
const list = new MessageList().add(messages, "memory");
|
|
1131
|
+
if (format === `v2`) return list.get.all.v2();
|
|
1132
|
+
return list.get.all.v1();
|
|
1018
1133
|
} catch (error) {
|
|
1019
1134
|
throw new MastraError(
|
|
1020
1135
|
{
|
|
1021
|
-
id: "
|
|
1136
|
+
id: "LIBSQL_STORE_GET_MESSAGES_FAILED",
|
|
1022
1137
|
domain: ErrorDomain.STORAGE,
|
|
1023
1138
|
category: ErrorCategory.THIRD_PARTY,
|
|
1024
|
-
details: {
|
|
1025
|
-
tableName
|
|
1026
|
-
}
|
|
1139
|
+
details: { threadId }
|
|
1027
1140
|
},
|
|
1028
1141
|
error
|
|
1029
1142
|
);
|
|
1030
1143
|
}
|
|
1031
1144
|
}
|
|
1032
|
-
async
|
|
1033
|
-
const
|
|
1145
|
+
async getMessagesPaginated(args) {
|
|
1146
|
+
const { threadId, format, selectBy } = args;
|
|
1147
|
+
const { page = 0, perPage: perPageInput, dateRange } = selectBy?.pagination || {};
|
|
1148
|
+
const perPage = perPageInput !== void 0 ? perPageInput : resolveMessageLimit({ last: selectBy?.last, defaultLimit: 40 });
|
|
1149
|
+
const fromDate = dateRange?.start;
|
|
1150
|
+
const toDate = dateRange?.end;
|
|
1151
|
+
const messages = [];
|
|
1152
|
+
if (selectBy?.include?.length) {
|
|
1153
|
+
try {
|
|
1154
|
+
const includeMessages = await this._getIncludedMessages({ threadId, selectBy });
|
|
1155
|
+
if (includeMessages) {
|
|
1156
|
+
messages.push(...includeMessages);
|
|
1157
|
+
}
|
|
1158
|
+
} catch (error) {
|
|
1159
|
+
throw new MastraError(
|
|
1160
|
+
{
|
|
1161
|
+
id: "LIBSQL_STORE_GET_MESSAGES_PAGINATED_GET_INCLUDE_MESSAGES_FAILED",
|
|
1162
|
+
domain: ErrorDomain.STORAGE,
|
|
1163
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1164
|
+
details: { threadId }
|
|
1165
|
+
},
|
|
1166
|
+
error
|
|
1167
|
+
);
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1034
1170
|
try {
|
|
1035
|
-
|
|
1036
|
-
|
|
1171
|
+
const currentOffset = page * perPage;
|
|
1172
|
+
const conditions = [`thread_id = ?`];
|
|
1173
|
+
const queryParams = [threadId];
|
|
1174
|
+
if (fromDate) {
|
|
1175
|
+
conditions.push(`"createdAt" >= ?`);
|
|
1176
|
+
queryParams.push(fromDate.toISOString());
|
|
1177
|
+
}
|
|
1178
|
+
if (toDate) {
|
|
1179
|
+
conditions.push(`"createdAt" <= ?`);
|
|
1180
|
+
queryParams.push(toDate.toISOString());
|
|
1181
|
+
}
|
|
1182
|
+
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
1183
|
+
const countResult = await this.client.execute({
|
|
1184
|
+
sql: `SELECT COUNT(*) as count FROM ${TABLE_MESSAGES} ${whereClause}`,
|
|
1185
|
+
args: queryParams
|
|
1186
|
+
});
|
|
1187
|
+
const total = Number(countResult.rows?.[0]?.count ?? 0);
|
|
1188
|
+
if (total === 0 && messages.length === 0) {
|
|
1189
|
+
return {
|
|
1190
|
+
messages: [],
|
|
1191
|
+
total: 0,
|
|
1192
|
+
page,
|
|
1193
|
+
perPage,
|
|
1194
|
+
hasMore: false
|
|
1195
|
+
};
|
|
1196
|
+
}
|
|
1197
|
+
const excludeIds = messages.map((m) => m.id);
|
|
1198
|
+
const excludeIdsParam = excludeIds.map((_, idx) => `$${idx + queryParams.length + 1}`).join(", ");
|
|
1199
|
+
const dataResult = await this.client.execute({
|
|
1200
|
+
sql: `SELECT id, content, role, type, "createdAt", "resourceId", "thread_id" FROM ${TABLE_MESSAGES} ${whereClause} ${excludeIds.length ? `AND id NOT IN (${excludeIdsParam})` : ""} ORDER BY "createdAt" DESC LIMIT ? OFFSET ?`,
|
|
1201
|
+
args: [...queryParams, ...excludeIds, perPage, currentOffset]
|
|
1202
|
+
});
|
|
1203
|
+
messages.push(...(dataResult.rows || []).map((row) => this.parseRow(row)));
|
|
1204
|
+
const messagesToReturn = format === "v1" ? new MessageList().add(messages, "memory").get.all.v1() : new MessageList().add(messages, "memory").get.all.v2();
|
|
1205
|
+
return {
|
|
1206
|
+
messages: messagesToReturn,
|
|
1207
|
+
total,
|
|
1208
|
+
page,
|
|
1209
|
+
perPage,
|
|
1210
|
+
hasMore: currentOffset + messages.length < total
|
|
1211
|
+
};
|
|
1212
|
+
} catch (error) {
|
|
1037
1213
|
const mastraError = new MastraError(
|
|
1038
1214
|
{
|
|
1039
|
-
id: "
|
|
1215
|
+
id: "LIBSQL_STORE_GET_MESSAGES_PAGINATED_FAILED",
|
|
1040
1216
|
domain: ErrorDomain.STORAGE,
|
|
1041
1217
|
category: ErrorCategory.THIRD_PARTY,
|
|
1042
|
-
details: {
|
|
1043
|
-
tableName
|
|
1044
|
-
}
|
|
1218
|
+
details: { threadId }
|
|
1045
1219
|
},
|
|
1046
|
-
|
|
1220
|
+
error
|
|
1047
1221
|
);
|
|
1048
1222
|
this.logger?.trackException?.(mastraError);
|
|
1049
1223
|
this.logger?.error?.(mastraError.toString());
|
|
1224
|
+
return { messages: [], total: 0, page, perPage, hasMore: false };
|
|
1050
1225
|
}
|
|
1051
1226
|
}
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
if (
|
|
1060
|
-
|
|
1227
|
+
async saveMessages({
|
|
1228
|
+
messages,
|
|
1229
|
+
format
|
|
1230
|
+
}) {
|
|
1231
|
+
if (messages.length === 0) return messages;
|
|
1232
|
+
try {
|
|
1233
|
+
const threadId = messages[0]?.threadId;
|
|
1234
|
+
if (!threadId) {
|
|
1235
|
+
throw new Error("Thread ID is required");
|
|
1061
1236
|
}
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
args: values
|
|
1068
|
-
};
|
|
1069
|
-
}
|
|
1070
|
-
async executeWriteOperationWithRetry(operationFn, operationDescription) {
|
|
1071
|
-
let retries = 0;
|
|
1072
|
-
while (true) {
|
|
1073
|
-
try {
|
|
1074
|
-
return await operationFn();
|
|
1075
|
-
} catch (error) {
|
|
1076
|
-
if (error.message && (error.message.includes("SQLITE_BUSY") || error.message.includes("database is locked")) && retries < this.maxRetries) {
|
|
1077
|
-
retries++;
|
|
1078
|
-
const backoffTime = this.initialBackoffMs * Math.pow(2, retries - 1);
|
|
1079
|
-
this.logger.warn(
|
|
1080
|
-
`LibSQLStore: Encountered SQLITE_BUSY during ${operationDescription}. Retrying (${retries}/${this.maxRetries}) in ${backoffTime}ms...`
|
|
1237
|
+
const batchStatements = messages.map((message) => {
|
|
1238
|
+
const time = message.createdAt || /* @__PURE__ */ new Date();
|
|
1239
|
+
if (!message.threadId) {
|
|
1240
|
+
throw new Error(
|
|
1241
|
+
`Expected to find a threadId for message, but couldn't find one. An unexpected error has occurred.`
|
|
1081
1242
|
);
|
|
1082
|
-
await new Promise((resolve) => setTimeout(resolve, backoffTime));
|
|
1083
|
-
} else {
|
|
1084
|
-
this.logger.error(`LibSQLStore: Error during ${operationDescription} after ${retries} retries: ${error}`);
|
|
1085
|
-
throw error;
|
|
1086
1243
|
}
|
|
1087
|
-
|
|
1244
|
+
if (!message.resourceId) {
|
|
1245
|
+
throw new Error(
|
|
1246
|
+
`Expected to find a resourceId for message, but couldn't find one. An unexpected error has occurred.`
|
|
1247
|
+
);
|
|
1248
|
+
}
|
|
1249
|
+
return {
|
|
1250
|
+
sql: `INSERT INTO ${TABLE_MESSAGES} (id, thread_id, content, role, type, createdAt, resourceId)
|
|
1251
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
1252
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
1253
|
+
thread_id=excluded.thread_id,
|
|
1254
|
+
content=excluded.content,
|
|
1255
|
+
role=excluded.role,
|
|
1256
|
+
type=excluded.type,
|
|
1257
|
+
resourceId=excluded.resourceId
|
|
1258
|
+
`,
|
|
1259
|
+
args: [
|
|
1260
|
+
message.id,
|
|
1261
|
+
message.threadId,
|
|
1262
|
+
typeof message.content === "object" ? JSON.stringify(message.content) : message.content,
|
|
1263
|
+
message.role,
|
|
1264
|
+
message.type || "v2",
|
|
1265
|
+
time instanceof Date ? time.toISOString() : time,
|
|
1266
|
+
message.resourceId
|
|
1267
|
+
]
|
|
1268
|
+
};
|
|
1269
|
+
});
|
|
1270
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1271
|
+
batchStatements.push({
|
|
1272
|
+
sql: `UPDATE ${TABLE_THREADS} SET updatedAt = ? WHERE id = ?`,
|
|
1273
|
+
args: [now, threadId]
|
|
1274
|
+
});
|
|
1275
|
+
await this.client.batch(batchStatements, "write");
|
|
1276
|
+
const list = new MessageList().add(messages, "memory");
|
|
1277
|
+
if (format === `v2`) return list.get.all.v2();
|
|
1278
|
+
return list.get.all.v1();
|
|
1279
|
+
} catch (error) {
|
|
1280
|
+
throw new MastraError(
|
|
1281
|
+
{
|
|
1282
|
+
id: "LIBSQL_STORE_SAVE_MESSAGES_FAILED",
|
|
1283
|
+
domain: ErrorDomain.STORAGE,
|
|
1284
|
+
category: ErrorCategory.THIRD_PARTY
|
|
1285
|
+
},
|
|
1286
|
+
error
|
|
1287
|
+
);
|
|
1088
1288
|
}
|
|
1089
1289
|
}
|
|
1090
|
-
|
|
1091
|
-
|
|
1290
|
+
async updateMessages({
|
|
1291
|
+
messages
|
|
1292
|
+
}) {
|
|
1293
|
+
if (messages.length === 0) {
|
|
1294
|
+
return [];
|
|
1295
|
+
}
|
|
1296
|
+
const messageIds = messages.map((m) => m.id);
|
|
1297
|
+
const placeholders = messageIds.map(() => "?").join(",");
|
|
1298
|
+
const selectSql = `SELECT * FROM ${TABLE_MESSAGES} WHERE id IN (${placeholders})`;
|
|
1299
|
+
const existingResult = await this.client.execute({ sql: selectSql, args: messageIds });
|
|
1300
|
+
const existingMessages = existingResult.rows.map((row) => this.parseRow(row));
|
|
1301
|
+
if (existingMessages.length === 0) {
|
|
1302
|
+
return [];
|
|
1303
|
+
}
|
|
1304
|
+
const batchStatements = [];
|
|
1305
|
+
const threadIdsToUpdate = /* @__PURE__ */ new Set();
|
|
1306
|
+
const columnMapping = {
|
|
1307
|
+
threadId: "thread_id"
|
|
1308
|
+
};
|
|
1309
|
+
for (const existingMessage of existingMessages) {
|
|
1310
|
+
const updatePayload = messages.find((m) => m.id === existingMessage.id);
|
|
1311
|
+
if (!updatePayload) continue;
|
|
1312
|
+
const { id, ...fieldsToUpdate } = updatePayload;
|
|
1313
|
+
if (Object.keys(fieldsToUpdate).length === 0) continue;
|
|
1314
|
+
threadIdsToUpdate.add(existingMessage.threadId);
|
|
1315
|
+
if (updatePayload.threadId && updatePayload.threadId !== existingMessage.threadId) {
|
|
1316
|
+
threadIdsToUpdate.add(updatePayload.threadId);
|
|
1317
|
+
}
|
|
1318
|
+
const setClauses = [];
|
|
1319
|
+
const args = [];
|
|
1320
|
+
const updatableFields = { ...fieldsToUpdate };
|
|
1321
|
+
if (updatableFields.content) {
|
|
1322
|
+
const newContent = {
|
|
1323
|
+
...existingMessage.content,
|
|
1324
|
+
...updatableFields.content,
|
|
1325
|
+
// Deep merge metadata if it exists on both
|
|
1326
|
+
...existingMessage.content?.metadata && updatableFields.content.metadata ? {
|
|
1327
|
+
metadata: {
|
|
1328
|
+
...existingMessage.content.metadata,
|
|
1329
|
+
...updatableFields.content.metadata
|
|
1330
|
+
}
|
|
1331
|
+
} : {}
|
|
1332
|
+
};
|
|
1333
|
+
setClauses.push(`${parseSqlIdentifier$1("content", "column name")} = ?`);
|
|
1334
|
+
args.push(JSON.stringify(newContent));
|
|
1335
|
+
delete updatableFields.content;
|
|
1336
|
+
}
|
|
1337
|
+
for (const key in updatableFields) {
|
|
1338
|
+
if (Object.prototype.hasOwnProperty.call(updatableFields, key)) {
|
|
1339
|
+
const dbKey = columnMapping[key] || key;
|
|
1340
|
+
setClauses.push(`${parseSqlIdentifier$1(dbKey, "column name")} = ?`);
|
|
1341
|
+
let value = updatableFields[key];
|
|
1342
|
+
if (typeof value === "object" && value !== null) {
|
|
1343
|
+
value = JSON.stringify(value);
|
|
1344
|
+
}
|
|
1345
|
+
args.push(value);
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
if (setClauses.length === 0) continue;
|
|
1349
|
+
args.push(id);
|
|
1350
|
+
const sql = `UPDATE ${TABLE_MESSAGES} SET ${setClauses.join(", ")} WHERE id = ?`;
|
|
1351
|
+
batchStatements.push({ sql, args });
|
|
1352
|
+
}
|
|
1353
|
+
if (batchStatements.length === 0) {
|
|
1354
|
+
return existingMessages;
|
|
1355
|
+
}
|
|
1356
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1357
|
+
for (const threadId of threadIdsToUpdate) {
|
|
1358
|
+
if (threadId) {
|
|
1359
|
+
batchStatements.push({
|
|
1360
|
+
sql: `UPDATE ${TABLE_THREADS} SET updatedAt = ? WHERE id = ?`,
|
|
1361
|
+
args: [now, threadId]
|
|
1362
|
+
});
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
await this.client.batch(batchStatements, "write");
|
|
1366
|
+
const updatedResult = await this.client.execute({ sql: selectSql, args: messageIds });
|
|
1367
|
+
return updatedResult.rows.map((row) => this.parseRow(row));
|
|
1092
1368
|
}
|
|
1093
|
-
async
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1369
|
+
async getResourceById({ resourceId }) {
|
|
1370
|
+
const result = await this.operations.load({
|
|
1371
|
+
tableName: TABLE_RESOURCES,
|
|
1372
|
+
keys: { id: resourceId }
|
|
1373
|
+
});
|
|
1374
|
+
if (!result) {
|
|
1375
|
+
return null;
|
|
1376
|
+
}
|
|
1377
|
+
return {
|
|
1378
|
+
...result,
|
|
1379
|
+
// Ensure workingMemory is always returned as a string, even if auto-parsed as JSON
|
|
1380
|
+
workingMemory: result.workingMemory && typeof result.workingMemory === "object" ? JSON.stringify(result.workingMemory) : result.workingMemory,
|
|
1381
|
+
metadata: typeof result.metadata === "string" ? JSON.parse(result.metadata) : result.metadata,
|
|
1382
|
+
createdAt: new Date(result.createdAt),
|
|
1383
|
+
updatedAt: new Date(result.updatedAt)
|
|
1384
|
+
};
|
|
1103
1385
|
}
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
domain: ErrorDomain.STORAGE,
|
|
1113
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
1114
|
-
details: {
|
|
1115
|
-
tableName: args.tableName
|
|
1116
|
-
}
|
|
1117
|
-
},
|
|
1118
|
-
error
|
|
1119
|
-
);
|
|
1386
|
+
async saveResource({ resource }) {
|
|
1387
|
+
console.log("resource", resource);
|
|
1388
|
+
await this.operations.insert({
|
|
1389
|
+
tableName: TABLE_RESOURCES,
|
|
1390
|
+
record: {
|
|
1391
|
+
...resource,
|
|
1392
|
+
metadata: JSON.stringify(resource.metadata)
|
|
1393
|
+
}
|
|
1120
1394
|
});
|
|
1395
|
+
return resource;
|
|
1121
1396
|
}
|
|
1122
|
-
async
|
|
1123
|
-
|
|
1124
|
-
|
|
1397
|
+
async updateResource({
|
|
1398
|
+
resourceId,
|
|
1399
|
+
workingMemory,
|
|
1400
|
+
metadata
|
|
1125
1401
|
}) {
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1402
|
+
const existingResource = await this.getResourceById({ resourceId });
|
|
1403
|
+
if (!existingResource) {
|
|
1404
|
+
const newResource = {
|
|
1405
|
+
id: resourceId,
|
|
1406
|
+
workingMemory,
|
|
1407
|
+
metadata: metadata || {},
|
|
1408
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
1409
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1410
|
+
};
|
|
1411
|
+
return this.saveResource({ resource: newResource });
|
|
1412
|
+
}
|
|
1413
|
+
const updatedResource = {
|
|
1414
|
+
...existingResource,
|
|
1415
|
+
workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
|
|
1416
|
+
metadata: {
|
|
1417
|
+
...existingResource.metadata,
|
|
1418
|
+
...metadata
|
|
1419
|
+
},
|
|
1420
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1421
|
+
};
|
|
1422
|
+
const updates = [];
|
|
1423
|
+
const values = [];
|
|
1424
|
+
if (workingMemory !== void 0) {
|
|
1425
|
+
updates.push("workingMemory = ?");
|
|
1426
|
+
values.push(workingMemory);
|
|
1427
|
+
}
|
|
1428
|
+
if (metadata) {
|
|
1429
|
+
updates.push("metadata = ?");
|
|
1430
|
+
values.push(JSON.stringify(updatedResource.metadata));
|
|
1431
|
+
}
|
|
1432
|
+
updates.push("updatedAt = ?");
|
|
1433
|
+
values.push(updatedResource.updatedAt.toISOString());
|
|
1434
|
+
values.push(resourceId);
|
|
1435
|
+
await this.client.execute({
|
|
1436
|
+
sql: `UPDATE ${TABLE_RESOURCES} SET ${updates.join(", ")} WHERE id = ?`,
|
|
1137
1437
|
args: values
|
|
1138
1438
|
});
|
|
1139
|
-
|
|
1140
|
-
return null;
|
|
1141
|
-
}
|
|
1142
|
-
const row = result.rows[0];
|
|
1143
|
-
const parsed = Object.fromEntries(
|
|
1144
|
-
Object.entries(row || {}).map(([k, v]) => {
|
|
1145
|
-
try {
|
|
1146
|
-
return [k, typeof v === "string" ? v.startsWith("{") || v.startsWith("[") ? JSON.parse(v) : v : v];
|
|
1147
|
-
} catch {
|
|
1148
|
-
return [k, v];
|
|
1149
|
-
}
|
|
1150
|
-
})
|
|
1151
|
-
);
|
|
1152
|
-
return parsed;
|
|
1439
|
+
return updatedResource;
|
|
1153
1440
|
}
|
|
1154
1441
|
async getThreadById({ threadId }) {
|
|
1155
1442
|
try {
|
|
1156
|
-
const result = await this.load({
|
|
1443
|
+
const result = await this.operations.load({
|
|
1157
1444
|
tableName: TABLE_THREADS,
|
|
1158
1445
|
keys: { id: threadId }
|
|
1159
1446
|
});
|
|
@@ -1162,7 +1449,9 @@ var LibSQLStore = class extends MastraStorage {
|
|
|
1162
1449
|
}
|
|
1163
1450
|
return {
|
|
1164
1451
|
...result,
|
|
1165
|
-
metadata: typeof result.metadata === "string" ? JSON.parse(result.metadata) : result.metadata
|
|
1452
|
+
metadata: typeof result.metadata === "string" ? JSON.parse(result.metadata) : result.metadata,
|
|
1453
|
+
createdAt: new Date(result.createdAt),
|
|
1454
|
+
updatedAt: new Date(result.updatedAt)
|
|
1166
1455
|
};
|
|
1167
1456
|
} catch (error) {
|
|
1168
1457
|
throw new MastraError(
|
|
@@ -1276,7 +1565,7 @@ var LibSQLStore = class extends MastraStorage {
|
|
|
1276
1565
|
}
|
|
1277
1566
|
async saveThread({ thread }) {
|
|
1278
1567
|
try {
|
|
1279
|
-
await this.insert({
|
|
1568
|
+
await this.operations.insert({
|
|
1280
1569
|
tableName: TABLE_THREADS,
|
|
1281
1570
|
record: {
|
|
1282
1571
|
...thread,
|
|
@@ -1311,7 +1600,10 @@ var LibSQLStore = class extends MastraStorage {
|
|
|
1311
1600
|
domain: ErrorDomain.STORAGE,
|
|
1312
1601
|
category: ErrorCategory.USER,
|
|
1313
1602
|
text: `Thread ${id} not found`,
|
|
1314
|
-
details: {
|
|
1603
|
+
details: {
|
|
1604
|
+
status: 404,
|
|
1605
|
+
threadId: id
|
|
1606
|
+
}
|
|
1315
1607
|
});
|
|
1316
1608
|
}
|
|
1317
1609
|
const updatedThread = {
|
|
@@ -1354,268 +1646,329 @@ var LibSQLStore = class extends MastraStorage {
|
|
|
1354
1646
|
} catch (error) {
|
|
1355
1647
|
throw new MastraError(
|
|
1356
1648
|
{
|
|
1357
|
-
id: "LIBSQL_STORE_DELETE_THREAD_FAILED",
|
|
1649
|
+
id: "LIBSQL_STORE_DELETE_THREAD_FAILED",
|
|
1650
|
+
domain: ErrorDomain.STORAGE,
|
|
1651
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1652
|
+
details: { threadId }
|
|
1653
|
+
},
|
|
1654
|
+
error
|
|
1655
|
+
);
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
};
|
|
1659
|
+
function createExecuteWriteOperationWithRetry({
|
|
1660
|
+
logger,
|
|
1661
|
+
maxRetries,
|
|
1662
|
+
initialBackoffMs
|
|
1663
|
+
}) {
|
|
1664
|
+
return async function executeWriteOperationWithRetry(operationFn, operationDescription) {
|
|
1665
|
+
let retries = 0;
|
|
1666
|
+
while (true) {
|
|
1667
|
+
try {
|
|
1668
|
+
return await operationFn();
|
|
1669
|
+
} catch (error) {
|
|
1670
|
+
if (error.message && (error.message.includes("SQLITE_BUSY") || error.message.includes("database is locked")) && retries < maxRetries) {
|
|
1671
|
+
retries++;
|
|
1672
|
+
const backoffTime = initialBackoffMs * Math.pow(2, retries - 1);
|
|
1673
|
+
logger.warn(
|
|
1674
|
+
`LibSQLStore: Encountered SQLITE_BUSY during ${operationDescription}. Retrying (${retries}/${maxRetries}) in ${backoffTime}ms...`
|
|
1675
|
+
);
|
|
1676
|
+
await new Promise((resolve) => setTimeout(resolve, backoffTime));
|
|
1677
|
+
} else {
|
|
1678
|
+
logger.error(`LibSQLStore: Error during ${operationDescription} after ${retries} retries: ${error}`);
|
|
1679
|
+
throw error;
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
};
|
|
1684
|
+
}
|
|
1685
|
+
function prepareStatement({ tableName, record }) {
|
|
1686
|
+
const parsedTableName = parseSqlIdentifier(tableName, "table name");
|
|
1687
|
+
const columns = Object.keys(record).map((col) => parseSqlIdentifier(col, "column name"));
|
|
1688
|
+
const values = Object.values(record).map((v) => {
|
|
1689
|
+
if (typeof v === `undefined`) {
|
|
1690
|
+
return null;
|
|
1691
|
+
}
|
|
1692
|
+
if (v instanceof Date) {
|
|
1693
|
+
return v.toISOString();
|
|
1694
|
+
}
|
|
1695
|
+
return typeof v === "object" ? JSON.stringify(v) : v;
|
|
1696
|
+
});
|
|
1697
|
+
const placeholders = values.map(() => "?").join(", ");
|
|
1698
|
+
return {
|
|
1699
|
+
sql: `INSERT OR REPLACE INTO ${parsedTableName} (${columns.join(", ")}) VALUES (${placeholders})`,
|
|
1700
|
+
args: values
|
|
1701
|
+
};
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
// src/storage/domains/operations/index.ts
|
|
1705
|
+
var StoreOperationsLibSQL = class extends StoreOperations {
|
|
1706
|
+
client;
|
|
1707
|
+
/**
|
|
1708
|
+
* Maximum number of retries for write operations if an SQLITE_BUSY error occurs.
|
|
1709
|
+
* @default 5
|
|
1710
|
+
*/
|
|
1711
|
+
maxRetries;
|
|
1712
|
+
/**
|
|
1713
|
+
* Initial backoff time in milliseconds for retrying write operations on SQLITE_BUSY.
|
|
1714
|
+
* The backoff time will double with each retry (exponential backoff).
|
|
1715
|
+
* @default 100
|
|
1716
|
+
*/
|
|
1717
|
+
initialBackoffMs;
|
|
1718
|
+
constructor({
|
|
1719
|
+
client,
|
|
1720
|
+
maxRetries,
|
|
1721
|
+
initialBackoffMs
|
|
1722
|
+
}) {
|
|
1723
|
+
super();
|
|
1724
|
+
this.client = client;
|
|
1725
|
+
this.maxRetries = maxRetries ?? 5;
|
|
1726
|
+
this.initialBackoffMs = initialBackoffMs ?? 100;
|
|
1727
|
+
}
|
|
1728
|
+
async hasColumn(table, column) {
|
|
1729
|
+
const result = await this.client.execute({
|
|
1730
|
+
sql: `PRAGMA table_info(${table})`
|
|
1731
|
+
});
|
|
1732
|
+
return (await result.rows)?.some((row) => row.name === column);
|
|
1733
|
+
}
|
|
1734
|
+
getCreateTableSQL(tableName, schema) {
|
|
1735
|
+
const parsedTableName = parseSqlIdentifier(tableName, "table name");
|
|
1736
|
+
const columns = Object.entries(schema).map(([name, col]) => {
|
|
1737
|
+
const parsedColumnName = parseSqlIdentifier(name, "column name");
|
|
1738
|
+
let type = col.type.toUpperCase();
|
|
1739
|
+
if (type === "TEXT") type = "TEXT";
|
|
1740
|
+
if (type === "TIMESTAMP") type = "TEXT";
|
|
1741
|
+
const nullable = col.nullable ? "" : "NOT NULL";
|
|
1742
|
+
const primaryKey = col.primaryKey ? "PRIMARY KEY" : "";
|
|
1743
|
+
return `${parsedColumnName} ${type} ${nullable} ${primaryKey}`.trim();
|
|
1744
|
+
});
|
|
1745
|
+
if (tableName === TABLE_WORKFLOW_SNAPSHOT) {
|
|
1746
|
+
const stmnt = `CREATE TABLE IF NOT EXISTS ${parsedTableName} (
|
|
1747
|
+
${columns.join(",\n")},
|
|
1748
|
+
PRIMARY KEY (workflow_name, run_id)
|
|
1749
|
+
)`;
|
|
1750
|
+
return stmnt;
|
|
1751
|
+
}
|
|
1752
|
+
return `CREATE TABLE IF NOT EXISTS ${parsedTableName} (${columns.join(", ")})`;
|
|
1753
|
+
}
|
|
1754
|
+
async createTable({
|
|
1755
|
+
tableName,
|
|
1756
|
+
schema
|
|
1757
|
+
}) {
|
|
1758
|
+
try {
|
|
1759
|
+
this.logger.debug(`Creating database table`, { tableName, operation: "schema init" });
|
|
1760
|
+
const sql = this.getCreateTableSQL(tableName, schema);
|
|
1761
|
+
await this.client.execute(sql);
|
|
1762
|
+
} catch (error) {
|
|
1763
|
+
throw new MastraError(
|
|
1764
|
+
{
|
|
1765
|
+
id: "LIBSQL_STORE_CREATE_TABLE_FAILED",
|
|
1766
|
+
domain: ErrorDomain.STORAGE,
|
|
1767
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1768
|
+
details: {
|
|
1769
|
+
tableName
|
|
1770
|
+
}
|
|
1771
|
+
},
|
|
1772
|
+
error
|
|
1773
|
+
);
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
getSqlType(type) {
|
|
1777
|
+
switch (type) {
|
|
1778
|
+
case "bigint":
|
|
1779
|
+
return "INTEGER";
|
|
1780
|
+
// SQLite uses INTEGER for all integer sizes
|
|
1781
|
+
case "jsonb":
|
|
1782
|
+
return "TEXT";
|
|
1783
|
+
// Store JSON as TEXT in SQLite
|
|
1784
|
+
default:
|
|
1785
|
+
return super.getSqlType(type);
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
async doInsert({
|
|
1789
|
+
tableName,
|
|
1790
|
+
record
|
|
1791
|
+
}) {
|
|
1792
|
+
await this.client.execute(
|
|
1793
|
+
prepareStatement({
|
|
1794
|
+
tableName,
|
|
1795
|
+
record
|
|
1796
|
+
})
|
|
1797
|
+
);
|
|
1798
|
+
}
|
|
1799
|
+
insert(args) {
|
|
1800
|
+
const executeWriteOperationWithRetry = createExecuteWriteOperationWithRetry({
|
|
1801
|
+
logger: this.logger,
|
|
1802
|
+
maxRetries: this.maxRetries,
|
|
1803
|
+
initialBackoffMs: this.initialBackoffMs
|
|
1804
|
+
});
|
|
1805
|
+
return executeWriteOperationWithRetry(() => this.doInsert(args), `insert into table ${args.tableName}`);
|
|
1806
|
+
}
|
|
1807
|
+
async load({ tableName, keys }) {
|
|
1808
|
+
const parsedTableName = parseSqlIdentifier(tableName, "table name");
|
|
1809
|
+
const parsedKeys = Object.keys(keys).map((key) => parseSqlIdentifier(key, "column name"));
|
|
1810
|
+
const conditions = parsedKeys.map((key) => `${key} = ?`).join(" AND ");
|
|
1811
|
+
const values = Object.values(keys);
|
|
1812
|
+
const result = await this.client.execute({
|
|
1813
|
+
sql: `SELECT * FROM ${parsedTableName} WHERE ${conditions} ORDER BY createdAt DESC LIMIT 1`,
|
|
1814
|
+
args: values
|
|
1815
|
+
});
|
|
1816
|
+
if (!result.rows || result.rows.length === 0) {
|
|
1817
|
+
return null;
|
|
1818
|
+
}
|
|
1819
|
+
const row = result.rows[0];
|
|
1820
|
+
const parsed = Object.fromEntries(
|
|
1821
|
+
Object.entries(row || {}).map(([k, v]) => {
|
|
1822
|
+
try {
|
|
1823
|
+
return [k, typeof v === "string" ? v.startsWith("{") || v.startsWith("[") ? JSON.parse(v) : v : v];
|
|
1824
|
+
} catch {
|
|
1825
|
+
return [k, v];
|
|
1826
|
+
}
|
|
1827
|
+
})
|
|
1828
|
+
);
|
|
1829
|
+
return parsed;
|
|
1830
|
+
}
|
|
1831
|
+
async doBatchInsert({
|
|
1832
|
+
tableName,
|
|
1833
|
+
records
|
|
1834
|
+
}) {
|
|
1835
|
+
if (records.length === 0) return;
|
|
1836
|
+
const batchStatements = records.map((r) => prepareStatement({ tableName, record: r }));
|
|
1837
|
+
await this.client.batch(batchStatements, "write");
|
|
1838
|
+
}
|
|
1839
|
+
batchInsert(args) {
|
|
1840
|
+
const executeWriteOperationWithRetry = createExecuteWriteOperationWithRetry({
|
|
1841
|
+
logger: this.logger,
|
|
1842
|
+
maxRetries: this.maxRetries,
|
|
1843
|
+
initialBackoffMs: this.initialBackoffMs
|
|
1844
|
+
});
|
|
1845
|
+
return executeWriteOperationWithRetry(
|
|
1846
|
+
() => this.doBatchInsert(args),
|
|
1847
|
+
`batch insert into table ${args.tableName}`
|
|
1848
|
+
).catch((error) => {
|
|
1849
|
+
throw new MastraError(
|
|
1850
|
+
{
|
|
1851
|
+
id: "LIBSQL_STORE_BATCH_INSERT_FAILED",
|
|
1358
1852
|
domain: ErrorDomain.STORAGE,
|
|
1359
1853
|
category: ErrorCategory.THIRD_PARTY,
|
|
1360
|
-
details: {
|
|
1854
|
+
details: {
|
|
1855
|
+
tableName: args.tableName
|
|
1856
|
+
}
|
|
1361
1857
|
},
|
|
1362
1858
|
error
|
|
1363
1859
|
);
|
|
1364
|
-
}
|
|
1365
|
-
}
|
|
1366
|
-
parseRow(row) {
|
|
1367
|
-
let content = row.content;
|
|
1368
|
-
try {
|
|
1369
|
-
content = JSON.parse(row.content);
|
|
1370
|
-
} catch {
|
|
1371
|
-
}
|
|
1372
|
-
const result = {
|
|
1373
|
-
id: row.id,
|
|
1374
|
-
content,
|
|
1375
|
-
role: row.role,
|
|
1376
|
-
createdAt: new Date(row.createdAt),
|
|
1377
|
-
threadId: row.thread_id,
|
|
1378
|
-
resourceId: row.resourceId
|
|
1379
|
-
};
|
|
1380
|
-
if (row.type && row.type !== `v2`) result.type = row.type;
|
|
1381
|
-
return result;
|
|
1382
|
-
}
|
|
1383
|
-
async _getIncludedMessages({
|
|
1384
|
-
threadId,
|
|
1385
|
-
selectBy
|
|
1386
|
-
}) {
|
|
1387
|
-
const include = selectBy?.include;
|
|
1388
|
-
if (!include) return null;
|
|
1389
|
-
const unionQueries = [];
|
|
1390
|
-
const params = [];
|
|
1391
|
-
for (const inc of include) {
|
|
1392
|
-
const { id, withPreviousMessages = 0, withNextMessages = 0 } = inc;
|
|
1393
|
-
const searchId = inc.threadId || threadId;
|
|
1394
|
-
unionQueries.push(
|
|
1395
|
-
`
|
|
1396
|
-
SELECT * FROM (
|
|
1397
|
-
WITH numbered_messages AS (
|
|
1398
|
-
SELECT
|
|
1399
|
-
id, content, role, type, "createdAt", thread_id, "resourceId",
|
|
1400
|
-
ROW_NUMBER() OVER (ORDER BY "createdAt" ASC) as row_num
|
|
1401
|
-
FROM "${TABLE_MESSAGES}"
|
|
1402
|
-
WHERE thread_id = ?
|
|
1403
|
-
),
|
|
1404
|
-
target_positions AS (
|
|
1405
|
-
SELECT row_num as target_pos
|
|
1406
|
-
FROM numbered_messages
|
|
1407
|
-
WHERE id = ?
|
|
1408
|
-
)
|
|
1409
|
-
SELECT DISTINCT m.*
|
|
1410
|
-
FROM numbered_messages m
|
|
1411
|
-
CROSS JOIN target_positions t
|
|
1412
|
-
WHERE m.row_num BETWEEN (t.target_pos - ?) AND (t.target_pos + ?)
|
|
1413
|
-
)
|
|
1414
|
-
`
|
|
1415
|
-
// Keep ASC for final sorting after fetching context
|
|
1416
|
-
);
|
|
1417
|
-
params.push(searchId, id, withPreviousMessages, withNextMessages);
|
|
1418
|
-
}
|
|
1419
|
-
const finalQuery = unionQueries.join(" UNION ALL ") + ' ORDER BY "createdAt" ASC';
|
|
1420
|
-
const includedResult = await this.client.execute({ sql: finalQuery, args: params });
|
|
1421
|
-
const includedRows = includedResult.rows?.map((row) => this.parseRow(row));
|
|
1422
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1423
|
-
const dedupedRows = includedRows.filter((row) => {
|
|
1424
|
-
if (seen.has(row.id)) return false;
|
|
1425
|
-
seen.add(row.id);
|
|
1426
|
-
return true;
|
|
1427
1860
|
});
|
|
1428
|
-
return dedupedRows;
|
|
1429
1861
|
}
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1862
|
+
/**
|
|
1863
|
+
* Alters table schema to add columns if they don't exist
|
|
1864
|
+
* @param tableName Name of the table
|
|
1865
|
+
* @param schema Schema of the table
|
|
1866
|
+
* @param ifNotExists Array of column names to add if they don't exist
|
|
1867
|
+
*/
|
|
1868
|
+
async alterTable({
|
|
1869
|
+
tableName,
|
|
1870
|
+
schema,
|
|
1871
|
+
ifNotExists
|
|
1434
1872
|
}) {
|
|
1873
|
+
const parsedTableName = parseSqlIdentifier(tableName, "table name");
|
|
1435
1874
|
try {
|
|
1436
|
-
const
|
|
1437
|
-
const
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
if (
|
|
1441
|
-
|
|
1875
|
+
const pragmaQuery = `PRAGMA table_info(${parsedTableName})`;
|
|
1876
|
+
const result = await this.client.execute(pragmaQuery);
|
|
1877
|
+
const existingColumnNames = new Set(result.rows.map((row) => row.name.toLowerCase()));
|
|
1878
|
+
for (const columnName of ifNotExists) {
|
|
1879
|
+
if (!existingColumnNames.has(columnName.toLowerCase()) && schema[columnName]) {
|
|
1880
|
+
const columnDef = schema[columnName];
|
|
1881
|
+
const sqlType = this.getSqlType(columnDef.type);
|
|
1882
|
+
const nullable = columnDef.nullable === false ? "NOT NULL" : "";
|
|
1883
|
+
const defaultValue = columnDef.nullable === false ? this.getDefaultValue(columnDef.type) : "";
|
|
1884
|
+
const alterSql = `ALTER TABLE ${parsedTableName} ADD COLUMN "${columnName}" ${sqlType} ${nullable} ${defaultValue}`.trim();
|
|
1885
|
+
await this.client.execute(alterSql);
|
|
1886
|
+
this.logger?.debug?.(`Added column ${columnName} to table ${parsedTableName}`);
|
|
1442
1887
|
}
|
|
1443
1888
|
}
|
|
1444
|
-
const excludeIds = messages.map((m) => m.id);
|
|
1445
|
-
const remainingSql = `
|
|
1446
|
-
SELECT
|
|
1447
|
-
id,
|
|
1448
|
-
content,
|
|
1449
|
-
role,
|
|
1450
|
-
type,
|
|
1451
|
-
"createdAt",
|
|
1452
|
-
thread_id,
|
|
1453
|
-
"resourceId"
|
|
1454
|
-
FROM "${TABLE_MESSAGES}"
|
|
1455
|
-
WHERE thread_id = ?
|
|
1456
|
-
${excludeIds.length ? `AND id NOT IN (${excludeIds.map(() => "?").join(", ")})` : ""}
|
|
1457
|
-
ORDER BY "createdAt" DESC
|
|
1458
|
-
LIMIT ?
|
|
1459
|
-
`;
|
|
1460
|
-
const remainingArgs = [threadId, ...excludeIds.length ? excludeIds : [], limit];
|
|
1461
|
-
const remainingResult = await this.client.execute({ sql: remainingSql, args: remainingArgs });
|
|
1462
|
-
if (remainingResult.rows) {
|
|
1463
|
-
messages.push(...remainingResult.rows.map((row) => this.parseRow(row)));
|
|
1464
|
-
}
|
|
1465
|
-
messages.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
|
|
1466
|
-
const list = new MessageList().add(messages, "memory");
|
|
1467
|
-
if (format === `v2`) return list.get.all.v2();
|
|
1468
|
-
return list.get.all.v1();
|
|
1469
1889
|
} catch (error) {
|
|
1470
1890
|
throw new MastraError(
|
|
1471
1891
|
{
|
|
1472
|
-
id: "
|
|
1892
|
+
id: "LIBSQL_STORE_ALTER_TABLE_FAILED",
|
|
1473
1893
|
domain: ErrorDomain.STORAGE,
|
|
1474
1894
|
category: ErrorCategory.THIRD_PARTY,
|
|
1475
|
-
details: {
|
|
1895
|
+
details: {
|
|
1896
|
+
tableName
|
|
1897
|
+
}
|
|
1476
1898
|
},
|
|
1477
1899
|
error
|
|
1478
1900
|
);
|
|
1479
1901
|
}
|
|
1480
1902
|
}
|
|
1481
|
-
async
|
|
1482
|
-
const
|
|
1483
|
-
const { page = 0, perPage: perPageInput, dateRange } = selectBy?.pagination || {};
|
|
1484
|
-
const perPage = perPageInput !== void 0 ? perPageInput : this.resolveMessageLimit({ last: selectBy?.last, defaultLimit: 40 });
|
|
1485
|
-
const fromDate = dateRange?.start;
|
|
1486
|
-
const toDate = dateRange?.end;
|
|
1487
|
-
const messages = [];
|
|
1488
|
-
if (selectBy?.include?.length) {
|
|
1489
|
-
try {
|
|
1490
|
-
const includeMessages = await this._getIncludedMessages({ threadId, selectBy });
|
|
1491
|
-
if (includeMessages) {
|
|
1492
|
-
messages.push(...includeMessages);
|
|
1493
|
-
}
|
|
1494
|
-
} catch (error) {
|
|
1495
|
-
throw new MastraError(
|
|
1496
|
-
{
|
|
1497
|
-
id: "LIBSQL_STORE_GET_MESSAGES_PAGINATED_GET_INCLUDE_MESSAGES_FAILED",
|
|
1498
|
-
domain: ErrorDomain.STORAGE,
|
|
1499
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
1500
|
-
details: { threadId }
|
|
1501
|
-
},
|
|
1502
|
-
error
|
|
1503
|
-
);
|
|
1504
|
-
}
|
|
1505
|
-
}
|
|
1903
|
+
async clearTable({ tableName }) {
|
|
1904
|
+
const parsedTableName = parseSqlIdentifier(tableName, "table name");
|
|
1506
1905
|
try {
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
const queryParams = [threadId];
|
|
1510
|
-
if (fromDate) {
|
|
1511
|
-
conditions.push(`"createdAt" >= ?`);
|
|
1512
|
-
queryParams.push(fromDate.toISOString());
|
|
1513
|
-
}
|
|
1514
|
-
if (toDate) {
|
|
1515
|
-
conditions.push(`"createdAt" <= ?`);
|
|
1516
|
-
queryParams.push(toDate.toISOString());
|
|
1517
|
-
}
|
|
1518
|
-
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
1519
|
-
const countResult = await this.client.execute({
|
|
1520
|
-
sql: `SELECT COUNT(*) as count FROM ${TABLE_MESSAGES} ${whereClause}`,
|
|
1521
|
-
args: queryParams
|
|
1522
|
-
});
|
|
1523
|
-
const total = Number(countResult.rows?.[0]?.count ?? 0);
|
|
1524
|
-
if (total === 0 && messages.length === 0) {
|
|
1525
|
-
return {
|
|
1526
|
-
messages: [],
|
|
1527
|
-
total: 0,
|
|
1528
|
-
page,
|
|
1529
|
-
perPage,
|
|
1530
|
-
hasMore: false
|
|
1531
|
-
};
|
|
1532
|
-
}
|
|
1533
|
-
const excludeIds = messages.map((m) => m.id);
|
|
1534
|
-
const excludeIdsParam = excludeIds.map((_, idx) => `$${idx + queryParams.length + 1}`).join(", ");
|
|
1535
|
-
const dataResult = await this.client.execute({
|
|
1536
|
-
sql: `SELECT id, content, role, type, "createdAt", "resourceId", "thread_id" FROM ${TABLE_MESSAGES} ${whereClause} ${excludeIds.length ? `AND id NOT IN (${excludeIdsParam})` : ""} ORDER BY "createdAt" DESC LIMIT ? OFFSET ?`,
|
|
1537
|
-
args: [...queryParams, ...excludeIds, perPage, currentOffset]
|
|
1538
|
-
});
|
|
1539
|
-
messages.push(...(dataResult.rows || []).map((row) => this.parseRow(row)));
|
|
1540
|
-
const messagesToReturn = format === "v1" ? new MessageList().add(messages, "memory").get.all.v1() : new MessageList().add(messages, "memory").get.all.v2();
|
|
1541
|
-
return {
|
|
1542
|
-
messages: messagesToReturn,
|
|
1543
|
-
total,
|
|
1544
|
-
page,
|
|
1545
|
-
perPage,
|
|
1546
|
-
hasMore: currentOffset + messages.length < total
|
|
1547
|
-
};
|
|
1548
|
-
} catch (error) {
|
|
1906
|
+
await this.client.execute(`DELETE FROM ${parsedTableName}`);
|
|
1907
|
+
} catch (e) {
|
|
1549
1908
|
const mastraError = new MastraError(
|
|
1550
1909
|
{
|
|
1551
|
-
id: "
|
|
1910
|
+
id: "LIBSQL_STORE_CLEAR_TABLE_FAILED",
|
|
1552
1911
|
domain: ErrorDomain.STORAGE,
|
|
1553
1912
|
category: ErrorCategory.THIRD_PARTY,
|
|
1554
|
-
details: {
|
|
1913
|
+
details: {
|
|
1914
|
+
tableName
|
|
1915
|
+
}
|
|
1555
1916
|
},
|
|
1556
|
-
|
|
1917
|
+
e
|
|
1557
1918
|
);
|
|
1558
1919
|
this.logger?.trackException?.(mastraError);
|
|
1559
1920
|
this.logger?.error?.(mastraError.toString());
|
|
1560
|
-
return { messages: [], total: 0, page, perPage, hasMore: false };
|
|
1561
1921
|
}
|
|
1562
1922
|
}
|
|
1563
|
-
async
|
|
1564
|
-
|
|
1565
|
-
format
|
|
1566
|
-
}) {
|
|
1567
|
-
if (messages.length === 0) return messages;
|
|
1923
|
+
async dropTable({ tableName }) {
|
|
1924
|
+
const parsedTableName = parseSqlIdentifier(tableName, "table name");
|
|
1568
1925
|
try {
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
}
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
time instanceof Date ? time.toISOString() : time,
|
|
1602
|
-
message.resourceId
|
|
1603
|
-
]
|
|
1604
|
-
};
|
|
1605
|
-
});
|
|
1606
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1607
|
-
batchStatements.push({
|
|
1608
|
-
sql: `UPDATE ${TABLE_THREADS} SET updatedAt = ? WHERE id = ?`,
|
|
1609
|
-
args: [now, threadId]
|
|
1926
|
+
await this.client.execute(`DROP TABLE IF EXISTS ${parsedTableName}`);
|
|
1927
|
+
} catch (e) {
|
|
1928
|
+
throw new MastraError(
|
|
1929
|
+
{
|
|
1930
|
+
id: "LIBSQL_STORE_DROP_TABLE_FAILED",
|
|
1931
|
+
domain: ErrorDomain.STORAGE,
|
|
1932
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1933
|
+
details: {
|
|
1934
|
+
tableName
|
|
1935
|
+
}
|
|
1936
|
+
},
|
|
1937
|
+
e
|
|
1938
|
+
);
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
};
|
|
1942
|
+
var ScoresLibSQL = class extends ScoresStorage {
|
|
1943
|
+
operations;
|
|
1944
|
+
client;
|
|
1945
|
+
constructor({ client, operations }) {
|
|
1946
|
+
super();
|
|
1947
|
+
this.operations = operations;
|
|
1948
|
+
this.client = client;
|
|
1949
|
+
}
|
|
1950
|
+
async getScoresByRunId({
|
|
1951
|
+
runId,
|
|
1952
|
+
pagination
|
|
1953
|
+
}) {
|
|
1954
|
+
try {
|
|
1955
|
+
const result = await this.client.execute({
|
|
1956
|
+
sql: `SELECT * FROM ${TABLE_SCORERS} WHERE runId = ? ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
|
|
1957
|
+
args: [runId, pagination.perPage + 1, pagination.page * pagination.perPage]
|
|
1610
1958
|
});
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1959
|
+
return {
|
|
1960
|
+
scores: result.rows?.slice(0, pagination.perPage).map((row) => this.transformScoreRow(row)) ?? [],
|
|
1961
|
+
pagination: {
|
|
1962
|
+
total: result.rows?.length ?? 0,
|
|
1963
|
+
page: pagination.page,
|
|
1964
|
+
perPage: pagination.perPage,
|
|
1965
|
+
hasMore: result.rows?.length > pagination.perPage
|
|
1966
|
+
}
|
|
1967
|
+
};
|
|
1615
1968
|
} catch (error) {
|
|
1616
1969
|
throw new MastraError(
|
|
1617
1970
|
{
|
|
1618
|
-
id: "
|
|
1971
|
+
id: "LIBSQL_STORE_GET_SCORES_BY_RUN_ID_FAILED",
|
|
1619
1972
|
domain: ErrorDomain.STORAGE,
|
|
1620
1973
|
category: ErrorCategory.THIRD_PARTY
|
|
1621
1974
|
},
|
|
@@ -1623,185 +1976,144 @@ var LibSQLStore = class extends MastraStorage {
|
|
|
1623
1976
|
);
|
|
1624
1977
|
}
|
|
1625
1978
|
}
|
|
1626
|
-
async
|
|
1627
|
-
|
|
1979
|
+
async getScoresByScorerId({
|
|
1980
|
+
scorerId,
|
|
1981
|
+
entityId,
|
|
1982
|
+
entityType,
|
|
1983
|
+
pagination
|
|
1628
1984
|
}) {
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
const existingResult = await this.client.execute({ sql: selectSql, args: messageIds });
|
|
1636
|
-
const existingMessages = existingResult.rows.map((row) => this.parseRow(row));
|
|
1637
|
-
if (existingMessages.length === 0) {
|
|
1638
|
-
return [];
|
|
1639
|
-
}
|
|
1640
|
-
const batchStatements = [];
|
|
1641
|
-
const threadIdsToUpdate = /* @__PURE__ */ new Set();
|
|
1642
|
-
const columnMapping = {
|
|
1643
|
-
threadId: "thread_id"
|
|
1644
|
-
};
|
|
1645
|
-
for (const existingMessage of existingMessages) {
|
|
1646
|
-
const updatePayload = messages.find((m) => m.id === existingMessage.id);
|
|
1647
|
-
if (!updatePayload) continue;
|
|
1648
|
-
const { id, ...fieldsToUpdate } = updatePayload;
|
|
1649
|
-
if (Object.keys(fieldsToUpdate).length === 0) continue;
|
|
1650
|
-
threadIdsToUpdate.add(existingMessage.threadId);
|
|
1651
|
-
if (updatePayload.threadId && updatePayload.threadId !== existingMessage.threadId) {
|
|
1652
|
-
threadIdsToUpdate.add(updatePayload.threadId);
|
|
1653
|
-
}
|
|
1654
|
-
const setClauses = [];
|
|
1655
|
-
const args = [];
|
|
1656
|
-
const updatableFields = { ...fieldsToUpdate };
|
|
1657
|
-
if (updatableFields.content) {
|
|
1658
|
-
const newContent = {
|
|
1659
|
-
...existingMessage.content,
|
|
1660
|
-
...updatableFields.content,
|
|
1661
|
-
// Deep merge metadata if it exists on both
|
|
1662
|
-
...existingMessage.content?.metadata && updatableFields.content.metadata ? {
|
|
1663
|
-
metadata: {
|
|
1664
|
-
...existingMessage.content.metadata,
|
|
1665
|
-
...updatableFields.content.metadata
|
|
1666
|
-
}
|
|
1667
|
-
} : {}
|
|
1668
|
-
};
|
|
1669
|
-
setClauses.push(`${parseSqlIdentifier("content", "column name")} = ?`);
|
|
1670
|
-
args.push(JSON.stringify(newContent));
|
|
1671
|
-
delete updatableFields.content;
|
|
1985
|
+
try {
|
|
1986
|
+
const conditions = [];
|
|
1987
|
+
const queryParams = [];
|
|
1988
|
+
if (scorerId) {
|
|
1989
|
+
conditions.push(`scorerId = ?`);
|
|
1990
|
+
queryParams.push(scorerId);
|
|
1672
1991
|
}
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
setClauses.push(`${parseSqlIdentifier(dbKey, "column name")} = ?`);
|
|
1677
|
-
let value = updatableFields[key];
|
|
1678
|
-
if (typeof value === "object" && value !== null) {
|
|
1679
|
-
value = JSON.stringify(value);
|
|
1680
|
-
}
|
|
1681
|
-
args.push(value);
|
|
1682
|
-
}
|
|
1992
|
+
if (entityId) {
|
|
1993
|
+
conditions.push(`entityId = ?`);
|
|
1994
|
+
queryParams.push(entityId);
|
|
1683
1995
|
}
|
|
1684
|
-
if (
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
batchStatements.push({ sql, args });
|
|
1688
|
-
}
|
|
1689
|
-
if (batchStatements.length === 0) {
|
|
1690
|
-
return existingMessages;
|
|
1691
|
-
}
|
|
1692
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1693
|
-
for (const threadId of threadIdsToUpdate) {
|
|
1694
|
-
if (threadId) {
|
|
1695
|
-
batchStatements.push({
|
|
1696
|
-
sql: `UPDATE ${TABLE_THREADS} SET updatedAt = ? WHERE id = ?`,
|
|
1697
|
-
args: [now, threadId]
|
|
1698
|
-
});
|
|
1996
|
+
if (entityType) {
|
|
1997
|
+
conditions.push(`entityType = ?`);
|
|
1998
|
+
queryParams.push(entityType);
|
|
1699
1999
|
}
|
|
2000
|
+
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
2001
|
+
const result = await this.client.execute({
|
|
2002
|
+
sql: `SELECT * FROM ${TABLE_SCORERS} ${whereClause} ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
|
|
2003
|
+
args: [...queryParams, pagination.perPage + 1, pagination.page * pagination.perPage]
|
|
2004
|
+
});
|
|
2005
|
+
return {
|
|
2006
|
+
scores: result.rows?.slice(0, pagination.perPage).map((row) => this.transformScoreRow(row)) ?? [],
|
|
2007
|
+
pagination: {
|
|
2008
|
+
total: result.rows?.length ?? 0,
|
|
2009
|
+
page: pagination.page,
|
|
2010
|
+
perPage: pagination.perPage,
|
|
2011
|
+
hasMore: result.rows?.length > pagination.perPage
|
|
2012
|
+
}
|
|
2013
|
+
};
|
|
2014
|
+
} catch (error) {
|
|
2015
|
+
throw new MastraError(
|
|
2016
|
+
{
|
|
2017
|
+
id: "LIBSQL_STORE_GET_SCORES_BY_SCORER_ID_FAILED",
|
|
2018
|
+
domain: ErrorDomain.STORAGE,
|
|
2019
|
+
category: ErrorCategory.THIRD_PARTY
|
|
2020
|
+
},
|
|
2021
|
+
error
|
|
2022
|
+
);
|
|
1700
2023
|
}
|
|
1701
|
-
await this.client.batch(batchStatements, "write");
|
|
1702
|
-
const updatedResult = await this.client.execute({ sql: selectSql, args: messageIds });
|
|
1703
|
-
return updatedResult.rows.map((row) => this.parseRow(row));
|
|
1704
2024
|
}
|
|
1705
|
-
|
|
1706
|
-
const
|
|
1707
|
-
const
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
2025
|
+
transformScoreRow(row) {
|
|
2026
|
+
const scorerValue = JSON.parse(row.scorer ?? "{}");
|
|
2027
|
+
const inputValue = JSON.parse(row.input ?? "{}");
|
|
2028
|
+
const outputValue = JSON.parse(row.output ?? "{}");
|
|
2029
|
+
const additionalLLMContextValue = row.additionalLLMContext ? JSON.parse(row.additionalLLMContext) : null;
|
|
2030
|
+
const runtimeContextValue = row.runtimeContext ? JSON.parse(row.runtimeContext) : null;
|
|
2031
|
+
const metadataValue = row.metadata ? JSON.parse(row.metadata) : null;
|
|
2032
|
+
const entityValue = row.entity ? JSON.parse(row.entity) : null;
|
|
2033
|
+
const extractStepResultValue = row.extractStepResult ? JSON.parse(row.extractStepResult) : null;
|
|
2034
|
+
const analyzeStepResultValue = row.analyzeStepResult ? JSON.parse(row.analyzeStepResult) : null;
|
|
1711
2035
|
return {
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
2036
|
+
id: row.id,
|
|
2037
|
+
traceId: row.traceId,
|
|
2038
|
+
runId: row.runId,
|
|
2039
|
+
scorer: scorerValue,
|
|
2040
|
+
score: row.score,
|
|
2041
|
+
reason: row.reason,
|
|
2042
|
+
extractStepResult: extractStepResultValue,
|
|
2043
|
+
analyzeStepResult: analyzeStepResultValue,
|
|
2044
|
+
analyzePrompt: row.analyzePrompt,
|
|
2045
|
+
extractPrompt: row.extractPrompt,
|
|
2046
|
+
metadata: metadataValue,
|
|
2047
|
+
input: inputValue,
|
|
2048
|
+
output: outputValue,
|
|
2049
|
+
additionalContext: additionalLLMContextValue,
|
|
2050
|
+
runtimeContext: runtimeContextValue,
|
|
2051
|
+
entityType: row.entityType,
|
|
2052
|
+
entity: entityValue,
|
|
2053
|
+
entityId: row.entityId,
|
|
2054
|
+
scorerId: row.scorerId,
|
|
2055
|
+
source: row.source,
|
|
2056
|
+
resourceId: row.resourceId,
|
|
2057
|
+
threadId: row.threadId,
|
|
2058
|
+
createdAt: row.createdAt,
|
|
2059
|
+
updatedAt: row.updatedAt
|
|
1722
2060
|
};
|
|
1723
2061
|
}
|
|
1724
|
-
|
|
1725
|
-
|
|
2062
|
+
async getScoreById({ id }) {
|
|
2063
|
+
const result = await this.client.execute({
|
|
2064
|
+
sql: `SELECT * FROM ${TABLE_SCORERS} WHERE id = ?`,
|
|
2065
|
+
args: [id]
|
|
2066
|
+
});
|
|
2067
|
+
return result.rows?.[0] ? this.transformScoreRow(result.rows[0]) : null;
|
|
2068
|
+
}
|
|
2069
|
+
async saveScore(score) {
|
|
1726
2070
|
try {
|
|
1727
|
-
const
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
2071
|
+
const id = crypto.randomUUID();
|
|
2072
|
+
await this.operations.insert({
|
|
2073
|
+
tableName: TABLE_SCORERS,
|
|
2074
|
+
record: {
|
|
2075
|
+
id,
|
|
2076
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2077
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2078
|
+
...score
|
|
2079
|
+
}
|
|
1732
2080
|
});
|
|
1733
|
-
|
|
2081
|
+
const scoreFromDb = await this.getScoreById({ id });
|
|
2082
|
+
return { score: scoreFromDb };
|
|
1734
2083
|
} catch (error) {
|
|
1735
|
-
if (error instanceof Error && error.message.includes("no such table")) {
|
|
1736
|
-
return [];
|
|
1737
|
-
}
|
|
1738
2084
|
throw new MastraError(
|
|
1739
2085
|
{
|
|
1740
|
-
id: "
|
|
2086
|
+
id: "LIBSQL_STORE_SAVE_SCORE_FAILED",
|
|
1741
2087
|
domain: ErrorDomain.STORAGE,
|
|
1742
|
-
category: ErrorCategory.THIRD_PARTY
|
|
1743
|
-
details: { agentName }
|
|
2088
|
+
category: ErrorCategory.THIRD_PARTY
|
|
1744
2089
|
},
|
|
1745
2090
|
error
|
|
1746
2091
|
);
|
|
1747
2092
|
}
|
|
1748
2093
|
}
|
|
1749
|
-
async
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
const queryParams = [];
|
|
1755
|
-
if (agentName) {
|
|
1756
|
-
conditions.push(`agent_name = ?`);
|
|
1757
|
-
queryParams.push(agentName);
|
|
1758
|
-
}
|
|
1759
|
-
if (type === "test") {
|
|
1760
|
-
conditions.push(`(test_info IS NOT NULL AND json_extract(test_info, '$.testPath') IS NOT NULL)`);
|
|
1761
|
-
} else if (type === "live") {
|
|
1762
|
-
conditions.push(`(test_info IS NULL OR json_extract(test_info, '$.testPath') IS NULL)`);
|
|
1763
|
-
}
|
|
1764
|
-
if (fromDate) {
|
|
1765
|
-
conditions.push(`created_at >= ?`);
|
|
1766
|
-
queryParams.push(fromDate.toISOString());
|
|
1767
|
-
}
|
|
1768
|
-
if (toDate) {
|
|
1769
|
-
conditions.push(`created_at <= ?`);
|
|
1770
|
-
queryParams.push(toDate.toISOString());
|
|
1771
|
-
}
|
|
1772
|
-
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
2094
|
+
async getScoresByEntityId({
|
|
2095
|
+
entityId,
|
|
2096
|
+
entityType,
|
|
2097
|
+
pagination
|
|
2098
|
+
}) {
|
|
1773
2099
|
try {
|
|
1774
|
-
const
|
|
1775
|
-
sql: `SELECT
|
|
1776
|
-
args:
|
|
1777
|
-
});
|
|
1778
|
-
const total = Number(countResult.rows?.[0]?.count ?? 0);
|
|
1779
|
-
const currentOffset = page * perPage;
|
|
1780
|
-
const hasMore = currentOffset + perPage < total;
|
|
1781
|
-
if (total === 0) {
|
|
1782
|
-
return {
|
|
1783
|
-
evals: [],
|
|
1784
|
-
total: 0,
|
|
1785
|
-
page,
|
|
1786
|
-
perPage,
|
|
1787
|
-
hasMore: false
|
|
1788
|
-
};
|
|
1789
|
-
}
|
|
1790
|
-
const dataResult = await this.client.execute({
|
|
1791
|
-
sql: `SELECT * FROM ${TABLE_EVALS} ${whereClause} ORDER BY created_at DESC LIMIT ? OFFSET ?`,
|
|
1792
|
-
args: [...queryParams, perPage, currentOffset]
|
|
2100
|
+
const result = await this.client.execute({
|
|
2101
|
+
sql: `SELECT * FROM ${TABLE_SCORERS} WHERE entityId = ? AND entityType = ? ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
|
|
2102
|
+
args: [entityId, entityType, pagination.perPage + 1, pagination.page * pagination.perPage]
|
|
1793
2103
|
});
|
|
1794
2104
|
return {
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
2105
|
+
scores: result.rows?.slice(0, pagination.perPage).map((row) => this.transformScoreRow(row)) ?? [],
|
|
2106
|
+
pagination: {
|
|
2107
|
+
total: result.rows?.length ?? 0,
|
|
2108
|
+
page: pagination.page,
|
|
2109
|
+
perPage: pagination.perPage,
|
|
2110
|
+
hasMore: result.rows?.length > pagination.perPage
|
|
2111
|
+
}
|
|
1800
2112
|
};
|
|
1801
2113
|
} catch (error) {
|
|
1802
2114
|
throw new MastraError(
|
|
1803
2115
|
{
|
|
1804
|
-
id: "
|
|
2116
|
+
id: "LIBSQL_STORE_GET_SCORES_BY_ENTITY_ID_FAILED",
|
|
1805
2117
|
domain: ErrorDomain.STORAGE,
|
|
1806
2118
|
category: ErrorCategory.THIRD_PARTY
|
|
1807
2119
|
},
|
|
@@ -1809,9 +2121,15 @@ var LibSQLStore = class extends MastraStorage {
|
|
|
1809
2121
|
);
|
|
1810
2122
|
}
|
|
1811
2123
|
}
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
2124
|
+
};
|
|
2125
|
+
var TracesLibSQL = class extends TracesStorage {
|
|
2126
|
+
client;
|
|
2127
|
+
operations;
|
|
2128
|
+
constructor({ client, operations }) {
|
|
2129
|
+
super();
|
|
2130
|
+
this.client = client;
|
|
2131
|
+
this.operations = operations;
|
|
2132
|
+
}
|
|
1815
2133
|
async getTraces(args) {
|
|
1816
2134
|
if (args.fromDate || args.toDate) {
|
|
1817
2135
|
args.dateRange = {
|
|
@@ -1888,35 +2206,133 @@ var LibSQLStore = class extends MastraStorage {
|
|
|
1888
2206
|
sql: `SELECT * FROM ${TABLE_TRACES} ${whereClause} ORDER BY "startTime" DESC LIMIT ? OFFSET ?`,
|
|
1889
2207
|
args: [...queryArgs, perPage, currentOffset]
|
|
1890
2208
|
});
|
|
1891
|
-
const traces = dataResult.rows?.map(
|
|
1892
|
-
(row) => ({
|
|
1893
|
-
id: row.id,
|
|
1894
|
-
parentSpanId: row.parentSpanId,
|
|
1895
|
-
traceId: row.traceId,
|
|
1896
|
-
name: row.name,
|
|
1897
|
-
scope: row.scope,
|
|
1898
|
-
kind: row.kind,
|
|
1899
|
-
status: safelyParseJSON(row.status),
|
|
1900
|
-
events: safelyParseJSON(row.events),
|
|
1901
|
-
links: safelyParseJSON(row.links),
|
|
1902
|
-
attributes: safelyParseJSON(row.attributes),
|
|
1903
|
-
startTime: row.startTime,
|
|
1904
|
-
endTime: row.endTime,
|
|
1905
|
-
other: safelyParseJSON(row.other),
|
|
1906
|
-
createdAt: row.createdAt
|
|
1907
|
-
})
|
|
1908
|
-
) ?? [];
|
|
1909
|
-
return {
|
|
1910
|
-
traces,
|
|
1911
|
-
total,
|
|
1912
|
-
page,
|
|
1913
|
-
perPage,
|
|
1914
|
-
hasMore: currentOffset + traces.length < total
|
|
1915
|
-
};
|
|
2209
|
+
const traces = dataResult.rows?.map(
|
|
2210
|
+
(row) => ({
|
|
2211
|
+
id: row.id,
|
|
2212
|
+
parentSpanId: row.parentSpanId,
|
|
2213
|
+
traceId: row.traceId,
|
|
2214
|
+
name: row.name,
|
|
2215
|
+
scope: row.scope,
|
|
2216
|
+
kind: row.kind,
|
|
2217
|
+
status: safelyParseJSON(row.status),
|
|
2218
|
+
events: safelyParseJSON(row.events),
|
|
2219
|
+
links: safelyParseJSON(row.links),
|
|
2220
|
+
attributes: safelyParseJSON(row.attributes),
|
|
2221
|
+
startTime: row.startTime,
|
|
2222
|
+
endTime: row.endTime,
|
|
2223
|
+
other: safelyParseJSON(row.other),
|
|
2224
|
+
createdAt: row.createdAt
|
|
2225
|
+
})
|
|
2226
|
+
) ?? [];
|
|
2227
|
+
return {
|
|
2228
|
+
traces,
|
|
2229
|
+
total,
|
|
2230
|
+
page,
|
|
2231
|
+
perPage,
|
|
2232
|
+
hasMore: currentOffset + traces.length < total
|
|
2233
|
+
};
|
|
2234
|
+
} catch (error) {
|
|
2235
|
+
throw new MastraError(
|
|
2236
|
+
{
|
|
2237
|
+
id: "LIBSQL_STORE_GET_TRACES_PAGINATED_FAILED",
|
|
2238
|
+
domain: ErrorDomain.STORAGE,
|
|
2239
|
+
category: ErrorCategory.THIRD_PARTY
|
|
2240
|
+
},
|
|
2241
|
+
error
|
|
2242
|
+
);
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
async batchTraceInsert({ records }) {
|
|
2246
|
+
this.logger.debug("Batch inserting traces", { count: records.length });
|
|
2247
|
+
await this.operations.batchInsert({
|
|
2248
|
+
tableName: TABLE_TRACES,
|
|
2249
|
+
records
|
|
2250
|
+
});
|
|
2251
|
+
}
|
|
2252
|
+
};
|
|
2253
|
+
function parseWorkflowRun(row) {
|
|
2254
|
+
let parsedSnapshot = row.snapshot;
|
|
2255
|
+
if (typeof parsedSnapshot === "string") {
|
|
2256
|
+
try {
|
|
2257
|
+
parsedSnapshot = JSON.parse(row.snapshot);
|
|
2258
|
+
} catch (e) {
|
|
2259
|
+
console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
|
|
2260
|
+
}
|
|
2261
|
+
}
|
|
2262
|
+
return {
|
|
2263
|
+
workflowName: row.workflow_name,
|
|
2264
|
+
runId: row.run_id,
|
|
2265
|
+
snapshot: parsedSnapshot,
|
|
2266
|
+
resourceId: row.resourceId,
|
|
2267
|
+
createdAt: new Date(row.createdAt),
|
|
2268
|
+
updatedAt: new Date(row.updatedAt)
|
|
2269
|
+
};
|
|
2270
|
+
}
|
|
2271
|
+
var WorkflowsLibSQL = class extends WorkflowsStorage {
|
|
2272
|
+
operations;
|
|
2273
|
+
client;
|
|
2274
|
+
constructor({ operations, client }) {
|
|
2275
|
+
super();
|
|
2276
|
+
this.operations = operations;
|
|
2277
|
+
this.client = client;
|
|
2278
|
+
}
|
|
2279
|
+
async persistWorkflowSnapshot({
|
|
2280
|
+
workflowName,
|
|
2281
|
+
runId,
|
|
2282
|
+
snapshot
|
|
2283
|
+
}) {
|
|
2284
|
+
const data = {
|
|
2285
|
+
workflow_name: workflowName,
|
|
2286
|
+
run_id: runId,
|
|
2287
|
+
snapshot,
|
|
2288
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
2289
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
2290
|
+
};
|
|
2291
|
+
this.logger.debug("Persisting workflow snapshot", { workflowName, runId, data });
|
|
2292
|
+
await this.operations.insert({
|
|
2293
|
+
tableName: TABLE_WORKFLOW_SNAPSHOT,
|
|
2294
|
+
record: data
|
|
2295
|
+
});
|
|
2296
|
+
}
|
|
2297
|
+
async loadWorkflowSnapshot({
|
|
2298
|
+
workflowName,
|
|
2299
|
+
runId
|
|
2300
|
+
}) {
|
|
2301
|
+
this.logger.debug("Loading workflow snapshot", { workflowName, runId });
|
|
2302
|
+
const d = await this.operations.load({
|
|
2303
|
+
tableName: TABLE_WORKFLOW_SNAPSHOT,
|
|
2304
|
+
keys: { workflow_name: workflowName, run_id: runId }
|
|
2305
|
+
});
|
|
2306
|
+
return d ? d.snapshot : null;
|
|
2307
|
+
}
|
|
2308
|
+
async getWorkflowRunById({
|
|
2309
|
+
runId,
|
|
2310
|
+
workflowName
|
|
2311
|
+
}) {
|
|
2312
|
+
const conditions = [];
|
|
2313
|
+
const args = [];
|
|
2314
|
+
if (runId) {
|
|
2315
|
+
conditions.push("run_id = ?");
|
|
2316
|
+
args.push(runId);
|
|
2317
|
+
}
|
|
2318
|
+
if (workflowName) {
|
|
2319
|
+
conditions.push("workflow_name = ?");
|
|
2320
|
+
args.push(workflowName);
|
|
2321
|
+
}
|
|
2322
|
+
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
2323
|
+
try {
|
|
2324
|
+
const result = await this.client.execute({
|
|
2325
|
+
sql: `SELECT * FROM ${TABLE_WORKFLOW_SNAPSHOT} ${whereClause}`,
|
|
2326
|
+
args
|
|
2327
|
+
});
|
|
2328
|
+
if (!result.rows?.[0]) {
|
|
2329
|
+
return null;
|
|
2330
|
+
}
|
|
2331
|
+
return parseWorkflowRun(result.rows[0]);
|
|
1916
2332
|
} catch (error) {
|
|
1917
2333
|
throw new MastraError(
|
|
1918
2334
|
{
|
|
1919
|
-
id: "
|
|
2335
|
+
id: "LIBSQL_STORE_GET_WORKFLOW_RUN_BY_ID_FAILED",
|
|
1920
2336
|
domain: ErrorDomain.STORAGE,
|
|
1921
2337
|
category: ErrorCategory.THIRD_PARTY
|
|
1922
2338
|
},
|
|
@@ -1948,7 +2364,7 @@ var LibSQLStore = class extends MastraStorage {
|
|
|
1948
2364
|
args.push(toDate.toISOString());
|
|
1949
2365
|
}
|
|
1950
2366
|
if (resourceId) {
|
|
1951
|
-
const hasResourceId = await this.hasColumn(TABLE_WORKFLOW_SNAPSHOT, "resourceId");
|
|
2367
|
+
const hasResourceId = await this.operations.hasColumn(TABLE_WORKFLOW_SNAPSHOT, "resourceId");
|
|
1952
2368
|
if (hasResourceId) {
|
|
1953
2369
|
conditions.push("resourceId = ?");
|
|
1954
2370
|
args.push(resourceId);
|
|
@@ -1969,7 +2385,7 @@ var LibSQLStore = class extends MastraStorage {
|
|
|
1969
2385
|
sql: `SELECT * FROM ${TABLE_WORKFLOW_SNAPSHOT} ${whereClause} ORDER BY createdAt DESC${limit !== void 0 && offset !== void 0 ? ` LIMIT ? OFFSET ?` : ""}`,
|
|
1970
2386
|
args: limit !== void 0 && offset !== void 0 ? [...args, limit, offset] : args
|
|
1971
2387
|
});
|
|
1972
|
-
const runs = (result.rows || []).map((row) =>
|
|
2388
|
+
const runs = (result.rows || []).map((row) => parseWorkflowRun(row));
|
|
1973
2389
|
return { runs, total: total || runs.length };
|
|
1974
2390
|
} catch (error) {
|
|
1975
2391
|
throw new MastraError(
|
|
@@ -1982,133 +2398,227 @@ var LibSQLStore = class extends MastraStorage {
|
|
|
1982
2398
|
);
|
|
1983
2399
|
}
|
|
1984
2400
|
}
|
|
2401
|
+
};
|
|
2402
|
+
|
|
2403
|
+
// src/storage/index.ts
|
|
2404
|
+
var LibSQLStore = class extends MastraStorage {
|
|
2405
|
+
client;
|
|
2406
|
+
maxRetries;
|
|
2407
|
+
initialBackoffMs;
|
|
2408
|
+
stores;
|
|
2409
|
+
constructor(config) {
|
|
2410
|
+
super({ name: `LibSQLStore` });
|
|
2411
|
+
this.maxRetries = config.maxRetries ?? 5;
|
|
2412
|
+
this.initialBackoffMs = config.initialBackoffMs ?? 100;
|
|
2413
|
+
if ("url" in config) {
|
|
2414
|
+
if (config.url.endsWith(":memory:")) {
|
|
2415
|
+
this.shouldCacheInit = false;
|
|
2416
|
+
}
|
|
2417
|
+
this.client = createClient({ url: config.url });
|
|
2418
|
+
if (config.url.startsWith("file:") || config.url.includes(":memory:")) {
|
|
2419
|
+
this.client.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));
|
|
2420
|
+
this.client.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.", err));
|
|
2421
|
+
}
|
|
2422
|
+
} else {
|
|
2423
|
+
this.client = config.client;
|
|
2424
|
+
}
|
|
2425
|
+
const operations = new StoreOperationsLibSQL({
|
|
2426
|
+
client: this.client,
|
|
2427
|
+
maxRetries: this.maxRetries,
|
|
2428
|
+
initialBackoffMs: this.initialBackoffMs
|
|
2429
|
+
});
|
|
2430
|
+
const scores = new ScoresLibSQL({ client: this.client, operations });
|
|
2431
|
+
const traces = new TracesLibSQL({ client: this.client, operations });
|
|
2432
|
+
const workflows = new WorkflowsLibSQL({ client: this.client, operations });
|
|
2433
|
+
const memory = new MemoryLibSQL({ client: this.client, operations });
|
|
2434
|
+
const legacyEvals = new LegacyEvalsLibSQL({ client: this.client });
|
|
2435
|
+
this.stores = {
|
|
2436
|
+
operations,
|
|
2437
|
+
scores,
|
|
2438
|
+
traces,
|
|
2439
|
+
workflows,
|
|
2440
|
+
memory,
|
|
2441
|
+
legacyEvals
|
|
2442
|
+
};
|
|
2443
|
+
}
|
|
2444
|
+
get supports() {
|
|
2445
|
+
return {
|
|
2446
|
+
selectByIncludeResourceScope: true,
|
|
2447
|
+
resourceWorkingMemory: true,
|
|
2448
|
+
hasColumn: true,
|
|
2449
|
+
createTable: true
|
|
2450
|
+
};
|
|
2451
|
+
}
|
|
2452
|
+
async createTable({
|
|
2453
|
+
tableName,
|
|
2454
|
+
schema
|
|
2455
|
+
}) {
|
|
2456
|
+
await this.stores.operations.createTable({ tableName, schema });
|
|
2457
|
+
}
|
|
2458
|
+
/**
|
|
2459
|
+
* Alters table schema to add columns if they don't exist
|
|
2460
|
+
* @param tableName Name of the table
|
|
2461
|
+
* @param schema Schema of the table
|
|
2462
|
+
* @param ifNotExists Array of column names to add if they don't exist
|
|
2463
|
+
*/
|
|
2464
|
+
async alterTable({
|
|
2465
|
+
tableName,
|
|
2466
|
+
schema,
|
|
2467
|
+
ifNotExists
|
|
2468
|
+
}) {
|
|
2469
|
+
await this.stores.operations.alterTable({ tableName, schema, ifNotExists });
|
|
2470
|
+
}
|
|
2471
|
+
async clearTable({ tableName }) {
|
|
2472
|
+
await this.stores.operations.clearTable({ tableName });
|
|
2473
|
+
}
|
|
2474
|
+
async dropTable({ tableName }) {
|
|
2475
|
+
await this.stores.operations.dropTable({ tableName });
|
|
2476
|
+
}
|
|
2477
|
+
insert(args) {
|
|
2478
|
+
return this.stores.operations.insert(args);
|
|
2479
|
+
}
|
|
2480
|
+
batchInsert(args) {
|
|
2481
|
+
return this.stores.operations.batchInsert(args);
|
|
2482
|
+
}
|
|
2483
|
+
async load({ tableName, keys }) {
|
|
2484
|
+
return this.stores.operations.load({ tableName, keys });
|
|
2485
|
+
}
|
|
2486
|
+
async getThreadById({ threadId }) {
|
|
2487
|
+
return this.stores.memory.getThreadById({ threadId });
|
|
2488
|
+
}
|
|
2489
|
+
/**
|
|
2490
|
+
* @deprecated use getThreadsByResourceIdPaginated instead for paginated results.
|
|
2491
|
+
*/
|
|
2492
|
+
async getThreadsByResourceId(args) {
|
|
2493
|
+
return this.stores.memory.getThreadsByResourceId(args);
|
|
2494
|
+
}
|
|
2495
|
+
async getThreadsByResourceIdPaginated(args) {
|
|
2496
|
+
return this.stores.memory.getThreadsByResourceIdPaginated(args);
|
|
2497
|
+
}
|
|
2498
|
+
async saveThread({ thread }) {
|
|
2499
|
+
return this.stores.memory.saveThread({ thread });
|
|
2500
|
+
}
|
|
2501
|
+
async updateThread({
|
|
2502
|
+
id,
|
|
2503
|
+
title,
|
|
2504
|
+
metadata
|
|
2505
|
+
}) {
|
|
2506
|
+
return this.stores.memory.updateThread({ id, title, metadata });
|
|
2507
|
+
}
|
|
2508
|
+
async deleteThread({ threadId }) {
|
|
2509
|
+
return this.stores.memory.deleteThread({ threadId });
|
|
2510
|
+
}
|
|
2511
|
+
async getMessages({
|
|
2512
|
+
threadId,
|
|
2513
|
+
selectBy,
|
|
2514
|
+
format
|
|
2515
|
+
}) {
|
|
2516
|
+
return this.stores.memory.getMessages({ threadId, selectBy, format });
|
|
2517
|
+
}
|
|
2518
|
+
async getMessagesPaginated(args) {
|
|
2519
|
+
return this.stores.memory.getMessagesPaginated(args);
|
|
2520
|
+
}
|
|
2521
|
+
async saveMessages(args) {
|
|
2522
|
+
return this.stores.memory.saveMessages(args);
|
|
2523
|
+
}
|
|
2524
|
+
async updateMessages({
|
|
2525
|
+
messages
|
|
2526
|
+
}) {
|
|
2527
|
+
return this.stores.memory.updateMessages({ messages });
|
|
2528
|
+
}
|
|
2529
|
+
/** @deprecated use getEvals instead */
|
|
2530
|
+
async getEvalsByAgentName(agentName, type) {
|
|
2531
|
+
return this.stores.legacyEvals.getEvalsByAgentName(agentName, type);
|
|
2532
|
+
}
|
|
2533
|
+
async getEvals(options = {}) {
|
|
2534
|
+
return this.stores.legacyEvals.getEvals(options);
|
|
2535
|
+
}
|
|
2536
|
+
async getScoreById({ id }) {
|
|
2537
|
+
return this.stores.scores.getScoreById({ id });
|
|
2538
|
+
}
|
|
2539
|
+
async saveScore(score) {
|
|
2540
|
+
return this.stores.scores.saveScore(score);
|
|
2541
|
+
}
|
|
2542
|
+
async getScoresByScorerId({
|
|
2543
|
+
scorerId,
|
|
2544
|
+
entityId,
|
|
2545
|
+
entityType,
|
|
2546
|
+
pagination
|
|
2547
|
+
}) {
|
|
2548
|
+
return this.stores.scores.getScoresByScorerId({ scorerId, entityId, entityType, pagination });
|
|
2549
|
+
}
|
|
2550
|
+
async getScoresByRunId({
|
|
2551
|
+
runId,
|
|
2552
|
+
pagination
|
|
2553
|
+
}) {
|
|
2554
|
+
return this.stores.scores.getScoresByRunId({ runId, pagination });
|
|
2555
|
+
}
|
|
2556
|
+
async getScoresByEntityId({
|
|
2557
|
+
entityId,
|
|
2558
|
+
entityType,
|
|
2559
|
+
pagination
|
|
2560
|
+
}) {
|
|
2561
|
+
return this.stores.scores.getScoresByEntityId({ entityId, entityType, pagination });
|
|
2562
|
+
}
|
|
2563
|
+
/**
|
|
2564
|
+
* TRACES
|
|
2565
|
+
*/
|
|
2566
|
+
/**
|
|
2567
|
+
* @deprecated use getTracesPaginated instead.
|
|
2568
|
+
*/
|
|
2569
|
+
async getTraces(args) {
|
|
2570
|
+
return this.stores.traces.getTraces(args);
|
|
2571
|
+
}
|
|
2572
|
+
async getTracesPaginated(args) {
|
|
2573
|
+
return this.stores.traces.getTracesPaginated(args);
|
|
2574
|
+
}
|
|
2575
|
+
async batchTraceInsert(args) {
|
|
2576
|
+
return this.stores.traces.batchTraceInsert(args);
|
|
2577
|
+
}
|
|
2578
|
+
/**
|
|
2579
|
+
* WORKFLOWS
|
|
2580
|
+
*/
|
|
2581
|
+
async persistWorkflowSnapshot({
|
|
2582
|
+
workflowName,
|
|
2583
|
+
runId,
|
|
2584
|
+
snapshot
|
|
2585
|
+
}) {
|
|
2586
|
+
return this.stores.workflows.persistWorkflowSnapshot({ workflowName, runId, snapshot });
|
|
2587
|
+
}
|
|
2588
|
+
async loadWorkflowSnapshot({
|
|
2589
|
+
workflowName,
|
|
2590
|
+
runId
|
|
2591
|
+
}) {
|
|
2592
|
+
return this.stores.workflows.loadWorkflowSnapshot({ workflowName, runId });
|
|
2593
|
+
}
|
|
2594
|
+
async getWorkflowRuns({
|
|
2595
|
+
workflowName,
|
|
2596
|
+
fromDate,
|
|
2597
|
+
toDate,
|
|
2598
|
+
limit,
|
|
2599
|
+
offset,
|
|
2600
|
+
resourceId
|
|
2601
|
+
} = {}) {
|
|
2602
|
+
return this.stores.workflows.getWorkflowRuns({ workflowName, fromDate, toDate, limit, offset, resourceId });
|
|
2603
|
+
}
|
|
1985
2604
|
async getWorkflowRunById({
|
|
1986
2605
|
runId,
|
|
1987
2606
|
workflowName
|
|
1988
2607
|
}) {
|
|
1989
|
-
|
|
1990
|
-
const args = [];
|
|
1991
|
-
if (runId) {
|
|
1992
|
-
conditions.push("run_id = ?");
|
|
1993
|
-
args.push(runId);
|
|
1994
|
-
}
|
|
1995
|
-
if (workflowName) {
|
|
1996
|
-
conditions.push("workflow_name = ?");
|
|
1997
|
-
args.push(workflowName);
|
|
1998
|
-
}
|
|
1999
|
-
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
2000
|
-
try {
|
|
2001
|
-
const result = await this.client.execute({
|
|
2002
|
-
sql: `SELECT * FROM ${TABLE_WORKFLOW_SNAPSHOT} ${whereClause}`,
|
|
2003
|
-
args
|
|
2004
|
-
});
|
|
2005
|
-
if (!result.rows?.[0]) {
|
|
2006
|
-
return null;
|
|
2007
|
-
}
|
|
2008
|
-
return this.parseWorkflowRun(result.rows[0]);
|
|
2009
|
-
} catch (error) {
|
|
2010
|
-
throw new MastraError(
|
|
2011
|
-
{
|
|
2012
|
-
id: "LIBSQL_STORE_GET_WORKFLOW_RUN_BY_ID_FAILED",
|
|
2013
|
-
domain: ErrorDomain.STORAGE,
|
|
2014
|
-
category: ErrorCategory.THIRD_PARTY
|
|
2015
|
-
},
|
|
2016
|
-
error
|
|
2017
|
-
);
|
|
2018
|
-
}
|
|
2608
|
+
return this.stores.workflows.getWorkflowRunById({ runId, workflowName });
|
|
2019
2609
|
}
|
|
2020
2610
|
async getResourceById({ resourceId }) {
|
|
2021
|
-
|
|
2022
|
-
tableName: TABLE_RESOURCES,
|
|
2023
|
-
keys: { id: resourceId }
|
|
2024
|
-
});
|
|
2025
|
-
if (!result) {
|
|
2026
|
-
return null;
|
|
2027
|
-
}
|
|
2028
|
-
return {
|
|
2029
|
-
...result,
|
|
2030
|
-
// Ensure workingMemory is always returned as a string, even if auto-parsed as JSON
|
|
2031
|
-
workingMemory: typeof result.workingMemory === "object" ? JSON.stringify(result.workingMemory) : result.workingMemory,
|
|
2032
|
-
metadata: typeof result.metadata === "string" ? JSON.parse(result.metadata) : result.metadata
|
|
2033
|
-
};
|
|
2611
|
+
return this.stores.memory.getResourceById({ resourceId });
|
|
2034
2612
|
}
|
|
2035
2613
|
async saveResource({ resource }) {
|
|
2036
|
-
|
|
2037
|
-
tableName: TABLE_RESOURCES,
|
|
2038
|
-
record: {
|
|
2039
|
-
...resource,
|
|
2040
|
-
metadata: JSON.stringify(resource.metadata)
|
|
2041
|
-
}
|
|
2042
|
-
});
|
|
2043
|
-
return resource;
|
|
2614
|
+
return this.stores.memory.saveResource({ resource });
|
|
2044
2615
|
}
|
|
2045
2616
|
async updateResource({
|
|
2046
2617
|
resourceId,
|
|
2047
2618
|
workingMemory,
|
|
2048
2619
|
metadata
|
|
2049
2620
|
}) {
|
|
2050
|
-
|
|
2051
|
-
if (!existingResource) {
|
|
2052
|
-
const newResource = {
|
|
2053
|
-
id: resourceId,
|
|
2054
|
-
workingMemory,
|
|
2055
|
-
metadata: metadata || {},
|
|
2056
|
-
createdAt: /* @__PURE__ */ new Date(),
|
|
2057
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
2058
|
-
};
|
|
2059
|
-
return this.saveResource({ resource: newResource });
|
|
2060
|
-
}
|
|
2061
|
-
const updatedResource = {
|
|
2062
|
-
...existingResource,
|
|
2063
|
-
workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
|
|
2064
|
-
metadata: {
|
|
2065
|
-
...existingResource.metadata,
|
|
2066
|
-
...metadata
|
|
2067
|
-
},
|
|
2068
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
2069
|
-
};
|
|
2070
|
-
const updates = [];
|
|
2071
|
-
const values = [];
|
|
2072
|
-
if (workingMemory !== void 0) {
|
|
2073
|
-
updates.push("workingMemory = ?");
|
|
2074
|
-
values.push(workingMemory);
|
|
2075
|
-
}
|
|
2076
|
-
if (metadata) {
|
|
2077
|
-
updates.push("metadata = ?");
|
|
2078
|
-
values.push(JSON.stringify(updatedResource.metadata));
|
|
2079
|
-
}
|
|
2080
|
-
updates.push("updatedAt = ?");
|
|
2081
|
-
values.push(updatedResource.updatedAt.toISOString());
|
|
2082
|
-
values.push(resourceId);
|
|
2083
|
-
await this.client.execute({
|
|
2084
|
-
sql: `UPDATE ${TABLE_RESOURCES} SET ${updates.join(", ")} WHERE id = ?`,
|
|
2085
|
-
args: values
|
|
2086
|
-
});
|
|
2087
|
-
return updatedResource;
|
|
2088
|
-
}
|
|
2089
|
-
async hasColumn(table, column) {
|
|
2090
|
-
const result = await this.client.execute({
|
|
2091
|
-
sql: `PRAGMA table_info(${table})`
|
|
2092
|
-
});
|
|
2093
|
-
return (await result.rows)?.some((row) => row.name === column);
|
|
2094
|
-
}
|
|
2095
|
-
parseWorkflowRun(row) {
|
|
2096
|
-
let parsedSnapshot = row.snapshot;
|
|
2097
|
-
if (typeof parsedSnapshot === "string") {
|
|
2098
|
-
try {
|
|
2099
|
-
parsedSnapshot = JSON.parse(row.snapshot);
|
|
2100
|
-
} catch (e) {
|
|
2101
|
-
console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
|
|
2102
|
-
}
|
|
2103
|
-
}
|
|
2104
|
-
return {
|
|
2105
|
-
workflowName: row.workflow_name,
|
|
2106
|
-
runId: row.run_id,
|
|
2107
|
-
snapshot: parsedSnapshot,
|
|
2108
|
-
resourceId: row.resourceId,
|
|
2109
|
-
createdAt: new Date(row.createdAt),
|
|
2110
|
-
updatedAt: new Date(row.updatedAt)
|
|
2111
|
-
};
|
|
2621
|
+
return this.stores.memory.updateResource({ resourceId, workingMemory, metadata });
|
|
2112
2622
|
}
|
|
2113
2623
|
};
|
|
2114
2624
|
|