@mastra/lance 0.0.0-share-agent-metadata-with-cloud-20250718123411 → 0.0.0-stream-vnext-usage-20250908171242
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 +367 -2
- package/README.md +3 -3
- package/dist/index.cjs +1932 -848
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1933 -849
- package/dist/index.js.map +1 -0
- package/dist/storage/domains/legacy-evals/index.d.ts +25 -0
- package/dist/storage/domains/legacy-evals/index.d.ts.map +1 -0
- package/dist/storage/domains/memory/index.d.ts +103 -0
- package/dist/storage/domains/memory/index.d.ts.map +1 -0
- package/dist/storage/domains/operations/index.d.ts +40 -0
- package/dist/storage/domains/operations/index.d.ts.map +1 -0
- package/dist/storage/domains/scores/index.d.ts +42 -0
- package/dist/storage/domains/scores/index.d.ts.map +1 -0
- package/dist/storage/domains/traces/index.d.ts +34 -0
- package/dist/storage/domains/traces/index.d.ts.map +1 -0
- package/dist/storage/domains/utils.d.ts +10 -0
- package/dist/storage/domains/utils.d.ts.map +1 -0
- package/dist/storage/domains/workflows/index.d.ts +56 -0
- package/dist/storage/domains/workflows/index.d.ts.map +1 -0
- package/dist/storage/index.d.ts +262 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/vector/filter.d.ts +41 -0
- package/dist/vector/filter.d.ts.map +1 -0
- package/dist/vector/index.d.ts +85 -0
- package/dist/vector/index.d.ts.map +1 -0
- package/dist/vector/types.d.ts +15 -0
- package/dist/vector/types.d.ts.map +1 -0
- package/package.json +23 -9
- package/dist/_tsup-dts-rollup.d.cts +0 -409
- package/dist/_tsup-dts-rollup.d.ts +0 -409
- package/dist/index.d.cts +0 -2
- package/eslint.config.js +0 -6
- package/src/index.ts +0 -2
- package/src/storage/index.test.ts +0 -1336
- package/src/storage/index.ts +0 -1447
- 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.json +0 -5
- package/vitest.config.ts +0 -11
package/dist/index.js
CHANGED
|
@@ -1,695 +1,570 @@
|
|
|
1
1
|
import { connect, Index } from '@lancedb/lancedb';
|
|
2
|
-
import { MessageList } from '@mastra/core/agent';
|
|
3
2
|
import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
|
|
4
|
-
import { MastraStorage, TABLE_THREADS, TABLE_MESSAGES, TABLE_TRACES,
|
|
3
|
+
import { MastraStorage, StoreOperations, LegacyEvalsStorage, TABLE_EVALS, MemoryStorage, TABLE_THREADS, TABLE_MESSAGES, resolveMessageLimit, TABLE_RESOURCES, ScoresStorage, TABLE_SCORERS, TracesStorage, TABLE_TRACES, WorkflowsStorage, TABLE_WORKFLOW_SNAPSHOT, ensureDate } from '@mastra/core/storage';
|
|
4
|
+
import { MessageList } from '@mastra/core/agent';
|
|
5
5
|
import { Utf8, Float64, Binary, Float32, Int32, Field, Schema } from 'apache-arrow';
|
|
6
6
|
import { MastraVector } from '@mastra/core/vector';
|
|
7
7
|
import { BaseFilterTranslator } from '@mastra/core/vector/filter';
|
|
8
8
|
|
|
9
9
|
// src/storage/index.ts
|
|
10
|
-
var
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
* @param options connection options
|
|
16
|
-
*
|
|
17
|
-
* Usage:
|
|
18
|
-
*
|
|
19
|
-
* Connect to a local database
|
|
20
|
-
* ```ts
|
|
21
|
-
* const store = await LanceStorage.create('/path/to/db');
|
|
22
|
-
* ```
|
|
23
|
-
*
|
|
24
|
-
* Connect to a LanceDB cloud database
|
|
25
|
-
* ```ts
|
|
26
|
-
* const store = await LanceStorage.create('db://host:port');
|
|
27
|
-
* ```
|
|
28
|
-
*
|
|
29
|
-
* Connect to a cloud database
|
|
30
|
-
* ```ts
|
|
31
|
-
* const store = await LanceStorage.create('s3://bucket/db', { storageOptions: { timeout: '60s' } });
|
|
32
|
-
* ```
|
|
33
|
-
*/
|
|
34
|
-
static async create(name, uri, options) {
|
|
35
|
-
const instance = new _LanceStorage(name);
|
|
36
|
-
try {
|
|
37
|
-
instance.lanceClient = await connect(uri, options);
|
|
38
|
-
return instance;
|
|
39
|
-
} catch (e) {
|
|
40
|
-
throw new MastraError(
|
|
41
|
-
{
|
|
42
|
-
id: "STORAGE_LANCE_STORAGE_CONNECT_FAILED",
|
|
43
|
-
domain: ErrorDomain.STORAGE,
|
|
44
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
45
|
-
text: `Failed to connect to LanceDB: ${e.message || e}`,
|
|
46
|
-
details: { uri, optionsProvided: !!options }
|
|
47
|
-
},
|
|
48
|
-
e
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
getPrimaryKeys(tableName) {
|
|
53
|
-
let primaryId = ["id"];
|
|
54
|
-
if (tableName === TABLE_WORKFLOW_SNAPSHOT) {
|
|
55
|
-
primaryId = ["workflow_name", "run_id"];
|
|
56
|
-
} else if (tableName === TABLE_EVALS) {
|
|
57
|
-
primaryId = ["agent_name", "metric_name", "run_id"];
|
|
58
|
-
}
|
|
59
|
-
return primaryId;
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* @internal
|
|
63
|
-
* Private constructor to enforce using the create factory method
|
|
64
|
-
*/
|
|
65
|
-
constructor(name) {
|
|
66
|
-
super({ name });
|
|
10
|
+
var StoreLegacyEvalsLance = class extends LegacyEvalsStorage {
|
|
11
|
+
client;
|
|
12
|
+
constructor({ client }) {
|
|
13
|
+
super();
|
|
14
|
+
this.client = client;
|
|
67
15
|
}
|
|
68
|
-
async
|
|
69
|
-
tableName,
|
|
70
|
-
schema
|
|
71
|
-
}) {
|
|
16
|
+
async getEvalsByAgentName(agentName, type) {
|
|
72
17
|
try {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (
|
|
80
|
-
|
|
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);
|
|
81
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
|
+
});
|
|
82
42
|
} catch (error) {
|
|
83
43
|
throw new MastraError(
|
|
84
44
|
{
|
|
85
|
-
id: "
|
|
45
|
+
id: "LANCE_STORE_GET_EVALS_BY_AGENT_NAME_FAILED",
|
|
86
46
|
domain: ErrorDomain.STORAGE,
|
|
87
|
-
category: ErrorCategory.
|
|
88
|
-
details: {
|
|
47
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
48
|
+
details: { agentName }
|
|
89
49
|
},
|
|
90
50
|
error
|
|
91
51
|
);
|
|
92
52
|
}
|
|
53
|
+
}
|
|
54
|
+
async getEvals(options) {
|
|
93
55
|
try {
|
|
94
|
-
const
|
|
95
|
-
|
|
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
|
+
};
|
|
96
111
|
} catch (error) {
|
|
97
112
|
throw new MastraError(
|
|
98
113
|
{
|
|
99
|
-
id: "
|
|
114
|
+
id: "LANCE_STORE_GET_EVALS_FAILED",
|
|
100
115
|
domain: ErrorDomain.STORAGE,
|
|
101
116
|
category: ErrorCategory.THIRD_PARTY,
|
|
102
|
-
details: {
|
|
117
|
+
details: { agentName: options.agentName ?? "" }
|
|
103
118
|
},
|
|
104
119
|
error
|
|
105
120
|
);
|
|
106
121
|
}
|
|
107
122
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
123
|
+
};
|
|
124
|
+
function getPrimaryKeys(tableName) {
|
|
125
|
+
let primaryId = ["id"];
|
|
126
|
+
if (tableName === TABLE_WORKFLOW_SNAPSHOT) {
|
|
127
|
+
primaryId = ["workflow_name", "run_id"];
|
|
128
|
+
} else if (tableName === TABLE_EVALS) {
|
|
129
|
+
primaryId = ["agent_name", "metric_name", "run_id"];
|
|
130
|
+
}
|
|
131
|
+
return primaryId;
|
|
132
|
+
}
|
|
133
|
+
function validateKeyTypes(keys, tableSchema) {
|
|
134
|
+
const fieldTypes = new Map(
|
|
135
|
+
tableSchema.fields.map((field) => [field.name, field.type?.toString().toLowerCase()])
|
|
136
|
+
);
|
|
137
|
+
for (const [key, value] of Object.entries(keys)) {
|
|
138
|
+
const fieldType = fieldTypes.get(key);
|
|
139
|
+
if (!fieldType) {
|
|
140
|
+
throw new Error(`Field '${key}' does not exist in table schema`);
|
|
141
|
+
}
|
|
142
|
+
if (value !== null) {
|
|
143
|
+
if ((fieldType.includes("int") || fieldType.includes("bigint")) && typeof value !== "number") {
|
|
144
|
+
throw new Error(`Expected numeric value for field '${key}', got ${typeof value}`);
|
|
145
|
+
}
|
|
146
|
+
if (fieldType.includes("utf8") && typeof value !== "string") {
|
|
147
|
+
throw new Error(`Expected string value for field '${key}', got ${typeof value}`);
|
|
148
|
+
}
|
|
149
|
+
if (fieldType.includes("timestamp") && !(value instanceof Date) && typeof value !== "string") {
|
|
150
|
+
throw new Error(`Expected Date or string value for field '${key}', got ${typeof value}`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function processResultWithTypeConversion(rawResult, tableSchema) {
|
|
156
|
+
const fieldTypeMap = /* @__PURE__ */ new Map();
|
|
157
|
+
tableSchema.fields.forEach((field) => {
|
|
158
|
+
const fieldName = field.name;
|
|
159
|
+
const fieldTypeStr = field.type.toString().toLowerCase();
|
|
160
|
+
fieldTypeMap.set(fieldName, fieldTypeStr);
|
|
161
|
+
});
|
|
162
|
+
if (Array.isArray(rawResult)) {
|
|
163
|
+
return rawResult.map((item) => processResultWithTypeConversion(item, tableSchema));
|
|
164
|
+
}
|
|
165
|
+
const processedResult = { ...rawResult };
|
|
166
|
+
for (const key in processedResult) {
|
|
167
|
+
const fieldTypeStr = fieldTypeMap.get(key);
|
|
168
|
+
if (!fieldTypeStr) continue;
|
|
169
|
+
if (typeof processedResult[key] === "string") {
|
|
170
|
+
if (fieldTypeStr.includes("int32") || fieldTypeStr.includes("float32")) {
|
|
171
|
+
if (!isNaN(Number(processedResult[key]))) {
|
|
172
|
+
processedResult[key] = Number(processedResult[key]);
|
|
173
|
+
}
|
|
174
|
+
} else if (fieldTypeStr.includes("int64")) {
|
|
175
|
+
processedResult[key] = Number(processedResult[key]);
|
|
176
|
+
} else if (fieldTypeStr.includes("utf8") && key !== "id") {
|
|
177
|
+
try {
|
|
178
|
+
const parsed = JSON.parse(processedResult[key]);
|
|
179
|
+
if (typeof parsed === "object") {
|
|
180
|
+
processedResult[key] = JSON.parse(processedResult[key]);
|
|
181
|
+
}
|
|
182
|
+
} catch {
|
|
183
|
+
}
|
|
138
184
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
185
|
+
} else if (typeof processedResult[key] === "bigint") {
|
|
186
|
+
processedResult[key] = Number(processedResult[key]);
|
|
187
|
+
} else if (fieldTypeStr.includes("float64") && ["createdAt", "updatedAt"].includes(key)) {
|
|
188
|
+
processedResult[key] = new Date(processedResult[key]);
|
|
189
|
+
}
|
|
190
|
+
console.log(key, "processedResult", processedResult);
|
|
142
191
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
192
|
+
return processedResult;
|
|
193
|
+
}
|
|
194
|
+
async function getTableSchema({
|
|
195
|
+
tableName,
|
|
196
|
+
client
|
|
197
|
+
}) {
|
|
198
|
+
try {
|
|
199
|
+
if (!client) {
|
|
200
|
+
throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
|
|
201
|
+
}
|
|
202
|
+
if (!tableName) {
|
|
203
|
+
throw new Error("tableName is required for getTableSchema.");
|
|
204
|
+
}
|
|
205
|
+
} catch (validationError) {
|
|
206
|
+
throw new MastraError(
|
|
207
|
+
{
|
|
208
|
+
id: "STORAGE_LANCE_STORAGE_GET_TABLE_SCHEMA_INVALID_ARGS",
|
|
209
|
+
domain: ErrorDomain.STORAGE,
|
|
210
|
+
category: ErrorCategory.USER,
|
|
211
|
+
text: validationError.message,
|
|
212
|
+
details: { tableName }
|
|
213
|
+
},
|
|
214
|
+
validationError
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
try {
|
|
218
|
+
const table = await client.openTable(tableName);
|
|
219
|
+
const rawSchema = await table.schema();
|
|
220
|
+
const fields = rawSchema.fields;
|
|
221
|
+
return {
|
|
222
|
+
fields,
|
|
223
|
+
metadata: /* @__PURE__ */ new Map(),
|
|
224
|
+
get names() {
|
|
225
|
+
return fields.map((field) => field.name);
|
|
151
226
|
}
|
|
152
|
-
|
|
153
|
-
|
|
227
|
+
};
|
|
228
|
+
} catch (error) {
|
|
229
|
+
throw new MastraError(
|
|
230
|
+
{
|
|
231
|
+
id: "STORAGE_LANCE_STORAGE_GET_TABLE_SCHEMA_FAILED",
|
|
232
|
+
domain: ErrorDomain.STORAGE,
|
|
233
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
234
|
+
details: { tableName }
|
|
235
|
+
},
|
|
236
|
+
error
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// src/storage/domains/memory/index.ts
|
|
242
|
+
var StoreMemoryLance = class extends MemoryStorage {
|
|
243
|
+
client;
|
|
244
|
+
operations;
|
|
245
|
+
constructor({ client, operations }) {
|
|
246
|
+
super();
|
|
247
|
+
this.client = client;
|
|
248
|
+
this.operations = operations;
|
|
249
|
+
}
|
|
250
|
+
async getThreadById({ threadId }) {
|
|
251
|
+
try {
|
|
252
|
+
const thread = await this.operations.load({ tableName: TABLE_THREADS, keys: { id: threadId } });
|
|
253
|
+
if (!thread) {
|
|
254
|
+
return null;
|
|
154
255
|
}
|
|
155
|
-
|
|
256
|
+
return {
|
|
257
|
+
...thread,
|
|
258
|
+
createdAt: new Date(thread.createdAt),
|
|
259
|
+
updatedAt: new Date(thread.updatedAt)
|
|
260
|
+
};
|
|
261
|
+
} catch (error) {
|
|
156
262
|
throw new MastraError(
|
|
157
263
|
{
|
|
158
|
-
id: "
|
|
264
|
+
id: "LANCE_STORE_GET_THREAD_BY_ID_FAILED",
|
|
159
265
|
domain: ErrorDomain.STORAGE,
|
|
160
|
-
category: ErrorCategory.
|
|
161
|
-
text: validationError.message,
|
|
162
|
-
details: { tableName }
|
|
266
|
+
category: ErrorCategory.THIRD_PARTY
|
|
163
267
|
},
|
|
164
|
-
|
|
268
|
+
error
|
|
165
269
|
);
|
|
166
270
|
}
|
|
271
|
+
}
|
|
272
|
+
async getThreadsByResourceId({ resourceId }) {
|
|
167
273
|
try {
|
|
168
|
-
await this.
|
|
274
|
+
const table = await this.client.openTable(TABLE_THREADS);
|
|
275
|
+
const query = table.query().where(`\`resourceId\` = '${resourceId}'`);
|
|
276
|
+
const records = await query.toArray();
|
|
277
|
+
return processResultWithTypeConversion(
|
|
278
|
+
records,
|
|
279
|
+
await getTableSchema({ tableName: TABLE_THREADS, client: this.client })
|
|
280
|
+
);
|
|
169
281
|
} catch (error) {
|
|
170
|
-
if (error.toString().includes("was not found") || error.message?.includes("Table not found")) {
|
|
171
|
-
this.logger.debug(`Table '${tableName}' does not exist, skipping drop`);
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
174
282
|
throw new MastraError(
|
|
175
283
|
{
|
|
176
|
-
id: "
|
|
284
|
+
id: "LANCE_STORE_GET_THREADS_BY_RESOURCE_ID_FAILED",
|
|
177
285
|
domain: ErrorDomain.STORAGE,
|
|
178
|
-
category: ErrorCategory.THIRD_PARTY
|
|
179
|
-
details: { tableName }
|
|
286
|
+
category: ErrorCategory.THIRD_PARTY
|
|
180
287
|
},
|
|
181
288
|
error
|
|
182
289
|
);
|
|
183
290
|
}
|
|
184
291
|
}
|
|
185
292
|
/**
|
|
186
|
-
*
|
|
187
|
-
* @param
|
|
188
|
-
* @returns
|
|
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
|
|
189
296
|
*/
|
|
190
|
-
async
|
|
191
|
-
try {
|
|
192
|
-
if (!this.lanceClient) {
|
|
193
|
-
throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
|
|
194
|
-
}
|
|
195
|
-
if (!tableName) {
|
|
196
|
-
throw new Error("tableName is required for getTableSchema.");
|
|
197
|
-
}
|
|
198
|
-
} catch (validationError) {
|
|
199
|
-
throw new MastraError(
|
|
200
|
-
{
|
|
201
|
-
id: "STORAGE_LANCE_STORAGE_GET_TABLE_SCHEMA_INVALID_ARGS",
|
|
202
|
-
domain: ErrorDomain.STORAGE,
|
|
203
|
-
category: ErrorCategory.USER,
|
|
204
|
-
text: validationError.message,
|
|
205
|
-
details: { tableName }
|
|
206
|
-
},
|
|
207
|
-
validationError
|
|
208
|
-
);
|
|
209
|
-
}
|
|
297
|
+
async saveThread({ thread }) {
|
|
210
298
|
try {
|
|
211
|
-
const
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
return
|
|
215
|
-
fields,
|
|
216
|
-
metadata: /* @__PURE__ */ new Map(),
|
|
217
|
-
get names() {
|
|
218
|
-
return fields.map((field) => field.name);
|
|
219
|
-
}
|
|
220
|
-
};
|
|
299
|
+
const record = { ...thread, metadata: JSON.stringify(thread.metadata) };
|
|
300
|
+
const table = await this.client.openTable(TABLE_THREADS);
|
|
301
|
+
await table.add([record], { mode: "append" });
|
|
302
|
+
return thread;
|
|
221
303
|
} catch (error) {
|
|
222
304
|
throw new MastraError(
|
|
223
305
|
{
|
|
224
|
-
id: "
|
|
306
|
+
id: "LANCE_STORE_SAVE_THREAD_FAILED",
|
|
225
307
|
domain: ErrorDomain.STORAGE,
|
|
226
|
-
category: ErrorCategory.THIRD_PARTY
|
|
227
|
-
details: { tableName }
|
|
308
|
+
category: ErrorCategory.THIRD_PARTY
|
|
228
309
|
},
|
|
229
310
|
error
|
|
230
311
|
);
|
|
231
312
|
}
|
|
232
313
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
case "timestamp":
|
|
238
|
-
return "CURRENT_TIMESTAMP";
|
|
239
|
-
case "integer":
|
|
240
|
-
case "bigint":
|
|
241
|
-
return "0";
|
|
242
|
-
case "jsonb":
|
|
243
|
-
return "'{}'";
|
|
244
|
-
case "uuid":
|
|
245
|
-
return "''";
|
|
246
|
-
default:
|
|
247
|
-
return super.getDefaultValue(type);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
/**
|
|
251
|
-
* Alters table schema to add columns if they don't exist
|
|
252
|
-
* @param tableName Name of the table
|
|
253
|
-
* @param schema Schema of the table
|
|
254
|
-
* @param ifNotExists Array of column names to add if they don't exist
|
|
255
|
-
*/
|
|
256
|
-
async alterTable({
|
|
257
|
-
tableName,
|
|
258
|
-
schema,
|
|
259
|
-
ifNotExists
|
|
314
|
+
async updateThread({
|
|
315
|
+
id,
|
|
316
|
+
title,
|
|
317
|
+
metadata
|
|
260
318
|
}) {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
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
|
+
);
|
|
274
354
|
}
|
|
275
|
-
} catch (validationError) {
|
|
276
|
-
throw new MastraError(
|
|
277
|
-
{
|
|
278
|
-
id: "STORAGE_LANCE_STORAGE_ALTER_TABLE_INVALID_ARGS",
|
|
279
|
-
domain: ErrorDomain.STORAGE,
|
|
280
|
-
category: ErrorCategory.USER,
|
|
281
|
-
text: validationError.message,
|
|
282
|
-
details: { tableName }
|
|
283
|
-
},
|
|
284
|
-
validationError
|
|
285
|
-
);
|
|
286
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 }) {
|
|
287
366
|
try {
|
|
288
|
-
const table = await this.
|
|
289
|
-
|
|
290
|
-
const
|
|
291
|
-
|
|
292
|
-
text: "string",
|
|
293
|
-
integer: "int",
|
|
294
|
-
bigint: "bigint",
|
|
295
|
-
timestamp: "timestamp",
|
|
296
|
-
jsonb: "string",
|
|
297
|
-
uuid: "string"
|
|
298
|
-
};
|
|
299
|
-
const columnsToAdd = ifNotExists.filter((col) => schema[col] && !existingFields.has(col)).map((col) => {
|
|
300
|
-
const colDef = schema[col];
|
|
301
|
-
return {
|
|
302
|
-
name: col,
|
|
303
|
-
valueSql: colDef?.nullable ? `cast(NULL as ${typeMap[colDef.type ?? "text"]})` : `cast(${this.getDefaultValue(colDef?.type ?? "text")} as ${typeMap[colDef?.type ?? "text"]})`
|
|
304
|
-
};
|
|
305
|
-
});
|
|
306
|
-
if (columnsToAdd.length > 0) {
|
|
307
|
-
await table.addColumns(columnsToAdd);
|
|
308
|
-
this.logger?.info?.(`Added columns [${columnsToAdd.map((c) => c.name).join(", ")}] to table ${tableName}`);
|
|
309
|
-
}
|
|
367
|
+
const table = await this.client.openTable(TABLE_THREADS);
|
|
368
|
+
await table.delete(`id = '${threadId}'`);
|
|
369
|
+
const messagesTable = await this.client.openTable(TABLE_MESSAGES);
|
|
370
|
+
await messagesTable.delete(`thread_id = '${threadId}'`);
|
|
310
371
|
} catch (error) {
|
|
311
372
|
throw new MastraError(
|
|
312
373
|
{
|
|
313
|
-
id: "
|
|
374
|
+
id: "LANCE_STORE_DELETE_THREAD_FAILED",
|
|
314
375
|
domain: ErrorDomain.STORAGE,
|
|
315
|
-
category: ErrorCategory.THIRD_PARTY
|
|
316
|
-
details: { tableName }
|
|
376
|
+
category: ErrorCategory.THIRD_PARTY
|
|
317
377
|
},
|
|
318
378
|
error
|
|
319
379
|
);
|
|
320
380
|
}
|
|
321
381
|
}
|
|
322
|
-
|
|
382
|
+
normalizeMessage(message) {
|
|
383
|
+
const { thread_id, ...rest } = message;
|
|
384
|
+
return {
|
|
385
|
+
...rest,
|
|
386
|
+
threadId: thread_id,
|
|
387
|
+
content: typeof message.content === "string" ? (() => {
|
|
388
|
+
try {
|
|
389
|
+
return JSON.parse(message.content);
|
|
390
|
+
} catch {
|
|
391
|
+
return message.content;
|
|
392
|
+
}
|
|
393
|
+
})() : message.content
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
async getMessages({
|
|
397
|
+
threadId,
|
|
398
|
+
resourceId,
|
|
399
|
+
selectBy,
|
|
400
|
+
format,
|
|
401
|
+
threadConfig
|
|
402
|
+
}) {
|
|
323
403
|
try {
|
|
324
|
-
if (!
|
|
325
|
-
|
|
404
|
+
if (!threadId.trim()) throw new Error("threadId must be a non-empty string");
|
|
405
|
+
if (threadConfig) {
|
|
406
|
+
throw new Error("ThreadConfig is not supported by LanceDB storage");
|
|
326
407
|
}
|
|
327
|
-
|
|
328
|
-
|
|
408
|
+
const limit = resolveMessageLimit({ last: selectBy?.last, defaultLimit: Number.MAX_SAFE_INTEGER });
|
|
409
|
+
const table = await this.client.openTable(TABLE_MESSAGES);
|
|
410
|
+
let allRecords = [];
|
|
411
|
+
if (selectBy?.include && selectBy.include.length > 0) {
|
|
412
|
+
const threadIds = [...new Set(selectBy.include.map((item) => item.threadId))];
|
|
413
|
+
for (const threadId2 of threadIds) {
|
|
414
|
+
const threadQuery = table.query().where(`thread_id = '${threadId2}'`);
|
|
415
|
+
let threadRecords = await threadQuery.toArray();
|
|
416
|
+
allRecords.push(...threadRecords);
|
|
417
|
+
}
|
|
418
|
+
} else {
|
|
419
|
+
let query = table.query().where(`\`thread_id\` = '${threadId}'`);
|
|
420
|
+
allRecords = await query.toArray();
|
|
329
421
|
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
422
|
+
allRecords.sort((a, b) => {
|
|
423
|
+
const dateA = new Date(a.createdAt).getTime();
|
|
424
|
+
const dateB = new Date(b.createdAt).getTime();
|
|
425
|
+
return dateA - dateB;
|
|
426
|
+
});
|
|
427
|
+
if (selectBy?.include && selectBy.include.length > 0) {
|
|
428
|
+
allRecords = this.processMessagesWithContext(allRecords, selectBy.include);
|
|
429
|
+
}
|
|
430
|
+
if (limit !== Number.MAX_SAFE_INTEGER) {
|
|
431
|
+
allRecords = allRecords.slice(-limit);
|
|
432
|
+
}
|
|
433
|
+
const messages = processResultWithTypeConversion(
|
|
434
|
+
allRecords,
|
|
435
|
+
await getTableSchema({ tableName: TABLE_MESSAGES, client: this.client })
|
|
340
436
|
);
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
await table.delete("1=1");
|
|
437
|
+
const list = new MessageList({ threadId, resourceId }).add(messages.map(this.normalizeMessage), "memory");
|
|
438
|
+
if (format === "v2") return list.get.all.v2();
|
|
439
|
+
return list.get.all.v1();
|
|
345
440
|
} catch (error) {
|
|
346
441
|
throw new MastraError(
|
|
347
442
|
{
|
|
348
|
-
id: "
|
|
443
|
+
id: "LANCE_STORE_GET_MESSAGES_FAILED",
|
|
349
444
|
domain: ErrorDomain.STORAGE,
|
|
350
445
|
category: ErrorCategory.THIRD_PARTY,
|
|
351
|
-
details: {
|
|
446
|
+
details: {
|
|
447
|
+
threadId,
|
|
448
|
+
resourceId: resourceId ?? ""
|
|
449
|
+
}
|
|
352
450
|
},
|
|
353
451
|
error
|
|
354
452
|
);
|
|
355
453
|
}
|
|
356
454
|
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
async insert({ tableName, record }) {
|
|
455
|
+
async getMessagesById({
|
|
456
|
+
messageIds,
|
|
457
|
+
format
|
|
458
|
+
}) {
|
|
459
|
+
if (messageIds.length === 0) return [];
|
|
363
460
|
try {
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
if (!record || Object.keys(record).length === 0) {
|
|
371
|
-
throw new Error("record is required and cannot be empty for insert.");
|
|
372
|
-
}
|
|
373
|
-
} catch (validationError) {
|
|
374
|
-
throw new MastraError(
|
|
375
|
-
{
|
|
376
|
-
id: "STORAGE_LANCE_STORAGE_INSERT_INVALID_ARGS",
|
|
377
|
-
domain: ErrorDomain.STORAGE,
|
|
378
|
-
category: ErrorCategory.USER,
|
|
379
|
-
text: validationError.message,
|
|
380
|
-
details: { tableName }
|
|
381
|
-
},
|
|
382
|
-
validationError
|
|
461
|
+
const table = await this.client.openTable(TABLE_MESSAGES);
|
|
462
|
+
const quotedIds = messageIds.map((id) => `'${id}'`).join(", ");
|
|
463
|
+
const allRecords = await table.query().where(`id IN (${quotedIds})`).toArray();
|
|
464
|
+
const messages = processResultWithTypeConversion(
|
|
465
|
+
allRecords,
|
|
466
|
+
await getTableSchema({ tableName: TABLE_MESSAGES, client: this.client })
|
|
383
467
|
);
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
const primaryId = this.getPrimaryKeys(tableName);
|
|
388
|
-
const processedRecord = { ...record };
|
|
389
|
-
for (const key in processedRecord) {
|
|
390
|
-
if (processedRecord[key] !== null && typeof processedRecord[key] === "object" && !(processedRecord[key] instanceof Date)) {
|
|
391
|
-
this.logger.debug("Converting object to JSON string: ", processedRecord[key]);
|
|
392
|
-
processedRecord[key] = JSON.stringify(processedRecord[key]);
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
await table.mergeInsert(primaryId).whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([processedRecord]);
|
|
468
|
+
const list = new MessageList().add(messages.map(this.normalizeMessage), "memory");
|
|
469
|
+
if (format === `v1`) return list.get.all.v1();
|
|
470
|
+
return list.get.all.v2();
|
|
396
471
|
} catch (error) {
|
|
397
472
|
throw new MastraError(
|
|
398
473
|
{
|
|
399
|
-
id: "
|
|
474
|
+
id: "LANCE_STORE_GET_MESSAGES_BY_ID_FAILED",
|
|
400
475
|
domain: ErrorDomain.STORAGE,
|
|
401
476
|
category: ErrorCategory.THIRD_PARTY,
|
|
402
|
-
details: {
|
|
477
|
+
details: {
|
|
478
|
+
messageIds: JSON.stringify(messageIds)
|
|
479
|
+
}
|
|
403
480
|
},
|
|
404
481
|
error
|
|
405
482
|
);
|
|
406
483
|
}
|
|
407
484
|
}
|
|
408
|
-
|
|
409
|
-
* Insert multiple records into a table. This function overwrites the existing records if they exist. Use this function for inserting records into tables with custom schemas.
|
|
410
|
-
* @param tableName The name of the table to insert into.
|
|
411
|
-
* @param records The records to insert.
|
|
412
|
-
*/
|
|
413
|
-
async batchInsert({ tableName, records }) {
|
|
485
|
+
async saveMessages(args) {
|
|
414
486
|
try {
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
if (!tableName) {
|
|
419
|
-
throw new Error("tableName is required for batchInsert.");
|
|
487
|
+
const { messages, format = "v1" } = args;
|
|
488
|
+
if (messages.length === 0) {
|
|
489
|
+
return [];
|
|
420
490
|
}
|
|
421
|
-
|
|
422
|
-
|
|
491
|
+
const threadId = messages[0]?.threadId;
|
|
492
|
+
if (!threadId) {
|
|
493
|
+
throw new Error("Thread ID is required");
|
|
423
494
|
}
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
id: "STORAGE_LANCE_STORAGE_BATCH_INSERT_INVALID_ARGS",
|
|
428
|
-
domain: ErrorDomain.STORAGE,
|
|
429
|
-
category: ErrorCategory.USER,
|
|
430
|
-
text: validationError.message,
|
|
431
|
-
details: { tableName }
|
|
432
|
-
},
|
|
433
|
-
validationError
|
|
434
|
-
);
|
|
435
|
-
}
|
|
436
|
-
try {
|
|
437
|
-
const table = await this.lanceClient.openTable(tableName);
|
|
438
|
-
const primaryId = this.getPrimaryKeys(tableName);
|
|
439
|
-
const processedRecords = records.map((record) => {
|
|
440
|
-
const processedRecord = { ...record };
|
|
441
|
-
for (const key in processedRecord) {
|
|
442
|
-
if (processedRecord[key] == null) continue;
|
|
443
|
-
if (processedRecord[key] !== null && typeof processedRecord[key] === "object" && !(processedRecord[key] instanceof Date)) {
|
|
444
|
-
processedRecord[key] = JSON.stringify(processedRecord[key]);
|
|
445
|
-
}
|
|
495
|
+
for (const message of messages) {
|
|
496
|
+
if (!message.id) {
|
|
497
|
+
throw new Error("Message ID is required");
|
|
446
498
|
}
|
|
447
|
-
|
|
499
|
+
if (!message.threadId) {
|
|
500
|
+
throw new Error("Thread ID is required for all messages");
|
|
501
|
+
}
|
|
502
|
+
if (message.resourceId === null || message.resourceId === void 0) {
|
|
503
|
+
throw new Error("Resource ID cannot be null or undefined");
|
|
504
|
+
}
|
|
505
|
+
if (!message.content) {
|
|
506
|
+
throw new Error("Message content is required");
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
const transformedMessages = messages.map((message) => {
|
|
510
|
+
const { threadId: threadId2, type, ...rest } = message;
|
|
511
|
+
return {
|
|
512
|
+
...rest,
|
|
513
|
+
thread_id: threadId2,
|
|
514
|
+
type: type ?? "v2",
|
|
515
|
+
content: JSON.stringify(message.content)
|
|
516
|
+
};
|
|
448
517
|
});
|
|
449
|
-
await
|
|
518
|
+
const table = await this.client.openTable(TABLE_MESSAGES);
|
|
519
|
+
await table.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute(transformedMessages);
|
|
520
|
+
const threadsTable = await this.client.openTable(TABLE_THREADS);
|
|
521
|
+
const currentTime = (/* @__PURE__ */ new Date()).getTime();
|
|
522
|
+
const updateRecord = { id: threadId, updatedAt: currentTime };
|
|
523
|
+
await threadsTable.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([updateRecord]);
|
|
524
|
+
const list = new MessageList().add(messages, "memory");
|
|
525
|
+
if (format === `v2`) return list.get.all.v2();
|
|
526
|
+
return list.get.all.v1();
|
|
450
527
|
} catch (error) {
|
|
451
528
|
throw new MastraError(
|
|
452
529
|
{
|
|
453
|
-
id: "
|
|
530
|
+
id: "LANCE_STORE_SAVE_MESSAGES_FAILED",
|
|
454
531
|
domain: ErrorDomain.STORAGE,
|
|
455
|
-
category: ErrorCategory.THIRD_PARTY
|
|
456
|
-
details: { tableName }
|
|
532
|
+
category: ErrorCategory.THIRD_PARTY
|
|
457
533
|
},
|
|
458
534
|
error
|
|
459
535
|
);
|
|
460
536
|
}
|
|
461
537
|
}
|
|
462
|
-
|
|
463
|
-
* Load a record from the database by its key(s)
|
|
464
|
-
* @param tableName The name of the table to query
|
|
465
|
-
* @param keys Record of key-value pairs to use for lookup
|
|
466
|
-
* @throws Error if invalid types are provided for keys
|
|
467
|
-
* @returns The loaded record with proper type conversions, or null if not found
|
|
468
|
-
*/
|
|
469
|
-
async load({ tableName, keys }) {
|
|
538
|
+
async getThreadsByResourceIdPaginated(args) {
|
|
470
539
|
try {
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
if (
|
|
478
|
-
|
|
540
|
+
const { resourceId, page = 0, perPage = 10 } = args;
|
|
541
|
+
const table = await this.client.openTable(TABLE_THREADS);
|
|
542
|
+
const total = await table.countRows(`\`resourceId\` = '${resourceId}'`);
|
|
543
|
+
const query = table.query().where(`\`resourceId\` = '${resourceId}'`);
|
|
544
|
+
const offset = page * perPage;
|
|
545
|
+
query.limit(perPage);
|
|
546
|
+
if (offset > 0) {
|
|
547
|
+
query.offset(offset);
|
|
479
548
|
}
|
|
480
|
-
|
|
549
|
+
const records = await query.toArray();
|
|
550
|
+
records.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());
|
|
551
|
+
const schema = await getTableSchema({ tableName: TABLE_THREADS, client: this.client });
|
|
552
|
+
const threads = records.map((record) => processResultWithTypeConversion(record, schema));
|
|
553
|
+
return {
|
|
554
|
+
threads,
|
|
555
|
+
total,
|
|
556
|
+
page,
|
|
557
|
+
perPage,
|
|
558
|
+
hasMore: total > (page + 1) * perPage
|
|
559
|
+
};
|
|
560
|
+
} catch (error) {
|
|
481
561
|
throw new MastraError(
|
|
482
562
|
{
|
|
483
|
-
id: "
|
|
563
|
+
id: "LANCE_STORE_GET_THREADS_BY_RESOURCE_ID_PAGINATED_FAILED",
|
|
484
564
|
domain: ErrorDomain.STORAGE,
|
|
485
|
-
category: ErrorCategory.
|
|
486
|
-
text: validationError.message,
|
|
487
|
-
details: { tableName }
|
|
565
|
+
category: ErrorCategory.THIRD_PARTY
|
|
488
566
|
},
|
|
489
|
-
|
|
490
|
-
);
|
|
491
|
-
}
|
|
492
|
-
try {
|
|
493
|
-
const table = await this.lanceClient.openTable(tableName);
|
|
494
|
-
const tableSchema = await this.getTableSchema(tableName);
|
|
495
|
-
const query = table.query();
|
|
496
|
-
if (Object.keys(keys).length > 0) {
|
|
497
|
-
this.validateKeyTypes(keys, tableSchema);
|
|
498
|
-
const filterConditions = Object.entries(keys).map(([key, value]) => {
|
|
499
|
-
const isCamelCase = /^[a-z][a-zA-Z]*$/.test(key) && /[A-Z]/.test(key);
|
|
500
|
-
const quotedKey = isCamelCase ? `\`${key}\`` : key;
|
|
501
|
-
if (typeof value === "string") {
|
|
502
|
-
return `${quotedKey} = '${value}'`;
|
|
503
|
-
} else if (value === null) {
|
|
504
|
-
return `${quotedKey} IS NULL`;
|
|
505
|
-
} else {
|
|
506
|
-
return `${quotedKey} = ${value}`;
|
|
507
|
-
}
|
|
508
|
-
}).join(" AND ");
|
|
509
|
-
this.logger.debug("where clause generated: " + filterConditions);
|
|
510
|
-
query.where(filterConditions);
|
|
511
|
-
}
|
|
512
|
-
const result = await query.limit(1).toArray();
|
|
513
|
-
if (result.length === 0) {
|
|
514
|
-
this.logger.debug("No record found");
|
|
515
|
-
return null;
|
|
516
|
-
}
|
|
517
|
-
return this.processResultWithTypeConversion(result[0], tableSchema);
|
|
518
|
-
} catch (error) {
|
|
519
|
-
if (error instanceof MastraError) throw error;
|
|
520
|
-
throw new MastraError(
|
|
521
|
-
{
|
|
522
|
-
id: "STORAGE_LANCE_STORAGE_LOAD_FAILED",
|
|
523
|
-
domain: ErrorDomain.STORAGE,
|
|
524
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
525
|
-
details: { tableName, keyCount: Object.keys(keys).length, firstKey: Object.keys(keys)[0] ?? "" }
|
|
526
|
-
},
|
|
527
|
-
error
|
|
528
|
-
);
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
/**
|
|
532
|
-
* Validates that key types match the schema definition
|
|
533
|
-
* @param keys The keys to validate
|
|
534
|
-
* @param tableSchema The table schema to validate against
|
|
535
|
-
* @throws Error if a key has an incompatible type
|
|
536
|
-
*/
|
|
537
|
-
validateKeyTypes(keys, tableSchema) {
|
|
538
|
-
const fieldTypes = new Map(
|
|
539
|
-
tableSchema.fields.map((field) => [field.name, field.type?.toString().toLowerCase()])
|
|
540
|
-
);
|
|
541
|
-
for (const [key, value] of Object.entries(keys)) {
|
|
542
|
-
const fieldType = fieldTypes.get(key);
|
|
543
|
-
if (!fieldType) {
|
|
544
|
-
throw new Error(`Field '${key}' does not exist in table schema`);
|
|
545
|
-
}
|
|
546
|
-
if (value !== null) {
|
|
547
|
-
if ((fieldType.includes("int") || fieldType.includes("bigint")) && typeof value !== "number") {
|
|
548
|
-
throw new Error(`Expected numeric value for field '${key}', got ${typeof value}`);
|
|
549
|
-
}
|
|
550
|
-
if (fieldType.includes("utf8") && typeof value !== "string") {
|
|
551
|
-
throw new Error(`Expected string value for field '${key}', got ${typeof value}`);
|
|
552
|
-
}
|
|
553
|
-
if (fieldType.includes("timestamp") && !(value instanceof Date) && typeof value !== "string") {
|
|
554
|
-
throw new Error(`Expected Date or string value for field '${key}', got ${typeof value}`);
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
/**
|
|
560
|
-
* Process a database result with appropriate type conversions based on the table schema
|
|
561
|
-
* @param rawResult The raw result object from the database
|
|
562
|
-
* @param tableSchema The schema of the table containing type information
|
|
563
|
-
* @returns Processed result with correct data types
|
|
564
|
-
*/
|
|
565
|
-
processResultWithTypeConversion(rawResult, tableSchema) {
|
|
566
|
-
const fieldTypeMap = /* @__PURE__ */ new Map();
|
|
567
|
-
tableSchema.fields.forEach((field) => {
|
|
568
|
-
const fieldName = field.name;
|
|
569
|
-
const fieldTypeStr = field.type.toString().toLowerCase();
|
|
570
|
-
fieldTypeMap.set(fieldName, fieldTypeStr);
|
|
571
|
-
});
|
|
572
|
-
if (Array.isArray(rawResult)) {
|
|
573
|
-
return rawResult.map((item) => this.processResultWithTypeConversion(item, tableSchema));
|
|
574
|
-
}
|
|
575
|
-
const processedResult = { ...rawResult };
|
|
576
|
-
for (const key in processedResult) {
|
|
577
|
-
const fieldTypeStr = fieldTypeMap.get(key);
|
|
578
|
-
if (!fieldTypeStr) continue;
|
|
579
|
-
if (typeof processedResult[key] === "string") {
|
|
580
|
-
if (fieldTypeStr.includes("int32") || fieldTypeStr.includes("float32")) {
|
|
581
|
-
if (!isNaN(Number(processedResult[key]))) {
|
|
582
|
-
processedResult[key] = Number(processedResult[key]);
|
|
583
|
-
}
|
|
584
|
-
} else if (fieldTypeStr.includes("int64")) {
|
|
585
|
-
processedResult[key] = Number(processedResult[key]);
|
|
586
|
-
} else if (fieldTypeStr.includes("utf8")) {
|
|
587
|
-
try {
|
|
588
|
-
processedResult[key] = JSON.parse(processedResult[key]);
|
|
589
|
-
} catch (e) {
|
|
590
|
-
this.logger.debug(`Failed to parse JSON for key ${key}: ${e}`);
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
} else if (typeof processedResult[key] === "bigint") {
|
|
594
|
-
processedResult[key] = Number(processedResult[key]);
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
return processedResult;
|
|
598
|
-
}
|
|
599
|
-
getThreadById({ threadId }) {
|
|
600
|
-
try {
|
|
601
|
-
return this.load({ tableName: TABLE_THREADS, keys: { id: threadId } });
|
|
602
|
-
} catch (error) {
|
|
603
|
-
throw new MastraError(
|
|
604
|
-
{
|
|
605
|
-
id: "LANCE_STORE_GET_THREAD_BY_ID_FAILED",
|
|
606
|
-
domain: ErrorDomain.STORAGE,
|
|
607
|
-
category: ErrorCategory.THIRD_PARTY
|
|
608
|
-
},
|
|
609
|
-
error
|
|
610
|
-
);
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
async getThreadsByResourceId({ resourceId }) {
|
|
614
|
-
try {
|
|
615
|
-
const table = await this.lanceClient.openTable(TABLE_THREADS);
|
|
616
|
-
const query = table.query().where(`\`resourceId\` = '${resourceId}'`);
|
|
617
|
-
const records = await query.toArray();
|
|
618
|
-
return this.processResultWithTypeConversion(
|
|
619
|
-
records,
|
|
620
|
-
await this.getTableSchema(TABLE_THREADS)
|
|
621
|
-
);
|
|
622
|
-
} catch (error) {
|
|
623
|
-
throw new MastraError(
|
|
624
|
-
{
|
|
625
|
-
id: "LANCE_STORE_GET_THREADS_BY_RESOURCE_ID_FAILED",
|
|
626
|
-
domain: ErrorDomain.STORAGE,
|
|
627
|
-
category: ErrorCategory.THIRD_PARTY
|
|
628
|
-
},
|
|
629
|
-
error
|
|
630
|
-
);
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
/**
|
|
634
|
-
* Saves a thread to the database. This function doesn't overwrite existing threads.
|
|
635
|
-
* @param thread - The thread to save
|
|
636
|
-
* @returns The saved thread
|
|
637
|
-
*/
|
|
638
|
-
async saveThread({ thread }) {
|
|
639
|
-
try {
|
|
640
|
-
const record = { ...thread, metadata: JSON.stringify(thread.metadata) };
|
|
641
|
-
const table = await this.lanceClient.openTable(TABLE_THREADS);
|
|
642
|
-
await table.add([record], { mode: "append" });
|
|
643
|
-
return thread;
|
|
644
|
-
} catch (error) {
|
|
645
|
-
throw new MastraError(
|
|
646
|
-
{
|
|
647
|
-
id: "LANCE_STORE_SAVE_THREAD_FAILED",
|
|
648
|
-
domain: ErrorDomain.STORAGE,
|
|
649
|
-
category: ErrorCategory.THIRD_PARTY
|
|
650
|
-
},
|
|
651
|
-
error
|
|
652
|
-
);
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
|
-
async updateThread({
|
|
656
|
-
id,
|
|
657
|
-
title,
|
|
658
|
-
metadata
|
|
659
|
-
}) {
|
|
660
|
-
try {
|
|
661
|
-
const record = { id, title, metadata: JSON.stringify(metadata) };
|
|
662
|
-
const table = await this.lanceClient.openTable(TABLE_THREADS);
|
|
663
|
-
await table.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([record]);
|
|
664
|
-
const query = table.query().where(`id = '${id}'`);
|
|
665
|
-
const records = await query.toArray();
|
|
666
|
-
return this.processResultWithTypeConversion(
|
|
667
|
-
records[0],
|
|
668
|
-
await this.getTableSchema(TABLE_THREADS)
|
|
669
|
-
);
|
|
670
|
-
} catch (error) {
|
|
671
|
-
throw new MastraError(
|
|
672
|
-
{
|
|
673
|
-
id: "LANCE_STORE_UPDATE_THREAD_FAILED",
|
|
674
|
-
domain: ErrorDomain.STORAGE,
|
|
675
|
-
category: ErrorCategory.THIRD_PARTY
|
|
676
|
-
},
|
|
677
|
-
error
|
|
678
|
-
);
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
async deleteThread({ threadId }) {
|
|
682
|
-
try {
|
|
683
|
-
const table = await this.lanceClient.openTable(TABLE_THREADS);
|
|
684
|
-
await table.delete(`id = '${threadId}'`);
|
|
685
|
-
} catch (error) {
|
|
686
|
-
throw new MastraError(
|
|
687
|
-
{
|
|
688
|
-
id: "LANCE_STORE_DELETE_THREAD_FAILED",
|
|
689
|
-
domain: ErrorDomain.STORAGE,
|
|
690
|
-
category: ErrorCategory.THIRD_PARTY
|
|
691
|
-
},
|
|
692
|
-
error
|
|
567
|
+
error
|
|
693
568
|
);
|
|
694
569
|
}
|
|
695
570
|
}
|
|
@@ -741,111 +616,287 @@ var LanceStorage = class _LanceStorage extends MastraStorage {
|
|
|
741
616
|
});
|
|
742
617
|
return Array.from(allIndices).sort((a, b) => a - b).map((index) => records[index]);
|
|
743
618
|
}
|
|
744
|
-
async
|
|
745
|
-
threadId,
|
|
746
|
-
|
|
747
|
-
selectBy
|
|
748
|
-
format,
|
|
749
|
-
threadConfig
|
|
750
|
-
}) {
|
|
619
|
+
async getMessagesPaginated(args) {
|
|
620
|
+
const { threadId, resourceId, selectBy, format = "v1" } = args;
|
|
621
|
+
const page = selectBy?.pagination?.page ?? 0;
|
|
622
|
+
const perPage = selectBy?.pagination?.perPage ?? 10;
|
|
751
623
|
try {
|
|
752
|
-
if (
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
const
|
|
756
|
-
const table = await this.
|
|
757
|
-
|
|
758
|
-
if (selectBy) {
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
624
|
+
if (!threadId.trim()) throw new Error("threadId must be a non-empty string");
|
|
625
|
+
const dateRange = selectBy?.pagination?.dateRange;
|
|
626
|
+
const fromDate = dateRange?.start;
|
|
627
|
+
const toDate = dateRange?.end;
|
|
628
|
+
const table = await this.client.openTable(TABLE_MESSAGES);
|
|
629
|
+
const messages = [];
|
|
630
|
+
if (selectBy?.include && Array.isArray(selectBy.include)) {
|
|
631
|
+
const threadIds = [...new Set(selectBy.include.map((item) => item.threadId))];
|
|
632
|
+
const allThreadMessages = [];
|
|
633
|
+
for (const threadId2 of threadIds) {
|
|
634
|
+
const threadQuery = table.query().where(`thread_id = '${threadId2}'`);
|
|
635
|
+
let threadRecords = await threadQuery.toArray();
|
|
636
|
+
if (fromDate) threadRecords = threadRecords.filter((m) => m.createdAt >= fromDate.getTime());
|
|
637
|
+
if (toDate) threadRecords = threadRecords.filter((m) => m.createdAt <= toDate.getTime());
|
|
638
|
+
allThreadMessages.push(...threadRecords);
|
|
763
639
|
}
|
|
640
|
+
allThreadMessages.sort((a, b) => a.createdAt - b.createdAt);
|
|
641
|
+
const contextMessages = this.processMessagesWithContext(allThreadMessages, selectBy.include);
|
|
642
|
+
messages.push(...contextMessages);
|
|
764
643
|
}
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
const dateB = new Date(b.createdAt).getTime();
|
|
769
|
-
return dateA - dateB;
|
|
770
|
-
});
|
|
771
|
-
if (selectBy?.include && selectBy.include.length > 0) {
|
|
772
|
-
records = this.processMessagesWithContext(records, selectBy.include);
|
|
644
|
+
const conditions = [`thread_id = '${threadId}'`];
|
|
645
|
+
if (resourceId) {
|
|
646
|
+
conditions.push(`\`resourceId\` = '${resourceId}'`);
|
|
773
647
|
}
|
|
774
|
-
if (
|
|
775
|
-
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
648
|
+
if (fromDate) {
|
|
649
|
+
conditions.push(`\`createdAt\` >= ${fromDate.getTime()}`);
|
|
650
|
+
}
|
|
651
|
+
if (toDate) {
|
|
652
|
+
conditions.push(`\`createdAt\` <= ${toDate.getTime()}`);
|
|
653
|
+
}
|
|
654
|
+
let total = 0;
|
|
655
|
+
if (conditions.length > 0) {
|
|
656
|
+
total = await table.countRows(conditions.join(" AND "));
|
|
657
|
+
} else {
|
|
658
|
+
total = await table.countRows();
|
|
659
|
+
}
|
|
660
|
+
if (total === 0 && messages.length === 0) {
|
|
661
|
+
return {
|
|
662
|
+
messages: [],
|
|
663
|
+
total: 0,
|
|
664
|
+
page,
|
|
665
|
+
perPage,
|
|
666
|
+
hasMore: false
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
const excludeIds = messages.map((m) => m.id);
|
|
670
|
+
let selectedMessages = [];
|
|
671
|
+
if (selectBy?.last && selectBy.last > 0) {
|
|
672
|
+
const query = table.query();
|
|
673
|
+
if (conditions.length > 0) {
|
|
674
|
+
query.where(conditions.join(" AND "));
|
|
675
|
+
}
|
|
676
|
+
let records = await query.toArray();
|
|
677
|
+
records = records.sort((a, b) => a.createdAt - b.createdAt);
|
|
678
|
+
if (excludeIds.length > 0) {
|
|
679
|
+
records = records.filter((m) => !excludeIds.includes(m.id));
|
|
680
|
+
}
|
|
681
|
+
selectedMessages = records.slice(-selectBy.last);
|
|
682
|
+
} else {
|
|
683
|
+
const query = table.query();
|
|
684
|
+
if (conditions.length > 0) {
|
|
685
|
+
query.where(conditions.join(" AND "));
|
|
686
|
+
}
|
|
687
|
+
let records = await query.toArray();
|
|
688
|
+
records = records.sort((a, b) => a.createdAt - b.createdAt);
|
|
689
|
+
if (excludeIds.length > 0) {
|
|
690
|
+
records = records.filter((m) => !excludeIds.includes(m.id));
|
|
691
|
+
}
|
|
692
|
+
selectedMessages = records.slice(page * perPage, (page + 1) * perPage);
|
|
693
|
+
}
|
|
694
|
+
const allMessages = [...messages, ...selectedMessages];
|
|
695
|
+
const seen = /* @__PURE__ */ new Set();
|
|
696
|
+
const dedupedMessages = allMessages.filter((m) => {
|
|
697
|
+
const key = `${m.id}:${m.thread_id}`;
|
|
698
|
+
if (seen.has(key)) return false;
|
|
699
|
+
seen.add(key);
|
|
700
|
+
return true;
|
|
701
|
+
});
|
|
702
|
+
const formattedMessages = dedupedMessages.map((msg) => {
|
|
703
|
+
const { thread_id, ...rest } = msg;
|
|
704
|
+
return {
|
|
705
|
+
...rest,
|
|
706
|
+
threadId: thread_id,
|
|
707
|
+
content: typeof msg.content === "string" ? (() => {
|
|
708
|
+
try {
|
|
709
|
+
return JSON.parse(msg.content);
|
|
710
|
+
} catch {
|
|
711
|
+
return msg.content;
|
|
712
|
+
}
|
|
713
|
+
})() : msg.content
|
|
714
|
+
};
|
|
715
|
+
});
|
|
716
|
+
const list = new MessageList().add(formattedMessages, "memory");
|
|
717
|
+
return {
|
|
718
|
+
messages: format === "v2" ? list.get.all.v2() : list.get.all.v1(),
|
|
719
|
+
total,
|
|
720
|
+
// Total should be the count of messages matching the filters
|
|
721
|
+
page,
|
|
722
|
+
perPage,
|
|
723
|
+
hasMore: total > (page + 1) * perPage
|
|
724
|
+
};
|
|
791
725
|
} catch (error) {
|
|
792
|
-
|
|
726
|
+
const mastraError = new MastraError(
|
|
793
727
|
{
|
|
794
|
-
id: "
|
|
728
|
+
id: "LANCE_STORE_GET_MESSAGES_PAGINATED_FAILED",
|
|
795
729
|
domain: ErrorDomain.STORAGE,
|
|
796
|
-
category: ErrorCategory.THIRD_PARTY
|
|
730
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
731
|
+
details: {
|
|
732
|
+
threadId,
|
|
733
|
+
resourceId: resourceId ?? ""
|
|
734
|
+
}
|
|
797
735
|
},
|
|
798
736
|
error
|
|
799
737
|
);
|
|
738
|
+
this.logger?.trackException?.(mastraError);
|
|
739
|
+
this.logger?.error?.(mastraError.toString());
|
|
740
|
+
return { messages: [], total: 0, page, perPage, hasMore: false };
|
|
800
741
|
}
|
|
801
742
|
}
|
|
802
|
-
|
|
743
|
+
/**
|
|
744
|
+
* Parse message data from LanceDB record format to MastraMessageV2 format
|
|
745
|
+
*/
|
|
746
|
+
parseMessageData(data) {
|
|
747
|
+
const { thread_id, ...rest } = data;
|
|
748
|
+
return {
|
|
749
|
+
...rest,
|
|
750
|
+
threadId: thread_id,
|
|
751
|
+
content: typeof data.content === "string" ? (() => {
|
|
752
|
+
try {
|
|
753
|
+
return JSON.parse(data.content);
|
|
754
|
+
} catch {
|
|
755
|
+
return data.content;
|
|
756
|
+
}
|
|
757
|
+
})() : data.content,
|
|
758
|
+
createdAt: new Date(data.createdAt),
|
|
759
|
+
updatedAt: new Date(data.updatedAt)
|
|
760
|
+
};
|
|
761
|
+
}
|
|
762
|
+
async updateMessages(args) {
|
|
763
|
+
const { messages } = args;
|
|
764
|
+
this.logger.debug("Updating messages", { count: messages.length });
|
|
765
|
+
if (!messages.length) {
|
|
766
|
+
return [];
|
|
767
|
+
}
|
|
768
|
+
const updatedMessages = [];
|
|
769
|
+
const affectedThreadIds = /* @__PURE__ */ new Set();
|
|
803
770
|
try {
|
|
804
|
-
const
|
|
805
|
-
|
|
806
|
-
|
|
771
|
+
for (const updateData of messages) {
|
|
772
|
+
const { id, ...updates } = updateData;
|
|
773
|
+
const existingMessage = await this.operations.load({ tableName: TABLE_MESSAGES, keys: { id } });
|
|
774
|
+
if (!existingMessage) {
|
|
775
|
+
this.logger.warn("Message not found for update", { id });
|
|
776
|
+
continue;
|
|
777
|
+
}
|
|
778
|
+
const existingMsg = this.parseMessageData(existingMessage);
|
|
779
|
+
const originalThreadId = existingMsg.threadId;
|
|
780
|
+
affectedThreadIds.add(originalThreadId);
|
|
781
|
+
const updatePayload = {};
|
|
782
|
+
if ("role" in updates && updates.role !== void 0) updatePayload.role = updates.role;
|
|
783
|
+
if ("type" in updates && updates.type !== void 0) updatePayload.type = updates.type;
|
|
784
|
+
if ("resourceId" in updates && updates.resourceId !== void 0) updatePayload.resourceId = updates.resourceId;
|
|
785
|
+
if ("threadId" in updates && updates.threadId !== void 0 && updates.threadId !== null) {
|
|
786
|
+
updatePayload.thread_id = updates.threadId;
|
|
787
|
+
affectedThreadIds.add(updates.threadId);
|
|
788
|
+
}
|
|
789
|
+
if (updates.content) {
|
|
790
|
+
const existingContent = existingMsg.content;
|
|
791
|
+
let newContent = { ...existingContent };
|
|
792
|
+
if (updates.content.metadata !== void 0) {
|
|
793
|
+
newContent.metadata = {
|
|
794
|
+
...existingContent.metadata || {},
|
|
795
|
+
...updates.content.metadata || {}
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
if (updates.content.content !== void 0) {
|
|
799
|
+
newContent.content = updates.content.content;
|
|
800
|
+
}
|
|
801
|
+
if ("parts" in updates.content && updates.content.parts !== void 0) {
|
|
802
|
+
newContent.parts = updates.content.parts;
|
|
803
|
+
}
|
|
804
|
+
updatePayload.content = JSON.stringify(newContent);
|
|
805
|
+
}
|
|
806
|
+
await this.operations.insert({ tableName: TABLE_MESSAGES, record: { id, ...updatePayload } });
|
|
807
|
+
const updatedMessage = await this.operations.load({ tableName: TABLE_MESSAGES, keys: { id } });
|
|
808
|
+
if (updatedMessage) {
|
|
809
|
+
updatedMessages.push(this.parseMessageData(updatedMessage));
|
|
810
|
+
}
|
|
807
811
|
}
|
|
808
|
-
const threadId
|
|
809
|
-
|
|
810
|
-
|
|
812
|
+
for (const threadId of affectedThreadIds) {
|
|
813
|
+
await this.operations.insert({
|
|
814
|
+
tableName: TABLE_THREADS,
|
|
815
|
+
record: { id: threadId, updatedAt: Date.now() }
|
|
816
|
+
});
|
|
811
817
|
}
|
|
812
|
-
|
|
813
|
-
...message,
|
|
814
|
-
content: JSON.stringify(message.content)
|
|
815
|
-
}));
|
|
816
|
-
const table = await this.lanceClient.openTable(TABLE_MESSAGES);
|
|
817
|
-
await table.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute(transformedMessages);
|
|
818
|
-
const list = new MessageList().add(messages, "memory");
|
|
819
|
-
if (format === `v2`) return list.get.all.v2();
|
|
820
|
-
return list.get.all.v1();
|
|
818
|
+
return updatedMessages;
|
|
821
819
|
} catch (error) {
|
|
822
820
|
throw new MastraError(
|
|
823
821
|
{
|
|
824
|
-
id: "
|
|
822
|
+
id: "LANCE_STORE_UPDATE_MESSAGES_FAILED",
|
|
825
823
|
domain: ErrorDomain.STORAGE,
|
|
826
|
-
category: ErrorCategory.THIRD_PARTY
|
|
824
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
825
|
+
details: { count: messages.length }
|
|
827
826
|
},
|
|
828
827
|
error
|
|
829
828
|
);
|
|
830
829
|
}
|
|
831
830
|
}
|
|
832
|
-
async
|
|
831
|
+
async getResourceById({ resourceId }) {
|
|
833
832
|
try {
|
|
834
|
-
const
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
833
|
+
const resource = await this.operations.load({ tableName: TABLE_RESOURCES, keys: { id: resourceId } });
|
|
834
|
+
if (!resource) {
|
|
835
|
+
return null;
|
|
836
|
+
}
|
|
837
|
+
let createdAt;
|
|
838
|
+
let updatedAt;
|
|
839
|
+
try {
|
|
840
|
+
if (resource.createdAt instanceof Date) {
|
|
841
|
+
createdAt = resource.createdAt;
|
|
842
|
+
} else if (typeof resource.createdAt === "string") {
|
|
843
|
+
createdAt = new Date(resource.createdAt);
|
|
844
|
+
} else if (typeof resource.createdAt === "number") {
|
|
845
|
+
createdAt = new Date(resource.createdAt);
|
|
846
|
+
} else {
|
|
847
|
+
createdAt = /* @__PURE__ */ new Date();
|
|
848
|
+
}
|
|
849
|
+
if (isNaN(createdAt.getTime())) {
|
|
850
|
+
createdAt = /* @__PURE__ */ new Date();
|
|
851
|
+
}
|
|
852
|
+
} catch {
|
|
853
|
+
createdAt = /* @__PURE__ */ new Date();
|
|
854
|
+
}
|
|
855
|
+
try {
|
|
856
|
+
if (resource.updatedAt instanceof Date) {
|
|
857
|
+
updatedAt = resource.updatedAt;
|
|
858
|
+
} else if (typeof resource.updatedAt === "string") {
|
|
859
|
+
updatedAt = new Date(resource.updatedAt);
|
|
860
|
+
} else if (typeof resource.updatedAt === "number") {
|
|
861
|
+
updatedAt = new Date(resource.updatedAt);
|
|
862
|
+
} else {
|
|
863
|
+
updatedAt = /* @__PURE__ */ new Date();
|
|
864
|
+
}
|
|
865
|
+
if (isNaN(updatedAt.getTime())) {
|
|
866
|
+
updatedAt = /* @__PURE__ */ new Date();
|
|
867
|
+
}
|
|
868
|
+
} catch {
|
|
869
|
+
updatedAt = /* @__PURE__ */ new Date();
|
|
870
|
+
}
|
|
871
|
+
let workingMemory = resource.workingMemory;
|
|
872
|
+
if (workingMemory === null || workingMemory === void 0) {
|
|
873
|
+
workingMemory = void 0;
|
|
874
|
+
} else if (workingMemory === "") {
|
|
875
|
+
workingMemory = "";
|
|
876
|
+
} else if (typeof workingMemory === "object") {
|
|
877
|
+
workingMemory = JSON.stringify(workingMemory);
|
|
878
|
+
}
|
|
879
|
+
let metadata = resource.metadata;
|
|
880
|
+
if (metadata === "" || metadata === null || metadata === void 0) {
|
|
881
|
+
metadata = void 0;
|
|
882
|
+
} else if (typeof metadata === "string") {
|
|
883
|
+
try {
|
|
884
|
+
metadata = JSON.parse(metadata);
|
|
885
|
+
} catch {
|
|
886
|
+
metadata = metadata;
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
return {
|
|
890
|
+
...resource,
|
|
891
|
+
createdAt,
|
|
892
|
+
updatedAt,
|
|
893
|
+
workingMemory,
|
|
894
|
+
metadata
|
|
842
895
|
};
|
|
843
|
-
await table.add([record], { mode: "append" });
|
|
844
|
-
return trace;
|
|
845
896
|
} catch (error) {
|
|
846
897
|
throw new MastraError(
|
|
847
898
|
{
|
|
848
|
-
id: "
|
|
899
|
+
id: "LANCE_STORE_GET_RESOURCE_BY_ID_FAILED",
|
|
849
900
|
domain: ErrorDomain.STORAGE,
|
|
850
901
|
category: ErrorCategory.THIRD_PARTY
|
|
851
902
|
},
|
|
@@ -853,16 +904,23 @@ var LanceStorage = class _LanceStorage extends MastraStorage {
|
|
|
853
904
|
);
|
|
854
905
|
}
|
|
855
906
|
}
|
|
856
|
-
async
|
|
907
|
+
async saveResource({ resource }) {
|
|
857
908
|
try {
|
|
858
|
-
const
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
909
|
+
const record = {
|
|
910
|
+
...resource,
|
|
911
|
+
metadata: resource.metadata ? JSON.stringify(resource.metadata) : "",
|
|
912
|
+
createdAt: resource.createdAt.getTime(),
|
|
913
|
+
// Store as timestamp (milliseconds)
|
|
914
|
+
updatedAt: resource.updatedAt.getTime()
|
|
915
|
+
// Store as timestamp (milliseconds)
|
|
916
|
+
};
|
|
917
|
+
const table = await this.client.openTable(TABLE_RESOURCES);
|
|
918
|
+
await table.add([record], { mode: "append" });
|
|
919
|
+
return resource;
|
|
862
920
|
} catch (error) {
|
|
863
921
|
throw new MastraError(
|
|
864
922
|
{
|
|
865
|
-
id: "
|
|
923
|
+
id: "LANCE_STORE_SAVE_RESOURCE_FAILED",
|
|
866
924
|
domain: ErrorDomain.STORAGE,
|
|
867
925
|
category: ErrorCategory.THIRD_PARTY
|
|
868
926
|
},
|
|
@@ -870,292 +928,1316 @@ var LanceStorage = class _LanceStorage extends MastraStorage {
|
|
|
870
928
|
);
|
|
871
929
|
}
|
|
872
930
|
}
|
|
873
|
-
async
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
perPage = 10,
|
|
878
|
-
attributes
|
|
931
|
+
async updateResource({
|
|
932
|
+
resourceId,
|
|
933
|
+
workingMemory,
|
|
934
|
+
metadata
|
|
879
935
|
}) {
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
936
|
+
const maxRetries = 3;
|
|
937
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
938
|
+
try {
|
|
939
|
+
const existingResource = await this.getResourceById({ resourceId });
|
|
940
|
+
if (!existingResource) {
|
|
941
|
+
const newResource = {
|
|
942
|
+
id: resourceId,
|
|
943
|
+
workingMemory,
|
|
944
|
+
metadata: metadata || {},
|
|
945
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
946
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
947
|
+
};
|
|
948
|
+
return this.saveResource({ resource: newResource });
|
|
949
|
+
}
|
|
950
|
+
const updatedResource = {
|
|
951
|
+
...existingResource,
|
|
952
|
+
workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
|
|
953
|
+
metadata: {
|
|
954
|
+
...existingResource.metadata,
|
|
955
|
+
...metadata
|
|
956
|
+
},
|
|
957
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
958
|
+
};
|
|
959
|
+
const record = {
|
|
960
|
+
id: resourceId,
|
|
961
|
+
workingMemory: updatedResource.workingMemory || "",
|
|
962
|
+
metadata: updatedResource.metadata ? JSON.stringify(updatedResource.metadata) : "",
|
|
963
|
+
updatedAt: updatedResource.updatedAt.getTime()
|
|
964
|
+
// Store as timestamp (milliseconds)
|
|
965
|
+
};
|
|
966
|
+
const table = await this.client.openTable(TABLE_RESOURCES);
|
|
967
|
+
await table.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([record]);
|
|
968
|
+
return updatedResource;
|
|
969
|
+
} catch (error) {
|
|
970
|
+
if (error.message?.includes("Commit conflict") && attempt < maxRetries - 1) {
|
|
971
|
+
const delay = Math.pow(2, attempt) * 10;
|
|
972
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
973
|
+
continue;
|
|
974
|
+
}
|
|
975
|
+
throw new MastraError(
|
|
976
|
+
{
|
|
977
|
+
id: "LANCE_STORE_UPDATE_RESOURCE_FAILED",
|
|
978
|
+
domain: ErrorDomain.STORAGE,
|
|
979
|
+
category: ErrorCategory.THIRD_PARTY
|
|
980
|
+
},
|
|
981
|
+
error
|
|
982
|
+
);
|
|
885
983
|
}
|
|
886
|
-
|
|
887
|
-
|
|
984
|
+
}
|
|
985
|
+
throw new Error("Unexpected end of retry loop");
|
|
986
|
+
}
|
|
987
|
+
};
|
|
988
|
+
var StoreOperationsLance = class extends StoreOperations {
|
|
989
|
+
client;
|
|
990
|
+
constructor({ client }) {
|
|
991
|
+
super();
|
|
992
|
+
this.client = client;
|
|
993
|
+
}
|
|
994
|
+
getDefaultValue(type) {
|
|
995
|
+
switch (type) {
|
|
996
|
+
case "text":
|
|
997
|
+
return "''";
|
|
998
|
+
case "timestamp":
|
|
999
|
+
return "CURRENT_TIMESTAMP";
|
|
1000
|
+
case "integer":
|
|
1001
|
+
case "bigint":
|
|
1002
|
+
return "0";
|
|
1003
|
+
case "jsonb":
|
|
1004
|
+
return "'{}'";
|
|
1005
|
+
case "uuid":
|
|
1006
|
+
return "''";
|
|
1007
|
+
default:
|
|
1008
|
+
return super.getDefaultValue(type);
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
async hasColumn(tableName, columnName) {
|
|
1012
|
+
const table = await this.client.openTable(tableName);
|
|
1013
|
+
const schema = await table.schema();
|
|
1014
|
+
return schema.fields.some((field) => field.name === columnName);
|
|
1015
|
+
}
|
|
1016
|
+
translateSchema(schema) {
|
|
1017
|
+
const fields = Object.entries(schema).map(([name, column]) => {
|
|
1018
|
+
let arrowType;
|
|
1019
|
+
switch (column.type.toLowerCase()) {
|
|
1020
|
+
case "text":
|
|
1021
|
+
case "uuid":
|
|
1022
|
+
arrowType = new Utf8();
|
|
1023
|
+
break;
|
|
1024
|
+
case "int":
|
|
1025
|
+
case "integer":
|
|
1026
|
+
arrowType = new Int32();
|
|
1027
|
+
break;
|
|
1028
|
+
case "bigint":
|
|
1029
|
+
arrowType = new Float64();
|
|
1030
|
+
break;
|
|
1031
|
+
case "float":
|
|
1032
|
+
arrowType = new Float32();
|
|
1033
|
+
break;
|
|
1034
|
+
case "jsonb":
|
|
1035
|
+
case "json":
|
|
1036
|
+
arrowType = new Utf8();
|
|
1037
|
+
break;
|
|
1038
|
+
case "binary":
|
|
1039
|
+
arrowType = new Binary();
|
|
1040
|
+
break;
|
|
1041
|
+
case "timestamp":
|
|
1042
|
+
arrowType = new Float64();
|
|
1043
|
+
break;
|
|
1044
|
+
default:
|
|
1045
|
+
arrowType = new Utf8();
|
|
888
1046
|
}
|
|
889
|
-
|
|
890
|
-
|
|
1047
|
+
return new Field(name, arrowType, column.nullable ?? true);
|
|
1048
|
+
});
|
|
1049
|
+
return new Schema(fields);
|
|
1050
|
+
}
|
|
1051
|
+
async createTable({
|
|
1052
|
+
tableName,
|
|
1053
|
+
schema
|
|
1054
|
+
}) {
|
|
1055
|
+
try {
|
|
1056
|
+
if (!this.client) {
|
|
1057
|
+
throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
|
|
891
1058
|
}
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
1059
|
+
if (!tableName) {
|
|
1060
|
+
throw new Error("tableName is required for createTable.");
|
|
1061
|
+
}
|
|
1062
|
+
if (!schema) {
|
|
1063
|
+
throw new Error("schema is required for createTable.");
|
|
896
1064
|
}
|
|
897
|
-
const records = await query.toArray();
|
|
898
|
-
return records.map((record) => {
|
|
899
|
-
return {
|
|
900
|
-
...record,
|
|
901
|
-
attributes: JSON.parse(record.attributes),
|
|
902
|
-
status: JSON.parse(record.status),
|
|
903
|
-
events: JSON.parse(record.events),
|
|
904
|
-
links: JSON.parse(record.links),
|
|
905
|
-
other: JSON.parse(record.other),
|
|
906
|
-
startTime: new Date(record.startTime),
|
|
907
|
-
endTime: new Date(record.endTime),
|
|
908
|
-
createdAt: new Date(record.createdAt)
|
|
909
|
-
};
|
|
910
|
-
});
|
|
911
1065
|
} catch (error) {
|
|
912
1066
|
throw new MastraError(
|
|
913
1067
|
{
|
|
914
|
-
id: "
|
|
1068
|
+
id: "STORAGE_LANCE_STORAGE_CREATE_TABLE_INVALID_ARGS",
|
|
915
1069
|
domain: ErrorDomain.STORAGE,
|
|
916
|
-
category: ErrorCategory.
|
|
917
|
-
details: {
|
|
1070
|
+
category: ErrorCategory.USER,
|
|
1071
|
+
details: { tableName }
|
|
918
1072
|
},
|
|
919
1073
|
error
|
|
920
1074
|
);
|
|
921
1075
|
}
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
const table = await this.lanceClient.openTable(TABLE_EVALS);
|
|
926
|
-
const transformedEvals = evals.map((evalRecord) => ({
|
|
927
|
-
input: evalRecord.input,
|
|
928
|
-
output: evalRecord.output,
|
|
929
|
-
agent_name: evalRecord.agentName,
|
|
930
|
-
metric_name: evalRecord.metricName,
|
|
931
|
-
result: JSON.stringify(evalRecord.result),
|
|
932
|
-
instructions: evalRecord.instructions,
|
|
933
|
-
test_info: JSON.stringify(evalRecord.testInfo),
|
|
934
|
-
global_run_id: evalRecord.globalRunId,
|
|
935
|
-
run_id: evalRecord.runId,
|
|
936
|
-
created_at: new Date(evalRecord.createdAt).getTime()
|
|
937
|
-
}));
|
|
938
|
-
await table.add(transformedEvals, { mode: "append" });
|
|
939
|
-
return evals;
|
|
1076
|
+
try {
|
|
1077
|
+
const arrowSchema = this.translateSchema(schema);
|
|
1078
|
+
await this.client.createEmptyTable(tableName, arrowSchema);
|
|
940
1079
|
} catch (error) {
|
|
1080
|
+
if (error.message?.includes("already exists")) {
|
|
1081
|
+
this.logger.debug(`Table '${tableName}' already exists, skipping create`);
|
|
1082
|
+
return;
|
|
1083
|
+
}
|
|
941
1084
|
throw new MastraError(
|
|
942
1085
|
{
|
|
943
|
-
id: "
|
|
1086
|
+
id: "STORAGE_LANCE_STORAGE_CREATE_TABLE_FAILED",
|
|
944
1087
|
domain: ErrorDomain.STORAGE,
|
|
945
|
-
category: ErrorCategory.THIRD_PARTY
|
|
1088
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1089
|
+
details: { tableName }
|
|
946
1090
|
},
|
|
947
1091
|
error
|
|
948
1092
|
);
|
|
949
1093
|
}
|
|
950
1094
|
}
|
|
951
|
-
async
|
|
1095
|
+
async dropTable({ tableName }) {
|
|
952
1096
|
try {
|
|
953
|
-
if (
|
|
954
|
-
|
|
1097
|
+
if (!this.client) {
|
|
1098
|
+
throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
|
|
955
1099
|
}
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
});
|
|
1100
|
+
if (!tableName) {
|
|
1101
|
+
throw new Error("tableName is required for dropTable.");
|
|
1102
|
+
}
|
|
1103
|
+
} catch (validationError) {
|
|
1104
|
+
throw new MastraError(
|
|
1105
|
+
{
|
|
1106
|
+
id: "STORAGE_LANCE_STORAGE_DROP_TABLE_INVALID_ARGS",
|
|
1107
|
+
domain: ErrorDomain.STORAGE,
|
|
1108
|
+
category: ErrorCategory.USER,
|
|
1109
|
+
text: validationError.message,
|
|
1110
|
+
details: { tableName }
|
|
1111
|
+
},
|
|
1112
|
+
validationError
|
|
1113
|
+
);
|
|
1114
|
+
}
|
|
1115
|
+
try {
|
|
1116
|
+
await this.client.dropTable(tableName);
|
|
974
1117
|
} catch (error) {
|
|
1118
|
+
if (error.toString().includes("was not found") || error.message?.includes("Table not found")) {
|
|
1119
|
+
this.logger.debug(`Table '${tableName}' does not exist, skipping drop`);
|
|
1120
|
+
return;
|
|
1121
|
+
}
|
|
975
1122
|
throw new MastraError(
|
|
976
1123
|
{
|
|
977
|
-
id: "
|
|
1124
|
+
id: "STORAGE_LANCE_STORAGE_DROP_TABLE_FAILED",
|
|
978
1125
|
domain: ErrorDomain.STORAGE,
|
|
979
1126
|
category: ErrorCategory.THIRD_PARTY,
|
|
980
|
-
details: {
|
|
1127
|
+
details: { tableName }
|
|
981
1128
|
},
|
|
982
1129
|
error
|
|
983
1130
|
);
|
|
984
1131
|
}
|
|
985
1132
|
}
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
} catch (e) {
|
|
992
|
-
console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
|
|
993
|
-
}
|
|
994
|
-
}
|
|
995
|
-
return {
|
|
996
|
-
workflowName: row.workflow_name,
|
|
997
|
-
runId: row.run_id,
|
|
998
|
-
snapshot: parsedSnapshot,
|
|
999
|
-
createdAt: this.ensureDate(row.createdAt),
|
|
1000
|
-
updatedAt: this.ensureDate(row.updatedAt),
|
|
1001
|
-
resourceId: row.resourceId
|
|
1002
|
-
};
|
|
1003
|
-
}
|
|
1004
|
-
async getWorkflowRuns(args) {
|
|
1133
|
+
async alterTable({
|
|
1134
|
+
tableName,
|
|
1135
|
+
schema,
|
|
1136
|
+
ifNotExists
|
|
1137
|
+
}) {
|
|
1005
1138
|
try {
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
if (args?.workflowName) {
|
|
1009
|
-
query.where(`workflow_name = '${args.workflowName}'`);
|
|
1010
|
-
}
|
|
1011
|
-
if (args?.fromDate) {
|
|
1012
|
-
query.where(`\`createdAt\` >= ${args.fromDate.getTime()}`);
|
|
1139
|
+
if (!this.client) {
|
|
1140
|
+
throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
|
|
1013
1141
|
}
|
|
1014
|
-
if (
|
|
1015
|
-
|
|
1142
|
+
if (!tableName) {
|
|
1143
|
+
throw new Error("tableName is required for alterTable.");
|
|
1016
1144
|
}
|
|
1017
|
-
if (
|
|
1018
|
-
|
|
1145
|
+
if (!schema) {
|
|
1146
|
+
throw new Error("schema is required for alterTable.");
|
|
1019
1147
|
}
|
|
1020
|
-
if (
|
|
1021
|
-
|
|
1148
|
+
if (!ifNotExists || ifNotExists.length === 0) {
|
|
1149
|
+
this.logger.debug("No columns specified to add in alterTable, skipping.");
|
|
1150
|
+
return;
|
|
1022
1151
|
}
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1152
|
+
} catch (validationError) {
|
|
1153
|
+
throw new MastraError(
|
|
1154
|
+
{
|
|
1155
|
+
id: "STORAGE_LANCE_STORAGE_ALTER_TABLE_INVALID_ARGS",
|
|
1156
|
+
domain: ErrorDomain.STORAGE,
|
|
1157
|
+
category: ErrorCategory.USER,
|
|
1158
|
+
text: validationError.message,
|
|
1159
|
+
details: { tableName }
|
|
1160
|
+
},
|
|
1161
|
+
validationError
|
|
1162
|
+
);
|
|
1163
|
+
}
|
|
1164
|
+
try {
|
|
1165
|
+
const table = await this.client.openTable(tableName);
|
|
1166
|
+
const currentSchema = await table.schema();
|
|
1167
|
+
const existingFields = new Set(currentSchema.fields.map((f) => f.name));
|
|
1168
|
+
const typeMap = {
|
|
1169
|
+
text: "string",
|
|
1170
|
+
integer: "int",
|
|
1171
|
+
bigint: "bigint",
|
|
1172
|
+
timestamp: "timestamp",
|
|
1173
|
+
jsonb: "string",
|
|
1174
|
+
uuid: "string"
|
|
1027
1175
|
};
|
|
1176
|
+
const columnsToAdd = ifNotExists.filter((col) => schema[col] && !existingFields.has(col)).map((col) => {
|
|
1177
|
+
const colDef = schema[col];
|
|
1178
|
+
return {
|
|
1179
|
+
name: col,
|
|
1180
|
+
valueSql: colDef?.nullable ? `cast(NULL as ${typeMap[colDef.type ?? "text"]})` : `cast(${this.getDefaultValue(colDef?.type ?? "text")} as ${typeMap[colDef?.type ?? "text"]})`
|
|
1181
|
+
};
|
|
1182
|
+
});
|
|
1183
|
+
if (columnsToAdd.length > 0) {
|
|
1184
|
+
await table.addColumns(columnsToAdd);
|
|
1185
|
+
this.logger?.info?.(`Added columns [${columnsToAdd.map((c) => c.name).join(", ")}] to table ${tableName}`);
|
|
1186
|
+
}
|
|
1028
1187
|
} catch (error) {
|
|
1029
1188
|
throw new MastraError(
|
|
1030
1189
|
{
|
|
1031
|
-
id: "
|
|
1190
|
+
id: "STORAGE_LANCE_STORAGE_ALTER_TABLE_FAILED",
|
|
1032
1191
|
domain: ErrorDomain.STORAGE,
|
|
1033
1192
|
category: ErrorCategory.THIRD_PARTY,
|
|
1034
|
-
details: {
|
|
1193
|
+
details: { tableName }
|
|
1035
1194
|
},
|
|
1036
1195
|
error
|
|
1037
1196
|
);
|
|
1038
1197
|
}
|
|
1039
1198
|
}
|
|
1040
|
-
|
|
1041
|
-
* Retrieve a single workflow run by its runId.
|
|
1042
|
-
* @param args The ID of the workflow run to retrieve
|
|
1043
|
-
* @returns The workflow run object or null if not found
|
|
1044
|
-
*/
|
|
1045
|
-
async getWorkflowRunById(args) {
|
|
1199
|
+
async clearTable({ tableName }) {
|
|
1046
1200
|
try {
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
if (args.workflowName) {
|
|
1050
|
-
whereClause += ` AND workflow_name = '${args.workflowName}'`;
|
|
1201
|
+
if (!this.client) {
|
|
1202
|
+
throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
|
|
1051
1203
|
}
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1204
|
+
if (!tableName) {
|
|
1205
|
+
throw new Error("tableName is required for clearTable.");
|
|
1206
|
+
}
|
|
1207
|
+
} catch (validationError) {
|
|
1208
|
+
throw new MastraError(
|
|
1209
|
+
{
|
|
1210
|
+
id: "STORAGE_LANCE_STORAGE_CLEAR_TABLE_INVALID_ARGS",
|
|
1211
|
+
domain: ErrorDomain.STORAGE,
|
|
1212
|
+
category: ErrorCategory.USER,
|
|
1213
|
+
text: validationError.message,
|
|
1214
|
+
details: { tableName }
|
|
1215
|
+
},
|
|
1216
|
+
validationError
|
|
1217
|
+
);
|
|
1218
|
+
}
|
|
1219
|
+
try {
|
|
1220
|
+
const table = await this.client.openTable(tableName);
|
|
1221
|
+
await table.delete("1=1");
|
|
1057
1222
|
} catch (error) {
|
|
1058
1223
|
throw new MastraError(
|
|
1059
1224
|
{
|
|
1060
|
-
id: "
|
|
1225
|
+
id: "STORAGE_LANCE_STORAGE_CLEAR_TABLE_FAILED",
|
|
1061
1226
|
domain: ErrorDomain.STORAGE,
|
|
1062
1227
|
category: ErrorCategory.THIRD_PARTY,
|
|
1063
|
-
details: {
|
|
1228
|
+
details: { tableName }
|
|
1064
1229
|
},
|
|
1065
1230
|
error
|
|
1066
1231
|
);
|
|
1067
1232
|
}
|
|
1068
1233
|
}
|
|
1069
|
-
async
|
|
1070
|
-
workflowName,
|
|
1071
|
-
runId,
|
|
1072
|
-
snapshot
|
|
1073
|
-
}) {
|
|
1234
|
+
async insert({ tableName, record }) {
|
|
1074
1235
|
try {
|
|
1075
|
-
|
|
1076
|
-
|
|
1236
|
+
if (!this.client) {
|
|
1237
|
+
throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
|
|
1238
|
+
}
|
|
1239
|
+
if (!tableName) {
|
|
1240
|
+
throw new Error("tableName is required for insert.");
|
|
1241
|
+
}
|
|
1242
|
+
if (!record || Object.keys(record).length === 0) {
|
|
1243
|
+
throw new Error("record is required and cannot be empty for insert.");
|
|
1244
|
+
}
|
|
1245
|
+
} catch (validationError) {
|
|
1246
|
+
throw new MastraError(
|
|
1247
|
+
{
|
|
1248
|
+
id: "STORAGE_LANCE_STORAGE_INSERT_INVALID_ARGS",
|
|
1249
|
+
domain: ErrorDomain.STORAGE,
|
|
1250
|
+
category: ErrorCategory.USER,
|
|
1251
|
+
text: validationError.message,
|
|
1252
|
+
details: { tableName }
|
|
1253
|
+
},
|
|
1254
|
+
validationError
|
|
1255
|
+
);
|
|
1256
|
+
}
|
|
1257
|
+
try {
|
|
1258
|
+
const table = await this.client.openTable(tableName);
|
|
1259
|
+
const primaryId = getPrimaryKeys(tableName);
|
|
1260
|
+
const processedRecord = { ...record };
|
|
1261
|
+
for (const key in processedRecord) {
|
|
1262
|
+
if (processedRecord[key] !== null && typeof processedRecord[key] === "object" && !(processedRecord[key] instanceof Date)) {
|
|
1263
|
+
this.logger.debug("Converting object to JSON string: ", processedRecord[key]);
|
|
1264
|
+
processedRecord[key] = JSON.stringify(processedRecord[key]);
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
console.log(await table.schema());
|
|
1268
|
+
await table.mergeInsert(primaryId).whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([processedRecord]);
|
|
1269
|
+
} catch (error) {
|
|
1270
|
+
throw new MastraError(
|
|
1271
|
+
{
|
|
1272
|
+
id: "STORAGE_LANCE_STORAGE_INSERT_FAILED",
|
|
1273
|
+
domain: ErrorDomain.STORAGE,
|
|
1274
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1275
|
+
details: { tableName }
|
|
1276
|
+
},
|
|
1277
|
+
error
|
|
1278
|
+
);
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
async batchInsert({ tableName, records }) {
|
|
1282
|
+
try {
|
|
1283
|
+
if (!this.client) {
|
|
1284
|
+
throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
|
|
1285
|
+
}
|
|
1286
|
+
if (!tableName) {
|
|
1287
|
+
throw new Error("tableName is required for batchInsert.");
|
|
1288
|
+
}
|
|
1289
|
+
if (!records || records.length === 0) {
|
|
1290
|
+
throw new Error("records array is required and cannot be empty for batchInsert.");
|
|
1291
|
+
}
|
|
1292
|
+
} catch (validationError) {
|
|
1293
|
+
throw new MastraError(
|
|
1294
|
+
{
|
|
1295
|
+
id: "STORAGE_LANCE_STORAGE_BATCH_INSERT_INVALID_ARGS",
|
|
1296
|
+
domain: ErrorDomain.STORAGE,
|
|
1297
|
+
category: ErrorCategory.USER,
|
|
1298
|
+
text: validationError.message,
|
|
1299
|
+
details: { tableName }
|
|
1300
|
+
},
|
|
1301
|
+
validationError
|
|
1302
|
+
);
|
|
1303
|
+
}
|
|
1304
|
+
try {
|
|
1305
|
+
const table = await this.client.openTable(tableName);
|
|
1306
|
+
const primaryId = getPrimaryKeys(tableName);
|
|
1307
|
+
const processedRecords = records.map((record) => {
|
|
1308
|
+
const processedRecord = { ...record };
|
|
1309
|
+
for (const key in processedRecord) {
|
|
1310
|
+
if (processedRecord[key] == null) continue;
|
|
1311
|
+
if (processedRecord[key] !== null && typeof processedRecord[key] === "object" && !(processedRecord[key] instanceof Date)) {
|
|
1312
|
+
processedRecord[key] = JSON.stringify(processedRecord[key]);
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
return processedRecord;
|
|
1316
|
+
});
|
|
1317
|
+
console.log(processedRecords);
|
|
1318
|
+
await table.mergeInsert(primaryId).whenMatchedUpdateAll().whenNotMatchedInsertAll().execute(processedRecords);
|
|
1319
|
+
} catch (error) {
|
|
1320
|
+
throw new MastraError(
|
|
1321
|
+
{
|
|
1322
|
+
id: "STORAGE_LANCE_STORAGE_BATCH_INSERT_FAILED",
|
|
1323
|
+
domain: ErrorDomain.STORAGE,
|
|
1324
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1325
|
+
details: { tableName }
|
|
1326
|
+
},
|
|
1327
|
+
error
|
|
1328
|
+
);
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
async load({ tableName, keys }) {
|
|
1332
|
+
try {
|
|
1333
|
+
if (!this.client) {
|
|
1334
|
+
throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
|
|
1335
|
+
}
|
|
1336
|
+
if (!tableName) {
|
|
1337
|
+
throw new Error("tableName is required for load.");
|
|
1338
|
+
}
|
|
1339
|
+
if (!keys || Object.keys(keys).length === 0) {
|
|
1340
|
+
throw new Error("keys are required and cannot be empty for load.");
|
|
1341
|
+
}
|
|
1342
|
+
} catch (validationError) {
|
|
1343
|
+
throw new MastraError(
|
|
1344
|
+
{
|
|
1345
|
+
id: "STORAGE_LANCE_STORAGE_LOAD_INVALID_ARGS",
|
|
1346
|
+
domain: ErrorDomain.STORAGE,
|
|
1347
|
+
category: ErrorCategory.USER,
|
|
1348
|
+
text: validationError.message,
|
|
1349
|
+
details: { tableName }
|
|
1350
|
+
},
|
|
1351
|
+
validationError
|
|
1352
|
+
);
|
|
1353
|
+
}
|
|
1354
|
+
try {
|
|
1355
|
+
const table = await this.client.openTable(tableName);
|
|
1356
|
+
const tableSchema = await getTableSchema({ tableName, client: this.client });
|
|
1357
|
+
const query = table.query();
|
|
1358
|
+
if (Object.keys(keys).length > 0) {
|
|
1359
|
+
validateKeyTypes(keys, tableSchema);
|
|
1360
|
+
const filterConditions = Object.entries(keys).map(([key, value]) => {
|
|
1361
|
+
const isCamelCase = /^[a-z][a-zA-Z]*$/.test(key) && /[A-Z]/.test(key);
|
|
1362
|
+
const quotedKey = isCamelCase ? `\`${key}\`` : key;
|
|
1363
|
+
if (typeof value === "string") {
|
|
1364
|
+
return `${quotedKey} = '${value}'`;
|
|
1365
|
+
} else if (value === null) {
|
|
1366
|
+
return `${quotedKey} IS NULL`;
|
|
1367
|
+
} else {
|
|
1368
|
+
return `${quotedKey} = ${value}`;
|
|
1369
|
+
}
|
|
1370
|
+
}).join(" AND ");
|
|
1371
|
+
this.logger.debug("where clause generated: " + filterConditions);
|
|
1372
|
+
query.where(filterConditions);
|
|
1373
|
+
}
|
|
1374
|
+
const result = await query.limit(1).toArray();
|
|
1375
|
+
if (result.length === 0) {
|
|
1376
|
+
this.logger.debug("No record found");
|
|
1377
|
+
return null;
|
|
1378
|
+
}
|
|
1379
|
+
return processResultWithTypeConversion(result[0], tableSchema);
|
|
1380
|
+
} catch (error) {
|
|
1381
|
+
if (error instanceof MastraError) throw error;
|
|
1382
|
+
throw new MastraError(
|
|
1383
|
+
{
|
|
1384
|
+
id: "STORAGE_LANCE_STORAGE_LOAD_FAILED",
|
|
1385
|
+
domain: ErrorDomain.STORAGE,
|
|
1386
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1387
|
+
details: { tableName, keyCount: Object.keys(keys).length, firstKey: Object.keys(keys)[0] ?? "" }
|
|
1388
|
+
},
|
|
1389
|
+
error
|
|
1390
|
+
);
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
};
|
|
1394
|
+
var StoreScoresLance = class extends ScoresStorage {
|
|
1395
|
+
client;
|
|
1396
|
+
constructor({ client }) {
|
|
1397
|
+
super();
|
|
1398
|
+
this.client = client;
|
|
1399
|
+
}
|
|
1400
|
+
async saveScore(score) {
|
|
1401
|
+
try {
|
|
1402
|
+
const id = crypto.randomUUID();
|
|
1403
|
+
const table = await this.client.openTable(TABLE_SCORERS);
|
|
1404
|
+
const schema = await getTableSchema({ tableName: TABLE_SCORERS, client: this.client });
|
|
1405
|
+
const allowedFields = new Set(schema.fields.map((f) => f.name));
|
|
1406
|
+
const filteredScore = {};
|
|
1407
|
+
Object.keys(score).forEach((key) => {
|
|
1408
|
+
if (allowedFields.has(key)) {
|
|
1409
|
+
filteredScore[key] = score[key];
|
|
1410
|
+
}
|
|
1411
|
+
});
|
|
1412
|
+
for (const key in filteredScore) {
|
|
1413
|
+
if (filteredScore[key] !== null && typeof filteredScore[key] === "object" && !(filteredScore[key] instanceof Date)) {
|
|
1414
|
+
filteredScore[key] = JSON.stringify(filteredScore[key]);
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
filteredScore.id = id;
|
|
1418
|
+
await table.add([filteredScore], { mode: "append" });
|
|
1419
|
+
return { score };
|
|
1420
|
+
} catch (error) {
|
|
1421
|
+
throw new MastraError(
|
|
1422
|
+
{
|
|
1423
|
+
id: "LANCE_STORAGE_SAVE_SCORE_FAILED",
|
|
1424
|
+
text: "Failed to save score in LanceStorage",
|
|
1425
|
+
domain: ErrorDomain.STORAGE,
|
|
1426
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1427
|
+
details: { error: error?.message }
|
|
1428
|
+
},
|
|
1429
|
+
error
|
|
1430
|
+
);
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
async getScoreById({ id }) {
|
|
1434
|
+
try {
|
|
1435
|
+
const table = await this.client.openTable(TABLE_SCORERS);
|
|
1436
|
+
const query = table.query().where(`id = '${id}'`).limit(1);
|
|
1077
1437
|
const records = await query.toArray();
|
|
1078
|
-
|
|
1079
|
-
const
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1438
|
+
if (records.length === 0) return null;
|
|
1439
|
+
const schema = await getTableSchema({ tableName: TABLE_SCORERS, client: this.client });
|
|
1440
|
+
return processResultWithTypeConversion(records[0], schema);
|
|
1441
|
+
} catch (error) {
|
|
1442
|
+
throw new MastraError(
|
|
1443
|
+
{
|
|
1444
|
+
id: "LANCE_STORAGE_GET_SCORE_BY_ID_FAILED",
|
|
1445
|
+
text: "Failed to get score by id in LanceStorage",
|
|
1446
|
+
domain: ErrorDomain.STORAGE,
|
|
1447
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1448
|
+
details: { error: error?.message }
|
|
1449
|
+
},
|
|
1450
|
+
error
|
|
1451
|
+
);
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
async getScoresByScorerId({
|
|
1455
|
+
scorerId,
|
|
1456
|
+
pagination,
|
|
1457
|
+
entityId,
|
|
1458
|
+
entityType,
|
|
1459
|
+
source
|
|
1460
|
+
}) {
|
|
1461
|
+
try {
|
|
1462
|
+
const table = await this.client.openTable(TABLE_SCORERS);
|
|
1463
|
+
const { page = 0, perPage = 10 } = pagination || {};
|
|
1464
|
+
const offset = page * perPage;
|
|
1465
|
+
let query = table.query().where(`\`scorerId\` = '${scorerId}'`);
|
|
1466
|
+
if (source) {
|
|
1467
|
+
query = query.where(`\`source\` = '${source}'`);
|
|
1084
1468
|
}
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1469
|
+
if (entityId) {
|
|
1470
|
+
query = query.where(`\`entityId\` = '${entityId}'`);
|
|
1471
|
+
}
|
|
1472
|
+
if (entityType) {
|
|
1473
|
+
query = query.where(`\`entityType\` = '${entityType}'`);
|
|
1474
|
+
}
|
|
1475
|
+
query = query.limit(perPage);
|
|
1476
|
+
if (offset > 0) query.offset(offset);
|
|
1477
|
+
const records = await query.toArray();
|
|
1478
|
+
const schema = await getTableSchema({ tableName: TABLE_SCORERS, client: this.client });
|
|
1479
|
+
const scores = processResultWithTypeConversion(records, schema);
|
|
1480
|
+
let totalQuery = table.query().where(`\`scorerId\` = '${scorerId}'`);
|
|
1481
|
+
if (source) {
|
|
1482
|
+
totalQuery = totalQuery.where(`\`source\` = '${source}'`);
|
|
1483
|
+
}
|
|
1484
|
+
const allRecords = await totalQuery.toArray();
|
|
1485
|
+
const total = allRecords.length;
|
|
1486
|
+
return {
|
|
1487
|
+
pagination: {
|
|
1488
|
+
page,
|
|
1489
|
+
perPage,
|
|
1490
|
+
total,
|
|
1491
|
+
hasMore: offset + scores.length < total
|
|
1492
|
+
},
|
|
1493
|
+
scores
|
|
1091
1494
|
};
|
|
1092
|
-
await table.mergeInsert(["workflow_name", "run_id"]).whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([record]);
|
|
1093
1495
|
} catch (error) {
|
|
1094
1496
|
throw new MastraError(
|
|
1095
1497
|
{
|
|
1096
|
-
id: "
|
|
1498
|
+
id: "LANCE_STORAGE_GET_SCORES_BY_SCORER_ID_FAILED",
|
|
1499
|
+
text: "Failed to get scores by scorerId in LanceStorage",
|
|
1097
1500
|
domain: ErrorDomain.STORAGE,
|
|
1098
1501
|
category: ErrorCategory.THIRD_PARTY,
|
|
1099
|
-
details: {
|
|
1502
|
+
details: { error: error?.message }
|
|
1100
1503
|
},
|
|
1101
1504
|
error
|
|
1102
1505
|
);
|
|
1103
1506
|
}
|
|
1104
1507
|
}
|
|
1105
|
-
async
|
|
1106
|
-
|
|
1107
|
-
|
|
1508
|
+
async getScoresByRunId({
|
|
1509
|
+
runId,
|
|
1510
|
+
pagination
|
|
1108
1511
|
}) {
|
|
1109
1512
|
try {
|
|
1110
|
-
const table = await this.
|
|
1111
|
-
const
|
|
1513
|
+
const table = await this.client.openTable(TABLE_SCORERS);
|
|
1514
|
+
const { page = 0, perPage = 10 } = pagination || {};
|
|
1515
|
+
const offset = page * perPage;
|
|
1516
|
+
const query = table.query().where(`\`runId\` = '${runId}'`).limit(perPage);
|
|
1517
|
+
if (offset > 0) query.offset(offset);
|
|
1112
1518
|
const records = await query.toArray();
|
|
1113
|
-
|
|
1519
|
+
const schema = await getTableSchema({ tableName: TABLE_SCORERS, client: this.client });
|
|
1520
|
+
const scores = processResultWithTypeConversion(records, schema);
|
|
1521
|
+
const allRecords = await table.query().where(`\`runId\` = '${runId}'`).toArray();
|
|
1522
|
+
const total = allRecords.length;
|
|
1523
|
+
return {
|
|
1524
|
+
pagination: {
|
|
1525
|
+
page,
|
|
1526
|
+
perPage,
|
|
1527
|
+
total,
|
|
1528
|
+
hasMore: offset + scores.length < total
|
|
1529
|
+
},
|
|
1530
|
+
scores
|
|
1531
|
+
};
|
|
1114
1532
|
} catch (error) {
|
|
1115
1533
|
throw new MastraError(
|
|
1116
1534
|
{
|
|
1117
|
-
id: "
|
|
1535
|
+
id: "LANCE_STORAGE_GET_SCORES_BY_RUN_ID_FAILED",
|
|
1536
|
+
text: "Failed to get scores by runId in LanceStorage",
|
|
1118
1537
|
domain: ErrorDomain.STORAGE,
|
|
1119
1538
|
category: ErrorCategory.THIRD_PARTY,
|
|
1120
|
-
details: {
|
|
1539
|
+
details: { error: error?.message }
|
|
1121
1540
|
},
|
|
1122
1541
|
error
|
|
1123
1542
|
);
|
|
1124
1543
|
}
|
|
1125
1544
|
}
|
|
1126
|
-
async
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
{
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1545
|
+
async getScoresByEntityId({
|
|
1546
|
+
entityId,
|
|
1547
|
+
entityType,
|
|
1548
|
+
pagination
|
|
1549
|
+
}) {
|
|
1550
|
+
try {
|
|
1551
|
+
const table = await this.client.openTable(TABLE_SCORERS);
|
|
1552
|
+
const { page = 0, perPage = 10 } = pagination || {};
|
|
1553
|
+
const offset = page * perPage;
|
|
1554
|
+
const query = table.query().where(`\`entityId\` = '${entityId}' AND \`entityType\` = '${entityType}'`).limit(perPage);
|
|
1555
|
+
if (offset > 0) query.offset(offset);
|
|
1556
|
+
const records = await query.toArray();
|
|
1557
|
+
const schema = await getTableSchema({ tableName: TABLE_SCORERS, client: this.client });
|
|
1558
|
+
const scores = processResultWithTypeConversion(records, schema);
|
|
1559
|
+
const allRecords = await table.query().where(`\`entityId\` = '${entityId}' AND \`entityType\` = '${entityType}'`).toArray();
|
|
1560
|
+
const total = allRecords.length;
|
|
1561
|
+
return {
|
|
1562
|
+
pagination: {
|
|
1563
|
+
page,
|
|
1564
|
+
perPage,
|
|
1565
|
+
total,
|
|
1566
|
+
hasMore: offset + scores.length < total
|
|
1567
|
+
},
|
|
1568
|
+
scores
|
|
1569
|
+
};
|
|
1570
|
+
} catch (error) {
|
|
1571
|
+
throw new MastraError(
|
|
1572
|
+
{
|
|
1573
|
+
id: "LANCE_STORAGE_GET_SCORES_BY_ENTITY_ID_FAILED",
|
|
1574
|
+
text: "Failed to get scores by entityId and entityType in LanceStorage",
|
|
1575
|
+
domain: ErrorDomain.STORAGE,
|
|
1576
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1577
|
+
details: { error: error?.message }
|
|
1578
|
+
},
|
|
1579
|
+
error
|
|
1580
|
+
);
|
|
1581
|
+
}
|
|
1145
1582
|
}
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
);
|
|
1583
|
+
};
|
|
1584
|
+
var StoreTracesLance = class extends TracesStorage {
|
|
1585
|
+
client;
|
|
1586
|
+
operations;
|
|
1587
|
+
constructor({ client, operations }) {
|
|
1588
|
+
super();
|
|
1589
|
+
this.client = client;
|
|
1590
|
+
this.operations = operations;
|
|
1155
1591
|
}
|
|
1156
|
-
async
|
|
1157
|
-
|
|
1158
|
-
|
|
1592
|
+
async saveTrace({ trace }) {
|
|
1593
|
+
try {
|
|
1594
|
+
const table = await this.client.openTable(TABLE_TRACES);
|
|
1595
|
+
const record = {
|
|
1596
|
+
...trace,
|
|
1597
|
+
attributes: JSON.stringify(trace.attributes),
|
|
1598
|
+
status: JSON.stringify(trace.status),
|
|
1599
|
+
events: JSON.stringify(trace.events),
|
|
1600
|
+
links: JSON.stringify(trace.links),
|
|
1601
|
+
other: JSON.stringify(trace.other)
|
|
1602
|
+
};
|
|
1603
|
+
await table.add([record], { mode: "append" });
|
|
1604
|
+
return trace;
|
|
1605
|
+
} catch (error) {
|
|
1606
|
+
throw new MastraError(
|
|
1607
|
+
{
|
|
1608
|
+
id: "LANCE_STORE_SAVE_TRACE_FAILED",
|
|
1609
|
+
domain: ErrorDomain.STORAGE,
|
|
1610
|
+
category: ErrorCategory.THIRD_PARTY
|
|
1611
|
+
},
|
|
1612
|
+
error
|
|
1613
|
+
);
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
async getTraceById({ traceId }) {
|
|
1617
|
+
try {
|
|
1618
|
+
const table = await this.client.openTable(TABLE_TRACES);
|
|
1619
|
+
const query = table.query().where(`id = '${traceId}'`);
|
|
1620
|
+
const records = await query.toArray();
|
|
1621
|
+
return records[0];
|
|
1622
|
+
} catch (error) {
|
|
1623
|
+
throw new MastraError(
|
|
1624
|
+
{
|
|
1625
|
+
id: "LANCE_STORE_GET_TRACE_BY_ID_FAILED",
|
|
1626
|
+
domain: ErrorDomain.STORAGE,
|
|
1627
|
+
category: ErrorCategory.THIRD_PARTY
|
|
1628
|
+
},
|
|
1629
|
+
error
|
|
1630
|
+
);
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
async getTraces({
|
|
1634
|
+
name,
|
|
1635
|
+
scope,
|
|
1636
|
+
page = 1,
|
|
1637
|
+
perPage = 10,
|
|
1638
|
+
attributes
|
|
1639
|
+
}) {
|
|
1640
|
+
try {
|
|
1641
|
+
const table = await this.client.openTable(TABLE_TRACES);
|
|
1642
|
+
const query = table.query();
|
|
1643
|
+
if (name) {
|
|
1644
|
+
query.where(`name = '${name}'`);
|
|
1645
|
+
}
|
|
1646
|
+
if (scope) {
|
|
1647
|
+
query.where(`scope = '${scope}'`);
|
|
1648
|
+
}
|
|
1649
|
+
if (attributes) {
|
|
1650
|
+
query.where(`attributes = '${JSON.stringify(attributes)}'`);
|
|
1651
|
+
}
|
|
1652
|
+
const offset = (page - 1) * perPage;
|
|
1653
|
+
query.limit(perPage);
|
|
1654
|
+
if (offset > 0) {
|
|
1655
|
+
query.offset(offset);
|
|
1656
|
+
}
|
|
1657
|
+
const records = await query.toArray();
|
|
1658
|
+
return records.map((record) => {
|
|
1659
|
+
const processed = {
|
|
1660
|
+
...record,
|
|
1661
|
+
attributes: record.attributes ? JSON.parse(record.attributes) : {},
|
|
1662
|
+
status: record.status ? JSON.parse(record.status) : {},
|
|
1663
|
+
events: record.events ? JSON.parse(record.events) : [],
|
|
1664
|
+
links: record.links ? JSON.parse(record.links) : [],
|
|
1665
|
+
other: record.other ? JSON.parse(record.other) : {},
|
|
1666
|
+
startTime: new Date(record.startTime),
|
|
1667
|
+
endTime: new Date(record.endTime),
|
|
1668
|
+
createdAt: new Date(record.createdAt)
|
|
1669
|
+
};
|
|
1670
|
+
if (processed.parentSpanId === null || processed.parentSpanId === void 0) {
|
|
1671
|
+
processed.parentSpanId = "";
|
|
1672
|
+
} else {
|
|
1673
|
+
processed.parentSpanId = String(processed.parentSpanId);
|
|
1674
|
+
}
|
|
1675
|
+
return processed;
|
|
1676
|
+
});
|
|
1677
|
+
} catch (error) {
|
|
1678
|
+
throw new MastraError(
|
|
1679
|
+
{
|
|
1680
|
+
id: "LANCE_STORE_GET_TRACES_FAILED",
|
|
1681
|
+
domain: ErrorDomain.STORAGE,
|
|
1682
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1683
|
+
details: { name: name ?? "", scope: scope ?? "" }
|
|
1684
|
+
},
|
|
1685
|
+
error
|
|
1686
|
+
);
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
async getTracesPaginated(args) {
|
|
1690
|
+
try {
|
|
1691
|
+
const table = await this.client.openTable(TABLE_TRACES);
|
|
1692
|
+
const query = table.query();
|
|
1693
|
+
const conditions = [];
|
|
1694
|
+
if (args.name) {
|
|
1695
|
+
conditions.push(`name = '${args.name}'`);
|
|
1696
|
+
}
|
|
1697
|
+
if (args.scope) {
|
|
1698
|
+
conditions.push(`scope = '${args.scope}'`);
|
|
1699
|
+
}
|
|
1700
|
+
if (args.attributes) {
|
|
1701
|
+
const attributesStr = JSON.stringify(args.attributes);
|
|
1702
|
+
conditions.push(`attributes LIKE '%${attributesStr.replace(/"/g, '\\"')}%'`);
|
|
1703
|
+
}
|
|
1704
|
+
if (args.dateRange?.start) {
|
|
1705
|
+
conditions.push(`\`createdAt\` >= ${args.dateRange.start.getTime()}`);
|
|
1706
|
+
}
|
|
1707
|
+
if (args.dateRange?.end) {
|
|
1708
|
+
conditions.push(`\`createdAt\` <= ${args.dateRange.end.getTime()}`);
|
|
1709
|
+
}
|
|
1710
|
+
if (conditions.length > 0) {
|
|
1711
|
+
const whereClause = conditions.join(" AND ");
|
|
1712
|
+
query.where(whereClause);
|
|
1713
|
+
}
|
|
1714
|
+
let total = 0;
|
|
1715
|
+
if (conditions.length > 0) {
|
|
1716
|
+
const countQuery = table.query().where(conditions.join(" AND "));
|
|
1717
|
+
const allRecords = await countQuery.toArray();
|
|
1718
|
+
total = allRecords.length;
|
|
1719
|
+
} else {
|
|
1720
|
+
total = await table.countRows();
|
|
1721
|
+
}
|
|
1722
|
+
const page = args.page || 0;
|
|
1723
|
+
const perPage = args.perPage || 10;
|
|
1724
|
+
const offset = page * perPage;
|
|
1725
|
+
query.limit(perPage);
|
|
1726
|
+
if (offset > 0) {
|
|
1727
|
+
query.offset(offset);
|
|
1728
|
+
}
|
|
1729
|
+
const records = await query.toArray();
|
|
1730
|
+
const traces = records.map((record) => {
|
|
1731
|
+
const processed = {
|
|
1732
|
+
...record,
|
|
1733
|
+
attributes: record.attributes ? JSON.parse(record.attributes) : {},
|
|
1734
|
+
status: record.status ? JSON.parse(record.status) : {},
|
|
1735
|
+
events: record.events ? JSON.parse(record.events) : [],
|
|
1736
|
+
links: record.links ? JSON.parse(record.links) : [],
|
|
1737
|
+
other: record.other ? JSON.parse(record.other) : {},
|
|
1738
|
+
startTime: new Date(record.startTime),
|
|
1739
|
+
endTime: new Date(record.endTime),
|
|
1740
|
+
createdAt: new Date(record.createdAt)
|
|
1741
|
+
};
|
|
1742
|
+
if (processed.parentSpanId === null || processed.parentSpanId === void 0) {
|
|
1743
|
+
processed.parentSpanId = "";
|
|
1744
|
+
} else {
|
|
1745
|
+
processed.parentSpanId = String(processed.parentSpanId);
|
|
1746
|
+
}
|
|
1747
|
+
return processed;
|
|
1748
|
+
});
|
|
1749
|
+
return {
|
|
1750
|
+
traces,
|
|
1751
|
+
total,
|
|
1752
|
+
page,
|
|
1753
|
+
perPage,
|
|
1754
|
+
hasMore: total > (page + 1) * perPage
|
|
1755
|
+
};
|
|
1756
|
+
} catch (error) {
|
|
1757
|
+
throw new MastraError(
|
|
1758
|
+
{
|
|
1759
|
+
id: "LANCE_STORE_GET_TRACES_PAGINATED_FAILED",
|
|
1760
|
+
domain: ErrorDomain.STORAGE,
|
|
1761
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1762
|
+
details: { name: args.name ?? "", scope: args.scope ?? "" }
|
|
1763
|
+
},
|
|
1764
|
+
error
|
|
1765
|
+
);
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
async batchTraceInsert({ records }) {
|
|
1769
|
+
this.logger.debug("Batch inserting traces", { count: records.length });
|
|
1770
|
+
await this.operations.batchInsert({
|
|
1771
|
+
tableName: TABLE_TRACES,
|
|
1772
|
+
records
|
|
1773
|
+
});
|
|
1774
|
+
}
|
|
1775
|
+
};
|
|
1776
|
+
function parseWorkflowRun(row) {
|
|
1777
|
+
let parsedSnapshot = row.snapshot;
|
|
1778
|
+
if (typeof parsedSnapshot === "string") {
|
|
1779
|
+
try {
|
|
1780
|
+
parsedSnapshot = JSON.parse(row.snapshot);
|
|
1781
|
+
} catch (e) {
|
|
1782
|
+
console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1785
|
+
return {
|
|
1786
|
+
workflowName: row.workflow_name,
|
|
1787
|
+
runId: row.run_id,
|
|
1788
|
+
snapshot: parsedSnapshot,
|
|
1789
|
+
createdAt: ensureDate(row.createdAt),
|
|
1790
|
+
updatedAt: ensureDate(row.updatedAt),
|
|
1791
|
+
resourceId: row.resourceId
|
|
1792
|
+
};
|
|
1793
|
+
}
|
|
1794
|
+
var StoreWorkflowsLance = class extends WorkflowsStorage {
|
|
1795
|
+
client;
|
|
1796
|
+
constructor({ client }) {
|
|
1797
|
+
super();
|
|
1798
|
+
this.client = client;
|
|
1799
|
+
}
|
|
1800
|
+
updateWorkflowResults({
|
|
1801
|
+
// workflowName,
|
|
1802
|
+
// runId,
|
|
1803
|
+
// stepId,
|
|
1804
|
+
// result,
|
|
1805
|
+
// runtimeContext,
|
|
1806
|
+
}) {
|
|
1807
|
+
throw new Error("Method not implemented.");
|
|
1808
|
+
}
|
|
1809
|
+
updateWorkflowState({
|
|
1810
|
+
// workflowName,
|
|
1811
|
+
// runId,
|
|
1812
|
+
// opts,
|
|
1813
|
+
}) {
|
|
1814
|
+
throw new Error("Method not implemented.");
|
|
1815
|
+
}
|
|
1816
|
+
async persistWorkflowSnapshot({
|
|
1817
|
+
workflowName,
|
|
1818
|
+
runId,
|
|
1819
|
+
snapshot
|
|
1820
|
+
}) {
|
|
1821
|
+
try {
|
|
1822
|
+
const table = await this.client.openTable(TABLE_WORKFLOW_SNAPSHOT);
|
|
1823
|
+
const query = table.query().where(`workflow_name = '${workflowName}' AND run_id = '${runId}'`);
|
|
1824
|
+
const records = await query.toArray();
|
|
1825
|
+
let createdAt;
|
|
1826
|
+
const now = Date.now();
|
|
1827
|
+
if (records.length > 0) {
|
|
1828
|
+
createdAt = records[0].createdAt ?? now;
|
|
1829
|
+
} else {
|
|
1830
|
+
createdAt = now;
|
|
1831
|
+
}
|
|
1832
|
+
const record = {
|
|
1833
|
+
workflow_name: workflowName,
|
|
1834
|
+
run_id: runId,
|
|
1835
|
+
snapshot: JSON.stringify(snapshot),
|
|
1836
|
+
createdAt,
|
|
1837
|
+
updatedAt: now
|
|
1838
|
+
};
|
|
1839
|
+
await table.mergeInsert(["workflow_name", "run_id"]).whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([record]);
|
|
1840
|
+
} catch (error) {
|
|
1841
|
+
throw new MastraError(
|
|
1842
|
+
{
|
|
1843
|
+
id: "LANCE_STORE_PERSIST_WORKFLOW_SNAPSHOT_FAILED",
|
|
1844
|
+
domain: ErrorDomain.STORAGE,
|
|
1845
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1846
|
+
details: { workflowName, runId }
|
|
1847
|
+
},
|
|
1848
|
+
error
|
|
1849
|
+
);
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
async loadWorkflowSnapshot({
|
|
1853
|
+
workflowName,
|
|
1854
|
+
runId
|
|
1855
|
+
}) {
|
|
1856
|
+
try {
|
|
1857
|
+
const table = await this.client.openTable(TABLE_WORKFLOW_SNAPSHOT);
|
|
1858
|
+
const query = table.query().where(`workflow_name = '${workflowName}' AND run_id = '${runId}'`);
|
|
1859
|
+
const records = await query.toArray();
|
|
1860
|
+
return records.length > 0 ? JSON.parse(records[0].snapshot) : null;
|
|
1861
|
+
} catch (error) {
|
|
1862
|
+
throw new MastraError(
|
|
1863
|
+
{
|
|
1864
|
+
id: "LANCE_STORE_LOAD_WORKFLOW_SNAPSHOT_FAILED",
|
|
1865
|
+
domain: ErrorDomain.STORAGE,
|
|
1866
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1867
|
+
details: { workflowName, runId }
|
|
1868
|
+
},
|
|
1869
|
+
error
|
|
1870
|
+
);
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1873
|
+
async getWorkflowRunById(args) {
|
|
1874
|
+
try {
|
|
1875
|
+
const table = await this.client.openTable(TABLE_WORKFLOW_SNAPSHOT);
|
|
1876
|
+
let whereClause = `run_id = '${args.runId}'`;
|
|
1877
|
+
if (args.workflowName) {
|
|
1878
|
+
whereClause += ` AND workflow_name = '${args.workflowName}'`;
|
|
1879
|
+
}
|
|
1880
|
+
const query = table.query().where(whereClause);
|
|
1881
|
+
const records = await query.toArray();
|
|
1882
|
+
if (records.length === 0) return null;
|
|
1883
|
+
const record = records[0];
|
|
1884
|
+
return parseWorkflowRun(record);
|
|
1885
|
+
} catch (error) {
|
|
1886
|
+
throw new MastraError(
|
|
1887
|
+
{
|
|
1888
|
+
id: "LANCE_STORE_GET_WORKFLOW_RUN_BY_ID_FAILED",
|
|
1889
|
+
domain: ErrorDomain.STORAGE,
|
|
1890
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1891
|
+
details: { runId: args.runId, workflowName: args.workflowName ?? "" }
|
|
1892
|
+
},
|
|
1893
|
+
error
|
|
1894
|
+
);
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
async getWorkflowRuns(args) {
|
|
1898
|
+
try {
|
|
1899
|
+
const table = await this.client.openTable(TABLE_WORKFLOW_SNAPSHOT);
|
|
1900
|
+
let query = table.query();
|
|
1901
|
+
const conditions = [];
|
|
1902
|
+
if (args?.workflowName) {
|
|
1903
|
+
conditions.push(`workflow_name = '${args.workflowName.replace(/'/g, "''")}'`);
|
|
1904
|
+
}
|
|
1905
|
+
if (args?.resourceId) {
|
|
1906
|
+
conditions.push(`\`resourceId\` = '${args.resourceId}'`);
|
|
1907
|
+
}
|
|
1908
|
+
if (args?.fromDate instanceof Date) {
|
|
1909
|
+
conditions.push(`\`createdAt\` >= ${args.fromDate.getTime()}`);
|
|
1910
|
+
}
|
|
1911
|
+
if (args?.toDate instanceof Date) {
|
|
1912
|
+
conditions.push(`\`createdAt\` <= ${args.toDate.getTime()}`);
|
|
1913
|
+
}
|
|
1914
|
+
let total = 0;
|
|
1915
|
+
if (conditions.length > 0) {
|
|
1916
|
+
query = query.where(conditions.join(" AND "));
|
|
1917
|
+
total = await table.countRows(conditions.join(" AND "));
|
|
1918
|
+
} else {
|
|
1919
|
+
total = await table.countRows();
|
|
1920
|
+
}
|
|
1921
|
+
if (args?.limit) {
|
|
1922
|
+
query.limit(args.limit);
|
|
1923
|
+
}
|
|
1924
|
+
if (args?.offset) {
|
|
1925
|
+
query.offset(args.offset);
|
|
1926
|
+
}
|
|
1927
|
+
const records = await query.toArray();
|
|
1928
|
+
return {
|
|
1929
|
+
runs: records.map((record) => parseWorkflowRun(record)),
|
|
1930
|
+
total: total || records.length
|
|
1931
|
+
};
|
|
1932
|
+
} catch (error) {
|
|
1933
|
+
throw new MastraError(
|
|
1934
|
+
{
|
|
1935
|
+
id: "LANCE_STORE_GET_WORKFLOW_RUNS_FAILED",
|
|
1936
|
+
domain: ErrorDomain.STORAGE,
|
|
1937
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1938
|
+
details: { namespace: args?.namespace ?? "", workflowName: args?.workflowName ?? "" }
|
|
1939
|
+
},
|
|
1940
|
+
error
|
|
1941
|
+
);
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
};
|
|
1945
|
+
|
|
1946
|
+
// src/storage/index.ts
|
|
1947
|
+
var LanceStorage = class _LanceStorage extends MastraStorage {
|
|
1948
|
+
stores;
|
|
1949
|
+
lanceClient;
|
|
1950
|
+
/**
|
|
1951
|
+
* Creates a new instance of LanceStorage
|
|
1952
|
+
* @param uri The URI to connect to LanceDB
|
|
1953
|
+
* @param options connection options
|
|
1954
|
+
*
|
|
1955
|
+
* Usage:
|
|
1956
|
+
*
|
|
1957
|
+
* Connect to a local database
|
|
1958
|
+
* ```ts
|
|
1959
|
+
* const store = await LanceStorage.create('/path/to/db');
|
|
1960
|
+
* ```
|
|
1961
|
+
*
|
|
1962
|
+
* Connect to a LanceDB cloud database
|
|
1963
|
+
* ```ts
|
|
1964
|
+
* const store = await LanceStorage.create('db://host:port');
|
|
1965
|
+
* ```
|
|
1966
|
+
*
|
|
1967
|
+
* Connect to a cloud database
|
|
1968
|
+
* ```ts
|
|
1969
|
+
* const store = await LanceStorage.create('s3://bucket/db', { storageOptions: { timeout: '60s' } });
|
|
1970
|
+
* ```
|
|
1971
|
+
*/
|
|
1972
|
+
static async create(name, uri, options) {
|
|
1973
|
+
const instance = new _LanceStorage(name);
|
|
1974
|
+
try {
|
|
1975
|
+
instance.lanceClient = await connect(uri, options);
|
|
1976
|
+
const operations = new StoreOperationsLance({ client: instance.lanceClient });
|
|
1977
|
+
instance.stores = {
|
|
1978
|
+
operations: new StoreOperationsLance({ client: instance.lanceClient }),
|
|
1979
|
+
workflows: new StoreWorkflowsLance({ client: instance.lanceClient }),
|
|
1980
|
+
traces: new StoreTracesLance({ client: instance.lanceClient, operations }),
|
|
1981
|
+
scores: new StoreScoresLance({ client: instance.lanceClient }),
|
|
1982
|
+
memory: new StoreMemoryLance({ client: instance.lanceClient, operations }),
|
|
1983
|
+
legacyEvals: new StoreLegacyEvalsLance({ client: instance.lanceClient })
|
|
1984
|
+
};
|
|
1985
|
+
return instance;
|
|
1986
|
+
} catch (e) {
|
|
1987
|
+
throw new MastraError(
|
|
1988
|
+
{
|
|
1989
|
+
id: "STORAGE_LANCE_STORAGE_CONNECT_FAILED",
|
|
1990
|
+
domain: ErrorDomain.STORAGE,
|
|
1991
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1992
|
+
text: `Failed to connect to LanceDB: ${e.message || e}`,
|
|
1993
|
+
details: { uri, optionsProvided: !!options }
|
|
1994
|
+
},
|
|
1995
|
+
e
|
|
1996
|
+
);
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
/**
|
|
2000
|
+
* @internal
|
|
2001
|
+
* Private constructor to enforce using the create factory method
|
|
2002
|
+
*/
|
|
2003
|
+
constructor(name) {
|
|
2004
|
+
super({ name });
|
|
2005
|
+
const operations = new StoreOperationsLance({ client: this.lanceClient });
|
|
2006
|
+
this.stores = {
|
|
2007
|
+
operations: new StoreOperationsLance({ client: this.lanceClient }),
|
|
2008
|
+
workflows: new StoreWorkflowsLance({ client: this.lanceClient }),
|
|
2009
|
+
traces: new StoreTracesLance({ client: this.lanceClient, operations }),
|
|
2010
|
+
scores: new StoreScoresLance({ client: this.lanceClient }),
|
|
2011
|
+
legacyEvals: new StoreLegacyEvalsLance({ client: this.lanceClient }),
|
|
2012
|
+
memory: new StoreMemoryLance({ client: this.lanceClient, operations })
|
|
2013
|
+
};
|
|
2014
|
+
}
|
|
2015
|
+
async createTable({
|
|
2016
|
+
tableName,
|
|
2017
|
+
schema
|
|
2018
|
+
}) {
|
|
2019
|
+
return this.stores.operations.createTable({ tableName, schema });
|
|
2020
|
+
}
|
|
2021
|
+
async dropTable({ tableName }) {
|
|
2022
|
+
return this.stores.operations.dropTable({ tableName });
|
|
2023
|
+
}
|
|
2024
|
+
async alterTable({
|
|
2025
|
+
tableName,
|
|
2026
|
+
schema,
|
|
2027
|
+
ifNotExists
|
|
2028
|
+
}) {
|
|
2029
|
+
return this.stores.operations.alterTable({ tableName, schema, ifNotExists });
|
|
2030
|
+
}
|
|
2031
|
+
async clearTable({ tableName }) {
|
|
2032
|
+
return this.stores.operations.clearTable({ tableName });
|
|
2033
|
+
}
|
|
2034
|
+
async insert({ tableName, record }) {
|
|
2035
|
+
return this.stores.operations.insert({ tableName, record });
|
|
2036
|
+
}
|
|
2037
|
+
async batchInsert({ tableName, records }) {
|
|
2038
|
+
return this.stores.operations.batchInsert({ tableName, records });
|
|
2039
|
+
}
|
|
2040
|
+
async load({ tableName, keys }) {
|
|
2041
|
+
return this.stores.operations.load({ tableName, keys });
|
|
2042
|
+
}
|
|
2043
|
+
async getThreadById({ threadId }) {
|
|
2044
|
+
return this.stores.memory.getThreadById({ threadId });
|
|
2045
|
+
}
|
|
2046
|
+
async getThreadsByResourceId({ resourceId }) {
|
|
2047
|
+
return this.stores.memory.getThreadsByResourceId({ resourceId });
|
|
2048
|
+
}
|
|
2049
|
+
/**
|
|
2050
|
+
* Saves a thread to the database. This function doesn't overwrite existing threads.
|
|
2051
|
+
* @param thread - The thread to save
|
|
2052
|
+
* @returns The saved thread
|
|
2053
|
+
*/
|
|
2054
|
+
async saveThread({ thread }) {
|
|
2055
|
+
return this.stores.memory.saveThread({ thread });
|
|
2056
|
+
}
|
|
2057
|
+
async updateThread({
|
|
2058
|
+
id,
|
|
2059
|
+
title,
|
|
2060
|
+
metadata
|
|
2061
|
+
}) {
|
|
2062
|
+
return this.stores.memory.updateThread({ id, title, metadata });
|
|
2063
|
+
}
|
|
2064
|
+
async deleteThread({ threadId }) {
|
|
2065
|
+
return this.stores.memory.deleteThread({ threadId });
|
|
2066
|
+
}
|
|
2067
|
+
get supports() {
|
|
2068
|
+
return {
|
|
2069
|
+
selectByIncludeResourceScope: true,
|
|
2070
|
+
resourceWorkingMemory: true,
|
|
2071
|
+
hasColumn: true,
|
|
2072
|
+
createTable: true,
|
|
2073
|
+
deleteMessages: false
|
|
2074
|
+
};
|
|
2075
|
+
}
|
|
2076
|
+
async getResourceById({ resourceId }) {
|
|
2077
|
+
return this.stores.memory.getResourceById({ resourceId });
|
|
2078
|
+
}
|
|
2079
|
+
async saveResource({ resource }) {
|
|
2080
|
+
return this.stores.memory.saveResource({ resource });
|
|
2081
|
+
}
|
|
2082
|
+
async updateResource({
|
|
2083
|
+
resourceId,
|
|
2084
|
+
workingMemory,
|
|
2085
|
+
metadata
|
|
2086
|
+
}) {
|
|
2087
|
+
return this.stores.memory.updateResource({ resourceId, workingMemory, metadata });
|
|
2088
|
+
}
|
|
2089
|
+
/**
|
|
2090
|
+
* Processes messages to include context messages based on withPreviousMessages and withNextMessages
|
|
2091
|
+
* @param records - The sorted array of records to process
|
|
2092
|
+
* @param include - The array of include specifications with context parameters
|
|
2093
|
+
* @returns The processed array with context messages included
|
|
2094
|
+
*/
|
|
2095
|
+
processMessagesWithContext(records, include) {
|
|
2096
|
+
const messagesWithContext = include.filter((item) => item.withPreviousMessages || item.withNextMessages);
|
|
2097
|
+
if (messagesWithContext.length === 0) {
|
|
2098
|
+
return records;
|
|
2099
|
+
}
|
|
2100
|
+
const messageIndexMap = /* @__PURE__ */ new Map();
|
|
2101
|
+
records.forEach((message, index) => {
|
|
2102
|
+
messageIndexMap.set(message.id, index);
|
|
2103
|
+
});
|
|
2104
|
+
const additionalIndices = /* @__PURE__ */ new Set();
|
|
2105
|
+
for (const item of messagesWithContext) {
|
|
2106
|
+
const messageIndex = messageIndexMap.get(item.id);
|
|
2107
|
+
if (messageIndex !== void 0) {
|
|
2108
|
+
if (item.withPreviousMessages) {
|
|
2109
|
+
const startIdx = Math.max(0, messageIndex - item.withPreviousMessages);
|
|
2110
|
+
for (let i = startIdx; i < messageIndex; i++) {
|
|
2111
|
+
additionalIndices.add(i);
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2114
|
+
if (item.withNextMessages) {
|
|
2115
|
+
const endIdx = Math.min(records.length - 1, messageIndex + item.withNextMessages);
|
|
2116
|
+
for (let i = messageIndex + 1; i <= endIdx; i++) {
|
|
2117
|
+
additionalIndices.add(i);
|
|
2118
|
+
}
|
|
2119
|
+
}
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
if (additionalIndices.size === 0) {
|
|
2123
|
+
return records;
|
|
2124
|
+
}
|
|
2125
|
+
const originalMatchIds = new Set(include.map((item) => item.id));
|
|
2126
|
+
const allIndices = /* @__PURE__ */ new Set();
|
|
2127
|
+
records.forEach((record, index) => {
|
|
2128
|
+
if (originalMatchIds.has(record.id)) {
|
|
2129
|
+
allIndices.add(index);
|
|
2130
|
+
}
|
|
2131
|
+
});
|
|
2132
|
+
additionalIndices.forEach((index) => {
|
|
2133
|
+
allIndices.add(index);
|
|
2134
|
+
});
|
|
2135
|
+
return Array.from(allIndices).sort((a, b) => a - b).map((index) => records[index]);
|
|
2136
|
+
}
|
|
2137
|
+
async getMessages({
|
|
2138
|
+
threadId,
|
|
2139
|
+
resourceId,
|
|
2140
|
+
selectBy,
|
|
2141
|
+
format,
|
|
2142
|
+
threadConfig
|
|
2143
|
+
}) {
|
|
2144
|
+
return this.stores.memory.getMessages({ threadId, resourceId, selectBy, format, threadConfig });
|
|
2145
|
+
}
|
|
2146
|
+
async getMessagesById({
|
|
2147
|
+
messageIds,
|
|
2148
|
+
format
|
|
2149
|
+
}) {
|
|
2150
|
+
return this.stores.memory.getMessagesById({ messageIds, format });
|
|
2151
|
+
}
|
|
2152
|
+
async saveMessages(args) {
|
|
2153
|
+
return this.stores.memory.saveMessages(args);
|
|
2154
|
+
}
|
|
2155
|
+
async getThreadsByResourceIdPaginated(args) {
|
|
2156
|
+
return this.stores.memory.getThreadsByResourceIdPaginated(args);
|
|
2157
|
+
}
|
|
2158
|
+
async getMessagesPaginated(args) {
|
|
2159
|
+
return this.stores.memory.getMessagesPaginated(args);
|
|
2160
|
+
}
|
|
2161
|
+
async updateMessages(_args) {
|
|
2162
|
+
return this.stores.memory.updateMessages(_args);
|
|
2163
|
+
}
|
|
2164
|
+
async getTraceById(args) {
|
|
2165
|
+
return this.stores.traces.getTraceById(args);
|
|
2166
|
+
}
|
|
2167
|
+
async getTraces(args) {
|
|
2168
|
+
return this.stores.traces.getTraces(args);
|
|
2169
|
+
}
|
|
2170
|
+
async getTracesPaginated(args) {
|
|
2171
|
+
return this.stores.traces.getTracesPaginated(args);
|
|
2172
|
+
}
|
|
2173
|
+
async getEvalsByAgentName(agentName, type) {
|
|
2174
|
+
return this.stores.legacyEvals.getEvalsByAgentName(agentName, type);
|
|
2175
|
+
}
|
|
2176
|
+
async getEvals(options) {
|
|
2177
|
+
return this.stores.legacyEvals.getEvals(options);
|
|
2178
|
+
}
|
|
2179
|
+
async getWorkflowRuns(args) {
|
|
2180
|
+
return this.stores.workflows.getWorkflowRuns(args);
|
|
2181
|
+
}
|
|
2182
|
+
async getWorkflowRunById(args) {
|
|
2183
|
+
return this.stores.workflows.getWorkflowRunById(args);
|
|
2184
|
+
}
|
|
2185
|
+
async updateWorkflowResults({
|
|
2186
|
+
workflowName,
|
|
2187
|
+
runId,
|
|
2188
|
+
stepId,
|
|
2189
|
+
result,
|
|
2190
|
+
runtimeContext
|
|
2191
|
+
}) {
|
|
2192
|
+
return this.stores.workflows.updateWorkflowResults({ workflowName, runId, stepId, result, runtimeContext });
|
|
2193
|
+
}
|
|
2194
|
+
async updateWorkflowState({
|
|
2195
|
+
workflowName,
|
|
2196
|
+
runId,
|
|
2197
|
+
opts
|
|
2198
|
+
}) {
|
|
2199
|
+
return this.stores.workflows.updateWorkflowState({ workflowName, runId, opts });
|
|
2200
|
+
}
|
|
2201
|
+
async persistWorkflowSnapshot({
|
|
2202
|
+
workflowName,
|
|
2203
|
+
runId,
|
|
2204
|
+
snapshot
|
|
2205
|
+
}) {
|
|
2206
|
+
return this.stores.workflows.persistWorkflowSnapshot({ workflowName, runId, snapshot });
|
|
2207
|
+
}
|
|
2208
|
+
async loadWorkflowSnapshot({
|
|
2209
|
+
workflowName,
|
|
2210
|
+
runId
|
|
2211
|
+
}) {
|
|
2212
|
+
return this.stores.workflows.loadWorkflowSnapshot({ workflowName, runId });
|
|
2213
|
+
}
|
|
2214
|
+
async getScoreById({ id: _id }) {
|
|
2215
|
+
return this.stores.scores.getScoreById({ id: _id });
|
|
2216
|
+
}
|
|
2217
|
+
async getScoresByScorerId({
|
|
2218
|
+
scorerId,
|
|
2219
|
+
source,
|
|
2220
|
+
entityId,
|
|
2221
|
+
entityType,
|
|
2222
|
+
pagination
|
|
2223
|
+
}) {
|
|
2224
|
+
return this.stores.scores.getScoresByScorerId({ scorerId, source, pagination, entityId, entityType });
|
|
2225
|
+
}
|
|
2226
|
+
async saveScore(_score) {
|
|
2227
|
+
return this.stores.scores.saveScore(_score);
|
|
2228
|
+
}
|
|
2229
|
+
async getScoresByRunId({
|
|
2230
|
+
runId,
|
|
2231
|
+
pagination
|
|
2232
|
+
}) {
|
|
2233
|
+
return this.stores.scores.getScoresByRunId({ runId, pagination });
|
|
2234
|
+
}
|
|
2235
|
+
async getScoresByEntityId({
|
|
2236
|
+
entityId,
|
|
2237
|
+
entityType,
|
|
2238
|
+
pagination
|
|
2239
|
+
}) {
|
|
2240
|
+
return this.stores.scores.getScoresByEntityId({ entityId, entityType, pagination });
|
|
1159
2241
|
}
|
|
1160
2242
|
};
|
|
1161
2243
|
var LanceFilterTranslator = class extends BaseFilterTranslator {
|
|
@@ -2214,3 +3296,5 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
|
|
|
2214
3296
|
};
|
|
2215
3297
|
|
|
2216
3298
|
export { LanceStorage, LanceVectorStore };
|
|
3299
|
+
//# sourceMappingURL=index.js.map
|
|
3300
|
+
//# sourceMappingURL=index.js.map
|