@mastra/libsql 0.10.2-alpha.0 → 0.10.2-alpha.2
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 +19 -0
- package/dist/_tsup-dts-rollup.d.cts +15 -0
- package/dist/_tsup-dts-rollup.d.ts +15 -0
- package/dist/index.cjs +135 -40
- package/dist/index.js +135 -40
- package/package.json +3 -3
- package/src/storage/index.test.ts +14 -11
- package/src/storage/index.ts +153 -40
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
|
|
2
|
-
> @mastra/libsql@0.10.2-alpha.
|
|
2
|
+
> @mastra/libsql@0.10.2-alpha.2 build /home/runner/work/mastra/mastra/stores/libsql
|
|
3
3
|
> tsup src/index.ts --format esm,cjs --experimental-dts --clean --treeshake=smallest --splitting
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
6
6
|
[34mCLI[39m Using tsconfig: tsconfig.json
|
|
7
7
|
[34mCLI[39m tsup v8.5.0
|
|
8
8
|
[34mTSC[39m Build start
|
|
9
|
-
[32mTSC[39m ⚡️ Build success in
|
|
9
|
+
[32mTSC[39m ⚡️ Build success in 9592ms
|
|
10
10
|
[34mDTS[39m Build start
|
|
11
11
|
[34mCLI[39m Target: es2022
|
|
12
12
|
Analysis will use the bundled TypeScript version 5.8.3
|
|
13
13
|
[36mWriting package typings: /home/runner/work/mastra/mastra/stores/libsql/dist/_tsup-dts-rollup.d.ts[39m
|
|
14
14
|
Analysis will use the bundled TypeScript version 5.8.3
|
|
15
15
|
[36mWriting package typings: /home/runner/work/mastra/mastra/stores/libsql/dist/_tsup-dts-rollup.d.cts[39m
|
|
16
|
-
[32mDTS[39m ⚡️ Build success in
|
|
16
|
+
[32mDTS[39m ⚡️ Build success in 10548ms
|
|
17
17
|
[34mCLI[39m Cleaning output folder
|
|
18
18
|
[34mESM[39m Build start
|
|
19
19
|
[34mCJS[39m Build start
|
|
20
|
-
[32mESM[39m [1mdist/index.js [22m[
|
|
21
|
-
[32mESM[39m ⚡️ Build success in
|
|
22
|
-
[32mCJS[39m [1mdist/index.cjs [22m[
|
|
23
|
-
[32mCJS[39m ⚡️ Build success in
|
|
20
|
+
[32mESM[39m [1mdist/index.js [22m[32m58.54 KB[39m
|
|
21
|
+
[32mESM[39m ⚡️ Build success in 1646ms
|
|
22
|
+
[32mCJS[39m [1mdist/index.cjs [22m[32m58.83 KB[39m
|
|
23
|
+
[32mCJS[39m ⚡️ Build success in 1647ms
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @mastra/libsql
|
|
2
2
|
|
|
3
|
+
## 0.10.2-alpha.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 48eddb9: update filter logic in Memory class to support semantic recall search scope
|
|
8
|
+
- Updated dependencies [48eddb9]
|
|
9
|
+
- @mastra/core@0.10.4-alpha.2
|
|
10
|
+
|
|
11
|
+
## 0.10.2-alpha.1
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- dffb67b: updated stores to add alter table and change tests
|
|
16
|
+
- Updated dependencies [f6fd25f]
|
|
17
|
+
- Updated dependencies [dffb67b]
|
|
18
|
+
- Updated dependencies [f1309d3]
|
|
19
|
+
- Updated dependencies [f7f8293]
|
|
20
|
+
- @mastra/core@0.10.4-alpha.1
|
|
21
|
+
|
|
3
22
|
## 0.10.2-alpha.0
|
|
4
23
|
|
|
5
24
|
### Patch Changes
|
|
@@ -84,11 +84,26 @@ declare class LibSQLStore extends MastraStorage {
|
|
|
84
84
|
private readonly maxRetries;
|
|
85
85
|
private readonly initialBackoffMs;
|
|
86
86
|
constructor(config: LibSQLConfig);
|
|
87
|
+
get supports(): {
|
|
88
|
+
selectByIncludeResourceScope: boolean;
|
|
89
|
+
};
|
|
87
90
|
private getCreateTableSQL;
|
|
88
91
|
createTable({ tableName, schema, }: {
|
|
89
92
|
tableName: TABLE_NAMES;
|
|
90
93
|
schema: Record<string, StorageColumn>;
|
|
91
94
|
}): Promise<void>;
|
|
95
|
+
protected getSqlType(type: StorageColumn['type']): string;
|
|
96
|
+
/**
|
|
97
|
+
* Alters table schema to add columns if they don't exist
|
|
98
|
+
* @param tableName Name of the table
|
|
99
|
+
* @param schema Schema of the table
|
|
100
|
+
* @param ifNotExists Array of column names to add if they don't exist
|
|
101
|
+
*/
|
|
102
|
+
alterTable({ tableName, schema, ifNotExists, }: {
|
|
103
|
+
tableName: TABLE_NAMES;
|
|
104
|
+
schema: Record<string, StorageColumn>;
|
|
105
|
+
ifNotExists: string[];
|
|
106
|
+
}): Promise<void>;
|
|
92
107
|
clearTable({ tableName }: {
|
|
93
108
|
tableName: TABLE_NAMES;
|
|
94
109
|
}): Promise<void>;
|
|
@@ -84,11 +84,26 @@ declare class LibSQLStore extends MastraStorage {
|
|
|
84
84
|
private readonly maxRetries;
|
|
85
85
|
private readonly initialBackoffMs;
|
|
86
86
|
constructor(config: LibSQLConfig);
|
|
87
|
+
get supports(): {
|
|
88
|
+
selectByIncludeResourceScope: boolean;
|
|
89
|
+
};
|
|
87
90
|
private getCreateTableSQL;
|
|
88
91
|
createTable({ tableName, schema, }: {
|
|
89
92
|
tableName: TABLE_NAMES;
|
|
90
93
|
schema: Record<string, StorageColumn>;
|
|
91
94
|
}): Promise<void>;
|
|
95
|
+
protected getSqlType(type: StorageColumn['type']): string;
|
|
96
|
+
/**
|
|
97
|
+
* Alters table schema to add columns if they don't exist
|
|
98
|
+
* @param tableName Name of the table
|
|
99
|
+
* @param schema Schema of the table
|
|
100
|
+
* @param ifNotExists Array of column names to add if they don't exist
|
|
101
|
+
*/
|
|
102
|
+
alterTable({ tableName, schema, ifNotExists, }: {
|
|
103
|
+
tableName: TABLE_NAMES;
|
|
104
|
+
schema: Record<string, StorageColumn>;
|
|
105
|
+
ifNotExists: string[];
|
|
106
|
+
}): Promise<void>;
|
|
92
107
|
clearTable({ tableName }: {
|
|
93
108
|
tableName: TABLE_NAMES;
|
|
94
109
|
}): Promise<void>;
|
package/dist/index.cjs
CHANGED
|
@@ -818,6 +818,11 @@ var LibSQLStore = class extends storage.MastraStorage {
|
|
|
818
818
|
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));
|
|
819
819
|
}
|
|
820
820
|
}
|
|
821
|
+
get supports() {
|
|
822
|
+
return {
|
|
823
|
+
selectByIncludeResourceScope: true
|
|
824
|
+
};
|
|
825
|
+
}
|
|
821
826
|
getCreateTableSQL(tableName, schema) {
|
|
822
827
|
const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
|
|
823
828
|
const columns = Object.entries(schema).map(([name, col]) => {
|
|
@@ -851,6 +856,52 @@ var LibSQLStore = class extends storage.MastraStorage {
|
|
|
851
856
|
throw error;
|
|
852
857
|
}
|
|
853
858
|
}
|
|
859
|
+
getSqlType(type) {
|
|
860
|
+
switch (type) {
|
|
861
|
+
case "bigint":
|
|
862
|
+
return "INTEGER";
|
|
863
|
+
// SQLite uses INTEGER for all integer sizes
|
|
864
|
+
case "jsonb":
|
|
865
|
+
return "TEXT";
|
|
866
|
+
// Store JSON as TEXT in SQLite
|
|
867
|
+
default:
|
|
868
|
+
return super.getSqlType(type);
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
/**
|
|
872
|
+
* Alters table schema to add columns if they don't exist
|
|
873
|
+
* @param tableName Name of the table
|
|
874
|
+
* @param schema Schema of the table
|
|
875
|
+
* @param ifNotExists Array of column names to add if they don't exist
|
|
876
|
+
*/
|
|
877
|
+
async alterTable({
|
|
878
|
+
tableName,
|
|
879
|
+
schema,
|
|
880
|
+
ifNotExists
|
|
881
|
+
}) {
|
|
882
|
+
const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
|
|
883
|
+
try {
|
|
884
|
+
const pragmaQuery = `PRAGMA table_info(${parsedTableName})`;
|
|
885
|
+
const result = await this.client.execute(pragmaQuery);
|
|
886
|
+
const existingColumnNames = new Set(result.rows.map((row) => row.name.toLowerCase()));
|
|
887
|
+
for (const columnName of ifNotExists) {
|
|
888
|
+
if (!existingColumnNames.has(columnName.toLowerCase()) && schema[columnName]) {
|
|
889
|
+
const columnDef = schema[columnName];
|
|
890
|
+
const sqlType = this.getSqlType(columnDef.type);
|
|
891
|
+
const nullable = columnDef.nullable === false ? "NOT NULL" : "";
|
|
892
|
+
const defaultValue = columnDef.nullable === false ? this.getDefaultValue(columnDef.type) : "";
|
|
893
|
+
const alterSql = `ALTER TABLE ${parsedTableName} ADD COLUMN "${columnName}" ${sqlType} ${nullable} ${defaultValue}`.trim();
|
|
894
|
+
await this.client.execute(alterSql);
|
|
895
|
+
this.logger?.debug?.(`Added column ${columnName} to table ${parsedTableName}`);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
} catch (error) {
|
|
899
|
+
this.logger?.error?.(
|
|
900
|
+
`Error altering table ${tableName}: ${error instanceof Error ? error.message : String(error)}`
|
|
901
|
+
);
|
|
902
|
+
throw new Error(`Failed to alter table ${tableName}: ${error}`);
|
|
903
|
+
}
|
|
904
|
+
}
|
|
854
905
|
async clearTable({ tableName }) {
|
|
855
906
|
const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
|
|
856
907
|
try {
|
|
@@ -1076,6 +1127,10 @@ var LibSQLStore = class extends storage.MastraStorage {
|
|
|
1076
1127
|
return updatedThread;
|
|
1077
1128
|
}
|
|
1078
1129
|
async deleteThread({ threadId }) {
|
|
1130
|
+
await this.client.execute({
|
|
1131
|
+
sql: `DELETE FROM ${storage.TABLE_MESSAGES} WHERE thread_id = ?`,
|
|
1132
|
+
args: [threadId]
|
|
1133
|
+
});
|
|
1079
1134
|
await this.client.execute({
|
|
1080
1135
|
sql: `DELETE FROM ${storage.TABLE_THREADS} WHERE id = ?`,
|
|
1081
1136
|
args: [threadId]
|
|
@@ -1092,40 +1147,61 @@ var LibSQLStore = class extends storage.MastraStorage {
|
|
|
1092
1147
|
content,
|
|
1093
1148
|
role: row.role,
|
|
1094
1149
|
createdAt: new Date(row.createdAt),
|
|
1095
|
-
threadId: row.thread_id
|
|
1150
|
+
threadId: row.thread_id,
|
|
1151
|
+
resourceId: row.resourceId
|
|
1096
1152
|
};
|
|
1097
1153
|
if (row.type && row.type !== `v2`) result.type = row.type;
|
|
1098
1154
|
return result;
|
|
1099
1155
|
}
|
|
1100
|
-
async _getIncludedMessages(
|
|
1156
|
+
async _getIncludedMessages({
|
|
1157
|
+
threadId,
|
|
1158
|
+
selectBy
|
|
1159
|
+
}) {
|
|
1101
1160
|
const include = selectBy?.include;
|
|
1102
1161
|
if (!include) return null;
|
|
1103
|
-
const
|
|
1104
|
-
const
|
|
1105
|
-
const
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1162
|
+
const unionQueries = [];
|
|
1163
|
+
const params = [];
|
|
1164
|
+
for (const inc of include) {
|
|
1165
|
+
const { id, withPreviousMessages = 0, withNextMessages = 0 } = inc;
|
|
1166
|
+
const searchId = inc.threadId || threadId;
|
|
1167
|
+
unionQueries.push(
|
|
1168
|
+
`
|
|
1169
|
+
SELECT * FROM (
|
|
1170
|
+
WITH numbered_messages AS (
|
|
1171
|
+
SELECT
|
|
1172
|
+
id, content, role, type, "createdAt", thread_id, "resourceId",
|
|
1173
|
+
ROW_NUMBER() OVER (ORDER BY "createdAt" ASC) as row_num
|
|
1174
|
+
FROM "${storage.TABLE_MESSAGES}"
|
|
1175
|
+
WHERE thread_id = ?
|
|
1176
|
+
),
|
|
1177
|
+
target_positions AS (
|
|
1178
|
+
SELECT row_num as target_pos
|
|
1179
|
+
FROM numbered_messages
|
|
1180
|
+
WHERE id = ?
|
|
1181
|
+
)
|
|
1182
|
+
SELECT DISTINCT m.*
|
|
1183
|
+
FROM numbered_messages m
|
|
1184
|
+
CROSS JOIN target_positions t
|
|
1185
|
+
WHERE m.row_num BETWEEN (t.target_pos - ?) AND (t.target_pos + ?)
|
|
1186
|
+
)
|
|
1187
|
+
`
|
|
1188
|
+
// Keep ASC for final sorting after fetching context
|
|
1189
|
+
);
|
|
1190
|
+
params.push(searchId, id, withPreviousMessages, withNextMessages);
|
|
1191
|
+
}
|
|
1192
|
+
const finalQuery = unionQueries.join(" UNION ALL ") + ' ORDER BY "createdAt" ASC';
|
|
1193
|
+
const includedResult = await this.client.execute({ sql: finalQuery, args: params });
|
|
1194
|
+
const includedRows = includedResult.rows?.map((row) => this.parseRow(row));
|
|
1195
|
+
const dedupedRows = Object.values(
|
|
1196
|
+
includedRows.reduce(
|
|
1197
|
+
(acc, row) => {
|
|
1198
|
+
acc[row.id] = row;
|
|
1199
|
+
return acc;
|
|
1200
|
+
},
|
|
1201
|
+
{}
|
|
1202
|
+
)
|
|
1203
|
+
);
|
|
1204
|
+
return dedupedRows;
|
|
1129
1205
|
}
|
|
1130
1206
|
async getMessages({
|
|
1131
1207
|
threadId,
|
|
@@ -1136,19 +1212,27 @@ var LibSQLStore = class extends storage.MastraStorage {
|
|
|
1136
1212
|
const messages = [];
|
|
1137
1213
|
const limit = typeof selectBy?.last === `number` ? selectBy.last : 40;
|
|
1138
1214
|
if (selectBy?.include?.length) {
|
|
1139
|
-
const includeMessages = await this._getIncludedMessages(threadId, selectBy);
|
|
1215
|
+
const includeMessages = await this._getIncludedMessages({ threadId, selectBy });
|
|
1140
1216
|
if (includeMessages) {
|
|
1141
1217
|
messages.push(...includeMessages);
|
|
1142
1218
|
}
|
|
1143
1219
|
}
|
|
1144
1220
|
const excludeIds = messages.map((m) => m.id);
|
|
1145
1221
|
const remainingSql = `
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1222
|
+
SELECT
|
|
1223
|
+
id,
|
|
1224
|
+
content,
|
|
1225
|
+
role,
|
|
1226
|
+
type,
|
|
1227
|
+
"createdAt",
|
|
1228
|
+
thread_id,
|
|
1229
|
+
"resourceId"
|
|
1230
|
+
FROM "${storage.TABLE_MESSAGES}"
|
|
1231
|
+
WHERE thread_id = ?
|
|
1232
|
+
${excludeIds.length ? `AND id NOT IN (${excludeIds.map(() => "?").join(", ")})` : ""}
|
|
1233
|
+
ORDER BY "createdAt" DESC
|
|
1234
|
+
LIMIT ?
|
|
1235
|
+
`;
|
|
1152
1236
|
const remainingArgs = [threadId, ...excludeIds.length ? excludeIds : [], limit];
|
|
1153
1237
|
const remainingResult = await this.client.execute({ sql: remainingSql, args: remainingArgs });
|
|
1154
1238
|
if (remainingResult.rows) {
|
|
@@ -1170,7 +1254,7 @@ var LibSQLStore = class extends storage.MastraStorage {
|
|
|
1170
1254
|
const toDate = dateRange?.end;
|
|
1171
1255
|
const messages = [];
|
|
1172
1256
|
if (selectBy?.include?.length) {
|
|
1173
|
-
const includeMessages = await this._getIncludedMessages(threadId, selectBy);
|
|
1257
|
+
const includeMessages = await this._getIncludedMessages({ threadId, selectBy });
|
|
1174
1258
|
if (includeMessages) {
|
|
1175
1259
|
messages.push(...includeMessages);
|
|
1176
1260
|
}
|
|
@@ -1232,16 +1316,27 @@ var LibSQLStore = class extends storage.MastraStorage {
|
|
|
1232
1316
|
}
|
|
1233
1317
|
const batchStatements = messages.map((message) => {
|
|
1234
1318
|
const time = message.createdAt || /* @__PURE__ */ new Date();
|
|
1319
|
+
if (!message.threadId) {
|
|
1320
|
+
throw new Error(
|
|
1321
|
+
`Expected to find a threadId for message, but couldn't find one. An unexpected error has occurred.`
|
|
1322
|
+
);
|
|
1323
|
+
}
|
|
1324
|
+
if (!message.resourceId) {
|
|
1325
|
+
throw new Error(
|
|
1326
|
+
`Expected to find a resourceId for message, but couldn't find one. An unexpected error has occurred.`
|
|
1327
|
+
);
|
|
1328
|
+
}
|
|
1235
1329
|
return {
|
|
1236
|
-
sql: `INSERT INTO ${storage.TABLE_MESSAGES} (id, thread_id, content, role, type, createdAt)
|
|
1237
|
-
VALUES (?, ?, ?, ?, ?, ?)`,
|
|
1330
|
+
sql: `INSERT INTO ${storage.TABLE_MESSAGES} (id, thread_id, content, role, type, createdAt, resourceId)
|
|
1331
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
1238
1332
|
args: [
|
|
1239
1333
|
message.id,
|
|
1240
|
-
threadId,
|
|
1334
|
+
message.threadId,
|
|
1241
1335
|
typeof message.content === "object" ? JSON.stringify(message.content) : message.content,
|
|
1242
1336
|
message.role,
|
|
1243
1337
|
message.type || "v2",
|
|
1244
|
-
time instanceof Date ? time.toISOString() : time
|
|
1338
|
+
time instanceof Date ? time.toISOString() : time,
|
|
1339
|
+
message.resourceId
|
|
1245
1340
|
]
|
|
1246
1341
|
};
|
|
1247
1342
|
});
|
package/dist/index.js
CHANGED
|
@@ -816,6 +816,11 @@ var LibSQLStore = class extends MastraStorage {
|
|
|
816
816
|
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));
|
|
817
817
|
}
|
|
818
818
|
}
|
|
819
|
+
get supports() {
|
|
820
|
+
return {
|
|
821
|
+
selectByIncludeResourceScope: true
|
|
822
|
+
};
|
|
823
|
+
}
|
|
819
824
|
getCreateTableSQL(tableName, schema) {
|
|
820
825
|
const parsedTableName = parseSqlIdentifier(tableName, "table name");
|
|
821
826
|
const columns = Object.entries(schema).map(([name, col]) => {
|
|
@@ -849,6 +854,52 @@ var LibSQLStore = class extends MastraStorage {
|
|
|
849
854
|
throw error;
|
|
850
855
|
}
|
|
851
856
|
}
|
|
857
|
+
getSqlType(type) {
|
|
858
|
+
switch (type) {
|
|
859
|
+
case "bigint":
|
|
860
|
+
return "INTEGER";
|
|
861
|
+
// SQLite uses INTEGER for all integer sizes
|
|
862
|
+
case "jsonb":
|
|
863
|
+
return "TEXT";
|
|
864
|
+
// Store JSON as TEXT in SQLite
|
|
865
|
+
default:
|
|
866
|
+
return super.getSqlType(type);
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
/**
|
|
870
|
+
* Alters table schema to add columns if they don't exist
|
|
871
|
+
* @param tableName Name of the table
|
|
872
|
+
* @param schema Schema of the table
|
|
873
|
+
* @param ifNotExists Array of column names to add if they don't exist
|
|
874
|
+
*/
|
|
875
|
+
async alterTable({
|
|
876
|
+
tableName,
|
|
877
|
+
schema,
|
|
878
|
+
ifNotExists
|
|
879
|
+
}) {
|
|
880
|
+
const parsedTableName = parseSqlIdentifier(tableName, "table name");
|
|
881
|
+
try {
|
|
882
|
+
const pragmaQuery = `PRAGMA table_info(${parsedTableName})`;
|
|
883
|
+
const result = await this.client.execute(pragmaQuery);
|
|
884
|
+
const existingColumnNames = new Set(result.rows.map((row) => row.name.toLowerCase()));
|
|
885
|
+
for (const columnName of ifNotExists) {
|
|
886
|
+
if (!existingColumnNames.has(columnName.toLowerCase()) && schema[columnName]) {
|
|
887
|
+
const columnDef = schema[columnName];
|
|
888
|
+
const sqlType = this.getSqlType(columnDef.type);
|
|
889
|
+
const nullable = columnDef.nullable === false ? "NOT NULL" : "";
|
|
890
|
+
const defaultValue = columnDef.nullable === false ? this.getDefaultValue(columnDef.type) : "";
|
|
891
|
+
const alterSql = `ALTER TABLE ${parsedTableName} ADD COLUMN "${columnName}" ${sqlType} ${nullable} ${defaultValue}`.trim();
|
|
892
|
+
await this.client.execute(alterSql);
|
|
893
|
+
this.logger?.debug?.(`Added column ${columnName} to table ${parsedTableName}`);
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
} catch (error) {
|
|
897
|
+
this.logger?.error?.(
|
|
898
|
+
`Error altering table ${tableName}: ${error instanceof Error ? error.message : String(error)}`
|
|
899
|
+
);
|
|
900
|
+
throw new Error(`Failed to alter table ${tableName}: ${error}`);
|
|
901
|
+
}
|
|
902
|
+
}
|
|
852
903
|
async clearTable({ tableName }) {
|
|
853
904
|
const parsedTableName = parseSqlIdentifier(tableName, "table name");
|
|
854
905
|
try {
|
|
@@ -1074,6 +1125,10 @@ var LibSQLStore = class extends MastraStorage {
|
|
|
1074
1125
|
return updatedThread;
|
|
1075
1126
|
}
|
|
1076
1127
|
async deleteThread({ threadId }) {
|
|
1128
|
+
await this.client.execute({
|
|
1129
|
+
sql: `DELETE FROM ${TABLE_MESSAGES} WHERE thread_id = ?`,
|
|
1130
|
+
args: [threadId]
|
|
1131
|
+
});
|
|
1077
1132
|
await this.client.execute({
|
|
1078
1133
|
sql: `DELETE FROM ${TABLE_THREADS} WHERE id = ?`,
|
|
1079
1134
|
args: [threadId]
|
|
@@ -1090,40 +1145,61 @@ var LibSQLStore = class extends MastraStorage {
|
|
|
1090
1145
|
content,
|
|
1091
1146
|
role: row.role,
|
|
1092
1147
|
createdAt: new Date(row.createdAt),
|
|
1093
|
-
threadId: row.thread_id
|
|
1148
|
+
threadId: row.thread_id,
|
|
1149
|
+
resourceId: row.resourceId
|
|
1094
1150
|
};
|
|
1095
1151
|
if (row.type && row.type !== `v2`) result.type = row.type;
|
|
1096
1152
|
return result;
|
|
1097
1153
|
}
|
|
1098
|
-
async _getIncludedMessages(
|
|
1154
|
+
async _getIncludedMessages({
|
|
1155
|
+
threadId,
|
|
1156
|
+
selectBy
|
|
1157
|
+
}) {
|
|
1099
1158
|
const include = selectBy?.include;
|
|
1100
1159
|
if (!include) return null;
|
|
1101
|
-
const
|
|
1102
|
-
const
|
|
1103
|
-
const
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1160
|
+
const unionQueries = [];
|
|
1161
|
+
const params = [];
|
|
1162
|
+
for (const inc of include) {
|
|
1163
|
+
const { id, withPreviousMessages = 0, withNextMessages = 0 } = inc;
|
|
1164
|
+
const searchId = inc.threadId || threadId;
|
|
1165
|
+
unionQueries.push(
|
|
1166
|
+
`
|
|
1167
|
+
SELECT * FROM (
|
|
1168
|
+
WITH numbered_messages AS (
|
|
1169
|
+
SELECT
|
|
1170
|
+
id, content, role, type, "createdAt", thread_id, "resourceId",
|
|
1171
|
+
ROW_NUMBER() OVER (ORDER BY "createdAt" ASC) as row_num
|
|
1172
|
+
FROM "${TABLE_MESSAGES}"
|
|
1173
|
+
WHERE thread_id = ?
|
|
1174
|
+
),
|
|
1175
|
+
target_positions AS (
|
|
1176
|
+
SELECT row_num as target_pos
|
|
1177
|
+
FROM numbered_messages
|
|
1178
|
+
WHERE id = ?
|
|
1179
|
+
)
|
|
1180
|
+
SELECT DISTINCT m.*
|
|
1181
|
+
FROM numbered_messages m
|
|
1182
|
+
CROSS JOIN target_positions t
|
|
1183
|
+
WHERE m.row_num BETWEEN (t.target_pos - ?) AND (t.target_pos + ?)
|
|
1184
|
+
)
|
|
1185
|
+
`
|
|
1186
|
+
// Keep ASC for final sorting after fetching context
|
|
1187
|
+
);
|
|
1188
|
+
params.push(searchId, id, withPreviousMessages, withNextMessages);
|
|
1189
|
+
}
|
|
1190
|
+
const finalQuery = unionQueries.join(" UNION ALL ") + ' ORDER BY "createdAt" ASC';
|
|
1191
|
+
const includedResult = await this.client.execute({ sql: finalQuery, args: params });
|
|
1192
|
+
const includedRows = includedResult.rows?.map((row) => this.parseRow(row));
|
|
1193
|
+
const dedupedRows = Object.values(
|
|
1194
|
+
includedRows.reduce(
|
|
1195
|
+
(acc, row) => {
|
|
1196
|
+
acc[row.id] = row;
|
|
1197
|
+
return acc;
|
|
1198
|
+
},
|
|
1199
|
+
{}
|
|
1200
|
+
)
|
|
1201
|
+
);
|
|
1202
|
+
return dedupedRows;
|
|
1127
1203
|
}
|
|
1128
1204
|
async getMessages({
|
|
1129
1205
|
threadId,
|
|
@@ -1134,19 +1210,27 @@ var LibSQLStore = class extends MastraStorage {
|
|
|
1134
1210
|
const messages = [];
|
|
1135
1211
|
const limit = typeof selectBy?.last === `number` ? selectBy.last : 40;
|
|
1136
1212
|
if (selectBy?.include?.length) {
|
|
1137
|
-
const includeMessages = await this._getIncludedMessages(threadId, selectBy);
|
|
1213
|
+
const includeMessages = await this._getIncludedMessages({ threadId, selectBy });
|
|
1138
1214
|
if (includeMessages) {
|
|
1139
1215
|
messages.push(...includeMessages);
|
|
1140
1216
|
}
|
|
1141
1217
|
}
|
|
1142
1218
|
const excludeIds = messages.map((m) => m.id);
|
|
1143
1219
|
const remainingSql = `
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1220
|
+
SELECT
|
|
1221
|
+
id,
|
|
1222
|
+
content,
|
|
1223
|
+
role,
|
|
1224
|
+
type,
|
|
1225
|
+
"createdAt",
|
|
1226
|
+
thread_id,
|
|
1227
|
+
"resourceId"
|
|
1228
|
+
FROM "${TABLE_MESSAGES}"
|
|
1229
|
+
WHERE thread_id = ?
|
|
1230
|
+
${excludeIds.length ? `AND id NOT IN (${excludeIds.map(() => "?").join(", ")})` : ""}
|
|
1231
|
+
ORDER BY "createdAt" DESC
|
|
1232
|
+
LIMIT ?
|
|
1233
|
+
`;
|
|
1150
1234
|
const remainingArgs = [threadId, ...excludeIds.length ? excludeIds : [], limit];
|
|
1151
1235
|
const remainingResult = await this.client.execute({ sql: remainingSql, args: remainingArgs });
|
|
1152
1236
|
if (remainingResult.rows) {
|
|
@@ -1168,7 +1252,7 @@ var LibSQLStore = class extends MastraStorage {
|
|
|
1168
1252
|
const toDate = dateRange?.end;
|
|
1169
1253
|
const messages = [];
|
|
1170
1254
|
if (selectBy?.include?.length) {
|
|
1171
|
-
const includeMessages = await this._getIncludedMessages(threadId, selectBy);
|
|
1255
|
+
const includeMessages = await this._getIncludedMessages({ threadId, selectBy });
|
|
1172
1256
|
if (includeMessages) {
|
|
1173
1257
|
messages.push(...includeMessages);
|
|
1174
1258
|
}
|
|
@@ -1230,16 +1314,27 @@ var LibSQLStore = class extends MastraStorage {
|
|
|
1230
1314
|
}
|
|
1231
1315
|
const batchStatements = messages.map((message) => {
|
|
1232
1316
|
const time = message.createdAt || /* @__PURE__ */ new Date();
|
|
1317
|
+
if (!message.threadId) {
|
|
1318
|
+
throw new Error(
|
|
1319
|
+
`Expected to find a threadId for message, but couldn't find one. An unexpected error has occurred.`
|
|
1320
|
+
);
|
|
1321
|
+
}
|
|
1322
|
+
if (!message.resourceId) {
|
|
1323
|
+
throw new Error(
|
|
1324
|
+
`Expected to find a resourceId for message, but couldn't find one. An unexpected error has occurred.`
|
|
1325
|
+
);
|
|
1326
|
+
}
|
|
1233
1327
|
return {
|
|
1234
|
-
sql: `INSERT INTO ${TABLE_MESSAGES} (id, thread_id, content, role, type, createdAt)
|
|
1235
|
-
VALUES (?, ?, ?, ?, ?, ?)`,
|
|
1328
|
+
sql: `INSERT INTO ${TABLE_MESSAGES} (id, thread_id, content, role, type, createdAt, resourceId)
|
|
1329
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
1236
1330
|
args: [
|
|
1237
1331
|
message.id,
|
|
1238
|
-
threadId,
|
|
1332
|
+
message.threadId,
|
|
1239
1333
|
typeof message.content === "object" ? JSON.stringify(message.content) : message.content,
|
|
1240
1334
|
message.role,
|
|
1241
1335
|
message.type || "v2",
|
|
1242
|
-
time instanceof Date ? time.toISOString() : time
|
|
1336
|
+
time instanceof Date ? time.toISOString() : time,
|
|
1337
|
+
message.resourceId
|
|
1243
1338
|
]
|
|
1244
1339
|
};
|
|
1245
1340
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mastra/libsql",
|
|
3
|
-
"version": "0.10.2-alpha.
|
|
3
|
+
"version": "0.10.2-alpha.2",
|
|
4
4
|
"description": "Libsql provider for Mastra - includes both vector and db storage capabilities",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -28,9 +28,9 @@
|
|
|
28
28
|
"eslint": "^9.28.0",
|
|
29
29
|
"tsup": "^8.5.0",
|
|
30
30
|
"typescript": "^5.8.3",
|
|
31
|
-
"vitest": "^3.
|
|
31
|
+
"vitest": "^3.2.2",
|
|
32
32
|
"@internal/lint": "0.0.10",
|
|
33
|
-
"@mastra/core": "0.10.4-alpha.
|
|
33
|
+
"@mastra/core": "0.10.4-alpha.2",
|
|
34
34
|
"@internal/storage-test-utils": "0.0.6"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
@@ -3,8 +3,9 @@ import {
|
|
|
3
3
|
createSampleEval,
|
|
4
4
|
createSampleTraceForDB,
|
|
5
5
|
createSampleThread,
|
|
6
|
-
createSampleMessage,
|
|
7
6
|
createTestSuite,
|
|
7
|
+
createSampleMessageV1,
|
|
8
|
+
resetRole,
|
|
8
9
|
} from '@internal/storage-test-utils';
|
|
9
10
|
import type { MastraMessageV1, StorageThreadType } from '@mastra/core';
|
|
10
11
|
import { Mastra } from '@mastra/core/mastra';
|
|
@@ -243,16 +244,14 @@ describe('LibSQLStore Pagination Features', () => {
|
|
|
243
244
|
|
|
244
245
|
describe('getMessages with pagination', () => {
|
|
245
246
|
it('should return paginated messages with total count', async () => {
|
|
247
|
+
resetRole();
|
|
246
248
|
const threadData = createSampleThread();
|
|
247
249
|
threadData.resourceId = 'resource-msg-pagination';
|
|
248
250
|
const thread = await store.saveThread({ thread: threadData as StorageThreadType });
|
|
249
251
|
|
|
250
252
|
const messageRecords: MastraMessageV1[] = [];
|
|
251
253
|
for (let i = 0; i < 15; i++) {
|
|
252
|
-
messageRecords.push({
|
|
253
|
-
...createSampleMessage(thread.id),
|
|
254
|
-
content: [{ type: 'text', text: `Message ${i + 1}` }],
|
|
255
|
-
} as MastraMessageV1);
|
|
254
|
+
messageRecords.push(createSampleMessageV1({ threadId: thread.id, content: `Message ${i + 1}` }));
|
|
256
255
|
}
|
|
257
256
|
await store.saveMessages({ messages: messageRecords });
|
|
258
257
|
|
|
@@ -300,17 +299,21 @@ describe('LibSQLStore Pagination Features', () => {
|
|
|
300
299
|
|
|
301
300
|
// Ensure timestamps are distinct for reliable sorting by creating them with a slight delay for testing clarity
|
|
302
301
|
const messagesToSave: MastraMessageV1[] = [];
|
|
303
|
-
messagesToSave.push(
|
|
302
|
+
messagesToSave.push(
|
|
303
|
+
createSampleMessageV1({ threadId: thread.id, content: 'Message 1', createdAt: dayBeforeYesterday }),
|
|
304
|
+
);
|
|
304
305
|
await new Promise(r => setTimeout(r, 5));
|
|
305
|
-
messagesToSave.push(
|
|
306
|
+
messagesToSave.push(
|
|
307
|
+
createSampleMessageV1({ threadId: thread.id, content: 'Message 2', createdAt: dayBeforeYesterday }),
|
|
308
|
+
);
|
|
306
309
|
await new Promise(r => setTimeout(r, 5));
|
|
307
|
-
messagesToSave.push(
|
|
310
|
+
messagesToSave.push(createSampleMessageV1({ threadId: thread.id, content: 'Message 3', createdAt: yesterday }));
|
|
308
311
|
await new Promise(r => setTimeout(r, 5));
|
|
309
|
-
messagesToSave.push(
|
|
312
|
+
messagesToSave.push(createSampleMessageV1({ threadId: thread.id, content: 'Message 4', createdAt: yesterday }));
|
|
310
313
|
await new Promise(r => setTimeout(r, 5));
|
|
311
|
-
messagesToSave.push(
|
|
314
|
+
messagesToSave.push(createSampleMessageV1({ threadId: thread.id, content: 'Message 5', createdAt: now }));
|
|
312
315
|
await new Promise(r => setTimeout(r, 5));
|
|
313
|
-
messagesToSave.push(
|
|
316
|
+
messagesToSave.push(createSampleMessageV1({ threadId: thread.id, content: 'Message 6', createdAt: now }));
|
|
314
317
|
|
|
315
318
|
await store.saveMessages({ messages: messagesToSave, format: 'v1' });
|
|
316
319
|
// Total 6 messages: 2 now, 2 yesterday, 2 dayBeforeYesterday (oldest to newest)
|
package/src/storage/index.ts
CHANGED
|
@@ -81,6 +81,14 @@ export class LibSQLStore extends MastraStorage {
|
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
public get supports(): {
|
|
85
|
+
selectByIncludeResourceScope: boolean;
|
|
86
|
+
} {
|
|
87
|
+
return {
|
|
88
|
+
selectByIncludeResourceScope: true,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
84
92
|
private getCreateTableSQL(tableName: TABLE_NAMES, schema: Record<string, StorageColumn>): string {
|
|
85
93
|
const parsedTableName = parseSqlIdentifier(tableName, 'table name');
|
|
86
94
|
const columns = Object.entries(schema).map(([name, col]) => {
|
|
@@ -125,6 +133,63 @@ export class LibSQLStore extends MastraStorage {
|
|
|
125
133
|
}
|
|
126
134
|
}
|
|
127
135
|
|
|
136
|
+
protected getSqlType(type: StorageColumn['type']): string {
|
|
137
|
+
switch (type) {
|
|
138
|
+
case 'bigint':
|
|
139
|
+
return 'INTEGER'; // SQLite uses INTEGER for all integer sizes
|
|
140
|
+
case 'jsonb':
|
|
141
|
+
return 'TEXT'; // Store JSON as TEXT in SQLite
|
|
142
|
+
default:
|
|
143
|
+
return super.getSqlType(type);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Alters table schema to add columns if they don't exist
|
|
149
|
+
* @param tableName Name of the table
|
|
150
|
+
* @param schema Schema of the table
|
|
151
|
+
* @param ifNotExists Array of column names to add if they don't exist
|
|
152
|
+
*/
|
|
153
|
+
async alterTable({
|
|
154
|
+
tableName,
|
|
155
|
+
schema,
|
|
156
|
+
ifNotExists,
|
|
157
|
+
}: {
|
|
158
|
+
tableName: TABLE_NAMES;
|
|
159
|
+
schema: Record<string, StorageColumn>;
|
|
160
|
+
ifNotExists: string[];
|
|
161
|
+
}): Promise<void> {
|
|
162
|
+
const parsedTableName = parseSqlIdentifier(tableName, 'table name');
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
// 1. Get existing columns using PRAGMA
|
|
166
|
+
const pragmaQuery = `PRAGMA table_info(${parsedTableName})`;
|
|
167
|
+
const result = await this.client.execute(pragmaQuery);
|
|
168
|
+
const existingColumnNames = new Set(result.rows.map((row: any) => row.name.toLowerCase()));
|
|
169
|
+
|
|
170
|
+
// 2. Add missing columns
|
|
171
|
+
for (const columnName of ifNotExists) {
|
|
172
|
+
if (!existingColumnNames.has(columnName.toLowerCase()) && schema[columnName]) {
|
|
173
|
+
const columnDef = schema[columnName];
|
|
174
|
+
const sqlType = this.getSqlType(columnDef.type); // ensure this exists or implement
|
|
175
|
+
const nullable = columnDef.nullable === false ? 'NOT NULL' : '';
|
|
176
|
+
// In SQLite, you must provide a DEFAULT if adding a NOT NULL column to a non-empty table
|
|
177
|
+
const defaultValue = columnDef.nullable === false ? this.getDefaultValue(columnDef.type) : '';
|
|
178
|
+
const alterSql =
|
|
179
|
+
`ALTER TABLE ${parsedTableName} ADD COLUMN "${columnName}" ${sqlType} ${nullable} ${defaultValue}`.trim();
|
|
180
|
+
|
|
181
|
+
await this.client.execute(alterSql);
|
|
182
|
+
this.logger?.debug?.(`Added column ${columnName} to table ${parsedTableName}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
} catch (error) {
|
|
186
|
+
this.logger?.error?.(
|
|
187
|
+
`Error altering table ${tableName}: ${error instanceof Error ? error.message : String(error)}`,
|
|
188
|
+
);
|
|
189
|
+
throw new Error(`Failed to alter table ${tableName}: ${error}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
128
193
|
async clearTable({ tableName }: { tableName: TABLE_NAMES }): Promise<void> {
|
|
129
194
|
const parsedTableName = parseSqlIdentifier(tableName, 'table name');
|
|
130
195
|
try {
|
|
@@ -414,11 +479,16 @@ export class LibSQLStore extends MastraStorage {
|
|
|
414
479
|
}
|
|
415
480
|
|
|
416
481
|
async deleteThread({ threadId }: { threadId: string }): Promise<void> {
|
|
482
|
+
// Delete messages for this thread (manual step)
|
|
483
|
+
await this.client.execute({
|
|
484
|
+
sql: `DELETE FROM ${TABLE_MESSAGES} WHERE thread_id = ?`,
|
|
485
|
+
args: [threadId],
|
|
486
|
+
});
|
|
417
487
|
await this.client.execute({
|
|
418
488
|
sql: `DELETE FROM ${TABLE_THREADS} WHERE id = ?`,
|
|
419
489
|
args: [threadId],
|
|
420
490
|
});
|
|
421
|
-
//
|
|
491
|
+
// TODO: Need to check if CASCADE is enabled so that messages will be automatically deleted due to CASCADE constraint
|
|
422
492
|
}
|
|
423
493
|
|
|
424
494
|
private parseRow(row: any): MastraMessageV2 {
|
|
@@ -434,42 +504,66 @@ export class LibSQLStore extends MastraStorage {
|
|
|
434
504
|
role: row.role,
|
|
435
505
|
createdAt: new Date(row.createdAt as string),
|
|
436
506
|
threadId: row.thread_id,
|
|
507
|
+
resourceId: row.resourceId,
|
|
437
508
|
} as MastraMessageV2;
|
|
438
509
|
if (row.type && row.type !== `v2`) result.type = row.type;
|
|
439
510
|
return result;
|
|
440
511
|
}
|
|
441
512
|
|
|
442
|
-
private async _getIncludedMessages(
|
|
513
|
+
private async _getIncludedMessages({
|
|
514
|
+
threadId,
|
|
515
|
+
selectBy,
|
|
516
|
+
}: {
|
|
517
|
+
threadId: string;
|
|
518
|
+
selectBy: StorageGetMessagesArg['selectBy'];
|
|
519
|
+
}) {
|
|
443
520
|
const include = selectBy?.include;
|
|
444
521
|
if (!include) return null;
|
|
445
522
|
|
|
446
|
-
const
|
|
447
|
-
const
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
523
|
+
const unionQueries: string[] = [];
|
|
524
|
+
const params: any[] = [];
|
|
525
|
+
|
|
526
|
+
for (const inc of include) {
|
|
527
|
+
const { id, withPreviousMessages = 0, withNextMessages = 0 } = inc;
|
|
528
|
+
// if threadId is provided, use it, otherwise use threadId from args
|
|
529
|
+
const searchId = inc.threadId || threadId;
|
|
530
|
+
unionQueries.push(
|
|
531
|
+
`
|
|
532
|
+
SELECT * FROM (
|
|
533
|
+
WITH numbered_messages AS (
|
|
534
|
+
SELECT
|
|
535
|
+
id, content, role, type, "createdAt", thread_id, "resourceId",
|
|
536
|
+
ROW_NUMBER() OVER (ORDER BY "createdAt" ASC) as row_num
|
|
537
|
+
FROM "${TABLE_MESSAGES}"
|
|
538
|
+
WHERE thread_id = ?
|
|
539
|
+
),
|
|
540
|
+
target_positions AS (
|
|
541
|
+
SELECT row_num as target_pos
|
|
542
|
+
FROM numbered_messages
|
|
543
|
+
WHERE id = ?
|
|
544
|
+
)
|
|
545
|
+
SELECT DISTINCT m.*
|
|
546
|
+
FROM numbered_messages m
|
|
547
|
+
CROSS JOIN target_positions t
|
|
548
|
+
WHERE m.row_num BETWEEN (t.target_pos - ?) AND (t.target_pos + ?)
|
|
549
|
+
)
|
|
550
|
+
`, // Keep ASC for final sorting after fetching context
|
|
551
|
+
);
|
|
552
|
+
params.push(searchId, id, withPreviousMessages, withNextMessages);
|
|
553
|
+
}
|
|
554
|
+
const finalQuery = unionQueries.join(' UNION ALL ') + ' ORDER BY "createdAt" ASC';
|
|
555
|
+
const includedResult = await this.client.execute({ sql: finalQuery, args: params });
|
|
556
|
+
const includedRows = includedResult.rows?.map(row => this.parseRow(row));
|
|
557
|
+
const dedupedRows = Object.values(
|
|
558
|
+
includedRows.reduce(
|
|
559
|
+
(acc, row) => {
|
|
560
|
+
acc[row.id] = row;
|
|
561
|
+
return acc;
|
|
562
|
+
},
|
|
563
|
+
{} as Record<string, MastraMessageV2>,
|
|
564
|
+
),
|
|
565
|
+
);
|
|
566
|
+
return dedupedRows;
|
|
473
567
|
}
|
|
474
568
|
|
|
475
569
|
/**
|
|
@@ -489,7 +583,7 @@ export class LibSQLStore extends MastraStorage {
|
|
|
489
583
|
const limit = typeof selectBy?.last === `number` ? selectBy.last : 40;
|
|
490
584
|
|
|
491
585
|
if (selectBy?.include?.length) {
|
|
492
|
-
const includeMessages = await this._getIncludedMessages(threadId, selectBy);
|
|
586
|
+
const includeMessages = await this._getIncludedMessages({ threadId, selectBy });
|
|
493
587
|
if (includeMessages) {
|
|
494
588
|
messages.push(...includeMessages);
|
|
495
589
|
}
|
|
@@ -497,12 +591,20 @@ export class LibSQLStore extends MastraStorage {
|
|
|
497
591
|
|
|
498
592
|
const excludeIds = messages.map(m => m.id);
|
|
499
593
|
const remainingSql = `
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
594
|
+
SELECT
|
|
595
|
+
id,
|
|
596
|
+
content,
|
|
597
|
+
role,
|
|
598
|
+
type,
|
|
599
|
+
"createdAt",
|
|
600
|
+
thread_id,
|
|
601
|
+
"resourceId"
|
|
602
|
+
FROM "${TABLE_MESSAGES}"
|
|
603
|
+
WHERE thread_id = ?
|
|
604
|
+
${excludeIds.length ? `AND id NOT IN (${excludeIds.map(() => '?').join(', ')})` : ''}
|
|
605
|
+
ORDER BY "createdAt" DESC
|
|
606
|
+
LIMIT ?
|
|
607
|
+
`;
|
|
506
608
|
const remainingArgs = [threadId, ...(excludeIds.length ? excludeIds : []), limit];
|
|
507
609
|
const remainingResult = await this.client.execute({ sql: remainingSql, args: remainingArgs });
|
|
508
610
|
if (remainingResult.rows) {
|
|
@@ -531,7 +633,7 @@ export class LibSQLStore extends MastraStorage {
|
|
|
531
633
|
const messages: MastraMessageV2[] = [];
|
|
532
634
|
|
|
533
635
|
if (selectBy?.include?.length) {
|
|
534
|
-
const includeMessages = await this._getIncludedMessages(threadId, selectBy);
|
|
636
|
+
const includeMessages = await this._getIncludedMessages({ threadId, selectBy });
|
|
535
637
|
if (includeMessages) {
|
|
536
638
|
messages.push(...includeMessages);
|
|
537
639
|
}
|
|
@@ -613,16 +715,27 @@ export class LibSQLStore extends MastraStorage {
|
|
|
613
715
|
// Prepare batch statements for all messages
|
|
614
716
|
const batchStatements = messages.map(message => {
|
|
615
717
|
const time = message.createdAt || new Date();
|
|
718
|
+
if (!message.threadId) {
|
|
719
|
+
throw new Error(
|
|
720
|
+
`Expected to find a threadId for message, but couldn't find one. An unexpected error has occurred.`,
|
|
721
|
+
);
|
|
722
|
+
}
|
|
723
|
+
if (!message.resourceId) {
|
|
724
|
+
throw new Error(
|
|
725
|
+
`Expected to find a resourceId for message, but couldn't find one. An unexpected error has occurred.`,
|
|
726
|
+
);
|
|
727
|
+
}
|
|
616
728
|
return {
|
|
617
|
-
sql: `INSERT INTO ${TABLE_MESSAGES} (id, thread_id, content, role, type, createdAt)
|
|
618
|
-
VALUES (?, ?, ?, ?, ?, ?)`,
|
|
729
|
+
sql: `INSERT INTO ${TABLE_MESSAGES} (id, thread_id, content, role, type, createdAt, resourceId)
|
|
730
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
619
731
|
args: [
|
|
620
732
|
message.id,
|
|
621
|
-
threadId
|
|
733
|
+
message.threadId!,
|
|
622
734
|
typeof message.content === 'object' ? JSON.stringify(message.content) : message.content,
|
|
623
735
|
message.role,
|
|
624
736
|
message.type || 'v2',
|
|
625
737
|
time instanceof Date ? time.toISOString() : time,
|
|
738
|
+
message.resourceId,
|
|
626
739
|
],
|
|
627
740
|
};
|
|
628
741
|
});
|