@mastra/lance 0.0.0-new-scorer-api-20250801075530 → 0.0.0-new-button-export-20251219130424
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1111 -3
- package/README.md +64 -7
- package/dist/index.cjs +1525 -1403
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1526 -1404
- package/dist/index.js.map +1 -1
- package/dist/storage/{domains/operations → db}/index.d.ts +21 -2
- package/dist/storage/db/index.d.ts.map +1 -0
- package/dist/storage/db/utils.d.ts.map +1 -0
- package/dist/storage/domains/memory/index.d.ts +23 -39
- package/dist/storage/domains/memory/index.d.ts.map +1 -1
- package/dist/storage/domains/scores/index.d.ts +29 -9
- package/dist/storage/domains/scores/index.d.ts.map +1 -1
- package/dist/storage/domains/workflows/index.d.ts +28 -14
- package/dist/storage/domains/workflows/index.d.ts.map +1 -1
- package/dist/storage/index.d.ts +88 -109
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/vector/filter.d.ts +5 -5
- package/dist/vector/index.d.ts +8 -5
- package/dist/vector/index.d.ts.map +1 -1
- package/package.json +29 -11
- package/dist/storage/domains/legacy-evals/index.d.ts +0 -25
- package/dist/storage/domains/legacy-evals/index.d.ts.map +0 -1
- package/dist/storage/domains/operations/index.d.ts.map +0 -1
- package/dist/storage/domains/traces/index.d.ts +0 -34
- package/dist/storage/domains/traces/index.d.ts.map +0 -1
- package/dist/storage/domains/utils.d.ts.map +0 -1
- package/eslint.config.js +0 -6
- package/src/index.ts +0 -2
- package/src/storage/domains/legacy-evals/index.ts +0 -156
- package/src/storage/domains/memory/index.ts +0 -947
- package/src/storage/domains/operations/index.ts +0 -489
- package/src/storage/domains/scores/index.ts +0 -221
- package/src/storage/domains/traces/index.ts +0 -212
- package/src/storage/domains/utils.ts +0 -158
- package/src/storage/domains/workflows/index.ts +0 -207
- package/src/storage/index.test.ts +0 -10
- package/src/storage/index.ts +0 -442
- package/src/vector/filter.test.ts +0 -295
- package/src/vector/filter.ts +0 -443
- package/src/vector/index.test.ts +0 -1493
- package/src/vector/index.ts +0 -941
- package/src/vector/types.ts +0 -16
- package/tsconfig.build.json +0 -9
- package/tsconfig.json +0 -5
- package/tsup.config.ts +0 -22
- package/vitest.config.ts +0 -11
- /package/dist/storage/{domains → db}/utils.d.ts +0 -0
package/dist/index.js
CHANGED
|
@@ -1,132 +1,18 @@
|
|
|
1
1
|
import { connect, Index } from '@lancedb/lancedb';
|
|
2
2
|
import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
|
|
3
|
-
import { MastraStorage,
|
|
3
|
+
import { MastraStorage, createStorageErrorId, createVectorErrorId, MemoryStorage, TABLE_SCHEMAS, TABLE_THREADS, TABLE_MESSAGES, TABLE_RESOURCES, normalizePerPage, calculatePagination, ScoresStorage, SCORERS_SCHEMA, TABLE_SCORERS, WorkflowsStorage, TABLE_WORKFLOW_SNAPSHOT, getDefaultValue, ensureDate } from '@mastra/core/storage';
|
|
4
4
|
import { MessageList } from '@mastra/core/agent';
|
|
5
|
+
import { MastraBase } from '@mastra/core/base';
|
|
5
6
|
import { Utf8, Float64, Binary, Float32, Int32, Field, Schema } from 'apache-arrow';
|
|
7
|
+
import { saveScorePayloadSchema } from '@mastra/core/evals';
|
|
6
8
|
import { MastraVector } from '@mastra/core/vector';
|
|
7
9
|
import { BaseFilterTranslator } from '@mastra/core/vector/filter';
|
|
8
10
|
|
|
9
11
|
// src/storage/index.ts
|
|
10
|
-
var StoreLegacyEvalsLance = class extends LegacyEvalsStorage {
|
|
11
|
-
client;
|
|
12
|
-
constructor({ client }) {
|
|
13
|
-
super();
|
|
14
|
-
this.client = client;
|
|
15
|
-
}
|
|
16
|
-
async getEvalsByAgentName(agentName, type) {
|
|
17
|
-
try {
|
|
18
|
-
const table = await this.client.openTable(TABLE_EVALS);
|
|
19
|
-
const query = table.query().where(`agent_name = '${agentName}'`);
|
|
20
|
-
const records = await query.toArray();
|
|
21
|
-
let filteredRecords = records;
|
|
22
|
-
if (type === "live") {
|
|
23
|
-
filteredRecords = records.filter((record) => record.test_info === null);
|
|
24
|
-
} else if (type === "test") {
|
|
25
|
-
filteredRecords = records.filter((record) => record.test_info !== null);
|
|
26
|
-
}
|
|
27
|
-
return filteredRecords.map((record) => {
|
|
28
|
-
return {
|
|
29
|
-
id: record.id,
|
|
30
|
-
input: record.input,
|
|
31
|
-
output: record.output,
|
|
32
|
-
agentName: record.agent_name,
|
|
33
|
-
metricName: record.metric_name,
|
|
34
|
-
result: JSON.parse(record.result),
|
|
35
|
-
instructions: record.instructions,
|
|
36
|
-
testInfo: record.test_info ? JSON.parse(record.test_info) : null,
|
|
37
|
-
globalRunId: record.global_run_id,
|
|
38
|
-
runId: record.run_id,
|
|
39
|
-
createdAt: new Date(record.created_at).toString()
|
|
40
|
-
};
|
|
41
|
-
});
|
|
42
|
-
} catch (error) {
|
|
43
|
-
throw new MastraError(
|
|
44
|
-
{
|
|
45
|
-
id: "LANCE_STORE_GET_EVALS_BY_AGENT_NAME_FAILED",
|
|
46
|
-
domain: ErrorDomain.STORAGE,
|
|
47
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
48
|
-
details: { agentName }
|
|
49
|
-
},
|
|
50
|
-
error
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
async getEvals(options) {
|
|
55
|
-
try {
|
|
56
|
-
const table = await this.client.openTable(TABLE_EVALS);
|
|
57
|
-
const conditions = [];
|
|
58
|
-
if (options.agentName) {
|
|
59
|
-
conditions.push(`agent_name = '${options.agentName}'`);
|
|
60
|
-
}
|
|
61
|
-
if (options.type === "live") {
|
|
62
|
-
conditions.push("length(test_info) = 0");
|
|
63
|
-
} else if (options.type === "test") {
|
|
64
|
-
conditions.push("length(test_info) > 0");
|
|
65
|
-
}
|
|
66
|
-
const startDate = options.dateRange?.start || options.fromDate;
|
|
67
|
-
const endDate = options.dateRange?.end || options.toDate;
|
|
68
|
-
if (startDate) {
|
|
69
|
-
conditions.push(`\`created_at\` >= ${startDate.getTime()}`);
|
|
70
|
-
}
|
|
71
|
-
if (endDate) {
|
|
72
|
-
conditions.push(`\`created_at\` <= ${endDate.getTime()}`);
|
|
73
|
-
}
|
|
74
|
-
let total = 0;
|
|
75
|
-
if (conditions.length > 0) {
|
|
76
|
-
total = await table.countRows(conditions.join(" AND "));
|
|
77
|
-
} else {
|
|
78
|
-
total = await table.countRows();
|
|
79
|
-
}
|
|
80
|
-
const query = table.query();
|
|
81
|
-
if (conditions.length > 0) {
|
|
82
|
-
const whereClause = conditions.join(" AND ");
|
|
83
|
-
query.where(whereClause);
|
|
84
|
-
}
|
|
85
|
-
const records = await query.toArray();
|
|
86
|
-
const evals = records.sort((a, b) => b.created_at - a.created_at).map((record) => {
|
|
87
|
-
return {
|
|
88
|
-
id: record.id,
|
|
89
|
-
input: record.input,
|
|
90
|
-
output: record.output,
|
|
91
|
-
agentName: record.agent_name,
|
|
92
|
-
metricName: record.metric_name,
|
|
93
|
-
result: JSON.parse(record.result),
|
|
94
|
-
instructions: record.instructions,
|
|
95
|
-
testInfo: record.test_info ? JSON.parse(record.test_info) : null,
|
|
96
|
-
globalRunId: record.global_run_id,
|
|
97
|
-
runId: record.run_id,
|
|
98
|
-
createdAt: new Date(record.created_at).toISOString()
|
|
99
|
-
};
|
|
100
|
-
});
|
|
101
|
-
const page = options.page || 0;
|
|
102
|
-
const perPage = options.perPage || 10;
|
|
103
|
-
const pagedEvals = evals.slice(page * perPage, (page + 1) * perPage);
|
|
104
|
-
return {
|
|
105
|
-
evals: pagedEvals,
|
|
106
|
-
total,
|
|
107
|
-
page,
|
|
108
|
-
perPage,
|
|
109
|
-
hasMore: total > (page + 1) * perPage
|
|
110
|
-
};
|
|
111
|
-
} catch (error) {
|
|
112
|
-
throw new MastraError(
|
|
113
|
-
{
|
|
114
|
-
id: "LANCE_STORE_GET_EVALS_FAILED",
|
|
115
|
-
domain: ErrorDomain.STORAGE,
|
|
116
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
117
|
-
details: { agentName: options.agentName ?? "" }
|
|
118
|
-
},
|
|
119
|
-
error
|
|
120
|
-
);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
};
|
|
124
12
|
function getPrimaryKeys(tableName) {
|
|
125
13
|
let primaryId = ["id"];
|
|
126
14
|
if (tableName === TABLE_WORKFLOW_SNAPSHOT) {
|
|
127
15
|
primaryId = ["workflow_name", "run_id"];
|
|
128
|
-
} else if (tableName === TABLE_EVALS) {
|
|
129
|
-
primaryId = ["agent_name", "metric_name", "run_id"];
|
|
130
16
|
}
|
|
131
17
|
return primaryId;
|
|
132
18
|
}
|
|
@@ -187,7 +73,6 @@ function processResultWithTypeConversion(rawResult, tableSchema) {
|
|
|
187
73
|
} else if (fieldTypeStr.includes("float64") && ["createdAt", "updatedAt"].includes(key)) {
|
|
188
74
|
processedResult[key] = new Date(processedResult[key]);
|
|
189
75
|
}
|
|
190
|
-
console.log(key, "processedResult", processedResult);
|
|
191
76
|
}
|
|
192
77
|
return processedResult;
|
|
193
78
|
}
|
|
@@ -205,7 +90,7 @@ async function getTableSchema({
|
|
|
205
90
|
} catch (validationError) {
|
|
206
91
|
throw new MastraError(
|
|
207
92
|
{
|
|
208
|
-
id: "
|
|
93
|
+
id: createStorageErrorId("LANCE", "GET_TABLE_SCHEMA", "INVALID_ARGS"),
|
|
209
94
|
domain: ErrorDomain.STORAGE,
|
|
210
95
|
category: ErrorCategory.USER,
|
|
211
96
|
text: validationError.message,
|
|
@@ -228,7 +113,7 @@ async function getTableSchema({
|
|
|
228
113
|
} catch (error) {
|
|
229
114
|
throw new MastraError(
|
|
230
115
|
{
|
|
231
|
-
id: "
|
|
116
|
+
id: createStorageErrorId("LANCE", "GET_TABLE_SCHEMA", "FAILED"),
|
|
232
117
|
domain: ErrorDomain.STORAGE,
|
|
233
118
|
category: ErrorCategory.THIRD_PARTY,
|
|
234
119
|
details: { tableName }
|
|
@@ -238,625 +123,503 @@ async function getTableSchema({
|
|
|
238
123
|
}
|
|
239
124
|
}
|
|
240
125
|
|
|
241
|
-
// src/storage/
|
|
242
|
-
|
|
126
|
+
// src/storage/db/index.ts
|
|
127
|
+
function resolveLanceConfig(config) {
|
|
128
|
+
return config.client;
|
|
129
|
+
}
|
|
130
|
+
var LanceDB = class extends MastraBase {
|
|
243
131
|
client;
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
super();
|
|
132
|
+
constructor({ client }) {
|
|
133
|
+
super({ name: "lance-db" });
|
|
247
134
|
this.client = client;
|
|
248
|
-
this.operations = operations;
|
|
249
135
|
}
|
|
250
|
-
|
|
136
|
+
getDefaultValue(type) {
|
|
137
|
+
switch (type) {
|
|
138
|
+
case "text":
|
|
139
|
+
return "''";
|
|
140
|
+
case "timestamp":
|
|
141
|
+
return "CURRENT_TIMESTAMP";
|
|
142
|
+
case "integer":
|
|
143
|
+
case "bigint":
|
|
144
|
+
return "0";
|
|
145
|
+
case "jsonb":
|
|
146
|
+
return "'{}'";
|
|
147
|
+
case "uuid":
|
|
148
|
+
return "''";
|
|
149
|
+
default:
|
|
150
|
+
return getDefaultValue(type);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
async hasColumn(tableName, columnName) {
|
|
154
|
+
const table = await this.client.openTable(tableName);
|
|
155
|
+
const schema = await table.schema();
|
|
156
|
+
return schema.fields.some((field) => field.name === columnName);
|
|
157
|
+
}
|
|
158
|
+
translateSchema(schema) {
|
|
159
|
+
const fields = Object.entries(schema).map(([name, column]) => {
|
|
160
|
+
let arrowType;
|
|
161
|
+
switch (column.type.toLowerCase()) {
|
|
162
|
+
case "text":
|
|
163
|
+
case "uuid":
|
|
164
|
+
arrowType = new Utf8();
|
|
165
|
+
break;
|
|
166
|
+
case "int":
|
|
167
|
+
case "integer":
|
|
168
|
+
arrowType = new Int32();
|
|
169
|
+
break;
|
|
170
|
+
case "bigint":
|
|
171
|
+
arrowType = new Float64();
|
|
172
|
+
break;
|
|
173
|
+
case "float":
|
|
174
|
+
arrowType = new Float32();
|
|
175
|
+
break;
|
|
176
|
+
case "jsonb":
|
|
177
|
+
case "json":
|
|
178
|
+
arrowType = new Utf8();
|
|
179
|
+
break;
|
|
180
|
+
case "binary":
|
|
181
|
+
arrowType = new Binary();
|
|
182
|
+
break;
|
|
183
|
+
case "timestamp":
|
|
184
|
+
arrowType = new Float64();
|
|
185
|
+
break;
|
|
186
|
+
default:
|
|
187
|
+
arrowType = new Utf8();
|
|
188
|
+
}
|
|
189
|
+
return new Field(name, arrowType, column.nullable ?? true);
|
|
190
|
+
});
|
|
191
|
+
return new Schema(fields);
|
|
192
|
+
}
|
|
193
|
+
async createTable({
|
|
194
|
+
tableName,
|
|
195
|
+
schema
|
|
196
|
+
}) {
|
|
251
197
|
try {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
198
|
+
if (!this.client) {
|
|
199
|
+
throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
|
|
200
|
+
}
|
|
201
|
+
if (!tableName) {
|
|
202
|
+
throw new Error("tableName is required for createTable.");
|
|
203
|
+
}
|
|
204
|
+
if (!schema) {
|
|
205
|
+
throw new Error("schema is required for createTable.");
|
|
255
206
|
}
|
|
256
|
-
return {
|
|
257
|
-
...thread,
|
|
258
|
-
createdAt: new Date(thread.createdAt),
|
|
259
|
-
updatedAt: new Date(thread.updatedAt)
|
|
260
|
-
};
|
|
261
207
|
} catch (error) {
|
|
262
208
|
throw new MastraError(
|
|
263
209
|
{
|
|
264
|
-
id: "
|
|
210
|
+
id: createStorageErrorId("LANCE", "CREATE_TABLE", "INVALID_ARGS"),
|
|
265
211
|
domain: ErrorDomain.STORAGE,
|
|
266
|
-
category: ErrorCategory.
|
|
212
|
+
category: ErrorCategory.USER,
|
|
213
|
+
details: { tableName }
|
|
267
214
|
},
|
|
268
215
|
error
|
|
269
216
|
);
|
|
270
217
|
}
|
|
271
|
-
}
|
|
272
|
-
async getThreadsByResourceId({ resourceId }) {
|
|
273
218
|
try {
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
const records = await query.toArray();
|
|
277
|
-
return processResultWithTypeConversion(
|
|
278
|
-
records,
|
|
279
|
-
await getTableSchema({ tableName: TABLE_THREADS, client: this.client })
|
|
280
|
-
);
|
|
219
|
+
const arrowSchema = this.translateSchema(schema);
|
|
220
|
+
await this.client.createEmptyTable(tableName, arrowSchema);
|
|
281
221
|
} catch (error) {
|
|
222
|
+
if (error.message?.includes("already exists")) {
|
|
223
|
+
this.logger.debug(`Table '${tableName}' already exists, skipping create`);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
282
226
|
throw new MastraError(
|
|
283
227
|
{
|
|
284
|
-
id: "
|
|
228
|
+
id: createStorageErrorId("LANCE", "CREATE_TABLE", "FAILED"),
|
|
285
229
|
domain: ErrorDomain.STORAGE,
|
|
286
|
-
category: ErrorCategory.THIRD_PARTY
|
|
230
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
231
|
+
details: { tableName }
|
|
287
232
|
},
|
|
288
233
|
error
|
|
289
234
|
);
|
|
290
235
|
}
|
|
291
236
|
}
|
|
292
|
-
|
|
293
|
-
* Saves a thread to the database. This function doesn't overwrite existing threads.
|
|
294
|
-
* @param thread - The thread to save
|
|
295
|
-
* @returns The saved thread
|
|
296
|
-
*/
|
|
297
|
-
async saveThread({ thread }) {
|
|
237
|
+
async dropTable({ tableName }) {
|
|
298
238
|
try {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
239
|
+
if (!this.client) {
|
|
240
|
+
throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
|
|
241
|
+
}
|
|
242
|
+
if (!tableName) {
|
|
243
|
+
throw new Error("tableName is required for dropTable.");
|
|
244
|
+
}
|
|
245
|
+
} catch (validationError) {
|
|
304
246
|
throw new MastraError(
|
|
305
247
|
{
|
|
306
|
-
id: "
|
|
248
|
+
id: createStorageErrorId("LANCE", "DROP_TABLE", "INVALID_ARGS"),
|
|
307
249
|
domain: ErrorDomain.STORAGE,
|
|
308
|
-
category: ErrorCategory.
|
|
250
|
+
category: ErrorCategory.USER,
|
|
251
|
+
text: validationError.message,
|
|
252
|
+
details: { tableName }
|
|
309
253
|
},
|
|
310
|
-
|
|
254
|
+
validationError
|
|
311
255
|
);
|
|
312
256
|
}
|
|
313
|
-
}
|
|
314
|
-
async updateThread({
|
|
315
|
-
id,
|
|
316
|
-
title,
|
|
317
|
-
metadata
|
|
318
|
-
}) {
|
|
319
|
-
const maxRetries = 5;
|
|
320
|
-
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
321
|
-
try {
|
|
322
|
-
const current = await this.getThreadById({ threadId: id });
|
|
323
|
-
if (!current) {
|
|
324
|
-
throw new Error(`Thread with id ${id} not found`);
|
|
325
|
-
}
|
|
326
|
-
const mergedMetadata = { ...current.metadata, ...metadata };
|
|
327
|
-
const record = {
|
|
328
|
-
id,
|
|
329
|
-
title,
|
|
330
|
-
metadata: JSON.stringify(mergedMetadata),
|
|
331
|
-
updatedAt: (/* @__PURE__ */ new Date()).getTime()
|
|
332
|
-
};
|
|
333
|
-
const table = await this.client.openTable(TABLE_THREADS);
|
|
334
|
-
await table.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([record]);
|
|
335
|
-
const updatedThread = await this.getThreadById({ threadId: id });
|
|
336
|
-
if (!updatedThread) {
|
|
337
|
-
throw new Error(`Failed to retrieve updated thread ${id}`);
|
|
338
|
-
}
|
|
339
|
-
return updatedThread;
|
|
340
|
-
} catch (error) {
|
|
341
|
-
if (error.message?.includes("Commit conflict") && attempt < maxRetries - 1) {
|
|
342
|
-
const delay = Math.pow(2, attempt) * 10;
|
|
343
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
344
|
-
continue;
|
|
345
|
-
}
|
|
346
|
-
throw new MastraError(
|
|
347
|
-
{
|
|
348
|
-
id: "LANCE_STORE_UPDATE_THREAD_FAILED",
|
|
349
|
-
domain: ErrorDomain.STORAGE,
|
|
350
|
-
category: ErrorCategory.THIRD_PARTY
|
|
351
|
-
},
|
|
352
|
-
error
|
|
353
|
-
);
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
throw new MastraError(
|
|
357
|
-
{
|
|
358
|
-
id: "LANCE_STORE_UPDATE_THREAD_FAILED",
|
|
359
|
-
domain: ErrorDomain.STORAGE,
|
|
360
|
-
category: ErrorCategory.THIRD_PARTY
|
|
361
|
-
},
|
|
362
|
-
new Error("All retries exhausted")
|
|
363
|
-
);
|
|
364
|
-
}
|
|
365
|
-
async deleteThread({ threadId }) {
|
|
366
257
|
try {
|
|
367
|
-
|
|
368
|
-
await table.delete(`id = '${threadId}'`);
|
|
369
|
-
const messagesTable = await this.client.openTable(TABLE_MESSAGES);
|
|
370
|
-
await messagesTable.delete(`thread_id = '${threadId}'`);
|
|
258
|
+
await this.client.dropTable(tableName);
|
|
371
259
|
} catch (error) {
|
|
260
|
+
if (error.toString().includes("was not found") || error.message?.includes("Table not found")) {
|
|
261
|
+
this.logger.debug(`Table '${tableName}' does not exist, skipping drop`);
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
372
264
|
throw new MastraError(
|
|
373
265
|
{
|
|
374
|
-
id: "
|
|
266
|
+
id: createStorageErrorId("LANCE", "DROP_TABLE", "FAILED"),
|
|
375
267
|
domain: ErrorDomain.STORAGE,
|
|
376
|
-
category: ErrorCategory.THIRD_PARTY
|
|
268
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
269
|
+
details: { tableName }
|
|
377
270
|
},
|
|
378
271
|
error
|
|
379
272
|
);
|
|
380
273
|
}
|
|
381
274
|
}
|
|
382
|
-
async
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
format,
|
|
387
|
-
threadConfig
|
|
275
|
+
async alterTable({
|
|
276
|
+
tableName,
|
|
277
|
+
schema,
|
|
278
|
+
ifNotExists
|
|
388
279
|
}) {
|
|
389
280
|
try {
|
|
390
|
-
if (
|
|
391
|
-
throw new Error("
|
|
392
|
-
}
|
|
393
|
-
const limit = resolveMessageLimit({ last: selectBy?.last, defaultLimit: Number.MAX_SAFE_INTEGER });
|
|
394
|
-
const table = await this.client.openTable(TABLE_MESSAGES);
|
|
395
|
-
let allRecords = [];
|
|
396
|
-
if (selectBy?.include && selectBy.include.length > 0) {
|
|
397
|
-
const threadIds = [...new Set(selectBy.include.map((item) => item.threadId))];
|
|
398
|
-
for (const threadId2 of threadIds) {
|
|
399
|
-
const threadQuery = table.query().where(`thread_id = '${threadId2}'`);
|
|
400
|
-
let threadRecords = await threadQuery.toArray();
|
|
401
|
-
allRecords.push(...threadRecords);
|
|
402
|
-
}
|
|
403
|
-
} else {
|
|
404
|
-
let query = table.query().where(`\`thread_id\` = '${threadId}'`);
|
|
405
|
-
allRecords = await query.toArray();
|
|
281
|
+
if (!this.client) {
|
|
282
|
+
throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
|
|
406
283
|
}
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
const dateB = new Date(b.createdAt).getTime();
|
|
410
|
-
return dateA - dateB;
|
|
411
|
-
});
|
|
412
|
-
if (selectBy?.include && selectBy.include.length > 0) {
|
|
413
|
-
allRecords = this.processMessagesWithContext(allRecords, selectBy.include);
|
|
284
|
+
if (!tableName) {
|
|
285
|
+
throw new Error("tableName is required for alterTable.");
|
|
414
286
|
}
|
|
415
|
-
if (
|
|
416
|
-
|
|
287
|
+
if (!schema) {
|
|
288
|
+
throw new Error("schema is required for alterTable.");
|
|
417
289
|
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
290
|
+
if (!ifNotExists || ifNotExists.length === 0) {
|
|
291
|
+
this.logger.debug("No columns specified to add in alterTable, skipping.");
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
} catch (validationError) {
|
|
295
|
+
throw new MastraError(
|
|
296
|
+
{
|
|
297
|
+
id: createStorageErrorId("LANCE", "ALTER_TABLE", "INVALID_ARGS"),
|
|
298
|
+
domain: ErrorDomain.STORAGE,
|
|
299
|
+
category: ErrorCategory.USER,
|
|
300
|
+
text: validationError.message,
|
|
301
|
+
details: { tableName }
|
|
302
|
+
},
|
|
303
|
+
validationError
|
|
421
304
|
);
|
|
422
|
-
|
|
423
|
-
|
|
305
|
+
}
|
|
306
|
+
try {
|
|
307
|
+
const table = await this.client.openTable(tableName);
|
|
308
|
+
const currentSchema = await table.schema();
|
|
309
|
+
const existingFields = new Set(currentSchema.fields.map((f) => f.name));
|
|
310
|
+
const typeMap = {
|
|
311
|
+
text: "string",
|
|
312
|
+
integer: "int",
|
|
313
|
+
bigint: "bigint",
|
|
314
|
+
timestamp: "timestamp",
|
|
315
|
+
jsonb: "string",
|
|
316
|
+
uuid: "string"
|
|
317
|
+
};
|
|
318
|
+
const columnsToAdd = ifNotExists.filter((col) => schema[col] && !existingFields.has(col)).map((col) => {
|
|
319
|
+
const colDef = schema[col];
|
|
424
320
|
return {
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
content: typeof msg.content === "string" ? (() => {
|
|
428
|
-
try {
|
|
429
|
-
return JSON.parse(msg.content);
|
|
430
|
-
} catch {
|
|
431
|
-
return msg.content;
|
|
432
|
-
}
|
|
433
|
-
})() : msg.content
|
|
321
|
+
name: col,
|
|
322
|
+
valueSql: colDef?.nullable ? `cast(NULL as ${typeMap[colDef.type ?? "text"]})` : `cast(${this.getDefaultValue(colDef?.type ?? "text")} as ${typeMap[colDef?.type ?? "text"]})`
|
|
434
323
|
};
|
|
435
324
|
});
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
325
|
+
if (columnsToAdd.length > 0) {
|
|
326
|
+
await table.addColumns(columnsToAdd);
|
|
327
|
+
this.logger?.info?.(`Added columns [${columnsToAdd.map((c) => c.name).join(", ")}] to table ${tableName}`);
|
|
328
|
+
}
|
|
439
329
|
} catch (error) {
|
|
440
330
|
throw new MastraError(
|
|
441
331
|
{
|
|
442
|
-
id: "
|
|
332
|
+
id: createStorageErrorId("LANCE", "ALTER_TABLE", "FAILED"),
|
|
443
333
|
domain: ErrorDomain.STORAGE,
|
|
444
|
-
category: ErrorCategory.THIRD_PARTY
|
|
334
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
335
|
+
details: { tableName }
|
|
445
336
|
},
|
|
446
337
|
error
|
|
447
338
|
);
|
|
448
339
|
}
|
|
449
340
|
}
|
|
450
|
-
async
|
|
341
|
+
async clearTable({ tableName }) {
|
|
451
342
|
try {
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
return [];
|
|
455
|
-
}
|
|
456
|
-
const threadId = messages[0]?.threadId;
|
|
457
|
-
if (!threadId) {
|
|
458
|
-
throw new Error("Thread ID is required");
|
|
343
|
+
if (!this.client) {
|
|
344
|
+
throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
|
|
459
345
|
}
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
throw new Error("Message ID is required");
|
|
463
|
-
}
|
|
464
|
-
if (!message.threadId) {
|
|
465
|
-
throw new Error("Thread ID is required for all messages");
|
|
466
|
-
}
|
|
467
|
-
if (message.resourceId === null || message.resourceId === void 0) {
|
|
468
|
-
throw new Error("Resource ID cannot be null or undefined");
|
|
469
|
-
}
|
|
470
|
-
if (!message.content) {
|
|
471
|
-
throw new Error("Message content is required");
|
|
472
|
-
}
|
|
346
|
+
if (!tableName) {
|
|
347
|
+
throw new Error("tableName is required for clearTable.");
|
|
473
348
|
}
|
|
474
|
-
|
|
475
|
-
const { threadId: threadId2, type, ...rest } = message;
|
|
476
|
-
return {
|
|
477
|
-
...rest,
|
|
478
|
-
thread_id: threadId2,
|
|
479
|
-
type: type ?? "v2",
|
|
480
|
-
content: JSON.stringify(message.content)
|
|
481
|
-
};
|
|
482
|
-
});
|
|
483
|
-
const table = await this.client.openTable(TABLE_MESSAGES);
|
|
484
|
-
await table.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute(transformedMessages);
|
|
485
|
-
const threadsTable = await this.client.openTable(TABLE_THREADS);
|
|
486
|
-
const currentTime = (/* @__PURE__ */ new Date()).getTime();
|
|
487
|
-
const updateRecord = { id: threadId, updatedAt: currentTime };
|
|
488
|
-
await threadsTable.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([updateRecord]);
|
|
489
|
-
const list = new MessageList().add(messages, "memory");
|
|
490
|
-
if (format === `v2`) return list.get.all.v2();
|
|
491
|
-
return list.get.all.v1();
|
|
492
|
-
} catch (error) {
|
|
349
|
+
} catch (validationError) {
|
|
493
350
|
throw new MastraError(
|
|
494
351
|
{
|
|
495
|
-
id: "
|
|
352
|
+
id: createStorageErrorId("LANCE", "CLEAR_TABLE", "INVALID_ARGS"),
|
|
496
353
|
domain: ErrorDomain.STORAGE,
|
|
497
|
-
category: ErrorCategory.
|
|
354
|
+
category: ErrorCategory.USER,
|
|
355
|
+
text: validationError.message,
|
|
356
|
+
details: { tableName }
|
|
498
357
|
},
|
|
499
|
-
|
|
358
|
+
validationError
|
|
500
359
|
);
|
|
501
360
|
}
|
|
502
|
-
}
|
|
503
|
-
async getThreadsByResourceIdPaginated(args) {
|
|
504
361
|
try {
|
|
505
|
-
const
|
|
506
|
-
|
|
507
|
-
const total = await table.countRows(`\`resourceId\` = '${resourceId}'`);
|
|
508
|
-
const query = table.query().where(`\`resourceId\` = '${resourceId}'`);
|
|
509
|
-
const offset = page * perPage;
|
|
510
|
-
query.limit(perPage);
|
|
511
|
-
if (offset > 0) {
|
|
512
|
-
query.offset(offset);
|
|
513
|
-
}
|
|
514
|
-
const records = await query.toArray();
|
|
515
|
-
records.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());
|
|
516
|
-
const schema = await getTableSchema({ tableName: TABLE_THREADS, client: this.client });
|
|
517
|
-
const threads = records.map((record) => processResultWithTypeConversion(record, schema));
|
|
518
|
-
return {
|
|
519
|
-
threads,
|
|
520
|
-
total,
|
|
521
|
-
page,
|
|
522
|
-
perPage,
|
|
523
|
-
hasMore: total > (page + 1) * perPage
|
|
524
|
-
};
|
|
362
|
+
const table = await this.client.openTable(tableName);
|
|
363
|
+
await table.delete("1=1");
|
|
525
364
|
} catch (error) {
|
|
526
365
|
throw new MastraError(
|
|
527
366
|
{
|
|
528
|
-
id: "
|
|
367
|
+
id: createStorageErrorId("LANCE", "CLEAR_TABLE", "FAILED"),
|
|
529
368
|
domain: ErrorDomain.STORAGE,
|
|
530
|
-
category: ErrorCategory.THIRD_PARTY
|
|
369
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
370
|
+
details: { tableName }
|
|
531
371
|
},
|
|
532
372
|
error
|
|
533
373
|
);
|
|
534
374
|
}
|
|
535
375
|
}
|
|
536
|
-
|
|
537
|
-
* Processes messages to include context messages based on withPreviousMessages and withNextMessages
|
|
538
|
-
* @param records - The sorted array of records to process
|
|
539
|
-
* @param include - The array of include specifications with context parameters
|
|
540
|
-
* @returns The processed array with context messages included
|
|
541
|
-
*/
|
|
542
|
-
processMessagesWithContext(records, include) {
|
|
543
|
-
const messagesWithContext = include.filter((item) => item.withPreviousMessages || item.withNextMessages);
|
|
544
|
-
if (messagesWithContext.length === 0) {
|
|
545
|
-
return records;
|
|
546
|
-
}
|
|
547
|
-
const messageIndexMap = /* @__PURE__ */ new Map();
|
|
548
|
-
records.forEach((message, index) => {
|
|
549
|
-
messageIndexMap.set(message.id, index);
|
|
550
|
-
});
|
|
551
|
-
const additionalIndices = /* @__PURE__ */ new Set();
|
|
552
|
-
for (const item of messagesWithContext) {
|
|
553
|
-
const messageIndex = messageIndexMap.get(item.id);
|
|
554
|
-
if (messageIndex !== void 0) {
|
|
555
|
-
if (item.withPreviousMessages) {
|
|
556
|
-
const startIdx = Math.max(0, messageIndex - item.withPreviousMessages);
|
|
557
|
-
for (let i = startIdx; i < messageIndex; i++) {
|
|
558
|
-
additionalIndices.add(i);
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
if (item.withNextMessages) {
|
|
562
|
-
const endIdx = Math.min(records.length - 1, messageIndex + item.withNextMessages);
|
|
563
|
-
for (let i = messageIndex + 1; i <= endIdx; i++) {
|
|
564
|
-
additionalIndices.add(i);
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
if (additionalIndices.size === 0) {
|
|
570
|
-
return records;
|
|
571
|
-
}
|
|
572
|
-
const originalMatchIds = new Set(include.map((item) => item.id));
|
|
573
|
-
const allIndices = /* @__PURE__ */ new Set();
|
|
574
|
-
records.forEach((record, index) => {
|
|
575
|
-
if (originalMatchIds.has(record.id)) {
|
|
576
|
-
allIndices.add(index);
|
|
577
|
-
}
|
|
578
|
-
});
|
|
579
|
-
additionalIndices.forEach((index) => {
|
|
580
|
-
allIndices.add(index);
|
|
581
|
-
});
|
|
582
|
-
return Array.from(allIndices).sort((a, b) => a - b).map((index) => records[index]);
|
|
583
|
-
}
|
|
584
|
-
async getMessagesPaginated(args) {
|
|
376
|
+
async insert({ tableName, record }) {
|
|
585
377
|
try {
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
throw new Error("Thread ID is required for getMessagesPaginated");
|
|
589
|
-
}
|
|
590
|
-
const page = selectBy?.pagination?.page ?? 0;
|
|
591
|
-
const perPage = selectBy?.pagination?.perPage ?? 10;
|
|
592
|
-
const dateRange = selectBy?.pagination?.dateRange;
|
|
593
|
-
const fromDate = dateRange?.start;
|
|
594
|
-
const toDate = dateRange?.end;
|
|
595
|
-
const table = await this.client.openTable(TABLE_MESSAGES);
|
|
596
|
-
const messages = [];
|
|
597
|
-
if (selectBy?.include && Array.isArray(selectBy.include)) {
|
|
598
|
-
const threadIds = [...new Set(selectBy.include.map((item) => item.threadId))];
|
|
599
|
-
const allThreadMessages = [];
|
|
600
|
-
for (const threadId2 of threadIds) {
|
|
601
|
-
const threadQuery = table.query().where(`thread_id = '${threadId2}'`);
|
|
602
|
-
let threadRecords = await threadQuery.toArray();
|
|
603
|
-
if (fromDate) threadRecords = threadRecords.filter((m) => m.createdAt >= fromDate.getTime());
|
|
604
|
-
if (toDate) threadRecords = threadRecords.filter((m) => m.createdAt <= toDate.getTime());
|
|
605
|
-
allThreadMessages.push(...threadRecords);
|
|
606
|
-
}
|
|
607
|
-
allThreadMessages.sort((a, b) => a.createdAt - b.createdAt);
|
|
608
|
-
const contextMessages = this.processMessagesWithContext(allThreadMessages, selectBy.include);
|
|
609
|
-
messages.push(...contextMessages);
|
|
610
|
-
}
|
|
611
|
-
const conditions = [`thread_id = '${threadId}'`];
|
|
612
|
-
if (resourceId) {
|
|
613
|
-
conditions.push(`\`resourceId\` = '${resourceId}'`);
|
|
614
|
-
}
|
|
615
|
-
if (fromDate) {
|
|
616
|
-
conditions.push(`\`createdAt\` >= ${fromDate.getTime()}`);
|
|
617
|
-
}
|
|
618
|
-
if (toDate) {
|
|
619
|
-
conditions.push(`\`createdAt\` <= ${toDate.getTime()}`);
|
|
378
|
+
if (!this.client) {
|
|
379
|
+
throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
|
|
620
380
|
}
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
total = await table.countRows(conditions.join(" AND "));
|
|
624
|
-
} else {
|
|
625
|
-
total = await table.countRows();
|
|
381
|
+
if (!tableName) {
|
|
382
|
+
throw new Error("tableName is required for insert.");
|
|
626
383
|
}
|
|
627
|
-
if (
|
|
628
|
-
|
|
629
|
-
messages: [],
|
|
630
|
-
total: 0,
|
|
631
|
-
page,
|
|
632
|
-
perPage,
|
|
633
|
-
hasMore: false
|
|
634
|
-
};
|
|
384
|
+
if (!record || Object.keys(record).length === 0) {
|
|
385
|
+
throw new Error("record is required and cannot be empty for insert.");
|
|
635
386
|
}
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
if (excludeIds.length > 0) {
|
|
657
|
-
records = records.filter((m) => !excludeIds.includes(m.id));
|
|
387
|
+
} catch (validationError) {
|
|
388
|
+
throw new MastraError(
|
|
389
|
+
{
|
|
390
|
+
id: createStorageErrorId("LANCE", "INSERT", "INVALID_ARGS"),
|
|
391
|
+
domain: ErrorDomain.STORAGE,
|
|
392
|
+
category: ErrorCategory.USER,
|
|
393
|
+
text: validationError.message,
|
|
394
|
+
details: { tableName }
|
|
395
|
+
},
|
|
396
|
+
validationError
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
try {
|
|
400
|
+
const table = await this.client.openTable(tableName);
|
|
401
|
+
const primaryId = getPrimaryKeys(tableName);
|
|
402
|
+
const processedRecord = { ...record };
|
|
403
|
+
for (const key in processedRecord) {
|
|
404
|
+
if (processedRecord[key] !== null && typeof processedRecord[key] === "object" && !(processedRecord[key] instanceof Date)) {
|
|
405
|
+
this.logger.debug("Converting object to JSON string: ", processedRecord[key]);
|
|
406
|
+
processedRecord[key] = JSON.stringify(processedRecord[key]);
|
|
658
407
|
}
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
const allMessages = [...messages, ...selectedMessages];
|
|
662
|
-
const seen = /* @__PURE__ */ new Set();
|
|
663
|
-
const dedupedMessages = allMessages.filter((m) => {
|
|
664
|
-
const key = `${m.id}:${m.thread_id}`;
|
|
665
|
-
if (seen.has(key)) return false;
|
|
666
|
-
seen.add(key);
|
|
667
|
-
return true;
|
|
668
|
-
});
|
|
669
|
-
const formattedMessages = dedupedMessages.map((msg) => {
|
|
670
|
-
const { thread_id, ...rest } = msg;
|
|
671
|
-
return {
|
|
672
|
-
...rest,
|
|
673
|
-
threadId: thread_id,
|
|
674
|
-
content: typeof msg.content === "string" ? (() => {
|
|
675
|
-
try {
|
|
676
|
-
return JSON.parse(msg.content);
|
|
677
|
-
} catch {
|
|
678
|
-
return msg.content;
|
|
679
|
-
}
|
|
680
|
-
})() : msg.content
|
|
681
|
-
};
|
|
682
|
-
});
|
|
683
|
-
const list = new MessageList().add(formattedMessages, "memory");
|
|
684
|
-
return {
|
|
685
|
-
messages: format === "v2" ? list.get.all.v2() : list.get.all.v1(),
|
|
686
|
-
total,
|
|
687
|
-
// Total should be the count of messages matching the filters
|
|
688
|
-
page,
|
|
689
|
-
perPage,
|
|
690
|
-
hasMore: total > (page + 1) * perPage
|
|
691
|
-
};
|
|
408
|
+
}
|
|
409
|
+
await table.mergeInsert(primaryId).whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([processedRecord]);
|
|
692
410
|
} catch (error) {
|
|
693
411
|
throw new MastraError(
|
|
694
412
|
{
|
|
695
|
-
id: "
|
|
413
|
+
id: createStorageErrorId("LANCE", "INSERT", "FAILED"),
|
|
696
414
|
domain: ErrorDomain.STORAGE,
|
|
697
|
-
category: ErrorCategory.THIRD_PARTY
|
|
415
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
416
|
+
details: { tableName }
|
|
698
417
|
},
|
|
699
418
|
error
|
|
700
419
|
);
|
|
701
420
|
}
|
|
702
421
|
}
|
|
703
|
-
|
|
704
|
-
* Parse message data from LanceDB record format to MastraMessageV2 format
|
|
705
|
-
*/
|
|
706
|
-
parseMessageData(data) {
|
|
707
|
-
const { thread_id, ...rest } = data;
|
|
708
|
-
return {
|
|
709
|
-
...rest,
|
|
710
|
-
threadId: thread_id,
|
|
711
|
-
content: typeof data.content === "string" ? (() => {
|
|
712
|
-
try {
|
|
713
|
-
return JSON.parse(data.content);
|
|
714
|
-
} catch {
|
|
715
|
-
return data.content;
|
|
716
|
-
}
|
|
717
|
-
})() : data.content,
|
|
718
|
-
createdAt: new Date(data.createdAt),
|
|
719
|
-
updatedAt: new Date(data.updatedAt)
|
|
720
|
-
};
|
|
721
|
-
}
|
|
722
|
-
async updateMessages(args) {
|
|
723
|
-
const { messages } = args;
|
|
724
|
-
this.logger.debug("Updating messages", { count: messages.length });
|
|
725
|
-
if (!messages.length) {
|
|
726
|
-
return [];
|
|
727
|
-
}
|
|
728
|
-
const updatedMessages = [];
|
|
729
|
-
const affectedThreadIds = /* @__PURE__ */ new Set();
|
|
422
|
+
async batchInsert({ tableName, records }) {
|
|
730
423
|
try {
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
const existingMessage = await this.operations.load({ tableName: TABLE_MESSAGES, keys: { id } });
|
|
734
|
-
if (!existingMessage) {
|
|
735
|
-
this.logger.warn("Message not found for update", { id });
|
|
736
|
-
continue;
|
|
737
|
-
}
|
|
738
|
-
const existingMsg = this.parseMessageData(existingMessage);
|
|
739
|
-
const originalThreadId = existingMsg.threadId;
|
|
740
|
-
affectedThreadIds.add(originalThreadId);
|
|
741
|
-
const updatePayload = {};
|
|
742
|
-
if ("role" in updates && updates.role !== void 0) updatePayload.role = updates.role;
|
|
743
|
-
if ("type" in updates && updates.type !== void 0) updatePayload.type = updates.type;
|
|
744
|
-
if ("resourceId" in updates && updates.resourceId !== void 0) updatePayload.resourceId = updates.resourceId;
|
|
745
|
-
if ("threadId" in updates && updates.threadId !== void 0 && updates.threadId !== null) {
|
|
746
|
-
updatePayload.thread_id = updates.threadId;
|
|
747
|
-
affectedThreadIds.add(updates.threadId);
|
|
748
|
-
}
|
|
749
|
-
if (updates.content) {
|
|
750
|
-
const existingContent = existingMsg.content;
|
|
751
|
-
let newContent = { ...existingContent };
|
|
752
|
-
if (updates.content.metadata !== void 0) {
|
|
753
|
-
newContent.metadata = {
|
|
754
|
-
...existingContent.metadata || {},
|
|
755
|
-
...updates.content.metadata || {}
|
|
756
|
-
};
|
|
757
|
-
}
|
|
758
|
-
if (updates.content.content !== void 0) {
|
|
759
|
-
newContent.content = updates.content.content;
|
|
760
|
-
}
|
|
761
|
-
if ("parts" in updates.content && updates.content.parts !== void 0) {
|
|
762
|
-
newContent.parts = updates.content.parts;
|
|
763
|
-
}
|
|
764
|
-
updatePayload.content = JSON.stringify(newContent);
|
|
765
|
-
}
|
|
766
|
-
await this.operations.insert({ tableName: TABLE_MESSAGES, record: { id, ...updatePayload } });
|
|
767
|
-
const updatedMessage = await this.operations.load({ tableName: TABLE_MESSAGES, keys: { id } });
|
|
768
|
-
if (updatedMessage) {
|
|
769
|
-
updatedMessages.push(this.parseMessageData(updatedMessage));
|
|
770
|
-
}
|
|
424
|
+
if (!this.client) {
|
|
425
|
+
throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
|
|
771
426
|
}
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
tableName: TABLE_THREADS,
|
|
775
|
-
record: { id: threadId, updatedAt: Date.now() }
|
|
776
|
-
});
|
|
427
|
+
if (!tableName) {
|
|
428
|
+
throw new Error("tableName is required for batchInsert.");
|
|
777
429
|
}
|
|
778
|
-
|
|
430
|
+
if (!records || records.length === 0) {
|
|
431
|
+
throw new Error("records array is required and cannot be empty for batchInsert.");
|
|
432
|
+
}
|
|
433
|
+
} catch (validationError) {
|
|
434
|
+
throw new MastraError(
|
|
435
|
+
{
|
|
436
|
+
id: createStorageErrorId("LANCE", "BATCH_INSERT", "INVALID_ARGS"),
|
|
437
|
+
domain: ErrorDomain.STORAGE,
|
|
438
|
+
category: ErrorCategory.USER,
|
|
439
|
+
text: validationError.message,
|
|
440
|
+
details: { tableName }
|
|
441
|
+
},
|
|
442
|
+
validationError
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
try {
|
|
446
|
+
const table = await this.client.openTable(tableName);
|
|
447
|
+
const primaryId = getPrimaryKeys(tableName);
|
|
448
|
+
const processedRecords = records.map((record) => {
|
|
449
|
+
const processedRecord = { ...record };
|
|
450
|
+
for (const key in processedRecord) {
|
|
451
|
+
if (processedRecord[key] == null) continue;
|
|
452
|
+
if (processedRecord[key] !== null && typeof processedRecord[key] === "object" && !(processedRecord[key] instanceof Date)) {
|
|
453
|
+
processedRecord[key] = JSON.stringify(processedRecord[key]);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
return processedRecord;
|
|
457
|
+
});
|
|
458
|
+
await table.mergeInsert(primaryId).whenMatchedUpdateAll().whenNotMatchedInsertAll().execute(processedRecords);
|
|
779
459
|
} catch (error) {
|
|
780
460
|
throw new MastraError(
|
|
781
461
|
{
|
|
782
|
-
id: "
|
|
462
|
+
id: createStorageErrorId("LANCE", "BATCH_INSERT", "FAILED"),
|
|
783
463
|
domain: ErrorDomain.STORAGE,
|
|
784
464
|
category: ErrorCategory.THIRD_PARTY,
|
|
785
|
-
details: {
|
|
465
|
+
details: { tableName }
|
|
786
466
|
},
|
|
787
467
|
error
|
|
788
468
|
);
|
|
789
469
|
}
|
|
790
470
|
}
|
|
791
|
-
async
|
|
471
|
+
async load({ tableName, keys }) {
|
|
792
472
|
try {
|
|
793
|
-
|
|
794
|
-
|
|
473
|
+
if (!this.client) {
|
|
474
|
+
throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
|
|
475
|
+
}
|
|
476
|
+
if (!tableName) {
|
|
477
|
+
throw new Error("tableName is required for load.");
|
|
478
|
+
}
|
|
479
|
+
if (!keys || Object.keys(keys).length === 0) {
|
|
480
|
+
throw new Error("keys are required and cannot be empty for load.");
|
|
481
|
+
}
|
|
482
|
+
} catch (validationError) {
|
|
483
|
+
throw new MastraError(
|
|
484
|
+
{
|
|
485
|
+
id: createStorageErrorId("LANCE", "LOAD", "INVALID_ARGS"),
|
|
486
|
+
domain: ErrorDomain.STORAGE,
|
|
487
|
+
category: ErrorCategory.USER,
|
|
488
|
+
text: validationError.message,
|
|
489
|
+
details: { tableName }
|
|
490
|
+
},
|
|
491
|
+
validationError
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
try {
|
|
495
|
+
const table = await this.client.openTable(tableName);
|
|
496
|
+
const tableSchema = await getTableSchema({ tableName, client: this.client });
|
|
497
|
+
const query = table.query();
|
|
498
|
+
if (Object.keys(keys).length > 0) {
|
|
499
|
+
validateKeyTypes(keys, tableSchema);
|
|
500
|
+
const filterConditions = Object.entries(keys).map(([key, value]) => {
|
|
501
|
+
const isCamelCase = /^[a-z][a-zA-Z]*$/.test(key) && /[A-Z]/.test(key);
|
|
502
|
+
const quotedKey = isCamelCase ? `\`${key}\`` : key;
|
|
503
|
+
if (typeof value === "string") {
|
|
504
|
+
return `${quotedKey} = '${value}'`;
|
|
505
|
+
} else if (value === null) {
|
|
506
|
+
return `${quotedKey} IS NULL`;
|
|
507
|
+
} else {
|
|
508
|
+
return `${quotedKey} = ${value}`;
|
|
509
|
+
}
|
|
510
|
+
}).join(" AND ");
|
|
511
|
+
this.logger.debug("where clause generated: " + filterConditions);
|
|
512
|
+
query.where(filterConditions);
|
|
513
|
+
}
|
|
514
|
+
const result = await query.limit(1).toArray();
|
|
515
|
+
if (result.length === 0) {
|
|
516
|
+
this.logger.debug("No record found");
|
|
795
517
|
return null;
|
|
796
518
|
}
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
519
|
+
return processResultWithTypeConversion(result[0], tableSchema);
|
|
520
|
+
} catch (error) {
|
|
521
|
+
if (error instanceof MastraError) throw error;
|
|
522
|
+
throw new MastraError(
|
|
523
|
+
{
|
|
524
|
+
id: createStorageErrorId("LANCE", "LOAD", "FAILED"),
|
|
525
|
+
domain: ErrorDomain.STORAGE,
|
|
526
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
527
|
+
details: { tableName, keyCount: Object.keys(keys).length, firstKey: Object.keys(keys)[0] ?? "" }
|
|
528
|
+
},
|
|
529
|
+
error
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
// src/storage/domains/memory/index.ts
|
|
536
|
+
var StoreMemoryLance = class extends MemoryStorage {
|
|
537
|
+
client;
|
|
538
|
+
#db;
|
|
539
|
+
constructor(config) {
|
|
540
|
+
super();
|
|
541
|
+
const client = resolveLanceConfig(config);
|
|
542
|
+
this.client = client;
|
|
543
|
+
this.#db = new LanceDB({ client });
|
|
544
|
+
}
|
|
545
|
+
async init() {
|
|
546
|
+
await this.#db.createTable({ tableName: TABLE_THREADS, schema: TABLE_SCHEMAS[TABLE_THREADS] });
|
|
547
|
+
await this.#db.createTable({ tableName: TABLE_MESSAGES, schema: TABLE_SCHEMAS[TABLE_MESSAGES] });
|
|
548
|
+
await this.#db.createTable({ tableName: TABLE_RESOURCES, schema: TABLE_SCHEMAS[TABLE_RESOURCES] });
|
|
549
|
+
await this.#db.alterTable({
|
|
550
|
+
tableName: TABLE_MESSAGES,
|
|
551
|
+
schema: TABLE_SCHEMAS[TABLE_MESSAGES],
|
|
552
|
+
ifNotExists: ["resourceId"]
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
async dangerouslyClearAll() {
|
|
556
|
+
await this.#db.clearTable({ tableName: TABLE_THREADS });
|
|
557
|
+
await this.#db.clearTable({ tableName: TABLE_MESSAGES });
|
|
558
|
+
await this.#db.clearTable({ tableName: TABLE_RESOURCES });
|
|
559
|
+
}
|
|
560
|
+
async deleteMessages(messageIds) {
|
|
561
|
+
if (!messageIds || messageIds.length === 0) {
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
this.logger.debug("Deleting messages", { count: messageIds.length });
|
|
565
|
+
try {
|
|
566
|
+
const threadIds = /* @__PURE__ */ new Set();
|
|
567
|
+
for (const messageId of messageIds) {
|
|
568
|
+
const message = await this.#db.load({ tableName: TABLE_MESSAGES, keys: { id: messageId } });
|
|
569
|
+
if (message?.thread_id) {
|
|
570
|
+
threadIds.add(message.thread_id);
|
|
811
571
|
}
|
|
812
|
-
} catch {
|
|
813
|
-
createdAt = /* @__PURE__ */ new Date();
|
|
814
572
|
}
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
573
|
+
const messagesTable = await this.client.openTable(TABLE_MESSAGES);
|
|
574
|
+
const idConditions = messageIds.map((id) => `id = '${this.escapeSql(id)}'`).join(" OR ");
|
|
575
|
+
await messagesTable.delete(idConditions);
|
|
576
|
+
const now = (/* @__PURE__ */ new Date()).getTime();
|
|
577
|
+
const threadsTable = await this.client.openTable(TABLE_THREADS);
|
|
578
|
+
for (const threadId of threadIds) {
|
|
579
|
+
const thread = await this.getThreadById({ threadId });
|
|
580
|
+
if (thread) {
|
|
581
|
+
const record = {
|
|
582
|
+
id: threadId,
|
|
583
|
+
resourceId: thread.resourceId,
|
|
584
|
+
title: thread.title,
|
|
585
|
+
metadata: JSON.stringify(thread.metadata),
|
|
586
|
+
createdAt: new Date(thread.createdAt).getTime(),
|
|
587
|
+
updatedAt: now
|
|
588
|
+
};
|
|
589
|
+
await threadsTable.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([record]);
|
|
827
590
|
}
|
|
828
|
-
} catch {
|
|
829
|
-
updatedAt = /* @__PURE__ */ new Date();
|
|
830
|
-
}
|
|
831
|
-
let workingMemory = resource.workingMemory;
|
|
832
|
-
if (workingMemory === null || workingMemory === void 0) {
|
|
833
|
-
workingMemory = void 0;
|
|
834
|
-
} else if (workingMemory === "") {
|
|
835
|
-
workingMemory = "";
|
|
836
|
-
} else if (typeof workingMemory === "object") {
|
|
837
|
-
workingMemory = JSON.stringify(workingMemory);
|
|
838
591
|
}
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
592
|
+
} catch (error) {
|
|
593
|
+
throw new MastraError(
|
|
594
|
+
{
|
|
595
|
+
id: createStorageErrorId("LANCE", "DELETE_MESSAGES", "FAILED"),
|
|
596
|
+
domain: ErrorDomain.STORAGE,
|
|
597
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
598
|
+
details: { count: messageIds.length }
|
|
599
|
+
},
|
|
600
|
+
error
|
|
601
|
+
);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
// Utility to escape single quotes in SQL strings
|
|
605
|
+
escapeSql(str) {
|
|
606
|
+
return str.replace(/'/g, "''");
|
|
607
|
+
}
|
|
608
|
+
async getThreadById({ threadId }) {
|
|
609
|
+
try {
|
|
610
|
+
const thread = await this.#db.load({ tableName: TABLE_THREADS, keys: { id: threadId } });
|
|
611
|
+
if (!thread) {
|
|
612
|
+
return null;
|
|
848
613
|
}
|
|
849
614
|
return {
|
|
850
|
-
...
|
|
851
|
-
createdAt,
|
|
852
|
-
updatedAt
|
|
853
|
-
workingMemory,
|
|
854
|
-
metadata
|
|
615
|
+
...thread,
|
|
616
|
+
createdAt: new Date(thread.createdAt),
|
|
617
|
+
updatedAt: new Date(thread.updatedAt)
|
|
855
618
|
};
|
|
856
619
|
} catch (error) {
|
|
857
620
|
throw new MastraError(
|
|
858
621
|
{
|
|
859
|
-
id: "
|
|
622
|
+
id: createStorageErrorId("LANCE", "GET_THREAD_BY_ID", "FAILED"),
|
|
860
623
|
domain: ErrorDomain.STORAGE,
|
|
861
624
|
category: ErrorCategory.THIRD_PARTY
|
|
862
625
|
},
|
|
@@ -864,23 +627,21 @@ var StoreMemoryLance = class extends MemoryStorage {
|
|
|
864
627
|
);
|
|
865
628
|
}
|
|
866
629
|
}
|
|
867
|
-
|
|
630
|
+
/**
|
|
631
|
+
* Saves a thread to the database. This function doesn't overwrite existing threads.
|
|
632
|
+
* @param thread - The thread to save
|
|
633
|
+
* @returns The saved thread
|
|
634
|
+
*/
|
|
635
|
+
async saveThread({ thread }) {
|
|
868
636
|
try {
|
|
869
|
-
const record = {
|
|
870
|
-
|
|
871
|
-
metadata: resource.metadata ? JSON.stringify(resource.metadata) : "",
|
|
872
|
-
createdAt: resource.createdAt.getTime(),
|
|
873
|
-
// Store as timestamp (milliseconds)
|
|
874
|
-
updatedAt: resource.updatedAt.getTime()
|
|
875
|
-
// Store as timestamp (milliseconds)
|
|
876
|
-
};
|
|
877
|
-
const table = await this.client.openTable(TABLE_RESOURCES);
|
|
637
|
+
const record = { ...thread, metadata: JSON.stringify(thread.metadata) };
|
|
638
|
+
const table = await this.client.openTable(TABLE_THREADS);
|
|
878
639
|
await table.add([record], { mode: "append" });
|
|
879
|
-
return
|
|
640
|
+
return thread;
|
|
880
641
|
} catch (error) {
|
|
881
642
|
throw new MastraError(
|
|
882
643
|
{
|
|
883
|
-
id: "
|
|
644
|
+
id: createStorageErrorId("LANCE", "SAVE_THREAD", "FAILED"),
|
|
884
645
|
domain: ErrorDomain.STORAGE,
|
|
885
646
|
category: ErrorCategory.THIRD_PARTY
|
|
886
647
|
},
|
|
@@ -888,44 +649,32 @@ var StoreMemoryLance = class extends MemoryStorage {
|
|
|
888
649
|
);
|
|
889
650
|
}
|
|
890
651
|
}
|
|
891
|
-
async
|
|
892
|
-
|
|
893
|
-
|
|
652
|
+
async updateThread({
|
|
653
|
+
id,
|
|
654
|
+
title,
|
|
894
655
|
metadata
|
|
895
656
|
}) {
|
|
896
|
-
const maxRetries =
|
|
657
|
+
const maxRetries = 5;
|
|
897
658
|
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
898
659
|
try {
|
|
899
|
-
const
|
|
900
|
-
if (!
|
|
901
|
-
|
|
902
|
-
id: resourceId,
|
|
903
|
-
workingMemory,
|
|
904
|
-
metadata: metadata || {},
|
|
905
|
-
createdAt: /* @__PURE__ */ new Date(),
|
|
906
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
907
|
-
};
|
|
908
|
-
return this.saveResource({ resource: newResource });
|
|
660
|
+
const current = await this.getThreadById({ threadId: id });
|
|
661
|
+
if (!current) {
|
|
662
|
+
throw new Error(`Thread with id ${id} not found`);
|
|
909
663
|
}
|
|
910
|
-
const
|
|
911
|
-
...existingResource,
|
|
912
|
-
workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
|
|
913
|
-
metadata: {
|
|
914
|
-
...existingResource.metadata,
|
|
915
|
-
...metadata
|
|
916
|
-
},
|
|
917
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
918
|
-
};
|
|
664
|
+
const mergedMetadata = { ...current.metadata, ...metadata };
|
|
919
665
|
const record = {
|
|
920
|
-
id
|
|
921
|
-
|
|
922
|
-
metadata:
|
|
923
|
-
updatedAt:
|
|
924
|
-
// Store as timestamp (milliseconds)
|
|
666
|
+
id,
|
|
667
|
+
title,
|
|
668
|
+
metadata: JSON.stringify(mergedMetadata),
|
|
669
|
+
updatedAt: (/* @__PURE__ */ new Date()).getTime()
|
|
925
670
|
};
|
|
926
|
-
const table = await this.client.openTable(
|
|
671
|
+
const table = await this.client.openTable(TABLE_THREADS);
|
|
927
672
|
await table.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([record]);
|
|
928
|
-
|
|
673
|
+
const updatedThread = await this.getThreadById({ threadId: id });
|
|
674
|
+
if (!updatedThread) {
|
|
675
|
+
throw new Error(`Failed to retrieve updated thread ${id}`);
|
|
676
|
+
}
|
|
677
|
+
return updatedThread;
|
|
929
678
|
} catch (error) {
|
|
930
679
|
if (error.message?.includes("Commit conflict") && attempt < maxRetries - 1) {
|
|
931
680
|
const delay = Math.pow(2, attempt) * 10;
|
|
@@ -934,7 +683,7 @@ var StoreMemoryLance = class extends MemoryStorage {
|
|
|
934
683
|
}
|
|
935
684
|
throw new MastraError(
|
|
936
685
|
{
|
|
937
|
-
id: "
|
|
686
|
+
id: createStorageErrorId("LANCE", "UPDATE_THREAD", "FAILED"),
|
|
938
687
|
domain: ErrorDomain.STORAGE,
|
|
939
688
|
category: ErrorCategory.THIRD_PARTY
|
|
940
689
|
},
|
|
@@ -942,444 +691,679 @@ var StoreMemoryLance = class extends MemoryStorage {
|
|
|
942
691
|
);
|
|
943
692
|
}
|
|
944
693
|
}
|
|
945
|
-
throw new
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
}
|
|
954
|
-
getDefaultValue(type) {
|
|
955
|
-
switch (type) {
|
|
956
|
-
case "text":
|
|
957
|
-
return "''";
|
|
958
|
-
case "timestamp":
|
|
959
|
-
return "CURRENT_TIMESTAMP";
|
|
960
|
-
case "integer":
|
|
961
|
-
case "bigint":
|
|
962
|
-
return "0";
|
|
963
|
-
case "jsonb":
|
|
964
|
-
return "'{}'";
|
|
965
|
-
case "uuid":
|
|
966
|
-
return "''";
|
|
967
|
-
default:
|
|
968
|
-
return super.getDefaultValue(type);
|
|
969
|
-
}
|
|
970
|
-
}
|
|
971
|
-
async hasColumn(tableName, columnName) {
|
|
972
|
-
const table = await this.client.openTable(tableName);
|
|
973
|
-
const schema = await table.schema();
|
|
974
|
-
return schema.fields.some((field) => field.name === columnName);
|
|
975
|
-
}
|
|
976
|
-
translateSchema(schema) {
|
|
977
|
-
const fields = Object.entries(schema).map(([name, column]) => {
|
|
978
|
-
let arrowType;
|
|
979
|
-
switch (column.type.toLowerCase()) {
|
|
980
|
-
case "text":
|
|
981
|
-
case "uuid":
|
|
982
|
-
arrowType = new Utf8();
|
|
983
|
-
break;
|
|
984
|
-
case "int":
|
|
985
|
-
case "integer":
|
|
986
|
-
arrowType = new Int32();
|
|
987
|
-
break;
|
|
988
|
-
case "bigint":
|
|
989
|
-
arrowType = new Float64();
|
|
990
|
-
break;
|
|
991
|
-
case "float":
|
|
992
|
-
arrowType = new Float32();
|
|
993
|
-
break;
|
|
994
|
-
case "jsonb":
|
|
995
|
-
case "json":
|
|
996
|
-
arrowType = new Utf8();
|
|
997
|
-
break;
|
|
998
|
-
case "binary":
|
|
999
|
-
arrowType = new Binary();
|
|
1000
|
-
break;
|
|
1001
|
-
case "timestamp":
|
|
1002
|
-
arrowType = new Float64();
|
|
1003
|
-
break;
|
|
1004
|
-
default:
|
|
1005
|
-
arrowType = new Utf8();
|
|
1006
|
-
}
|
|
1007
|
-
return new Field(name, arrowType, column.nullable ?? true);
|
|
1008
|
-
});
|
|
1009
|
-
return new Schema(fields);
|
|
694
|
+
throw new MastraError(
|
|
695
|
+
{
|
|
696
|
+
id: createStorageErrorId("LANCE", "UPDATE_THREAD", "FAILED"),
|
|
697
|
+
domain: ErrorDomain.STORAGE,
|
|
698
|
+
category: ErrorCategory.THIRD_PARTY
|
|
699
|
+
},
|
|
700
|
+
new Error("All retries exhausted")
|
|
701
|
+
);
|
|
1010
702
|
}
|
|
1011
|
-
async
|
|
1012
|
-
tableName,
|
|
1013
|
-
schema
|
|
1014
|
-
}) {
|
|
703
|
+
async deleteThread({ threadId }) {
|
|
1015
704
|
try {
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
throw new Error("tableName is required for createTable.");
|
|
1021
|
-
}
|
|
1022
|
-
if (!schema) {
|
|
1023
|
-
throw new Error("schema is required for createTable.");
|
|
1024
|
-
}
|
|
705
|
+
const table = await this.client.openTable(TABLE_THREADS);
|
|
706
|
+
await table.delete(`id = '${threadId}'`);
|
|
707
|
+
const messagesTable = await this.client.openTable(TABLE_MESSAGES);
|
|
708
|
+
await messagesTable.delete(`thread_id = '${threadId}'`);
|
|
1025
709
|
} catch (error) {
|
|
1026
710
|
throw new MastraError(
|
|
1027
711
|
{
|
|
1028
|
-
id: "
|
|
712
|
+
id: createStorageErrorId("LANCE", "DELETE_THREAD", "FAILED"),
|
|
1029
713
|
domain: ErrorDomain.STORAGE,
|
|
1030
|
-
category: ErrorCategory.
|
|
1031
|
-
details: { tableName }
|
|
714
|
+
category: ErrorCategory.THIRD_PARTY
|
|
1032
715
|
},
|
|
1033
716
|
error
|
|
1034
717
|
);
|
|
1035
718
|
}
|
|
719
|
+
}
|
|
720
|
+
normalizeMessage(message) {
|
|
721
|
+
const { thread_id, ...rest } = message;
|
|
722
|
+
return {
|
|
723
|
+
...rest,
|
|
724
|
+
threadId: thread_id,
|
|
725
|
+
content: typeof message.content === "string" ? (() => {
|
|
726
|
+
try {
|
|
727
|
+
return JSON.parse(message.content);
|
|
728
|
+
} catch {
|
|
729
|
+
return message.content;
|
|
730
|
+
}
|
|
731
|
+
})() : message.content
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
async listMessagesById({ messageIds }) {
|
|
735
|
+
if (messageIds.length === 0) return { messages: [] };
|
|
1036
736
|
try {
|
|
1037
|
-
const
|
|
1038
|
-
|
|
737
|
+
const table = await this.client.openTable(TABLE_MESSAGES);
|
|
738
|
+
const quotedIds = messageIds.map((id) => `'${id}'`).join(", ");
|
|
739
|
+
const allRecords = await table.query().where(`id IN (${quotedIds})`).toArray();
|
|
740
|
+
const messages = processResultWithTypeConversion(
|
|
741
|
+
allRecords,
|
|
742
|
+
await getTableSchema({ tableName: TABLE_MESSAGES, client: this.client })
|
|
743
|
+
);
|
|
744
|
+
const list = new MessageList().add(
|
|
745
|
+
messages.map(this.normalizeMessage),
|
|
746
|
+
"memory"
|
|
747
|
+
);
|
|
748
|
+
return { messages: list.get.all.db() };
|
|
1039
749
|
} catch (error) {
|
|
1040
|
-
if (error.message?.includes("already exists")) {
|
|
1041
|
-
this.logger.debug(`Table '${tableName}' already exists, skipping create`);
|
|
1042
|
-
return;
|
|
1043
|
-
}
|
|
1044
750
|
throw new MastraError(
|
|
1045
751
|
{
|
|
1046
|
-
id: "
|
|
752
|
+
id: createStorageErrorId("LANCE", "LIST_MESSAGES_BY_ID", "FAILED"),
|
|
1047
753
|
domain: ErrorDomain.STORAGE,
|
|
1048
754
|
category: ErrorCategory.THIRD_PARTY,
|
|
1049
|
-
details: {
|
|
755
|
+
details: {
|
|
756
|
+
messageIds: JSON.stringify(messageIds)
|
|
757
|
+
}
|
|
1050
758
|
},
|
|
1051
759
|
error
|
|
1052
760
|
);
|
|
1053
761
|
}
|
|
1054
762
|
}
|
|
1055
|
-
async
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
}
|
|
1060
|
-
if (!tableName) {
|
|
1061
|
-
throw new Error("tableName is required for dropTable.");
|
|
1062
|
-
}
|
|
1063
|
-
} catch (validationError) {
|
|
1064
|
-
throw new MastraError(
|
|
1065
|
-
{
|
|
1066
|
-
id: "STORAGE_LANCE_STORAGE_DROP_TABLE_INVALID_ARGS",
|
|
1067
|
-
domain: ErrorDomain.STORAGE,
|
|
1068
|
-
category: ErrorCategory.USER,
|
|
1069
|
-
text: validationError.message,
|
|
1070
|
-
details: { tableName }
|
|
1071
|
-
},
|
|
1072
|
-
validationError
|
|
1073
|
-
);
|
|
1074
|
-
}
|
|
1075
|
-
try {
|
|
1076
|
-
await this.client.dropTable(tableName);
|
|
1077
|
-
} catch (error) {
|
|
1078
|
-
if (error.toString().includes("was not found") || error.message?.includes("Table not found")) {
|
|
1079
|
-
this.logger.debug(`Table '${tableName}' does not exist, skipping drop`);
|
|
1080
|
-
return;
|
|
1081
|
-
}
|
|
763
|
+
async listMessages(args) {
|
|
764
|
+
const { threadId, resourceId, include, filter, perPage: perPageInput, page = 0, orderBy } = args;
|
|
765
|
+
const threadIds = Array.isArray(threadId) ? threadId : [threadId];
|
|
766
|
+
if (threadIds.length === 0 || threadIds.some((id) => !id.trim())) {
|
|
1082
767
|
throw new MastraError(
|
|
1083
768
|
{
|
|
1084
|
-
id: "
|
|
769
|
+
id: createStorageErrorId("LANCE", "LIST_MESSAGES", "INVALID_THREAD_ID"),
|
|
1085
770
|
domain: ErrorDomain.STORAGE,
|
|
1086
771
|
category: ErrorCategory.THIRD_PARTY,
|
|
1087
|
-
details: {
|
|
772
|
+
details: { threadId: Array.isArray(threadId) ? threadId.join(",") : threadId }
|
|
1088
773
|
},
|
|
1089
|
-
|
|
774
|
+
new Error("threadId must be a non-empty string or array of non-empty strings")
|
|
1090
775
|
);
|
|
1091
776
|
}
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
tableName,
|
|
1095
|
-
schema,
|
|
1096
|
-
ifNotExists
|
|
1097
|
-
}) {
|
|
777
|
+
const perPage = normalizePerPage(perPageInput, 40);
|
|
778
|
+
const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
1098
779
|
try {
|
|
1099
|
-
if (
|
|
1100
|
-
throw new
|
|
780
|
+
if (page < 0) {
|
|
781
|
+
throw new MastraError(
|
|
782
|
+
{
|
|
783
|
+
id: createStorageErrorId("LANCE", "LIST_MESSAGES", "INVALID_PAGE"),
|
|
784
|
+
domain: ErrorDomain.STORAGE,
|
|
785
|
+
category: ErrorCategory.USER,
|
|
786
|
+
details: { page }
|
|
787
|
+
},
|
|
788
|
+
new Error("page must be >= 0")
|
|
789
|
+
);
|
|
1101
790
|
}
|
|
1102
|
-
|
|
1103
|
-
|
|
791
|
+
const { field, direction } = this.parseOrderBy(orderBy, "ASC");
|
|
792
|
+
const table = await this.client.openTable(TABLE_MESSAGES);
|
|
793
|
+
const threadCondition = threadIds.length === 1 ? `thread_id = '${this.escapeSql(threadIds[0])}'` : `thread_id IN (${threadIds.map((t) => `'${this.escapeSql(t)}'`).join(", ")})`;
|
|
794
|
+
const conditions = [threadCondition];
|
|
795
|
+
if (resourceId) {
|
|
796
|
+
conditions.push(`\`resourceId\` = '${this.escapeSql(resourceId)}'`);
|
|
1104
797
|
}
|
|
1105
|
-
if (
|
|
1106
|
-
|
|
798
|
+
if (filter?.dateRange?.start) {
|
|
799
|
+
const startTime = filter.dateRange.start instanceof Date ? filter.dateRange.start.getTime() : new Date(filter.dateRange.start).getTime();
|
|
800
|
+
conditions.push(`\`createdAt\` >= ${startTime}`);
|
|
1107
801
|
}
|
|
1108
|
-
if (
|
|
1109
|
-
|
|
1110
|
-
|
|
802
|
+
if (filter?.dateRange?.end) {
|
|
803
|
+
const endTime = filter.dateRange.end instanceof Date ? filter.dateRange.end.getTime() : new Date(filter.dateRange.end).getTime();
|
|
804
|
+
conditions.push(`\`createdAt\` <= ${endTime}`);
|
|
1111
805
|
}
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
const
|
|
1128
|
-
const
|
|
1129
|
-
|
|
1130
|
-
integer: "int",
|
|
1131
|
-
bigint: "bigint",
|
|
1132
|
-
timestamp: "timestamp",
|
|
1133
|
-
jsonb: "string",
|
|
1134
|
-
uuid: "string"
|
|
1135
|
-
};
|
|
1136
|
-
const columnsToAdd = ifNotExists.filter((col) => schema[col] && !existingFields.has(col)).map((col) => {
|
|
1137
|
-
const colDef = schema[col];
|
|
806
|
+
const whereClause = conditions.join(" AND ");
|
|
807
|
+
const total = await table.countRows(whereClause);
|
|
808
|
+
const query = table.query().where(whereClause);
|
|
809
|
+
let allRecords = await query.toArray();
|
|
810
|
+
allRecords.sort((a, b) => {
|
|
811
|
+
const aValue = field === "createdAt" ? a.createdAt : a[field];
|
|
812
|
+
const bValue = field === "createdAt" ? b.createdAt : b[field];
|
|
813
|
+
if (aValue == null && bValue == null) return 0;
|
|
814
|
+
if (aValue == null) return direction === "ASC" ? -1 : 1;
|
|
815
|
+
if (bValue == null) return direction === "ASC" ? 1 : -1;
|
|
816
|
+
if (typeof aValue === "string" && typeof bValue === "string") {
|
|
817
|
+
return direction === "ASC" ? aValue.localeCompare(bValue) : bValue.localeCompare(aValue);
|
|
818
|
+
}
|
|
819
|
+
return direction === "ASC" ? aValue - bValue : bValue - aValue;
|
|
820
|
+
});
|
|
821
|
+
const paginatedRecords = allRecords.slice(offset, offset + perPage);
|
|
822
|
+
const messages = paginatedRecords.map((row) => this.normalizeMessage(row));
|
|
823
|
+
if (total === 0 && messages.length === 0 && (!include || include.length === 0)) {
|
|
1138
824
|
return {
|
|
1139
|
-
|
|
1140
|
-
|
|
825
|
+
messages: [],
|
|
826
|
+
total: 0,
|
|
827
|
+
page,
|
|
828
|
+
perPage: perPageForResponse,
|
|
829
|
+
hasMore: false
|
|
1141
830
|
};
|
|
1142
|
-
});
|
|
1143
|
-
if (columnsToAdd.length > 0) {
|
|
1144
|
-
await table.addColumns(columnsToAdd);
|
|
1145
|
-
this.logger?.info?.(`Added columns [${columnsToAdd.map((c) => c.name).join(", ")}] to table ${tableName}`);
|
|
1146
831
|
}
|
|
832
|
+
const messageIds = new Set(messages.map((m) => m.id));
|
|
833
|
+
if (include && include.length > 0) {
|
|
834
|
+
const threadIds2 = [...new Set(include.map((item) => item.threadId || threadId))];
|
|
835
|
+
const allThreadMessages = [];
|
|
836
|
+
for (const tid of threadIds2) {
|
|
837
|
+
const threadQuery = table.query().where(`thread_id = '${tid}'`);
|
|
838
|
+
let threadRecords = await threadQuery.toArray();
|
|
839
|
+
allThreadMessages.push(...threadRecords);
|
|
840
|
+
}
|
|
841
|
+
allThreadMessages.sort((a, b) => a.createdAt - b.createdAt);
|
|
842
|
+
const contextMessages = this.processMessagesWithContext(allThreadMessages, include);
|
|
843
|
+
const includedMessages = contextMessages.map((row) => this.normalizeMessage(row));
|
|
844
|
+
for (const includeMsg of includedMessages) {
|
|
845
|
+
if (!messageIds.has(includeMsg.id)) {
|
|
846
|
+
messages.push(includeMsg);
|
|
847
|
+
messageIds.add(includeMsg.id);
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
const list = new MessageList().add(messages, "memory");
|
|
852
|
+
let finalMessages = list.get.all.db();
|
|
853
|
+
finalMessages = finalMessages.sort((a, b) => {
|
|
854
|
+
const aValue = field === "createdAt" ? new Date(a.createdAt).getTime() : a[field];
|
|
855
|
+
const bValue = field === "createdAt" ? new Date(b.createdAt).getTime() : b[field];
|
|
856
|
+
if (aValue == null && bValue == null) return 0;
|
|
857
|
+
if (aValue == null) return direction === "ASC" ? -1 : 1;
|
|
858
|
+
if (bValue == null) return direction === "ASC" ? 1 : -1;
|
|
859
|
+
if (typeof aValue === "string" && typeof bValue === "string") {
|
|
860
|
+
return direction === "ASC" ? aValue.localeCompare(bValue) : bValue.localeCompare(aValue);
|
|
861
|
+
}
|
|
862
|
+
return direction === "ASC" ? aValue - bValue : bValue - aValue;
|
|
863
|
+
});
|
|
864
|
+
const returnedThreadMessageIds = new Set(finalMessages.filter((m) => m.threadId === threadId).map((m) => m.id));
|
|
865
|
+
const allThreadMessagesReturned = returnedThreadMessageIds.size >= total;
|
|
866
|
+
const fetchedAll = perPageInput === false || allThreadMessagesReturned;
|
|
867
|
+
const hasMore = !fetchedAll && offset + perPage < total;
|
|
868
|
+
return {
|
|
869
|
+
messages: finalMessages,
|
|
870
|
+
total,
|
|
871
|
+
page,
|
|
872
|
+
perPage: perPageForResponse,
|
|
873
|
+
hasMore
|
|
874
|
+
};
|
|
1147
875
|
} catch (error) {
|
|
1148
|
-
|
|
876
|
+
const mastraError = new MastraError(
|
|
1149
877
|
{
|
|
1150
|
-
id: "
|
|
878
|
+
id: createStorageErrorId("LANCE", "LIST_MESSAGES", "FAILED"),
|
|
1151
879
|
domain: ErrorDomain.STORAGE,
|
|
1152
880
|
category: ErrorCategory.THIRD_PARTY,
|
|
1153
|
-
details: {
|
|
881
|
+
details: {
|
|
882
|
+
threadId: Array.isArray(threadId) ? threadId.join(",") : threadId,
|
|
883
|
+
resourceId: resourceId ?? ""
|
|
884
|
+
}
|
|
1154
885
|
},
|
|
1155
886
|
error
|
|
1156
887
|
);
|
|
888
|
+
this.logger?.error?.(mastraError.toString());
|
|
889
|
+
this.logger?.trackException?.(mastraError);
|
|
890
|
+
return {
|
|
891
|
+
messages: [],
|
|
892
|
+
total: 0,
|
|
893
|
+
page,
|
|
894
|
+
perPage: perPageForResponse,
|
|
895
|
+
hasMore: false
|
|
896
|
+
};
|
|
1157
897
|
}
|
|
1158
898
|
}
|
|
1159
|
-
async
|
|
899
|
+
async saveMessages(args) {
|
|
1160
900
|
try {
|
|
1161
|
-
|
|
1162
|
-
|
|
901
|
+
const { messages } = args;
|
|
902
|
+
if (messages.length === 0) {
|
|
903
|
+
return { messages: [] };
|
|
1163
904
|
}
|
|
1164
|
-
|
|
1165
|
-
|
|
905
|
+
const threadId = messages[0]?.threadId;
|
|
906
|
+
if (!threadId) {
|
|
907
|
+
throw new Error("Thread ID is required");
|
|
1166
908
|
}
|
|
1167
|
-
|
|
909
|
+
for (const message of messages) {
|
|
910
|
+
if (!message.id) {
|
|
911
|
+
throw new Error("Message ID is required");
|
|
912
|
+
}
|
|
913
|
+
if (!message.threadId) {
|
|
914
|
+
throw new Error("Thread ID is required for all messages");
|
|
915
|
+
}
|
|
916
|
+
if (message.resourceId === null || message.resourceId === void 0) {
|
|
917
|
+
throw new Error("Resource ID cannot be null or undefined");
|
|
918
|
+
}
|
|
919
|
+
if (!message.content) {
|
|
920
|
+
throw new Error("Message content is required");
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
const transformedMessages = messages.map((message) => {
|
|
924
|
+
const { threadId: threadId2, type, ...rest } = message;
|
|
925
|
+
return {
|
|
926
|
+
...rest,
|
|
927
|
+
thread_id: threadId2,
|
|
928
|
+
type: type ?? "v2",
|
|
929
|
+
content: JSON.stringify(message.content)
|
|
930
|
+
};
|
|
931
|
+
});
|
|
932
|
+
const table = await this.client.openTable(TABLE_MESSAGES);
|
|
933
|
+
await table.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute(transformedMessages);
|
|
934
|
+
const threadsTable = await this.client.openTable(TABLE_THREADS);
|
|
935
|
+
const currentTime = (/* @__PURE__ */ new Date()).getTime();
|
|
936
|
+
const updateRecord = { id: threadId, updatedAt: currentTime };
|
|
937
|
+
await threadsTable.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([updateRecord]);
|
|
938
|
+
const list = new MessageList().add(messages, "memory");
|
|
939
|
+
return { messages: list.get.all.db() };
|
|
940
|
+
} catch (error) {
|
|
1168
941
|
throw new MastraError(
|
|
1169
942
|
{
|
|
1170
|
-
id: "
|
|
943
|
+
id: createStorageErrorId("LANCE", "SAVE_MESSAGES", "FAILED"),
|
|
1171
944
|
domain: ErrorDomain.STORAGE,
|
|
1172
|
-
category: ErrorCategory.
|
|
1173
|
-
text: validationError.message,
|
|
1174
|
-
details: { tableName }
|
|
945
|
+
category: ErrorCategory.THIRD_PARTY
|
|
1175
946
|
},
|
|
1176
|
-
|
|
947
|
+
error
|
|
1177
948
|
);
|
|
1178
949
|
}
|
|
950
|
+
}
|
|
951
|
+
async listThreadsByResourceId(args) {
|
|
1179
952
|
try {
|
|
1180
|
-
const
|
|
1181
|
-
|
|
953
|
+
const { resourceId, page = 0, perPage: perPageInput, orderBy } = args;
|
|
954
|
+
const perPage = normalizePerPage(perPageInput, 100);
|
|
955
|
+
if (page < 0) {
|
|
956
|
+
throw new MastraError(
|
|
957
|
+
{
|
|
958
|
+
id: createStorageErrorId("LANCE", "LIST_THREADS_BY_RESOURCE_ID", "INVALID_PAGE"),
|
|
959
|
+
domain: ErrorDomain.STORAGE,
|
|
960
|
+
category: ErrorCategory.USER,
|
|
961
|
+
details: { page }
|
|
962
|
+
},
|
|
963
|
+
new Error("page must be >= 0")
|
|
964
|
+
);
|
|
965
|
+
}
|
|
966
|
+
const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
967
|
+
const { field, direction } = this.parseOrderBy(orderBy);
|
|
968
|
+
const table = await this.client.openTable(TABLE_THREADS);
|
|
969
|
+
const total = await table.countRows(`\`resourceId\` = '${this.escapeSql(resourceId)}'`);
|
|
970
|
+
const query = table.query().where(`\`resourceId\` = '${this.escapeSql(resourceId)}'`);
|
|
971
|
+
const records = await query.toArray();
|
|
972
|
+
records.sort((a, b) => {
|
|
973
|
+
const aValue = ["createdAt", "updatedAt"].includes(field) ? new Date(a[field]).getTime() : a[field];
|
|
974
|
+
const bValue = ["createdAt", "updatedAt"].includes(field) ? new Date(b[field]).getTime() : b[field];
|
|
975
|
+
if (aValue == null && bValue == null) return 0;
|
|
976
|
+
if (aValue == null) return direction === "ASC" ? -1 : 1;
|
|
977
|
+
if (bValue == null) return direction === "ASC" ? 1 : -1;
|
|
978
|
+
if (typeof aValue === "string" && typeof bValue === "string") {
|
|
979
|
+
return direction === "ASC" ? aValue.localeCompare(bValue) : bValue.localeCompare(aValue);
|
|
980
|
+
}
|
|
981
|
+
return direction === "ASC" ? aValue - bValue : bValue - aValue;
|
|
982
|
+
});
|
|
983
|
+
const paginatedRecords = records.slice(offset, offset + perPage);
|
|
984
|
+
const schema = await getTableSchema({ tableName: TABLE_THREADS, client: this.client });
|
|
985
|
+
const threads = paginatedRecords.map(
|
|
986
|
+
(record) => processResultWithTypeConversion(record, schema)
|
|
987
|
+
);
|
|
988
|
+
return {
|
|
989
|
+
threads,
|
|
990
|
+
total,
|
|
991
|
+
page,
|
|
992
|
+
perPage: perPageForResponse,
|
|
993
|
+
hasMore: offset + perPage < total
|
|
994
|
+
};
|
|
1182
995
|
} catch (error) {
|
|
1183
996
|
throw new MastraError(
|
|
1184
997
|
{
|
|
1185
|
-
id: "
|
|
998
|
+
id: createStorageErrorId("LANCE", "LIST_THREADS_BY_RESOURCE_ID", "FAILED"),
|
|
1186
999
|
domain: ErrorDomain.STORAGE,
|
|
1187
|
-
category: ErrorCategory.THIRD_PARTY
|
|
1188
|
-
details: { tableName }
|
|
1000
|
+
category: ErrorCategory.THIRD_PARTY
|
|
1189
1001
|
},
|
|
1190
1002
|
error
|
|
1191
1003
|
);
|
|
1192
1004
|
}
|
|
1193
1005
|
}
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1006
|
+
/**
|
|
1007
|
+
* Processes messages to include context messages based on withPreviousMessages and withNextMessages
|
|
1008
|
+
* @param records - The sorted array of records to process
|
|
1009
|
+
* @param include - The array of include specifications with context parameters
|
|
1010
|
+
* @returns The processed array with context messages included
|
|
1011
|
+
*/
|
|
1012
|
+
processMessagesWithContext(records, include) {
|
|
1013
|
+
const messagesWithContext = include.filter((item) => item.withPreviousMessages || item.withNextMessages);
|
|
1014
|
+
if (messagesWithContext.length === 0) {
|
|
1015
|
+
return records;
|
|
1016
|
+
}
|
|
1017
|
+
const messageIndexMap = /* @__PURE__ */ new Map();
|
|
1018
|
+
records.forEach((message, index) => {
|
|
1019
|
+
messageIndexMap.set(message.id, index);
|
|
1020
|
+
});
|
|
1021
|
+
const additionalIndices = /* @__PURE__ */ new Set();
|
|
1022
|
+
for (const item of messagesWithContext) {
|
|
1023
|
+
const messageIndex = messageIndexMap.get(item.id);
|
|
1024
|
+
if (messageIndex !== void 0) {
|
|
1025
|
+
if (item.withPreviousMessages) {
|
|
1026
|
+
const startIdx = Math.max(0, messageIndex - item.withPreviousMessages);
|
|
1027
|
+
for (let i = startIdx; i < messageIndex; i++) {
|
|
1028
|
+
additionalIndices.add(i);
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
if (item.withNextMessages) {
|
|
1032
|
+
const endIdx = Math.min(records.length - 1, messageIndex + item.withNextMessages);
|
|
1033
|
+
for (let i = messageIndex + 1; i <= endIdx; i++) {
|
|
1034
|
+
additionalIndices.add(i);
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1201
1037
|
}
|
|
1202
|
-
|
|
1203
|
-
|
|
1038
|
+
}
|
|
1039
|
+
if (additionalIndices.size === 0) {
|
|
1040
|
+
return records;
|
|
1041
|
+
}
|
|
1042
|
+
const originalMatchIds = new Set(include.map((item) => item.id));
|
|
1043
|
+
const allIndices = /* @__PURE__ */ new Set();
|
|
1044
|
+
records.forEach((record, index) => {
|
|
1045
|
+
if (originalMatchIds.has(record.id)) {
|
|
1046
|
+
allIndices.add(index);
|
|
1204
1047
|
}
|
|
1205
|
-
}
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1048
|
+
});
|
|
1049
|
+
additionalIndices.forEach((index) => {
|
|
1050
|
+
allIndices.add(index);
|
|
1051
|
+
});
|
|
1052
|
+
return Array.from(allIndices).sort((a, b) => a - b).map((index) => records[index]);
|
|
1053
|
+
}
|
|
1054
|
+
/**
|
|
1055
|
+
* Parse message data from LanceDB record format to MastraDBMessage format
|
|
1056
|
+
*/
|
|
1057
|
+
parseMessageData(data) {
|
|
1058
|
+
const { thread_id, ...rest } = data;
|
|
1059
|
+
return {
|
|
1060
|
+
...rest,
|
|
1061
|
+
threadId: thread_id,
|
|
1062
|
+
content: typeof data.content === "string" ? (() => {
|
|
1063
|
+
try {
|
|
1064
|
+
return JSON.parse(data.content);
|
|
1065
|
+
} catch {
|
|
1066
|
+
return data.content;
|
|
1067
|
+
}
|
|
1068
|
+
})() : data.content,
|
|
1069
|
+
createdAt: new Date(data.createdAt),
|
|
1070
|
+
updatedAt: new Date(data.updatedAt)
|
|
1071
|
+
};
|
|
1072
|
+
}
|
|
1073
|
+
async updateMessages(args) {
|
|
1074
|
+
const { messages } = args;
|
|
1075
|
+
this.logger.debug("Updating messages", { count: messages.length });
|
|
1076
|
+
if (!messages.length) {
|
|
1077
|
+
return [];
|
|
1216
1078
|
}
|
|
1079
|
+
const updatedMessages = [];
|
|
1080
|
+
const affectedThreadIds = /* @__PURE__ */ new Set();
|
|
1217
1081
|
try {
|
|
1218
|
-
const
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1082
|
+
for (const updateData of messages) {
|
|
1083
|
+
const { id, ...updates } = updateData;
|
|
1084
|
+
const existingMessage = await this.#db.load({ tableName: TABLE_MESSAGES, keys: { id } });
|
|
1085
|
+
if (!existingMessage) {
|
|
1086
|
+
this.logger.warn("Message not found for update", { id });
|
|
1087
|
+
continue;
|
|
1088
|
+
}
|
|
1089
|
+
const existingMsg = this.parseMessageData(existingMessage);
|
|
1090
|
+
const originalThreadId = existingMsg.threadId;
|
|
1091
|
+
affectedThreadIds.add(originalThreadId);
|
|
1092
|
+
const updatePayload = {};
|
|
1093
|
+
if ("role" in updates && updates.role !== void 0) updatePayload.role = updates.role;
|
|
1094
|
+
if ("type" in updates && updates.type !== void 0) updatePayload.type = updates.type;
|
|
1095
|
+
if ("resourceId" in updates && updates.resourceId !== void 0) updatePayload.resourceId = updates.resourceId;
|
|
1096
|
+
if ("threadId" in updates && updates.threadId !== void 0 && updates.threadId !== null) {
|
|
1097
|
+
updatePayload.thread_id = updates.threadId;
|
|
1098
|
+
affectedThreadIds.add(updates.threadId);
|
|
1099
|
+
}
|
|
1100
|
+
if (updates.content) {
|
|
1101
|
+
const existingContent = existingMsg.content;
|
|
1102
|
+
let newContent = { ...existingContent };
|
|
1103
|
+
if (updates.content.metadata !== void 0) {
|
|
1104
|
+
newContent.metadata = {
|
|
1105
|
+
...existingContent.metadata || {},
|
|
1106
|
+
...updates.content.metadata || {}
|
|
1107
|
+
};
|
|
1108
|
+
}
|
|
1109
|
+
if (updates.content.content !== void 0) {
|
|
1110
|
+
newContent.content = updates.content.content;
|
|
1111
|
+
}
|
|
1112
|
+
if ("parts" in updates.content && updates.content.parts !== void 0) {
|
|
1113
|
+
newContent.parts = updates.content.parts;
|
|
1114
|
+
}
|
|
1115
|
+
updatePayload.content = JSON.stringify(newContent);
|
|
1116
|
+
}
|
|
1117
|
+
await this.#db.insert({ tableName: TABLE_MESSAGES, record: { id, ...updatePayload } });
|
|
1118
|
+
const updatedMessage = await this.#db.load({ tableName: TABLE_MESSAGES, keys: { id } });
|
|
1119
|
+
if (updatedMessage) {
|
|
1120
|
+
updatedMessages.push(this.parseMessageData(updatedMessage));
|
|
1225
1121
|
}
|
|
1226
1122
|
}
|
|
1227
|
-
|
|
1228
|
-
|
|
1123
|
+
for (const threadId of affectedThreadIds) {
|
|
1124
|
+
await this.#db.insert({
|
|
1125
|
+
tableName: TABLE_THREADS,
|
|
1126
|
+
record: { id: threadId, updatedAt: Date.now() }
|
|
1127
|
+
});
|
|
1128
|
+
}
|
|
1129
|
+
return updatedMessages;
|
|
1229
1130
|
} catch (error) {
|
|
1230
1131
|
throw new MastraError(
|
|
1231
1132
|
{
|
|
1232
|
-
id: "
|
|
1133
|
+
id: createStorageErrorId("LANCE", "UPDATE_MESSAGES", "FAILED"),
|
|
1233
1134
|
domain: ErrorDomain.STORAGE,
|
|
1234
1135
|
category: ErrorCategory.THIRD_PARTY,
|
|
1235
|
-
details: {
|
|
1136
|
+
details: { count: messages.length }
|
|
1236
1137
|
},
|
|
1237
1138
|
error
|
|
1238
1139
|
);
|
|
1239
1140
|
}
|
|
1240
1141
|
}
|
|
1241
|
-
async
|
|
1142
|
+
async getResourceById({ resourceId }) {
|
|
1242
1143
|
try {
|
|
1243
|
-
|
|
1244
|
-
|
|
1144
|
+
const resource = await this.#db.load({ tableName: TABLE_RESOURCES, keys: { id: resourceId } });
|
|
1145
|
+
if (!resource) {
|
|
1146
|
+
return null;
|
|
1245
1147
|
}
|
|
1246
|
-
|
|
1247
|
-
|
|
1148
|
+
let createdAt;
|
|
1149
|
+
let updatedAt;
|
|
1150
|
+
try {
|
|
1151
|
+
if (resource.createdAt instanceof Date) {
|
|
1152
|
+
createdAt = resource.createdAt;
|
|
1153
|
+
} else if (typeof resource.createdAt === "string") {
|
|
1154
|
+
createdAt = new Date(resource.createdAt);
|
|
1155
|
+
} else if (typeof resource.createdAt === "number") {
|
|
1156
|
+
createdAt = new Date(resource.createdAt);
|
|
1157
|
+
} else {
|
|
1158
|
+
createdAt = /* @__PURE__ */ new Date();
|
|
1159
|
+
}
|
|
1160
|
+
if (isNaN(createdAt.getTime())) {
|
|
1161
|
+
createdAt = /* @__PURE__ */ new Date();
|
|
1162
|
+
}
|
|
1163
|
+
} catch {
|
|
1164
|
+
createdAt = /* @__PURE__ */ new Date();
|
|
1248
1165
|
}
|
|
1249
|
-
|
|
1250
|
-
|
|
1166
|
+
try {
|
|
1167
|
+
if (resource.updatedAt instanceof Date) {
|
|
1168
|
+
updatedAt = resource.updatedAt;
|
|
1169
|
+
} else if (typeof resource.updatedAt === "string") {
|
|
1170
|
+
updatedAt = new Date(resource.updatedAt);
|
|
1171
|
+
} else if (typeof resource.updatedAt === "number") {
|
|
1172
|
+
updatedAt = new Date(resource.updatedAt);
|
|
1173
|
+
} else {
|
|
1174
|
+
updatedAt = /* @__PURE__ */ new Date();
|
|
1175
|
+
}
|
|
1176
|
+
if (isNaN(updatedAt.getTime())) {
|
|
1177
|
+
updatedAt = /* @__PURE__ */ new Date();
|
|
1178
|
+
}
|
|
1179
|
+
} catch {
|
|
1180
|
+
updatedAt = /* @__PURE__ */ new Date();
|
|
1181
|
+
}
|
|
1182
|
+
let workingMemory = resource.workingMemory;
|
|
1183
|
+
if (workingMemory === null || workingMemory === void 0) {
|
|
1184
|
+
workingMemory = void 0;
|
|
1185
|
+
} else if (workingMemory === "") {
|
|
1186
|
+
workingMemory = "";
|
|
1187
|
+
} else if (typeof workingMemory === "object") {
|
|
1188
|
+
workingMemory = JSON.stringify(workingMemory);
|
|
1189
|
+
}
|
|
1190
|
+
let metadata = resource.metadata;
|
|
1191
|
+
if (metadata === "" || metadata === null || metadata === void 0) {
|
|
1192
|
+
metadata = void 0;
|
|
1193
|
+
} else if (typeof metadata === "string") {
|
|
1194
|
+
try {
|
|
1195
|
+
metadata = JSON.parse(metadata);
|
|
1196
|
+
} catch {
|
|
1197
|
+
metadata = metadata;
|
|
1198
|
+
}
|
|
1251
1199
|
}
|
|
1252
|
-
|
|
1200
|
+
return {
|
|
1201
|
+
...resource,
|
|
1202
|
+
createdAt,
|
|
1203
|
+
updatedAt,
|
|
1204
|
+
workingMemory,
|
|
1205
|
+
metadata
|
|
1206
|
+
};
|
|
1207
|
+
} catch (error) {
|
|
1253
1208
|
throw new MastraError(
|
|
1254
1209
|
{
|
|
1255
|
-
id: "
|
|
1210
|
+
id: createStorageErrorId("LANCE", "GET_RESOURCE_BY_ID", "FAILED"),
|
|
1256
1211
|
domain: ErrorDomain.STORAGE,
|
|
1257
|
-
category: ErrorCategory.
|
|
1258
|
-
text: validationError.message,
|
|
1259
|
-
details: { tableName }
|
|
1212
|
+
category: ErrorCategory.THIRD_PARTY
|
|
1260
1213
|
},
|
|
1261
|
-
|
|
1214
|
+
error
|
|
1262
1215
|
);
|
|
1263
1216
|
}
|
|
1217
|
+
}
|
|
1218
|
+
async saveResource({ resource }) {
|
|
1264
1219
|
try {
|
|
1265
|
-
const
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
});
|
|
1277
|
-
console.log(processedRecords);
|
|
1278
|
-
await table.mergeInsert(primaryId).whenMatchedUpdateAll().whenNotMatchedInsertAll().execute(processedRecords);
|
|
1220
|
+
const record = {
|
|
1221
|
+
...resource,
|
|
1222
|
+
metadata: resource.metadata ? JSON.stringify(resource.metadata) : "",
|
|
1223
|
+
createdAt: resource.createdAt.getTime(),
|
|
1224
|
+
// Store as timestamp (milliseconds)
|
|
1225
|
+
updatedAt: resource.updatedAt.getTime()
|
|
1226
|
+
// Store as timestamp (milliseconds)
|
|
1227
|
+
};
|
|
1228
|
+
const table = await this.client.openTable(TABLE_RESOURCES);
|
|
1229
|
+
await table.add([record], { mode: "append" });
|
|
1230
|
+
return resource;
|
|
1279
1231
|
} catch (error) {
|
|
1280
1232
|
throw new MastraError(
|
|
1281
1233
|
{
|
|
1282
|
-
id: "
|
|
1234
|
+
id: createStorageErrorId("LANCE", "SAVE_RESOURCE", "FAILED"),
|
|
1283
1235
|
domain: ErrorDomain.STORAGE,
|
|
1284
|
-
category: ErrorCategory.THIRD_PARTY
|
|
1285
|
-
details: { tableName }
|
|
1236
|
+
category: ErrorCategory.THIRD_PARTY
|
|
1286
1237
|
},
|
|
1287
1238
|
error
|
|
1288
1239
|
);
|
|
1289
1240
|
}
|
|
1290
1241
|
}
|
|
1291
|
-
async
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1242
|
+
async updateResource({
|
|
1243
|
+
resourceId,
|
|
1244
|
+
workingMemory,
|
|
1245
|
+
metadata
|
|
1246
|
+
}) {
|
|
1247
|
+
const maxRetries = 3;
|
|
1248
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
1249
|
+
try {
|
|
1250
|
+
const existingResource = await this.getResourceById({ resourceId });
|
|
1251
|
+
if (!existingResource) {
|
|
1252
|
+
const newResource = {
|
|
1253
|
+
id: resourceId,
|
|
1254
|
+
workingMemory,
|
|
1255
|
+
metadata: metadata || {},
|
|
1256
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
1257
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1258
|
+
};
|
|
1259
|
+
return this.saveResource({ resource: newResource });
|
|
1260
|
+
}
|
|
1261
|
+
const updatedResource = {
|
|
1262
|
+
...existingResource,
|
|
1263
|
+
workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
|
|
1264
|
+
metadata: {
|
|
1265
|
+
...existingResource.metadata,
|
|
1266
|
+
...metadata
|
|
1267
|
+
},
|
|
1268
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1269
|
+
};
|
|
1270
|
+
const record = {
|
|
1271
|
+
id: resourceId,
|
|
1272
|
+
workingMemory: updatedResource.workingMemory || "",
|
|
1273
|
+
metadata: updatedResource.metadata ? JSON.stringify(updatedResource.metadata) : "",
|
|
1274
|
+
updatedAt: updatedResource.updatedAt.getTime()
|
|
1275
|
+
// Store as timestamp (milliseconds)
|
|
1276
|
+
};
|
|
1277
|
+
const table = await this.client.openTable(TABLE_RESOURCES);
|
|
1278
|
+
await table.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([record]);
|
|
1279
|
+
return updatedResource;
|
|
1280
|
+
} catch (error) {
|
|
1281
|
+
if (error.message?.includes("Commit conflict") && attempt < maxRetries - 1) {
|
|
1282
|
+
const delay = Math.pow(2, attempt) * 10;
|
|
1283
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
1284
|
+
continue;
|
|
1285
|
+
}
|
|
1286
|
+
throw new MastraError(
|
|
1287
|
+
{
|
|
1288
|
+
id: createStorageErrorId("LANCE", "UPDATE_RESOURCE", "FAILED"),
|
|
1289
|
+
domain: ErrorDomain.STORAGE,
|
|
1290
|
+
category: ErrorCategory.THIRD_PARTY
|
|
1291
|
+
},
|
|
1292
|
+
error
|
|
1293
|
+
);
|
|
1301
1294
|
}
|
|
1302
|
-
} catch (validationError) {
|
|
1303
|
-
throw new MastraError(
|
|
1304
|
-
{
|
|
1305
|
-
id: "STORAGE_LANCE_STORAGE_LOAD_INVALID_ARGS",
|
|
1306
|
-
domain: ErrorDomain.STORAGE,
|
|
1307
|
-
category: ErrorCategory.USER,
|
|
1308
|
-
text: validationError.message,
|
|
1309
|
-
details: { tableName }
|
|
1310
|
-
},
|
|
1311
|
-
validationError
|
|
1312
|
-
);
|
|
1313
1295
|
}
|
|
1296
|
+
throw new Error("Unexpected end of retry loop");
|
|
1297
|
+
}
|
|
1298
|
+
};
|
|
1299
|
+
var StoreScoresLance = class extends ScoresStorage {
|
|
1300
|
+
client;
|
|
1301
|
+
#db;
|
|
1302
|
+
constructor(config) {
|
|
1303
|
+
super();
|
|
1304
|
+
const client = resolveLanceConfig(config);
|
|
1305
|
+
this.client = client;
|
|
1306
|
+
this.#db = new LanceDB({ client });
|
|
1307
|
+
}
|
|
1308
|
+
async init() {
|
|
1309
|
+
await this.#db.createTable({ tableName: TABLE_SCORERS, schema: SCORERS_SCHEMA });
|
|
1310
|
+
await this.#db.alterTable({
|
|
1311
|
+
tableName: TABLE_SCORERS,
|
|
1312
|
+
schema: SCORERS_SCHEMA,
|
|
1313
|
+
ifNotExists: ["spanId", "requestContext"]
|
|
1314
|
+
});
|
|
1315
|
+
}
|
|
1316
|
+
async dangerouslyClearAll() {
|
|
1317
|
+
await this.#db.clearTable({ tableName: TABLE_SCORERS });
|
|
1318
|
+
}
|
|
1319
|
+
async saveScore(score) {
|
|
1320
|
+
let validatedScore;
|
|
1314
1321
|
try {
|
|
1315
|
-
|
|
1316
|
-
const tableSchema = await getTableSchema({ tableName, client: this.client });
|
|
1317
|
-
const query = table.query();
|
|
1318
|
-
if (Object.keys(keys).length > 0) {
|
|
1319
|
-
validateKeyTypes(keys, tableSchema);
|
|
1320
|
-
const filterConditions = Object.entries(keys).map(([key, value]) => {
|
|
1321
|
-
const isCamelCase = /^[a-z][a-zA-Z]*$/.test(key) && /[A-Z]/.test(key);
|
|
1322
|
-
const quotedKey = isCamelCase ? `\`${key}\`` : key;
|
|
1323
|
-
if (typeof value === "string") {
|
|
1324
|
-
return `${quotedKey} = '${value}'`;
|
|
1325
|
-
} else if (value === null) {
|
|
1326
|
-
return `${quotedKey} IS NULL`;
|
|
1327
|
-
} else {
|
|
1328
|
-
return `${quotedKey} = ${value}`;
|
|
1329
|
-
}
|
|
1330
|
-
}).join(" AND ");
|
|
1331
|
-
this.logger.debug("where clause generated: " + filterConditions);
|
|
1332
|
-
query.where(filterConditions);
|
|
1333
|
-
}
|
|
1334
|
-
const result = await query.limit(1).toArray();
|
|
1335
|
-
if (result.length === 0) {
|
|
1336
|
-
this.logger.debug("No record found");
|
|
1337
|
-
return null;
|
|
1338
|
-
}
|
|
1339
|
-
return processResultWithTypeConversion(result[0], tableSchema);
|
|
1322
|
+
validatedScore = saveScorePayloadSchema.parse(score);
|
|
1340
1323
|
} catch (error) {
|
|
1341
|
-
if (error instanceof MastraError) throw error;
|
|
1342
1324
|
throw new MastraError(
|
|
1343
1325
|
{
|
|
1344
|
-
id: "
|
|
1326
|
+
id: createStorageErrorId("LANCE", "SAVE_SCORE", "VALIDATION_FAILED"),
|
|
1327
|
+
text: "Failed to save score in LanceStorage",
|
|
1345
1328
|
domain: ErrorDomain.STORAGE,
|
|
1346
|
-
category: ErrorCategory.
|
|
1347
|
-
details: {
|
|
1329
|
+
category: ErrorCategory.USER,
|
|
1330
|
+
details: {
|
|
1331
|
+
scorer: score.scorer?.id ?? "unknown",
|
|
1332
|
+
entityId: score.entityId ?? "unknown",
|
|
1333
|
+
entityType: score.entityType ?? "unknown",
|
|
1334
|
+
traceId: score.traceId ?? "",
|
|
1335
|
+
spanId: score.spanId ?? ""
|
|
1336
|
+
}
|
|
1348
1337
|
},
|
|
1349
1338
|
error
|
|
1350
1339
|
);
|
|
1351
1340
|
}
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
var StoreScoresLance = class extends ScoresStorage {
|
|
1355
|
-
client;
|
|
1356
|
-
constructor({ client }) {
|
|
1357
|
-
super();
|
|
1358
|
-
this.client = client;
|
|
1359
|
-
}
|
|
1360
|
-
async saveScore(score) {
|
|
1341
|
+
const id = crypto.randomUUID();
|
|
1342
|
+
const now = /* @__PURE__ */ new Date();
|
|
1361
1343
|
try {
|
|
1362
1344
|
const table = await this.client.openTable(TABLE_SCORERS);
|
|
1363
1345
|
const schema = await getTableSchema({ tableName: TABLE_SCORERS, client: this.client });
|
|
1364
1346
|
const allowedFields = new Set(schema.fields.map((f) => f.name));
|
|
1365
1347
|
const filteredScore = {};
|
|
1366
|
-
Object.keys(
|
|
1348
|
+
for (const key of Object.keys(validatedScore)) {
|
|
1367
1349
|
if (allowedFields.has(key)) {
|
|
1368
|
-
filteredScore[key] =
|
|
1350
|
+
filteredScore[key] = validatedScore[key];
|
|
1369
1351
|
}
|
|
1370
|
-
}
|
|
1352
|
+
}
|
|
1371
1353
|
for (const key in filteredScore) {
|
|
1372
1354
|
if (filteredScore[key] !== null && typeof filteredScore[key] === "object" && !(filteredScore[key] instanceof Date)) {
|
|
1373
1355
|
filteredScore[key] = JSON.stringify(filteredScore[key]);
|
|
1374
1356
|
}
|
|
1375
1357
|
}
|
|
1376
|
-
|
|
1358
|
+
filteredScore.id = id;
|
|
1359
|
+
filteredScore.createdAt = now;
|
|
1360
|
+
filteredScore.updatedAt = now;
|
|
1377
1361
|
await table.add([filteredScore], { mode: "append" });
|
|
1378
|
-
return { score };
|
|
1362
|
+
return { score: { ...validatedScore, id, createdAt: now, updatedAt: now } };
|
|
1379
1363
|
} catch (error) {
|
|
1380
1364
|
throw new MastraError(
|
|
1381
1365
|
{
|
|
1382
|
-
id: "
|
|
1366
|
+
id: createStorageErrorId("LANCE", "SAVE_SCORE", "FAILED"),
|
|
1383
1367
|
text: "Failed to save score in LanceStorage",
|
|
1384
1368
|
domain: ErrorDomain.STORAGE,
|
|
1385
1369
|
category: ErrorCategory.THIRD_PARTY,
|
|
@@ -1395,12 +1379,11 @@ var StoreScoresLance = class extends ScoresStorage {
|
|
|
1395
1379
|
const query = table.query().where(`id = '${id}'`).limit(1);
|
|
1396
1380
|
const records = await query.toArray();
|
|
1397
1381
|
if (records.length === 0) return null;
|
|
1398
|
-
|
|
1399
|
-
return processResultWithTypeConversion(records[0], schema);
|
|
1382
|
+
return await this.transformScoreRow(records[0]);
|
|
1400
1383
|
} catch (error) {
|
|
1401
1384
|
throw new MastraError(
|
|
1402
1385
|
{
|
|
1403
|
-
id: "
|
|
1386
|
+
id: createStorageErrorId("LANCE", "GET_SCORE_BY_ID", "FAILED"),
|
|
1404
1387
|
text: "Failed to get score by id in LanceStorage",
|
|
1405
1388
|
domain: ErrorDomain.STORAGE,
|
|
1406
1389
|
category: ErrorCategory.THIRD_PARTY,
|
|
@@ -1410,34 +1393,76 @@ var StoreScoresLance = class extends ScoresStorage {
|
|
|
1410
1393
|
);
|
|
1411
1394
|
}
|
|
1412
1395
|
}
|
|
1413
|
-
|
|
1396
|
+
/**
|
|
1397
|
+
* LanceDB-specific score row transformation.
|
|
1398
|
+
*
|
|
1399
|
+
* Note: This implementation does NOT use coreTransformScoreRow because:
|
|
1400
|
+
* 1. LanceDB stores schema information in the table itself (requires async fetch)
|
|
1401
|
+
* 2. Uses processResultWithTypeConversion utility for LanceDB-specific type handling
|
|
1402
|
+
*/
|
|
1403
|
+
async transformScoreRow(row) {
|
|
1404
|
+
const schema = await getTableSchema({ tableName: TABLE_SCORERS, client: this.client });
|
|
1405
|
+
const transformed = processResultWithTypeConversion(row, schema);
|
|
1406
|
+
return {
|
|
1407
|
+
...transformed,
|
|
1408
|
+
createdAt: row.createdAt,
|
|
1409
|
+
updatedAt: row.updatedAt
|
|
1410
|
+
};
|
|
1411
|
+
}
|
|
1412
|
+
async listScoresByScorerId({
|
|
1414
1413
|
scorerId,
|
|
1415
|
-
pagination
|
|
1414
|
+
pagination,
|
|
1415
|
+
entityId,
|
|
1416
|
+
entityType,
|
|
1417
|
+
source
|
|
1416
1418
|
}) {
|
|
1417
1419
|
try {
|
|
1420
|
+
const { page, perPage: perPageInput } = pagination;
|
|
1421
|
+
const perPage = normalizePerPage(perPageInput, 100);
|
|
1422
|
+
const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
1418
1423
|
const table = await this.client.openTable(TABLE_SCORERS);
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1424
|
+
let query = table.query().where(`\`scorerId\` = '${scorerId}'`);
|
|
1425
|
+
if (source) {
|
|
1426
|
+
query = query.where(`\`source\` = '${source}'`);
|
|
1427
|
+
}
|
|
1428
|
+
if (entityId) {
|
|
1429
|
+
query = query.where(`\`entityId\` = '${entityId}'`);
|
|
1430
|
+
}
|
|
1431
|
+
if (entityType) {
|
|
1432
|
+
query = query.where(`\`entityType\` = '${entityType}'`);
|
|
1433
|
+
}
|
|
1434
|
+
let totalQuery = table.query().where(`\`scorerId\` = '${scorerId}'`);
|
|
1435
|
+
if (source) {
|
|
1436
|
+
totalQuery = totalQuery.where(`\`source\` = '${source}'`);
|
|
1437
|
+
}
|
|
1438
|
+
if (entityId) {
|
|
1439
|
+
totalQuery = totalQuery.where(`\`entityId\` = '${entityId}'`);
|
|
1440
|
+
}
|
|
1441
|
+
if (entityType) {
|
|
1442
|
+
totalQuery = totalQuery.where(`\`entityType\` = '${entityType}'`);
|
|
1443
|
+
}
|
|
1444
|
+
const allRecords = await totalQuery.toArray();
|
|
1427
1445
|
const total = allRecords.length;
|
|
1446
|
+
const end = perPageInput === false ? total : start + perPage;
|
|
1447
|
+
if (perPageInput !== false) {
|
|
1448
|
+
query = query.limit(perPage);
|
|
1449
|
+
if (start > 0) query = query.offset(start);
|
|
1450
|
+
}
|
|
1451
|
+
const records = await query.toArray();
|
|
1452
|
+
const scores = await Promise.all(records.map(async (record) => await this.transformScoreRow(record)));
|
|
1428
1453
|
return {
|
|
1429
1454
|
pagination: {
|
|
1430
1455
|
page,
|
|
1431
|
-
perPage,
|
|
1456
|
+
perPage: perPageForResponse,
|
|
1432
1457
|
total,
|
|
1433
|
-
hasMore:
|
|
1458
|
+
hasMore: end < total
|
|
1434
1459
|
},
|
|
1435
1460
|
scores
|
|
1436
1461
|
};
|
|
1437
1462
|
} catch (error) {
|
|
1438
1463
|
throw new MastraError(
|
|
1439
1464
|
{
|
|
1440
|
-
id: "
|
|
1465
|
+
id: createStorageErrorId("LANCE", "LIST_SCORES_BY_SCORER_ID", "FAILED"),
|
|
1441
1466
|
text: "Failed to get scores by scorerId in LanceStorage",
|
|
1442
1467
|
domain: ErrorDomain.STORAGE,
|
|
1443
1468
|
category: ErrorCategory.THIRD_PARTY,
|
|
@@ -1447,274 +1472,135 @@ var StoreScoresLance = class extends ScoresStorage {
|
|
|
1447
1472
|
);
|
|
1448
1473
|
}
|
|
1449
1474
|
}
|
|
1450
|
-
async
|
|
1475
|
+
async listScoresByRunId({
|
|
1451
1476
|
runId,
|
|
1452
1477
|
pagination
|
|
1453
1478
|
}) {
|
|
1454
1479
|
try {
|
|
1480
|
+
const { page, perPage: perPageInput } = pagination;
|
|
1481
|
+
const perPage = normalizePerPage(perPageInput, 100);
|
|
1482
|
+
const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
1455
1483
|
const table = await this.client.openTable(TABLE_SCORERS);
|
|
1456
|
-
const { page = 0, perPage = 10 } = pagination || {};
|
|
1457
|
-
const offset = page * perPage;
|
|
1458
|
-
const query = table.query().where(`\`runId\` = '${runId}'`).limit(perPage);
|
|
1459
|
-
if (offset > 0) query.offset(offset);
|
|
1460
|
-
const records = await query.toArray();
|
|
1461
|
-
const schema = await getTableSchema({ tableName: TABLE_SCORERS, client: this.client });
|
|
1462
|
-
const scores = processResultWithTypeConversion(records, schema);
|
|
1463
1484
|
const allRecords = await table.query().where(`\`runId\` = '${runId}'`).toArray();
|
|
1464
1485
|
const total = allRecords.length;
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
},
|
|
1472
|
-
scores
|
|
1473
|
-
};
|
|
1474
|
-
} catch (error) {
|
|
1475
|
-
throw new MastraError(
|
|
1476
|
-
{
|
|
1477
|
-
id: "LANCE_STORAGE_GET_SCORES_BY_RUN_ID_FAILED",
|
|
1478
|
-
text: "Failed to get scores by runId in LanceStorage",
|
|
1479
|
-
domain: ErrorDomain.STORAGE,
|
|
1480
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
1481
|
-
details: { error: error?.message }
|
|
1482
|
-
},
|
|
1483
|
-
error
|
|
1484
|
-
);
|
|
1485
|
-
}
|
|
1486
|
-
}
|
|
1487
|
-
async getScoresByEntityId({
|
|
1488
|
-
entityId,
|
|
1489
|
-
entityType,
|
|
1490
|
-
pagination
|
|
1491
|
-
}) {
|
|
1492
|
-
try {
|
|
1493
|
-
const table = await this.client.openTable(TABLE_SCORERS);
|
|
1494
|
-
const { page = 0, perPage = 10 } = pagination || {};
|
|
1495
|
-
const offset = page * perPage;
|
|
1496
|
-
const query = table.query().where(`\`entityId\` = '${entityId}' AND \`entityType\` = '${entityType}'`).limit(perPage);
|
|
1497
|
-
if (offset > 0) query.offset(offset);
|
|
1486
|
+
const end = perPageInput === false ? total : start + perPage;
|
|
1487
|
+
let query = table.query().where(`\`runId\` = '${runId}'`);
|
|
1488
|
+
if (perPageInput !== false) {
|
|
1489
|
+
query = query.limit(perPage);
|
|
1490
|
+
if (start > 0) query = query.offset(start);
|
|
1491
|
+
}
|
|
1498
1492
|
const records = await query.toArray();
|
|
1499
|
-
const
|
|
1500
|
-
const scores = processResultWithTypeConversion(records, schema);
|
|
1501
|
-
const allRecords = await table.query().where(`\`entityId\` = '${entityId}' AND \`entityType\` = '${entityType}'`).toArray();
|
|
1502
|
-
const total = allRecords.length;
|
|
1493
|
+
const scores = await Promise.all(records.map(async (record) => await this.transformScoreRow(record)));
|
|
1503
1494
|
return {
|
|
1504
1495
|
pagination: {
|
|
1505
1496
|
page,
|
|
1506
|
-
perPage,
|
|
1497
|
+
perPage: perPageForResponse,
|
|
1507
1498
|
total,
|
|
1508
|
-
hasMore:
|
|
1499
|
+
hasMore: end < total
|
|
1509
1500
|
},
|
|
1510
1501
|
scores
|
|
1511
|
-
};
|
|
1512
|
-
} catch (error) {
|
|
1513
|
-
throw new MastraError(
|
|
1514
|
-
{
|
|
1515
|
-
id: "LANCE_STORAGE_GET_SCORES_BY_ENTITY_ID_FAILED",
|
|
1516
|
-
text: "Failed to get scores by entityId and entityType in LanceStorage",
|
|
1517
|
-
domain: ErrorDomain.STORAGE,
|
|
1518
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
1519
|
-
details: { error: error?.message }
|
|
1520
|
-
},
|
|
1521
|
-
error
|
|
1522
|
-
);
|
|
1523
|
-
}
|
|
1524
|
-
}
|
|
1525
|
-
};
|
|
1526
|
-
var StoreTracesLance = class extends TracesStorage {
|
|
1527
|
-
client;
|
|
1528
|
-
operations;
|
|
1529
|
-
constructor({ client, operations }) {
|
|
1530
|
-
super();
|
|
1531
|
-
this.client = client;
|
|
1532
|
-
this.operations = operations;
|
|
1533
|
-
}
|
|
1534
|
-
async saveTrace({ trace }) {
|
|
1535
|
-
try {
|
|
1536
|
-
const table = await this.client.openTable(TABLE_TRACES);
|
|
1537
|
-
const record = {
|
|
1538
|
-
...trace,
|
|
1539
|
-
attributes: JSON.stringify(trace.attributes),
|
|
1540
|
-
status: JSON.stringify(trace.status),
|
|
1541
|
-
events: JSON.stringify(trace.events),
|
|
1542
|
-
links: JSON.stringify(trace.links),
|
|
1543
|
-
other: JSON.stringify(trace.other)
|
|
1544
|
-
};
|
|
1545
|
-
await table.add([record], { mode: "append" });
|
|
1546
|
-
return trace;
|
|
1547
|
-
} catch (error) {
|
|
1548
|
-
throw new MastraError(
|
|
1549
|
-
{
|
|
1550
|
-
id: "LANCE_STORE_SAVE_TRACE_FAILED",
|
|
1551
|
-
domain: ErrorDomain.STORAGE,
|
|
1552
|
-
category: ErrorCategory.THIRD_PARTY
|
|
1553
|
-
},
|
|
1554
|
-
error
|
|
1555
|
-
);
|
|
1556
|
-
}
|
|
1557
|
-
}
|
|
1558
|
-
async getTraceById({ traceId }) {
|
|
1559
|
-
try {
|
|
1560
|
-
const table = await this.client.openTable(TABLE_TRACES);
|
|
1561
|
-
const query = table.query().where(`id = '${traceId}'`);
|
|
1562
|
-
const records = await query.toArray();
|
|
1563
|
-
return records[0];
|
|
1502
|
+
};
|
|
1564
1503
|
} catch (error) {
|
|
1565
1504
|
throw new MastraError(
|
|
1566
1505
|
{
|
|
1567
|
-
id: "
|
|
1506
|
+
id: createStorageErrorId("LANCE", "LIST_SCORES_BY_RUN_ID", "FAILED"),
|
|
1507
|
+
text: "Failed to get scores by runId in LanceStorage",
|
|
1568
1508
|
domain: ErrorDomain.STORAGE,
|
|
1569
|
-
category: ErrorCategory.THIRD_PARTY
|
|
1509
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1510
|
+
details: { error: error?.message }
|
|
1570
1511
|
},
|
|
1571
1512
|
error
|
|
1572
1513
|
);
|
|
1573
1514
|
}
|
|
1574
1515
|
}
|
|
1575
|
-
async
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
perPage = 10,
|
|
1580
|
-
attributes
|
|
1516
|
+
async listScoresByEntityId({
|
|
1517
|
+
entityId,
|
|
1518
|
+
entityType,
|
|
1519
|
+
pagination
|
|
1581
1520
|
}) {
|
|
1582
1521
|
try {
|
|
1583
|
-
const
|
|
1584
|
-
const
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
}
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
}
|
|
1591
|
-
if (
|
|
1592
|
-
query
|
|
1593
|
-
|
|
1594
|
-
const offset = (page - 1) * perPage;
|
|
1595
|
-
query.limit(perPage);
|
|
1596
|
-
if (offset > 0) {
|
|
1597
|
-
query.offset(offset);
|
|
1522
|
+
const { page, perPage: perPageInput } = pagination;
|
|
1523
|
+
const perPage = normalizePerPage(perPageInput, 100);
|
|
1524
|
+
const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
1525
|
+
const table = await this.client.openTable(TABLE_SCORERS);
|
|
1526
|
+
const allRecords = await table.query().where(`\`entityId\` = '${entityId}' AND \`entityType\` = '${entityType}'`).toArray();
|
|
1527
|
+
const total = allRecords.length;
|
|
1528
|
+
const end = perPageInput === false ? total : start + perPage;
|
|
1529
|
+
let query = table.query().where(`\`entityId\` = '${entityId}' AND \`entityType\` = '${entityType}'`);
|
|
1530
|
+
if (perPageInput !== false) {
|
|
1531
|
+
query = query.limit(perPage);
|
|
1532
|
+
if (start > 0) query = query.offset(start);
|
|
1598
1533
|
}
|
|
1599
1534
|
const records = await query.toArray();
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
createdAt: new Date(record.createdAt)
|
|
1611
|
-
};
|
|
1612
|
-
if (processed.parentSpanId === null || processed.parentSpanId === void 0) {
|
|
1613
|
-
processed.parentSpanId = "";
|
|
1614
|
-
} else {
|
|
1615
|
-
processed.parentSpanId = String(processed.parentSpanId);
|
|
1616
|
-
}
|
|
1617
|
-
return processed;
|
|
1618
|
-
});
|
|
1535
|
+
const scores = await Promise.all(records.map(async (record) => await this.transformScoreRow(record)));
|
|
1536
|
+
return {
|
|
1537
|
+
pagination: {
|
|
1538
|
+
page,
|
|
1539
|
+
perPage: perPageForResponse,
|
|
1540
|
+
total,
|
|
1541
|
+
hasMore: end < total
|
|
1542
|
+
},
|
|
1543
|
+
scores
|
|
1544
|
+
};
|
|
1619
1545
|
} catch (error) {
|
|
1620
1546
|
throw new MastraError(
|
|
1621
1547
|
{
|
|
1622
|
-
id: "
|
|
1548
|
+
id: createStorageErrorId("LANCE", "LIST_SCORES_BY_ENTITY_ID", "FAILED"),
|
|
1549
|
+
text: "Failed to get scores by entityId and entityType in LanceStorage",
|
|
1623
1550
|
domain: ErrorDomain.STORAGE,
|
|
1624
1551
|
category: ErrorCategory.THIRD_PARTY,
|
|
1625
|
-
details: {
|
|
1552
|
+
details: { error: error?.message }
|
|
1626
1553
|
},
|
|
1627
1554
|
error
|
|
1628
1555
|
);
|
|
1629
1556
|
}
|
|
1630
1557
|
}
|
|
1631
|
-
async
|
|
1558
|
+
async listScoresBySpan({
|
|
1559
|
+
traceId,
|
|
1560
|
+
spanId,
|
|
1561
|
+
pagination
|
|
1562
|
+
}) {
|
|
1632
1563
|
try {
|
|
1633
|
-
const
|
|
1634
|
-
const
|
|
1635
|
-
const
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
conditions.push(`attributes LIKE '%${attributesStr.replace(/"/g, '\\"')}%'`);
|
|
1645
|
-
}
|
|
1646
|
-
if (args.dateRange?.start) {
|
|
1647
|
-
conditions.push(`\`createdAt\` >= ${args.dateRange.start.getTime()}`);
|
|
1648
|
-
}
|
|
1649
|
-
if (args.dateRange?.end) {
|
|
1650
|
-
conditions.push(`\`createdAt\` <= ${args.dateRange.end.getTime()}`);
|
|
1651
|
-
}
|
|
1652
|
-
if (conditions.length > 0) {
|
|
1653
|
-
const whereClause = conditions.join(" AND ");
|
|
1654
|
-
query.where(whereClause);
|
|
1655
|
-
}
|
|
1656
|
-
let total = 0;
|
|
1657
|
-
if (conditions.length > 0) {
|
|
1658
|
-
const countQuery = table.query().where(conditions.join(" AND "));
|
|
1659
|
-
const allRecords = await countQuery.toArray();
|
|
1660
|
-
total = allRecords.length;
|
|
1661
|
-
} else {
|
|
1662
|
-
total = await table.countRows();
|
|
1663
|
-
}
|
|
1664
|
-
const page = args.page || 0;
|
|
1665
|
-
const perPage = args.perPage || 10;
|
|
1666
|
-
const offset = page * perPage;
|
|
1667
|
-
query.limit(perPage);
|
|
1668
|
-
if (offset > 0) {
|
|
1669
|
-
query.offset(offset);
|
|
1564
|
+
const { page, perPage: perPageInput } = pagination;
|
|
1565
|
+
const perPage = normalizePerPage(perPageInput, 100);
|
|
1566
|
+
const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
1567
|
+
const table = await this.client.openTable(TABLE_SCORERS);
|
|
1568
|
+
const allRecords = await table.query().where(`\`traceId\` = '${traceId}' AND \`spanId\` = '${spanId}'`).toArray();
|
|
1569
|
+
const total = allRecords.length;
|
|
1570
|
+
const end = perPageInput === false ? total : start + perPage;
|
|
1571
|
+
let query = table.query().where(`\`traceId\` = '${traceId}' AND \`spanId\` = '${spanId}'`);
|
|
1572
|
+
if (perPageInput !== false) {
|
|
1573
|
+
query = query.limit(perPage);
|
|
1574
|
+
if (start > 0) query = query.offset(start);
|
|
1670
1575
|
}
|
|
1671
1576
|
const records = await query.toArray();
|
|
1672
|
-
const
|
|
1673
|
-
const processed = {
|
|
1674
|
-
...record,
|
|
1675
|
-
attributes: record.attributes ? JSON.parse(record.attributes) : {},
|
|
1676
|
-
status: record.status ? JSON.parse(record.status) : {},
|
|
1677
|
-
events: record.events ? JSON.parse(record.events) : [],
|
|
1678
|
-
links: record.links ? JSON.parse(record.links) : [],
|
|
1679
|
-
other: record.other ? JSON.parse(record.other) : {},
|
|
1680
|
-
startTime: new Date(record.startTime),
|
|
1681
|
-
endTime: new Date(record.endTime),
|
|
1682
|
-
createdAt: new Date(record.createdAt)
|
|
1683
|
-
};
|
|
1684
|
-
if (processed.parentSpanId === null || processed.parentSpanId === void 0) {
|
|
1685
|
-
processed.parentSpanId = "";
|
|
1686
|
-
} else {
|
|
1687
|
-
processed.parentSpanId = String(processed.parentSpanId);
|
|
1688
|
-
}
|
|
1689
|
-
return processed;
|
|
1690
|
-
});
|
|
1577
|
+
const scores = await Promise.all(records.map(async (record) => await this.transformScoreRow(record)));
|
|
1691
1578
|
return {
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1579
|
+
pagination: {
|
|
1580
|
+
page,
|
|
1581
|
+
perPage: perPageForResponse,
|
|
1582
|
+
total,
|
|
1583
|
+
hasMore: end < total
|
|
1584
|
+
},
|
|
1585
|
+
scores
|
|
1697
1586
|
};
|
|
1698
1587
|
} catch (error) {
|
|
1699
1588
|
throw new MastraError(
|
|
1700
1589
|
{
|
|
1701
|
-
id: "
|
|
1590
|
+
id: createStorageErrorId("LANCE", "LIST_SCORES_BY_SPAN", "FAILED"),
|
|
1591
|
+
text: "Failed to get scores by traceId and spanId in LanceStorage",
|
|
1702
1592
|
domain: ErrorDomain.STORAGE,
|
|
1703
1593
|
category: ErrorCategory.THIRD_PARTY,
|
|
1704
|
-
details: {
|
|
1594
|
+
details: { error: error?.message }
|
|
1705
1595
|
},
|
|
1706
1596
|
error
|
|
1707
1597
|
);
|
|
1708
1598
|
}
|
|
1709
1599
|
}
|
|
1710
|
-
async batchTraceInsert({ records }) {
|
|
1711
|
-
this.logger.debug("Batch inserting traces", { count: records.length });
|
|
1712
|
-
await this.operations.batchInsert({
|
|
1713
|
-
tableName: TABLE_TRACES,
|
|
1714
|
-
records
|
|
1715
|
-
});
|
|
1716
|
-
}
|
|
1717
1600
|
};
|
|
1601
|
+
function escapeSql(str) {
|
|
1602
|
+
return str.replace(/'/g, "''");
|
|
1603
|
+
}
|
|
1718
1604
|
function parseWorkflowRun(row) {
|
|
1719
1605
|
let parsedSnapshot = row.snapshot;
|
|
1720
1606
|
if (typeof parsedSnapshot === "string") {
|
|
@@ -1735,38 +1621,104 @@ function parseWorkflowRun(row) {
|
|
|
1735
1621
|
}
|
|
1736
1622
|
var StoreWorkflowsLance = class extends WorkflowsStorage {
|
|
1737
1623
|
client;
|
|
1738
|
-
|
|
1624
|
+
#db;
|
|
1625
|
+
constructor(config) {
|
|
1739
1626
|
super();
|
|
1627
|
+
const client = resolveLanceConfig(config);
|
|
1740
1628
|
this.client = client;
|
|
1629
|
+
this.#db = new LanceDB({ client });
|
|
1630
|
+
}
|
|
1631
|
+
async init() {
|
|
1632
|
+
const schema = TABLE_SCHEMAS[TABLE_WORKFLOW_SNAPSHOT];
|
|
1633
|
+
await this.#db.createTable({ tableName: TABLE_WORKFLOW_SNAPSHOT, schema });
|
|
1634
|
+
await this.#db.alterTable({
|
|
1635
|
+
tableName: TABLE_WORKFLOW_SNAPSHOT,
|
|
1636
|
+
schema,
|
|
1637
|
+
ifNotExists: ["resourceId"]
|
|
1638
|
+
});
|
|
1639
|
+
}
|
|
1640
|
+
async dangerouslyClearAll() {
|
|
1641
|
+
await this.#db.clearTable({ tableName: TABLE_WORKFLOW_SNAPSHOT });
|
|
1642
|
+
}
|
|
1643
|
+
async updateWorkflowResults({
|
|
1644
|
+
workflowName,
|
|
1645
|
+
runId,
|
|
1646
|
+
stepId,
|
|
1647
|
+
result,
|
|
1648
|
+
requestContext
|
|
1649
|
+
}) {
|
|
1650
|
+
let snapshot = await this.loadWorkflowSnapshot({ workflowName, runId });
|
|
1651
|
+
if (!snapshot) {
|
|
1652
|
+
snapshot = {
|
|
1653
|
+
context: {},
|
|
1654
|
+
activePaths: [],
|
|
1655
|
+
timestamp: Date.now(),
|
|
1656
|
+
suspendedPaths: {},
|
|
1657
|
+
activeStepsPath: {},
|
|
1658
|
+
resumeLabels: {},
|
|
1659
|
+
serializedStepGraph: [],
|
|
1660
|
+
status: "pending",
|
|
1661
|
+
value: {},
|
|
1662
|
+
waitingPaths: {},
|
|
1663
|
+
runId,
|
|
1664
|
+
requestContext: {}
|
|
1665
|
+
};
|
|
1666
|
+
}
|
|
1667
|
+
snapshot.context[stepId] = result;
|
|
1668
|
+
snapshot.requestContext = { ...snapshot.requestContext, ...requestContext };
|
|
1669
|
+
await this.persistWorkflowSnapshot({ workflowName, runId, snapshot });
|
|
1670
|
+
return snapshot.context;
|
|
1671
|
+
}
|
|
1672
|
+
async updateWorkflowState({
|
|
1673
|
+
workflowName,
|
|
1674
|
+
runId,
|
|
1675
|
+
opts
|
|
1676
|
+
}) {
|
|
1677
|
+
const snapshot = await this.loadWorkflowSnapshot({ workflowName, runId });
|
|
1678
|
+
if (!snapshot) {
|
|
1679
|
+
return void 0;
|
|
1680
|
+
}
|
|
1681
|
+
if (!snapshot.context) {
|
|
1682
|
+
throw new Error(`Snapshot not found for runId ${runId}`);
|
|
1683
|
+
}
|
|
1684
|
+
const updatedSnapshot = { ...snapshot, ...opts };
|
|
1685
|
+
await this.persistWorkflowSnapshot({ workflowName, runId, snapshot: updatedSnapshot });
|
|
1686
|
+
return updatedSnapshot;
|
|
1741
1687
|
}
|
|
1742
1688
|
async persistWorkflowSnapshot({
|
|
1743
1689
|
workflowName,
|
|
1744
1690
|
runId,
|
|
1745
|
-
|
|
1691
|
+
resourceId,
|
|
1692
|
+
snapshot,
|
|
1693
|
+
createdAt,
|
|
1694
|
+
updatedAt
|
|
1746
1695
|
}) {
|
|
1747
1696
|
try {
|
|
1748
1697
|
const table = await this.client.openTable(TABLE_WORKFLOW_SNAPSHOT);
|
|
1749
|
-
const query = table.query().where(`workflow_name = '${workflowName}' AND run_id = '${runId}'`);
|
|
1698
|
+
const query = table.query().where(`workflow_name = '${escapeSql(workflowName)}' AND run_id = '${escapeSql(runId)}'`);
|
|
1750
1699
|
const records = await query.toArray();
|
|
1751
|
-
let
|
|
1752
|
-
const now = Date.now();
|
|
1700
|
+
let createdAtValue;
|
|
1701
|
+
const now = createdAt?.getTime() ?? Date.now();
|
|
1753
1702
|
if (records.length > 0) {
|
|
1754
|
-
|
|
1703
|
+
createdAtValue = records[0].createdAt ?? now;
|
|
1755
1704
|
} else {
|
|
1756
|
-
|
|
1705
|
+
createdAtValue = now;
|
|
1757
1706
|
}
|
|
1707
|
+
const { status, value, ...rest } = snapshot;
|
|
1758
1708
|
const record = {
|
|
1759
1709
|
workflow_name: workflowName,
|
|
1760
1710
|
run_id: runId,
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1711
|
+
resourceId,
|
|
1712
|
+
snapshot: JSON.stringify({ status, value, ...rest }),
|
|
1713
|
+
// this is to ensure status is always just before value, for when querying the db by status
|
|
1714
|
+
createdAt: createdAtValue,
|
|
1715
|
+
updatedAt: updatedAt ?? now
|
|
1764
1716
|
};
|
|
1765
1717
|
await table.mergeInsert(["workflow_name", "run_id"]).whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([record]);
|
|
1766
1718
|
} catch (error) {
|
|
1767
1719
|
throw new MastraError(
|
|
1768
1720
|
{
|
|
1769
|
-
id: "
|
|
1721
|
+
id: createStorageErrorId("LANCE", "PERSIST_WORKFLOW_SNAPSHOT", "FAILED"),
|
|
1770
1722
|
domain: ErrorDomain.STORAGE,
|
|
1771
1723
|
category: ErrorCategory.THIRD_PARTY,
|
|
1772
1724
|
details: { workflowName, runId }
|
|
@@ -1781,13 +1733,13 @@ var StoreWorkflowsLance = class extends WorkflowsStorage {
|
|
|
1781
1733
|
}) {
|
|
1782
1734
|
try {
|
|
1783
1735
|
const table = await this.client.openTable(TABLE_WORKFLOW_SNAPSHOT);
|
|
1784
|
-
const query = table.query().where(`workflow_name = '${workflowName}' AND run_id = '${runId}'`);
|
|
1736
|
+
const query = table.query().where(`workflow_name = '${escapeSql(workflowName)}' AND run_id = '${escapeSql(runId)}'`);
|
|
1785
1737
|
const records = await query.toArray();
|
|
1786
1738
|
return records.length > 0 ? JSON.parse(records[0].snapshot) : null;
|
|
1787
1739
|
} catch (error) {
|
|
1788
1740
|
throw new MastraError(
|
|
1789
1741
|
{
|
|
1790
|
-
id: "
|
|
1742
|
+
id: createStorageErrorId("LANCE", "LOAD_WORKFLOW_SNAPSHOT", "FAILED"),
|
|
1791
1743
|
domain: ErrorDomain.STORAGE,
|
|
1792
1744
|
category: ErrorCategory.THIRD_PARTY,
|
|
1793
1745
|
details: { workflowName, runId }
|
|
@@ -1799,9 +1751,9 @@ var StoreWorkflowsLance = class extends WorkflowsStorage {
|
|
|
1799
1751
|
async getWorkflowRunById(args) {
|
|
1800
1752
|
try {
|
|
1801
1753
|
const table = await this.client.openTable(TABLE_WORKFLOW_SNAPSHOT);
|
|
1802
|
-
let whereClause = `run_id = '${args.runId}'`;
|
|
1754
|
+
let whereClause = `run_id = '${escapeSql(args.runId)}'`;
|
|
1803
1755
|
if (args.workflowName) {
|
|
1804
|
-
whereClause += ` AND workflow_name = '${args.workflowName}'`;
|
|
1756
|
+
whereClause += ` AND workflow_name = '${escapeSql(args.workflowName)}'`;
|
|
1805
1757
|
}
|
|
1806
1758
|
const query = table.query().where(whereClause);
|
|
1807
1759
|
const records = await query.toArray();
|
|
@@ -1811,7 +1763,7 @@ var StoreWorkflowsLance = class extends WorkflowsStorage {
|
|
|
1811
1763
|
} catch (error) {
|
|
1812
1764
|
throw new MastraError(
|
|
1813
1765
|
{
|
|
1814
|
-
id: "
|
|
1766
|
+
id: createStorageErrorId("LANCE", "GET_WORKFLOW_RUN_BY_ID", "FAILED"),
|
|
1815
1767
|
domain: ErrorDomain.STORAGE,
|
|
1816
1768
|
category: ErrorCategory.THIRD_PARTY,
|
|
1817
1769
|
details: { runId: args.runId, workflowName: args.workflowName ?? "" }
|
|
@@ -1820,16 +1772,37 @@ var StoreWorkflowsLance = class extends WorkflowsStorage {
|
|
|
1820
1772
|
);
|
|
1821
1773
|
}
|
|
1822
1774
|
}
|
|
1823
|
-
async
|
|
1775
|
+
async deleteWorkflowRunById({ runId, workflowName }) {
|
|
1776
|
+
try {
|
|
1777
|
+
const table = await this.client.openTable(TABLE_WORKFLOW_SNAPSHOT);
|
|
1778
|
+
const whereClause = `run_id = '${escapeSql(runId)}' AND workflow_name = '${escapeSql(workflowName)}'`;
|
|
1779
|
+
await table.delete(whereClause);
|
|
1780
|
+
} catch (error) {
|
|
1781
|
+
throw new MastraError(
|
|
1782
|
+
{
|
|
1783
|
+
id: createStorageErrorId("LANCE", "DELETE_WORKFLOW_RUN_BY_ID", "FAILED"),
|
|
1784
|
+
domain: ErrorDomain.STORAGE,
|
|
1785
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1786
|
+
details: { runId, workflowName }
|
|
1787
|
+
},
|
|
1788
|
+
error
|
|
1789
|
+
);
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
async listWorkflowRuns(args) {
|
|
1824
1793
|
try {
|
|
1825
1794
|
const table = await this.client.openTable(TABLE_WORKFLOW_SNAPSHOT);
|
|
1826
1795
|
let query = table.query();
|
|
1827
1796
|
const conditions = [];
|
|
1828
1797
|
if (args?.workflowName) {
|
|
1829
|
-
conditions.push(`workflow_name = '${args.workflowName
|
|
1798
|
+
conditions.push(`workflow_name = '${escapeSql(args.workflowName)}'`);
|
|
1799
|
+
}
|
|
1800
|
+
if (args?.status) {
|
|
1801
|
+
const escapedStatus = args.status.replace(/\\/g, "\\\\").replace(/'/g, "''").replace(/%/g, "\\%").replace(/_/g, "\\_");
|
|
1802
|
+
conditions.push(`\`snapshot\` LIKE '%"status":"${escapedStatus}","value"%'`);
|
|
1830
1803
|
}
|
|
1831
1804
|
if (args?.resourceId) {
|
|
1832
|
-
conditions.push(`\`resourceId\` = '${args.resourceId}'`);
|
|
1805
|
+
conditions.push(`\`resourceId\` = '${escapeSql(args.resourceId)}'`);
|
|
1833
1806
|
}
|
|
1834
1807
|
if (args?.fromDate instanceof Date) {
|
|
1835
1808
|
conditions.push(`\`createdAt\` >= ${args.fromDate.getTime()}`);
|
|
@@ -1844,11 +1817,22 @@ var StoreWorkflowsLance = class extends WorkflowsStorage {
|
|
|
1844
1817
|
} else {
|
|
1845
1818
|
total = await table.countRows();
|
|
1846
1819
|
}
|
|
1847
|
-
if (args?.
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1820
|
+
if (args?.perPage !== void 0 && args?.page !== void 0) {
|
|
1821
|
+
const normalizedPerPage = normalizePerPage(args.perPage, Number.MAX_SAFE_INTEGER);
|
|
1822
|
+
if (args.page < 0 || !Number.isInteger(args.page)) {
|
|
1823
|
+
throw new MastraError(
|
|
1824
|
+
{
|
|
1825
|
+
id: createStorageErrorId("LANCE", "LIST_WORKFLOW_RUNS", "INVALID_PAGINATION"),
|
|
1826
|
+
domain: ErrorDomain.STORAGE,
|
|
1827
|
+
category: ErrorCategory.USER,
|
|
1828
|
+
details: { page: args.page, perPage: args.perPage }
|
|
1829
|
+
},
|
|
1830
|
+
new Error(`Invalid pagination parameters: page=${args.page}, perPage=${args.perPage}`)
|
|
1831
|
+
);
|
|
1832
|
+
}
|
|
1833
|
+
const offset = args.page * normalizedPerPage;
|
|
1834
|
+
query.limit(normalizedPerPage);
|
|
1835
|
+
query.offset(offset);
|
|
1852
1836
|
}
|
|
1853
1837
|
const records = await query.toArray();
|
|
1854
1838
|
return {
|
|
@@ -1858,10 +1842,10 @@ var StoreWorkflowsLance = class extends WorkflowsStorage {
|
|
|
1858
1842
|
} catch (error) {
|
|
1859
1843
|
throw new MastraError(
|
|
1860
1844
|
{
|
|
1861
|
-
id: "
|
|
1845
|
+
id: createStorageErrorId("LANCE", "LIST_WORKFLOW_RUNS", "FAILED"),
|
|
1862
1846
|
domain: ErrorDomain.STORAGE,
|
|
1863
1847
|
category: ErrorCategory.THIRD_PARTY,
|
|
1864
|
-
details: {
|
|
1848
|
+
details: { resourceId: args?.resourceId ?? "", workflowName: args?.workflowName ?? "" }
|
|
1865
1849
|
},
|
|
1866
1850
|
error
|
|
1867
1851
|
);
|
|
@@ -1875,48 +1859,52 @@ var LanceStorage = class _LanceStorage extends MastraStorage {
|
|
|
1875
1859
|
lanceClient;
|
|
1876
1860
|
/**
|
|
1877
1861
|
* Creates a new instance of LanceStorage
|
|
1862
|
+
* @param id The unique identifier for this storage instance
|
|
1863
|
+
* @param name The name for this storage instance
|
|
1878
1864
|
* @param uri The URI to connect to LanceDB
|
|
1879
|
-
* @param
|
|
1865
|
+
* @param connectionOptions connection options for LanceDB
|
|
1866
|
+
* @param storageOptions storage options including disableInit
|
|
1880
1867
|
*
|
|
1881
1868
|
* Usage:
|
|
1882
1869
|
*
|
|
1883
1870
|
* Connect to a local database
|
|
1884
1871
|
* ```ts
|
|
1885
|
-
* const store = await LanceStorage.create('/path/to/db');
|
|
1872
|
+
* const store = await LanceStorage.create('my-storage-id', 'MyStorage', '/path/to/db');
|
|
1886
1873
|
* ```
|
|
1887
1874
|
*
|
|
1888
1875
|
* Connect to a LanceDB cloud database
|
|
1889
1876
|
* ```ts
|
|
1890
|
-
* const store = await LanceStorage.create('db://host:port');
|
|
1877
|
+
* const store = await LanceStorage.create('my-storage-id', 'MyStorage', 'db://host:port');
|
|
1891
1878
|
* ```
|
|
1892
1879
|
*
|
|
1893
1880
|
* Connect to a cloud database
|
|
1894
1881
|
* ```ts
|
|
1895
|
-
* const store = await LanceStorage.create('s3://bucket/db', { storageOptions: { timeout: '60s' } });
|
|
1882
|
+
* const store = await LanceStorage.create('my-storage-id', 'MyStorage', 's3://bucket/db', { storageOptions: { timeout: '60s' } });
|
|
1883
|
+
* ```
|
|
1884
|
+
*
|
|
1885
|
+
* Disable auto-init for runtime (after CI/CD has run migrations)
|
|
1886
|
+
* ```ts
|
|
1887
|
+
* const store = await LanceStorage.create('my-storage-id', 'MyStorage', '/path/to/db', undefined, { disableInit: true });
|
|
1896
1888
|
* ```
|
|
1897
1889
|
*/
|
|
1898
|
-
static async create(name, uri,
|
|
1899
|
-
const instance = new _LanceStorage(name);
|
|
1890
|
+
static async create(id, name, uri, connectionOptions, storageOptions) {
|
|
1891
|
+
const instance = new _LanceStorage(id, name, storageOptions?.disableInit);
|
|
1900
1892
|
try {
|
|
1901
|
-
instance.lanceClient = await connect(uri,
|
|
1902
|
-
const operations = new StoreOperationsLance({ client: instance.lanceClient });
|
|
1893
|
+
instance.lanceClient = await connect(uri, connectionOptions);
|
|
1903
1894
|
instance.stores = {
|
|
1904
|
-
operations: new StoreOperationsLance({ client: instance.lanceClient }),
|
|
1905
1895
|
workflows: new StoreWorkflowsLance({ client: instance.lanceClient }),
|
|
1906
|
-
traces: new StoreTracesLance({ client: instance.lanceClient, operations }),
|
|
1907
1896
|
scores: new StoreScoresLance({ client: instance.lanceClient }),
|
|
1908
|
-
memory: new StoreMemoryLance({ client: instance.lanceClient
|
|
1909
|
-
legacyEvals: new StoreLegacyEvalsLance({ client: instance.lanceClient })
|
|
1897
|
+
memory: new StoreMemoryLance({ client: instance.lanceClient })
|
|
1910
1898
|
};
|
|
1911
1899
|
return instance;
|
|
1912
1900
|
} catch (e) {
|
|
1913
1901
|
throw new MastraError(
|
|
1914
1902
|
{
|
|
1915
|
-
id: "
|
|
1903
|
+
id: createStorageErrorId("LANCE", "CONNECT", "FAILED"),
|
|
1916
1904
|
domain: ErrorDomain.STORAGE,
|
|
1917
1905
|
category: ErrorCategory.THIRD_PARTY,
|
|
1918
1906
|
text: `Failed to connect to LanceDB: ${e.message || e}`,
|
|
1919
|
-
details: { uri, optionsProvided: !!
|
|
1907
|
+
details: { uri, optionsProvided: !!connectionOptions }
|
|
1920
1908
|
},
|
|
1921
1909
|
e
|
|
1922
1910
|
);
|
|
@@ -1924,54 +1912,16 @@ var LanceStorage = class _LanceStorage extends MastraStorage {
|
|
|
1924
1912
|
}
|
|
1925
1913
|
/**
|
|
1926
1914
|
* @internal
|
|
1927
|
-
* Private constructor to enforce using the create factory method
|
|
1915
|
+
* Private constructor to enforce using the create factory method.
|
|
1916
|
+
* Note: stores is initialized in create() after the lanceClient is connected.
|
|
1928
1917
|
*/
|
|
1929
|
-
constructor(name) {
|
|
1930
|
-
super({ name });
|
|
1931
|
-
|
|
1932
|
-
this.stores = {
|
|
1933
|
-
operations: new StoreOperationsLance({ client: this.lanceClient }),
|
|
1934
|
-
workflows: new StoreWorkflowsLance({ client: this.lanceClient }),
|
|
1935
|
-
traces: new StoreTracesLance({ client: this.lanceClient, operations }),
|
|
1936
|
-
scores: new StoreScoresLance({ client: this.lanceClient }),
|
|
1937
|
-
legacyEvals: new StoreLegacyEvalsLance({ client: this.lanceClient }),
|
|
1938
|
-
memory: new StoreMemoryLance({ client: this.lanceClient, operations })
|
|
1939
|
-
};
|
|
1940
|
-
}
|
|
1941
|
-
async createTable({
|
|
1942
|
-
tableName,
|
|
1943
|
-
schema
|
|
1944
|
-
}) {
|
|
1945
|
-
return this.stores.operations.createTable({ tableName, schema });
|
|
1946
|
-
}
|
|
1947
|
-
async dropTable({ tableName }) {
|
|
1948
|
-
return this.stores.operations.dropTable({ tableName });
|
|
1949
|
-
}
|
|
1950
|
-
async alterTable({
|
|
1951
|
-
tableName,
|
|
1952
|
-
schema,
|
|
1953
|
-
ifNotExists
|
|
1954
|
-
}) {
|
|
1955
|
-
return this.stores.operations.alterTable({ tableName, schema, ifNotExists });
|
|
1956
|
-
}
|
|
1957
|
-
async clearTable({ tableName }) {
|
|
1958
|
-
return this.stores.operations.clearTable({ tableName });
|
|
1959
|
-
}
|
|
1960
|
-
async insert({ tableName, record }) {
|
|
1961
|
-
return this.stores.operations.insert({ tableName, record });
|
|
1962
|
-
}
|
|
1963
|
-
async batchInsert({ tableName, records }) {
|
|
1964
|
-
return this.stores.operations.batchInsert({ tableName, records });
|
|
1965
|
-
}
|
|
1966
|
-
async load({ tableName, keys }) {
|
|
1967
|
-
return this.stores.operations.load({ tableName, keys });
|
|
1918
|
+
constructor(id, name, disableInit) {
|
|
1919
|
+
super({ id, name, disableInit });
|
|
1920
|
+
this.stores = {};
|
|
1968
1921
|
}
|
|
1969
1922
|
async getThreadById({ threadId }) {
|
|
1970
1923
|
return this.stores.memory.getThreadById({ threadId });
|
|
1971
1924
|
}
|
|
1972
|
-
async getThreadsByResourceId({ resourceId }) {
|
|
1973
|
-
return this.stores.memory.getThreadsByResourceId({ resourceId });
|
|
1974
|
-
}
|
|
1975
1925
|
/**
|
|
1976
1926
|
* Saves a thread to the database. This function doesn't overwrite existing threads.
|
|
1977
1927
|
* @param thread - The thread to save
|
|
@@ -1990,13 +1940,17 @@ var LanceStorage = class _LanceStorage extends MastraStorage {
|
|
|
1990
1940
|
async deleteThread({ threadId }) {
|
|
1991
1941
|
return this.stores.memory.deleteThread({ threadId });
|
|
1992
1942
|
}
|
|
1943
|
+
async deleteMessages(messageIds) {
|
|
1944
|
+
return this.stores.memory.deleteMessages(messageIds);
|
|
1945
|
+
}
|
|
1993
1946
|
get supports() {
|
|
1994
1947
|
return {
|
|
1995
1948
|
selectByIncludeResourceScope: true,
|
|
1996
1949
|
resourceWorkingMemory: true,
|
|
1997
1950
|
hasColumn: true,
|
|
1998
1951
|
createTable: true,
|
|
1999
|
-
deleteMessages:
|
|
1952
|
+
deleteMessages: true,
|
|
1953
|
+
listScoresBySpan: true
|
|
2000
1954
|
};
|
|
2001
1955
|
}
|
|
2002
1956
|
async getResourceById({ resourceId }) {
|
|
@@ -2060,54 +2014,47 @@ var LanceStorage = class _LanceStorage extends MastraStorage {
|
|
|
2060
2014
|
});
|
|
2061
2015
|
return Array.from(allIndices).sort((a, b) => a - b).map((index) => records[index]);
|
|
2062
2016
|
}
|
|
2063
|
-
async
|
|
2064
|
-
|
|
2065
|
-
resourceId,
|
|
2066
|
-
selectBy,
|
|
2067
|
-
format,
|
|
2068
|
-
threadConfig
|
|
2069
|
-
}) {
|
|
2070
|
-
return this.stores.memory.getMessages({ threadId, resourceId, selectBy, format, threadConfig });
|
|
2017
|
+
async listMessagesById({ messageIds }) {
|
|
2018
|
+
return this.stores.memory.listMessagesById({ messageIds });
|
|
2071
2019
|
}
|
|
2072
2020
|
async saveMessages(args) {
|
|
2073
2021
|
return this.stores.memory.saveMessages(args);
|
|
2074
2022
|
}
|
|
2075
|
-
async getThreadsByResourceIdPaginated(args) {
|
|
2076
|
-
return this.stores.memory.getThreadsByResourceIdPaginated(args);
|
|
2077
|
-
}
|
|
2078
|
-
async getMessagesPaginated(args) {
|
|
2079
|
-
return this.stores.memory.getMessagesPaginated(args);
|
|
2080
|
-
}
|
|
2081
2023
|
async updateMessages(_args) {
|
|
2082
2024
|
return this.stores.memory.updateMessages(_args);
|
|
2083
2025
|
}
|
|
2084
|
-
async
|
|
2085
|
-
return this.stores.
|
|
2026
|
+
async listWorkflowRuns(args) {
|
|
2027
|
+
return this.stores.workflows.listWorkflowRuns(args);
|
|
2086
2028
|
}
|
|
2087
|
-
async
|
|
2088
|
-
return this.stores.
|
|
2089
|
-
}
|
|
2090
|
-
async getTracesPaginated(args) {
|
|
2091
|
-
return this.stores.traces.getTracesPaginated(args);
|
|
2092
|
-
}
|
|
2093
|
-
async getEvalsByAgentName(agentName, type) {
|
|
2094
|
-
return this.stores.legacyEvals.getEvalsByAgentName(agentName, type);
|
|
2029
|
+
async getWorkflowRunById(args) {
|
|
2030
|
+
return this.stores.workflows.getWorkflowRunById(args);
|
|
2095
2031
|
}
|
|
2096
|
-
async
|
|
2097
|
-
return this.stores.
|
|
2032
|
+
async deleteWorkflowRunById({ runId, workflowName }) {
|
|
2033
|
+
return this.stores.workflows.deleteWorkflowRunById({ runId, workflowName });
|
|
2098
2034
|
}
|
|
2099
|
-
async
|
|
2100
|
-
|
|
2035
|
+
async updateWorkflowResults({
|
|
2036
|
+
workflowName,
|
|
2037
|
+
runId,
|
|
2038
|
+
stepId,
|
|
2039
|
+
result,
|
|
2040
|
+
requestContext
|
|
2041
|
+
}) {
|
|
2042
|
+
return this.stores.workflows.updateWorkflowResults({ workflowName, runId, stepId, result, requestContext });
|
|
2101
2043
|
}
|
|
2102
|
-
async
|
|
2103
|
-
|
|
2044
|
+
async updateWorkflowState({
|
|
2045
|
+
workflowName,
|
|
2046
|
+
runId,
|
|
2047
|
+
opts
|
|
2048
|
+
}) {
|
|
2049
|
+
return this.stores.workflows.updateWorkflowState({ workflowName, runId, opts });
|
|
2104
2050
|
}
|
|
2105
2051
|
async persistWorkflowSnapshot({
|
|
2106
2052
|
workflowName,
|
|
2107
2053
|
runId,
|
|
2054
|
+
resourceId,
|
|
2108
2055
|
snapshot
|
|
2109
2056
|
}) {
|
|
2110
|
-
return this.stores.workflows.persistWorkflowSnapshot({ workflowName, runId, snapshot });
|
|
2057
|
+
return this.stores.workflows.persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot });
|
|
2111
2058
|
}
|
|
2112
2059
|
async loadWorkflowSnapshot({
|
|
2113
2060
|
workflowName,
|
|
@@ -2118,27 +2065,37 @@ var LanceStorage = class _LanceStorage extends MastraStorage {
|
|
|
2118
2065
|
async getScoreById({ id: _id }) {
|
|
2119
2066
|
return this.stores.scores.getScoreById({ id: _id });
|
|
2120
2067
|
}
|
|
2121
|
-
async
|
|
2068
|
+
async listScoresByScorerId({
|
|
2122
2069
|
scorerId,
|
|
2070
|
+
source,
|
|
2071
|
+
entityId,
|
|
2072
|
+
entityType,
|
|
2123
2073
|
pagination
|
|
2124
2074
|
}) {
|
|
2125
|
-
return this.stores.scores.
|
|
2075
|
+
return this.stores.scores.listScoresByScorerId({ scorerId, source, pagination, entityId, entityType });
|
|
2126
2076
|
}
|
|
2127
|
-
async saveScore(
|
|
2128
|
-
return this.stores.scores.saveScore(
|
|
2077
|
+
async saveScore(score) {
|
|
2078
|
+
return this.stores.scores.saveScore(score);
|
|
2129
2079
|
}
|
|
2130
|
-
async
|
|
2080
|
+
async listScoresByRunId({
|
|
2131
2081
|
runId,
|
|
2132
2082
|
pagination
|
|
2133
2083
|
}) {
|
|
2134
|
-
return this.stores.scores.
|
|
2084
|
+
return this.stores.scores.listScoresByRunId({ runId, pagination });
|
|
2135
2085
|
}
|
|
2136
|
-
async
|
|
2086
|
+
async listScoresByEntityId({
|
|
2137
2087
|
entityId,
|
|
2138
2088
|
entityType,
|
|
2139
2089
|
pagination
|
|
2140
2090
|
}) {
|
|
2141
|
-
return this.stores.scores.
|
|
2091
|
+
return this.stores.scores.listScoresByEntityId({ entityId, entityType, pagination });
|
|
2092
|
+
}
|
|
2093
|
+
async listScoresBySpan({
|
|
2094
|
+
traceId,
|
|
2095
|
+
spanId,
|
|
2096
|
+
pagination
|
|
2097
|
+
}) {
|
|
2098
|
+
return this.stores.scores.listScoresBySpan({ traceId, spanId, pagination });
|
|
2142
2099
|
}
|
|
2143
2100
|
};
|
|
2144
2101
|
var LanceFilterTranslator = class extends BaseFilterTranslator {
|
|
@@ -2487,14 +2444,14 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
|
|
|
2487
2444
|
* ```
|
|
2488
2445
|
*/
|
|
2489
2446
|
static async create(uri, options) {
|
|
2490
|
-
const instance = new _LanceVectorStore();
|
|
2447
|
+
const instance = new _LanceVectorStore(options?.id || crypto.randomUUID());
|
|
2491
2448
|
try {
|
|
2492
2449
|
instance.lanceClient = await connect(uri, options);
|
|
2493
2450
|
return instance;
|
|
2494
2451
|
} catch (e) {
|
|
2495
2452
|
throw new MastraError(
|
|
2496
2453
|
{
|
|
2497
|
-
id: "
|
|
2454
|
+
id: createVectorErrorId("LANCE", "CONNECT", "FAILED"),
|
|
2498
2455
|
domain: ErrorDomain.STORAGE,
|
|
2499
2456
|
category: ErrorCategory.THIRD_PARTY,
|
|
2500
2457
|
details: { uri }
|
|
@@ -2507,8 +2464,8 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
|
|
|
2507
2464
|
* @internal
|
|
2508
2465
|
* Private constructor to enforce using the create factory method
|
|
2509
2466
|
*/
|
|
2510
|
-
constructor() {
|
|
2511
|
-
super();
|
|
2467
|
+
constructor(id) {
|
|
2468
|
+
super({ id });
|
|
2512
2469
|
}
|
|
2513
2470
|
close() {
|
|
2514
2471
|
if (this.lanceClient) {
|
|
@@ -2537,7 +2494,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
|
|
|
2537
2494
|
} catch (error) {
|
|
2538
2495
|
throw new MastraError(
|
|
2539
2496
|
{
|
|
2540
|
-
id: "
|
|
2497
|
+
id: createVectorErrorId("LANCE", "QUERY", "INVALID_ARGS"),
|
|
2541
2498
|
domain: ErrorDomain.STORAGE,
|
|
2542
2499
|
category: ErrorCategory.USER,
|
|
2543
2500
|
text: "LanceDB client not initialized. Use LanceVectorStore.create() to create an instance",
|
|
@@ -2585,7 +2542,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
|
|
|
2585
2542
|
} catch (error) {
|
|
2586
2543
|
throw new MastraError(
|
|
2587
2544
|
{
|
|
2588
|
-
id: "
|
|
2545
|
+
id: createVectorErrorId("LANCE", "QUERY", "FAILED"),
|
|
2589
2546
|
domain: ErrorDomain.STORAGE,
|
|
2590
2547
|
category: ErrorCategory.THIRD_PARTY,
|
|
2591
2548
|
details: { tableName, includeVector, columnsCount: columns?.length, includeAllColumns }
|
|
@@ -2637,7 +2594,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
|
|
|
2637
2594
|
} catch (error) {
|
|
2638
2595
|
throw new MastraError(
|
|
2639
2596
|
{
|
|
2640
|
-
id: "
|
|
2597
|
+
id: createVectorErrorId("LANCE", "UPSERT", "INVALID_ARGS"),
|
|
2641
2598
|
domain: ErrorDomain.STORAGE,
|
|
2642
2599
|
category: ErrorCategory.USER,
|
|
2643
2600
|
text: "LanceDB client not initialized. Use LanceVectorStore.create() to create an instance",
|
|
@@ -2673,7 +2630,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
|
|
|
2673
2630
|
} catch (error) {
|
|
2674
2631
|
throw new MastraError(
|
|
2675
2632
|
{
|
|
2676
|
-
id: "
|
|
2633
|
+
id: createVectorErrorId("LANCE", "UPSERT", "FAILED"),
|
|
2677
2634
|
domain: ErrorDomain.STORAGE,
|
|
2678
2635
|
category: ErrorCategory.THIRD_PARTY,
|
|
2679
2636
|
details: { tableName, vectorCount: vectors.length, metadataCount: metadata.length, idsCount: ids.length }
|
|
@@ -2700,7 +2657,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
|
|
|
2700
2657
|
async createTable(tableName, data, options) {
|
|
2701
2658
|
if (!this.lanceClient) {
|
|
2702
2659
|
throw new MastraError({
|
|
2703
|
-
id: "
|
|
2660
|
+
id: createVectorErrorId("LANCE", "CREATE_TABLE", "INVALID_ARGS"),
|
|
2704
2661
|
domain: ErrorDomain.STORAGE,
|
|
2705
2662
|
category: ErrorCategory.USER,
|
|
2706
2663
|
text: "LanceDB client not initialized. Use LanceVectorStore.create() to create an instance",
|
|
@@ -2715,7 +2672,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
|
|
|
2715
2672
|
} catch (error) {
|
|
2716
2673
|
throw new MastraError(
|
|
2717
2674
|
{
|
|
2718
|
-
id: "
|
|
2675
|
+
id: createVectorErrorId("LANCE", "CREATE_TABLE", "FAILED"),
|
|
2719
2676
|
domain: ErrorDomain.STORAGE,
|
|
2720
2677
|
category: ErrorCategory.THIRD_PARTY,
|
|
2721
2678
|
details: { tableName }
|
|
@@ -2727,7 +2684,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
|
|
|
2727
2684
|
async listTables() {
|
|
2728
2685
|
if (!this.lanceClient) {
|
|
2729
2686
|
throw new MastraError({
|
|
2730
|
-
id: "
|
|
2687
|
+
id: createVectorErrorId("LANCE", "LIST_TABLES", "INVALID_ARGS"),
|
|
2731
2688
|
domain: ErrorDomain.STORAGE,
|
|
2732
2689
|
category: ErrorCategory.USER,
|
|
2733
2690
|
text: "LanceDB client not initialized. Use LanceVectorStore.create() to create an instance",
|
|
@@ -2739,7 +2696,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
|
|
|
2739
2696
|
} catch (error) {
|
|
2740
2697
|
throw new MastraError(
|
|
2741
2698
|
{
|
|
2742
|
-
id: "
|
|
2699
|
+
id: createVectorErrorId("LANCE", "LIST_TABLES", "FAILED"),
|
|
2743
2700
|
domain: ErrorDomain.STORAGE,
|
|
2744
2701
|
category: ErrorCategory.THIRD_PARTY
|
|
2745
2702
|
},
|
|
@@ -2750,7 +2707,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
|
|
|
2750
2707
|
async getTableSchema(tableName) {
|
|
2751
2708
|
if (!this.lanceClient) {
|
|
2752
2709
|
throw new MastraError({
|
|
2753
|
-
id: "
|
|
2710
|
+
id: createVectorErrorId("LANCE", "GET_TABLE_SCHEMA", "INVALID_ARGS"),
|
|
2754
2711
|
domain: ErrorDomain.STORAGE,
|
|
2755
2712
|
category: ErrorCategory.USER,
|
|
2756
2713
|
text: "LanceDB client not initialized. Use LanceVectorStore.create() to create an instance",
|
|
@@ -2763,7 +2720,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
|
|
|
2763
2720
|
} catch (error) {
|
|
2764
2721
|
throw new MastraError(
|
|
2765
2722
|
{
|
|
2766
|
-
id: "
|
|
2723
|
+
id: createVectorErrorId("LANCE", "GET_TABLE_SCHEMA", "FAILED"),
|
|
2767
2724
|
domain: ErrorDomain.STORAGE,
|
|
2768
2725
|
category: ErrorCategory.THIRD_PARTY,
|
|
2769
2726
|
details: { tableName }
|
|
@@ -2798,7 +2755,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
|
|
|
2798
2755
|
} catch (err) {
|
|
2799
2756
|
throw new MastraError(
|
|
2800
2757
|
{
|
|
2801
|
-
id: "
|
|
2758
|
+
id: createVectorErrorId("LANCE", "CREATE_INDEX", "INVALID_ARGS"),
|
|
2802
2759
|
domain: ErrorDomain.STORAGE,
|
|
2803
2760
|
category: ErrorCategory.USER,
|
|
2804
2761
|
details: { tableName: tableName || "", indexName, dimension, metric }
|
|
@@ -2843,7 +2800,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
|
|
|
2843
2800
|
} catch (error) {
|
|
2844
2801
|
throw new MastraError(
|
|
2845
2802
|
{
|
|
2846
|
-
id: "
|
|
2803
|
+
id: createVectorErrorId("LANCE", "CREATE_INDEX", "FAILED"),
|
|
2847
2804
|
domain: ErrorDomain.STORAGE,
|
|
2848
2805
|
category: ErrorCategory.THIRD_PARTY,
|
|
2849
2806
|
details: { tableName: tableName || "", indexName, dimension }
|
|
@@ -2855,7 +2812,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
|
|
|
2855
2812
|
async listIndexes() {
|
|
2856
2813
|
if (!this.lanceClient) {
|
|
2857
2814
|
throw new MastraError({
|
|
2858
|
-
id: "
|
|
2815
|
+
id: createVectorErrorId("LANCE", "LIST_INDEXES", "INVALID_ARGS"),
|
|
2859
2816
|
domain: ErrorDomain.STORAGE,
|
|
2860
2817
|
category: ErrorCategory.USER,
|
|
2861
2818
|
text: "LanceDB client not initialized. Use LanceVectorStore.create() to create an instance",
|
|
@@ -2874,7 +2831,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
|
|
|
2874
2831
|
} catch (error) {
|
|
2875
2832
|
throw new MastraError(
|
|
2876
2833
|
{
|
|
2877
|
-
id: "
|
|
2834
|
+
id: createVectorErrorId("LANCE", "LIST_INDEXES", "FAILED"),
|
|
2878
2835
|
domain: ErrorDomain.STORAGE,
|
|
2879
2836
|
category: ErrorCategory.THIRD_PARTY
|
|
2880
2837
|
},
|
|
@@ -2893,7 +2850,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
|
|
|
2893
2850
|
} catch (err) {
|
|
2894
2851
|
throw new MastraError(
|
|
2895
2852
|
{
|
|
2896
|
-
id: "
|
|
2853
|
+
id: createVectorErrorId("LANCE", "DESCRIBE_INDEX", "INVALID_ARGS"),
|
|
2897
2854
|
domain: ErrorDomain.STORAGE,
|
|
2898
2855
|
category: ErrorCategory.USER,
|
|
2899
2856
|
details: { indexName }
|
|
@@ -2928,7 +2885,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
|
|
|
2928
2885
|
} catch (error) {
|
|
2929
2886
|
throw new MastraError(
|
|
2930
2887
|
{
|
|
2931
|
-
id: "
|
|
2888
|
+
id: createVectorErrorId("LANCE", "DESCRIBE_INDEX", "FAILED"),
|
|
2932
2889
|
domain: ErrorDomain.STORAGE,
|
|
2933
2890
|
category: ErrorCategory.THIRD_PARTY,
|
|
2934
2891
|
details: { indexName }
|
|
@@ -2948,7 +2905,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
|
|
|
2948
2905
|
} catch (err) {
|
|
2949
2906
|
throw new MastraError(
|
|
2950
2907
|
{
|
|
2951
|
-
id: "
|
|
2908
|
+
id: createVectorErrorId("LANCE", "DELETE_INDEX", "INVALID_ARGS"),
|
|
2952
2909
|
domain: ErrorDomain.STORAGE,
|
|
2953
2910
|
category: ErrorCategory.USER,
|
|
2954
2911
|
details: { indexName }
|
|
@@ -2971,7 +2928,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
|
|
|
2971
2928
|
} catch (error) {
|
|
2972
2929
|
throw new MastraError(
|
|
2973
2930
|
{
|
|
2974
|
-
id: "
|
|
2931
|
+
id: createVectorErrorId("LANCE", "DELETE_INDEX", "FAILED"),
|
|
2975
2932
|
domain: ErrorDomain.STORAGE,
|
|
2976
2933
|
category: ErrorCategory.THIRD_PARTY,
|
|
2977
2934
|
details: { indexName }
|
|
@@ -2986,7 +2943,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
|
|
|
2986
2943
|
async deleteAllTables() {
|
|
2987
2944
|
if (!this.lanceClient) {
|
|
2988
2945
|
throw new MastraError({
|
|
2989
|
-
id: "
|
|
2946
|
+
id: createVectorErrorId("LANCE", "DELETE_ALL_TABLES", "INVALID_ARGS"),
|
|
2990
2947
|
domain: ErrorDomain.STORAGE,
|
|
2991
2948
|
category: ErrorCategory.USER,
|
|
2992
2949
|
details: { methodName: "deleteAllTables" },
|
|
@@ -2998,7 +2955,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
|
|
|
2998
2955
|
} catch (error) {
|
|
2999
2956
|
throw new MastraError(
|
|
3000
2957
|
{
|
|
3001
|
-
id: "
|
|
2958
|
+
id: createVectorErrorId("LANCE", "DELETE_ALL_TABLES", "FAILED"),
|
|
3002
2959
|
domain: ErrorDomain.STORAGE,
|
|
3003
2960
|
category: ErrorCategory.THIRD_PARTY,
|
|
3004
2961
|
details: { methodName: "deleteAllTables" }
|
|
@@ -3010,7 +2967,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
|
|
|
3010
2967
|
async deleteTable(tableName) {
|
|
3011
2968
|
if (!this.lanceClient) {
|
|
3012
2969
|
throw new MastraError({
|
|
3013
|
-
id: "
|
|
2970
|
+
id: createVectorErrorId("LANCE", "DELETE_TABLE", "INVALID_ARGS"),
|
|
3014
2971
|
domain: ErrorDomain.STORAGE,
|
|
3015
2972
|
category: ErrorCategory.USER,
|
|
3016
2973
|
details: { tableName },
|
|
@@ -3022,7 +2979,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
|
|
|
3022
2979
|
} catch (error) {
|
|
3023
2980
|
throw new MastraError(
|
|
3024
2981
|
{
|
|
3025
|
-
id: "
|
|
2982
|
+
id: createVectorErrorId("LANCE", "DELETE_TABLE", "FAILED"),
|
|
3026
2983
|
domain: ErrorDomain.STORAGE,
|
|
3027
2984
|
category: ErrorCategory.THIRD_PARTY,
|
|
3028
2985
|
details: { tableName }
|
|
@@ -3031,7 +2988,44 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
|
|
|
3031
2988
|
);
|
|
3032
2989
|
}
|
|
3033
2990
|
}
|
|
3034
|
-
async updateVector(
|
|
2991
|
+
async updateVector(params) {
|
|
2992
|
+
const { indexName, update } = params;
|
|
2993
|
+
if ("id" in params && "filter" in params && params.id && params.filter) {
|
|
2994
|
+
throw new MastraError({
|
|
2995
|
+
id: createVectorErrorId("LANCE", "UPDATE_VECTOR", "MUTUALLY_EXCLUSIVE"),
|
|
2996
|
+
domain: ErrorDomain.STORAGE,
|
|
2997
|
+
category: ErrorCategory.USER,
|
|
2998
|
+
text: "id and filter are mutually exclusive",
|
|
2999
|
+
details: { indexName }
|
|
3000
|
+
});
|
|
3001
|
+
}
|
|
3002
|
+
if (!("id" in params || "filter" in params) || !params.id && !params.filter) {
|
|
3003
|
+
throw new MastraError({
|
|
3004
|
+
id: createVectorErrorId("LANCE", "UPDATE_VECTOR", "NO_TARGET"),
|
|
3005
|
+
domain: ErrorDomain.STORAGE,
|
|
3006
|
+
category: ErrorCategory.USER,
|
|
3007
|
+
text: "Either id or filter must be provided",
|
|
3008
|
+
details: { indexName }
|
|
3009
|
+
});
|
|
3010
|
+
}
|
|
3011
|
+
if ("filter" in params && params.filter && Object.keys(params.filter).length === 0) {
|
|
3012
|
+
throw new MastraError({
|
|
3013
|
+
id: createVectorErrorId("LANCE", "UPDATE_VECTOR", "EMPTY_FILTER"),
|
|
3014
|
+
domain: ErrorDomain.STORAGE,
|
|
3015
|
+
category: ErrorCategory.USER,
|
|
3016
|
+
text: "Cannot update with empty filter",
|
|
3017
|
+
details: { indexName }
|
|
3018
|
+
});
|
|
3019
|
+
}
|
|
3020
|
+
if (!update.vector && !update.metadata) {
|
|
3021
|
+
throw new MastraError({
|
|
3022
|
+
id: createVectorErrorId("LANCE", "UPDATE_VECTOR", "NO_PAYLOAD"),
|
|
3023
|
+
domain: ErrorDomain.STORAGE,
|
|
3024
|
+
category: ErrorCategory.USER,
|
|
3025
|
+
text: "No updates provided",
|
|
3026
|
+
details: { indexName }
|
|
3027
|
+
});
|
|
3028
|
+
}
|
|
3035
3029
|
try {
|
|
3036
3030
|
if (!this.lanceClient) {
|
|
3037
3031
|
throw new Error("LanceDB client not initialized. Use LanceVectorStore.create() to create an instance");
|
|
@@ -3039,21 +3033,6 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
|
|
|
3039
3033
|
if (!indexName) {
|
|
3040
3034
|
throw new Error("indexName is required");
|
|
3041
3035
|
}
|
|
3042
|
-
if (!id) {
|
|
3043
|
-
throw new Error("id is required");
|
|
3044
|
-
}
|
|
3045
|
-
} catch (err) {
|
|
3046
|
-
throw new MastraError(
|
|
3047
|
-
{
|
|
3048
|
-
id: "STORAGE_LANCE_VECTOR_UPDATE_VECTOR_FAILED_INVALID_ARGS",
|
|
3049
|
-
domain: ErrorDomain.STORAGE,
|
|
3050
|
-
category: ErrorCategory.USER,
|
|
3051
|
-
details: { indexName, id }
|
|
3052
|
-
},
|
|
3053
|
-
err
|
|
3054
|
-
);
|
|
3055
|
-
}
|
|
3056
|
-
try {
|
|
3057
3036
|
const tables = await this.lanceClient.tableNames();
|
|
3058
3037
|
for (const tableName of tables) {
|
|
3059
3038
|
this.logger.debug("Checking table:" + tableName);
|
|
@@ -3063,39 +3042,66 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
|
|
|
3063
3042
|
const hasColumn = schema.fields.some((field) => field.name === indexName);
|
|
3064
3043
|
if (hasColumn) {
|
|
3065
3044
|
this.logger.debug(`Found column ${indexName} in table ${tableName}`);
|
|
3066
|
-
|
|
3067
|
-
if (
|
|
3068
|
-
|
|
3045
|
+
let whereClause;
|
|
3046
|
+
if ("id" in params && params.id) {
|
|
3047
|
+
whereClause = `id = '${params.id}'`;
|
|
3048
|
+
} else if ("filter" in params && params.filter) {
|
|
3049
|
+
const translator = new LanceFilterTranslator();
|
|
3050
|
+
const processFilterKeys = (filter) => {
|
|
3051
|
+
const processedFilter = {};
|
|
3052
|
+
Object.entries(filter).forEach(([key, value]) => {
|
|
3053
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
3054
|
+
Object.entries(value).forEach(([nestedKey, nestedValue]) => {
|
|
3055
|
+
processedFilter[`metadata_${key}_${nestedKey}`] = nestedValue;
|
|
3056
|
+
});
|
|
3057
|
+
} else {
|
|
3058
|
+
processedFilter[`metadata_${key}`] = value;
|
|
3059
|
+
}
|
|
3060
|
+
});
|
|
3061
|
+
return processedFilter;
|
|
3062
|
+
};
|
|
3063
|
+
const prefixedFilter = processFilterKeys(params.filter);
|
|
3064
|
+
whereClause = translator.translate(prefixedFilter) || "";
|
|
3065
|
+
if (!whereClause) {
|
|
3066
|
+
throw new Error("Failed to translate filter to SQL");
|
|
3067
|
+
}
|
|
3068
|
+
} else {
|
|
3069
|
+
throw new Error("Either id or filter must be provided");
|
|
3069
3070
|
}
|
|
3070
|
-
const
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3071
|
+
const existingRecords = await table.query().where(whereClause).select(schema.fields.map((field) => field.name)).toArray();
|
|
3072
|
+
if (existingRecords.length === 0) {
|
|
3073
|
+
this.logger.info(`No records found matching criteria in table ${tableName}`);
|
|
3074
|
+
return;
|
|
3075
|
+
}
|
|
3076
|
+
const updatedRecords = existingRecords.map((record) => {
|
|
3077
|
+
const rowData = {};
|
|
3078
|
+
Object.entries(record).forEach(([key, value]) => {
|
|
3079
|
+
if (key !== "_distance") {
|
|
3080
|
+
if (key === indexName) {
|
|
3081
|
+
if (update.vector) {
|
|
3082
|
+
rowData[key] = update.vector;
|
|
3081
3083
|
} else {
|
|
3082
|
-
|
|
3084
|
+
if (Array.isArray(value)) {
|
|
3085
|
+
rowData[key] = [...value];
|
|
3086
|
+
} else if (typeof value === "object" && value !== null) {
|
|
3087
|
+
rowData[key] = Array.from(value);
|
|
3088
|
+
} else {
|
|
3089
|
+
rowData[key] = value;
|
|
3090
|
+
}
|
|
3083
3091
|
}
|
|
3092
|
+
} else {
|
|
3093
|
+
rowData[key] = value;
|
|
3084
3094
|
}
|
|
3085
|
-
} else {
|
|
3086
|
-
rowData[key] = value;
|
|
3087
3095
|
}
|
|
3096
|
+
});
|
|
3097
|
+
if (update.metadata) {
|
|
3098
|
+
Object.entries(update.metadata).forEach(([key, value]) => {
|
|
3099
|
+
rowData[`metadata_${key}`] = value;
|
|
3100
|
+
});
|
|
3088
3101
|
}
|
|
3102
|
+
return rowData;
|
|
3089
3103
|
});
|
|
3090
|
-
|
|
3091
|
-
rowData[indexName] = update.vector;
|
|
3092
|
-
}
|
|
3093
|
-
if (update.metadata) {
|
|
3094
|
-
Object.entries(update.metadata).forEach(([key, value]) => {
|
|
3095
|
-
rowData[`metadata_${key}`] = value;
|
|
3096
|
-
});
|
|
3097
|
-
}
|
|
3098
|
-
await table.add([rowData], { mode: "overwrite" });
|
|
3104
|
+
await table.add(updatedRecords, { mode: "overwrite" });
|
|
3099
3105
|
return;
|
|
3100
3106
|
}
|
|
3101
3107
|
} catch (err) {
|
|
@@ -3105,12 +3111,19 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
|
|
|
3105
3111
|
}
|
|
3106
3112
|
throw new Error(`No table found with column/index '${indexName}'`);
|
|
3107
3113
|
} catch (error) {
|
|
3114
|
+
if (error instanceof MastraError) throw error;
|
|
3108
3115
|
throw new MastraError(
|
|
3109
3116
|
{
|
|
3110
|
-
id: "
|
|
3117
|
+
id: createVectorErrorId("LANCE", "UPDATE_VECTOR", "FAILED"),
|
|
3111
3118
|
domain: ErrorDomain.STORAGE,
|
|
3112
3119
|
category: ErrorCategory.THIRD_PARTY,
|
|
3113
|
-
details: {
|
|
3120
|
+
details: {
|
|
3121
|
+
indexName,
|
|
3122
|
+
..."id" in params && params.id && { id: params.id },
|
|
3123
|
+
..."filter" in params && params.filter && { filter: JSON.stringify(params.filter) },
|
|
3124
|
+
hasVector: !!update.vector,
|
|
3125
|
+
hasMetadata: !!update.metadata
|
|
3126
|
+
}
|
|
3114
3127
|
},
|
|
3115
3128
|
error
|
|
3116
3129
|
);
|
|
@@ -3130,10 +3143,13 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
|
|
|
3130
3143
|
} catch (err) {
|
|
3131
3144
|
throw new MastraError(
|
|
3132
3145
|
{
|
|
3133
|
-
id: "
|
|
3146
|
+
id: createVectorErrorId("LANCE", "DELETE_VECTOR", "INVALID_ARGS"),
|
|
3134
3147
|
domain: ErrorDomain.STORAGE,
|
|
3135
3148
|
category: ErrorCategory.USER,
|
|
3136
|
-
details: {
|
|
3149
|
+
details: {
|
|
3150
|
+
indexName,
|
|
3151
|
+
...id && { id }
|
|
3152
|
+
}
|
|
3137
3153
|
},
|
|
3138
3154
|
err
|
|
3139
3155
|
);
|
|
@@ -3160,10 +3176,13 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
|
|
|
3160
3176
|
} catch (error) {
|
|
3161
3177
|
throw new MastraError(
|
|
3162
3178
|
{
|
|
3163
|
-
id: "
|
|
3179
|
+
id: createVectorErrorId("LANCE", "DELETE_VECTOR", "FAILED"),
|
|
3164
3180
|
domain: ErrorDomain.STORAGE,
|
|
3165
3181
|
category: ErrorCategory.THIRD_PARTY,
|
|
3166
|
-
details: {
|
|
3182
|
+
details: {
|
|
3183
|
+
indexName,
|
|
3184
|
+
...id && { id }
|
|
3185
|
+
}
|
|
3167
3186
|
},
|
|
3168
3187
|
error
|
|
3169
3188
|
);
|
|
@@ -3194,6 +3213,109 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
|
|
|
3194
3213
|
});
|
|
3195
3214
|
return result;
|
|
3196
3215
|
}
|
|
3216
|
+
async deleteVectors({ indexName, filter, ids }) {
|
|
3217
|
+
if (ids && filter) {
|
|
3218
|
+
throw new MastraError({
|
|
3219
|
+
id: createVectorErrorId("LANCE", "DELETE_VECTORS", "MUTUALLY_EXCLUSIVE"),
|
|
3220
|
+
domain: ErrorDomain.STORAGE,
|
|
3221
|
+
category: ErrorCategory.USER,
|
|
3222
|
+
text: "ids and filter are mutually exclusive",
|
|
3223
|
+
details: { indexName }
|
|
3224
|
+
});
|
|
3225
|
+
}
|
|
3226
|
+
if (!ids && !filter) {
|
|
3227
|
+
throw new MastraError({
|
|
3228
|
+
id: createVectorErrorId("LANCE", "DELETE_VECTORS", "NO_TARGET"),
|
|
3229
|
+
domain: ErrorDomain.STORAGE,
|
|
3230
|
+
category: ErrorCategory.USER,
|
|
3231
|
+
text: "Either filter or ids must be provided",
|
|
3232
|
+
details: { indexName }
|
|
3233
|
+
});
|
|
3234
|
+
}
|
|
3235
|
+
if (ids && ids.length === 0) {
|
|
3236
|
+
throw new MastraError({
|
|
3237
|
+
id: createVectorErrorId("LANCE", "DELETE_VECTORS", "EMPTY_IDS"),
|
|
3238
|
+
domain: ErrorDomain.STORAGE,
|
|
3239
|
+
category: ErrorCategory.USER,
|
|
3240
|
+
text: "Cannot delete with empty ids array",
|
|
3241
|
+
details: { indexName }
|
|
3242
|
+
});
|
|
3243
|
+
}
|
|
3244
|
+
if (filter && Object.keys(filter).length === 0) {
|
|
3245
|
+
throw new MastraError({
|
|
3246
|
+
id: createVectorErrorId("LANCE", "DELETE_VECTORS", "EMPTY_FILTER"),
|
|
3247
|
+
domain: ErrorDomain.STORAGE,
|
|
3248
|
+
category: ErrorCategory.USER,
|
|
3249
|
+
text: "Cannot delete with empty filter",
|
|
3250
|
+
details: { indexName }
|
|
3251
|
+
});
|
|
3252
|
+
}
|
|
3253
|
+
try {
|
|
3254
|
+
if (!this.lanceClient) {
|
|
3255
|
+
throw new Error("LanceDB client not initialized. Use LanceVectorStore.create() to create an instance");
|
|
3256
|
+
}
|
|
3257
|
+
if (!indexName) {
|
|
3258
|
+
throw new Error("indexName is required");
|
|
3259
|
+
}
|
|
3260
|
+
const tables = await this.lanceClient.tableNames();
|
|
3261
|
+
for (const tableName of tables) {
|
|
3262
|
+
this.logger.debug("Checking table:" + tableName);
|
|
3263
|
+
const table = await this.lanceClient.openTable(tableName);
|
|
3264
|
+
try {
|
|
3265
|
+
const schema = await table.schema();
|
|
3266
|
+
const hasColumn = schema.fields.some((field) => field.name === indexName);
|
|
3267
|
+
if (hasColumn) {
|
|
3268
|
+
this.logger.debug(`Found column ${indexName} in table ${tableName}`);
|
|
3269
|
+
if (ids) {
|
|
3270
|
+
const idsConditions = ids.map((id) => `id = '${id}'`).join(" OR ");
|
|
3271
|
+
await table.delete(idsConditions);
|
|
3272
|
+
} else if (filter) {
|
|
3273
|
+
const translator = new LanceFilterTranslator();
|
|
3274
|
+
const processFilterKeys = (filter2) => {
|
|
3275
|
+
const processedFilter = {};
|
|
3276
|
+
Object.entries(filter2).forEach(([key, value]) => {
|
|
3277
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
3278
|
+
Object.entries(value).forEach(([nestedKey, nestedValue]) => {
|
|
3279
|
+
processedFilter[`metadata_${key}_${nestedKey}`] = nestedValue;
|
|
3280
|
+
});
|
|
3281
|
+
} else {
|
|
3282
|
+
processedFilter[`metadata_${key}`] = value;
|
|
3283
|
+
}
|
|
3284
|
+
});
|
|
3285
|
+
return processedFilter;
|
|
3286
|
+
};
|
|
3287
|
+
const prefixedFilter = processFilterKeys(filter);
|
|
3288
|
+
const whereClause = translator.translate(prefixedFilter);
|
|
3289
|
+
if (!whereClause) {
|
|
3290
|
+
throw new Error("Failed to translate filter to SQL");
|
|
3291
|
+
}
|
|
3292
|
+
await table.delete(whereClause);
|
|
3293
|
+
}
|
|
3294
|
+
return;
|
|
3295
|
+
}
|
|
3296
|
+
} catch (err) {
|
|
3297
|
+
this.logger.error(`Error checking schema for table ${tableName}:` + err);
|
|
3298
|
+
continue;
|
|
3299
|
+
}
|
|
3300
|
+
}
|
|
3301
|
+
throw new Error(`No table found with column/index '${indexName}'`);
|
|
3302
|
+
} catch (error) {
|
|
3303
|
+
if (error instanceof MastraError) throw error;
|
|
3304
|
+
throw new MastraError(
|
|
3305
|
+
{
|
|
3306
|
+
id: createVectorErrorId("LANCE", "DELETE_VECTORS", "FAILED"),
|
|
3307
|
+
domain: ErrorDomain.STORAGE,
|
|
3308
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
3309
|
+
details: {
|
|
3310
|
+
indexName,
|
|
3311
|
+
...filter && { filter: JSON.stringify(filter) },
|
|
3312
|
+
...ids && { idsCount: ids.length }
|
|
3313
|
+
}
|
|
3314
|
+
},
|
|
3315
|
+
error
|
|
3316
|
+
);
|
|
3317
|
+
}
|
|
3318
|
+
}
|
|
3197
3319
|
};
|
|
3198
3320
|
|
|
3199
3321
|
export { LanceStorage, LanceVectorStore };
|