@mastra/lance 0.0.0-working-memory-per-user-20250620163010 → 0.0.0-zod-v4-compat-part-2-20250822105954
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 +170 -3
- package/LICENSE.md +11 -42
- package/dist/index.cjs +2321 -695
- 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 +2286 -660
- 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 +94 -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 +39 -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 +38 -0
- package/dist/storage/domains/workflows/index.d.ts.map +1 -0
- package/dist/storage/index.d.ts +233 -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 +9 -9
- package/src/storage/domains/legacy-evals/index.ts +156 -0
- package/src/storage/domains/memory/index.ts +947 -0
- package/src/storage/domains/operations/index.ts +489 -0
- package/src/storage/domains/scores/index.ts +221 -0
- package/src/storage/domains/traces/index.ts +212 -0
- package/src/storage/domains/utils.ts +158 -0
- package/src/storage/domains/workflows/index.ts +207 -0
- package/src/storage/index.test.ts +6 -1262
- package/src/storage/index.ts +168 -755
- package/src/vector/filter.test.ts +3 -3
- package/src/vector/filter.ts +24 -4
- package/src/vector/index.test.ts +3 -3
- package/src/vector/index.ts +320 -79
- package/tsconfig.build.json +9 -0
- package/tsconfig.json +1 -1
- package/tsup.config.ts +22 -0
- package/dist/_tsup-dts-rollup.d.cts +0 -395
- package/dist/_tsup-dts-rollup.d.ts +0 -395
- package/dist/index.d.cts +0 -2
package/src/storage/index.ts
CHANGED
|
@@ -1,34 +1,34 @@
|
|
|
1
1
|
import { connect } from '@lancedb/lancedb';
|
|
2
|
-
import type { Connection, ConnectionOptions
|
|
2
|
+
import type { Connection, ConnectionOptions } from '@lancedb/lancedb';
|
|
3
3
|
import type { MastraMessageContentV2 } from '@mastra/core/agent';
|
|
4
|
-
import {
|
|
4
|
+
import { ErrorCategory, ErrorDomain, MastraError } from '@mastra/core/error';
|
|
5
5
|
import type { MastraMessageV1, MastraMessageV2, StorageThreadType, TraceType } from '@mastra/core/memory';
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
TABLE_EVALS,
|
|
9
|
-
TABLE_MESSAGES,
|
|
10
|
-
TABLE_THREADS,
|
|
11
|
-
TABLE_TRACES,
|
|
12
|
-
TABLE_WORKFLOW_SNAPSHOT,
|
|
13
|
-
} from '@mastra/core/storage';
|
|
6
|
+
import type { ScoreRowData } from '@mastra/core/scores';
|
|
7
|
+
import { MastraStorage } from '@mastra/core/storage';
|
|
14
8
|
import type {
|
|
15
9
|
TABLE_NAMES,
|
|
16
10
|
PaginationInfo,
|
|
17
11
|
StorageGetMessagesArg,
|
|
18
|
-
StorageGetTracesArg,
|
|
19
12
|
StorageColumn,
|
|
20
13
|
EvalRow,
|
|
21
|
-
WorkflowRun,
|
|
22
14
|
WorkflowRuns,
|
|
15
|
+
StoragePagination,
|
|
16
|
+
StorageDomains,
|
|
17
|
+
StorageGetTracesPaginatedArg,
|
|
18
|
+
StorageResourceType,
|
|
23
19
|
} from '@mastra/core/storage';
|
|
24
20
|
import type { Trace } from '@mastra/core/telemetry';
|
|
25
21
|
import type { WorkflowRunState } from '@mastra/core/workflows';
|
|
26
|
-
import
|
|
27
|
-
import {
|
|
22
|
+
import { StoreLegacyEvalsLance } from './domains/legacy-evals';
|
|
23
|
+
import { StoreMemoryLance } from './domains/memory';
|
|
24
|
+
import { StoreOperationsLance } from './domains/operations';
|
|
25
|
+
import { StoreScoresLance } from './domains/scores';
|
|
26
|
+
import { StoreTracesLance } from './domains/traces';
|
|
27
|
+
import { StoreWorkflowsLance } from './domains/workflows';
|
|
28
28
|
|
|
29
29
|
export class LanceStorage extends MastraStorage {
|
|
30
|
+
stores: StorageDomains;
|
|
30
31
|
private lanceClient!: Connection;
|
|
31
|
-
|
|
32
32
|
/**
|
|
33
33
|
* Creates a new instance of LanceStorage
|
|
34
34
|
* @param uri The URI to connect to LanceDB
|
|
@@ -55,9 +55,27 @@ export class LanceStorage extends MastraStorage {
|
|
|
55
55
|
const instance = new LanceStorage(name);
|
|
56
56
|
try {
|
|
57
57
|
instance.lanceClient = await connect(uri, options);
|
|
58
|
+
const operations = new StoreOperationsLance({ client: instance.lanceClient });
|
|
59
|
+
instance.stores = {
|
|
60
|
+
operations: new StoreOperationsLance({ client: instance.lanceClient }),
|
|
61
|
+
workflows: new StoreWorkflowsLance({ client: instance.lanceClient }),
|
|
62
|
+
traces: new StoreTracesLance({ client: instance.lanceClient, operations }),
|
|
63
|
+
scores: new StoreScoresLance({ client: instance.lanceClient }),
|
|
64
|
+
memory: new StoreMemoryLance({ client: instance.lanceClient, operations }),
|
|
65
|
+
legacyEvals: new StoreLegacyEvalsLance({ client: instance.lanceClient }),
|
|
66
|
+
};
|
|
58
67
|
return instance;
|
|
59
68
|
} catch (e: any) {
|
|
60
|
-
throw new
|
|
69
|
+
throw new MastraError(
|
|
70
|
+
{
|
|
71
|
+
id: 'STORAGE_LANCE_STORAGE_CONNECT_FAILED',
|
|
72
|
+
domain: ErrorDomain.STORAGE,
|
|
73
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
74
|
+
text: `Failed to connect to LanceDB: ${e.message || e}`,
|
|
75
|
+
details: { uri, optionsProvided: !!options },
|
|
76
|
+
},
|
|
77
|
+
e,
|
|
78
|
+
);
|
|
61
79
|
}
|
|
62
80
|
}
|
|
63
81
|
|
|
@@ -67,6 +85,16 @@ export class LanceStorage extends MastraStorage {
|
|
|
67
85
|
*/
|
|
68
86
|
private constructor(name: string) {
|
|
69
87
|
super({ name });
|
|
88
|
+
const operations = new StoreOperationsLance({ client: this.lanceClient });
|
|
89
|
+
|
|
90
|
+
this.stores = {
|
|
91
|
+
operations: new StoreOperationsLance({ client: this.lanceClient }),
|
|
92
|
+
workflows: new StoreWorkflowsLance({ client: this.lanceClient }),
|
|
93
|
+
traces: new StoreTracesLance({ client: this.lanceClient, operations }),
|
|
94
|
+
scores: new StoreScoresLance({ client: this.lanceClient }),
|
|
95
|
+
legacyEvals: new StoreLegacyEvalsLance({ client: this.lanceClient }),
|
|
96
|
+
memory: new StoreMemoryLance({ client: this.lanceClient, operations }),
|
|
97
|
+
};
|
|
70
98
|
}
|
|
71
99
|
|
|
72
100
|
async createTable({
|
|
@@ -76,405 +104,47 @@ export class LanceStorage extends MastraStorage {
|
|
|
76
104
|
tableName: TABLE_NAMES;
|
|
77
105
|
schema: Record<string, StorageColumn>;
|
|
78
106
|
}): Promise<void> {
|
|
79
|
-
|
|
80
|
-
const arrowSchema = this.translateSchema(schema);
|
|
81
|
-
await this.lanceClient.createEmptyTable(tableName, arrowSchema);
|
|
82
|
-
} catch (error: any) {
|
|
83
|
-
throw new Error(`Failed to create table: ${error}`);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
private translateSchema(schema: Record<string, StorageColumn>): Schema {
|
|
88
|
-
const fields = Object.entries(schema).map(([name, column]) => {
|
|
89
|
-
// Convert string type to Arrow DataType
|
|
90
|
-
let arrowType: DataType;
|
|
91
|
-
switch (column.type.toLowerCase()) {
|
|
92
|
-
case 'text':
|
|
93
|
-
case 'uuid':
|
|
94
|
-
arrowType = new Utf8();
|
|
95
|
-
break;
|
|
96
|
-
case 'int':
|
|
97
|
-
case 'integer':
|
|
98
|
-
arrowType = new Int32();
|
|
99
|
-
break;
|
|
100
|
-
case 'bigint':
|
|
101
|
-
arrowType = new Float64();
|
|
102
|
-
break;
|
|
103
|
-
case 'float':
|
|
104
|
-
arrowType = new Float32();
|
|
105
|
-
break;
|
|
106
|
-
case 'jsonb':
|
|
107
|
-
case 'json':
|
|
108
|
-
arrowType = new Utf8();
|
|
109
|
-
break;
|
|
110
|
-
case 'binary':
|
|
111
|
-
arrowType = new Binary();
|
|
112
|
-
break;
|
|
113
|
-
case 'timestamp':
|
|
114
|
-
arrowType = new Float64();
|
|
115
|
-
break;
|
|
116
|
-
default:
|
|
117
|
-
// Default to string for unknown types
|
|
118
|
-
arrowType = new Utf8();
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Create a field with the appropriate arrow type
|
|
122
|
-
return new Field(name, arrowType, column.nullable ?? true);
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
return new Schema(fields);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Drop a table if it exists
|
|
130
|
-
* @param tableName Name of the table to drop
|
|
131
|
-
*/
|
|
132
|
-
async dropTable(tableName: TABLE_NAMES): Promise<void> {
|
|
133
|
-
try {
|
|
134
|
-
await this.lanceClient.dropTable(tableName);
|
|
135
|
-
} catch (error: any) {
|
|
136
|
-
// Don't throw if the table doesn't exist
|
|
137
|
-
if (error.toString().includes('was not found')) {
|
|
138
|
-
this.logger.debug(`Table '${tableName}' does not exist, skipping drop`);
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
throw new Error(`Failed to drop table: ${error}`);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Get table schema
|
|
147
|
-
* @param tableName Name of the table
|
|
148
|
-
* @returns Table schema
|
|
149
|
-
*/
|
|
150
|
-
async getTableSchema(tableName: TABLE_NAMES): Promise<SchemaLike> {
|
|
151
|
-
try {
|
|
152
|
-
const table = await this.lanceClient.openTable(tableName);
|
|
153
|
-
const rawSchema = await table.schema();
|
|
154
|
-
const fields = rawSchema.fields as FieldLike[];
|
|
155
|
-
|
|
156
|
-
// Convert schema to SchemaLike format
|
|
157
|
-
return {
|
|
158
|
-
fields,
|
|
159
|
-
metadata: new Map<string, string>(),
|
|
160
|
-
get names() {
|
|
161
|
-
return fields.map((field: FieldLike) => field.name);
|
|
162
|
-
},
|
|
163
|
-
};
|
|
164
|
-
} catch (error: any) {
|
|
165
|
-
throw new Error(`Failed to get table schema: ${error}`);
|
|
166
|
-
}
|
|
107
|
+
return this.stores.operations.createTable({ tableName, schema });
|
|
167
108
|
}
|
|
168
109
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
case 'text':
|
|
172
|
-
return "''";
|
|
173
|
-
case 'timestamp':
|
|
174
|
-
return 'CURRENT_TIMESTAMP';
|
|
175
|
-
case 'integer':
|
|
176
|
-
case 'bigint':
|
|
177
|
-
return '0';
|
|
178
|
-
case 'jsonb':
|
|
179
|
-
return "'{}'";
|
|
180
|
-
case 'uuid':
|
|
181
|
-
return "''";
|
|
182
|
-
default:
|
|
183
|
-
return super.getDefaultValue(type);
|
|
184
|
-
}
|
|
110
|
+
async dropTable({ tableName }: { tableName: TABLE_NAMES }): Promise<void> {
|
|
111
|
+
return this.stores.operations.dropTable({ tableName });
|
|
185
112
|
}
|
|
186
113
|
|
|
187
|
-
/**
|
|
188
|
-
* Alters table schema to add columns if they don't exist
|
|
189
|
-
* @param tableName Name of the table
|
|
190
|
-
* @param schema Schema of the table
|
|
191
|
-
* @param ifNotExists Array of column names to add if they don't exist
|
|
192
|
-
*/
|
|
193
114
|
async alterTable({
|
|
194
115
|
tableName,
|
|
195
116
|
schema,
|
|
196
117
|
ifNotExists,
|
|
197
118
|
}: {
|
|
198
|
-
tableName:
|
|
119
|
+
tableName: TABLE_NAMES;
|
|
199
120
|
schema: Record<string, StorageColumn>;
|
|
200
121
|
ifNotExists: string[];
|
|
201
122
|
}): Promise<void> {
|
|
202
|
-
|
|
203
|
-
const currentSchema = await table.schema();
|
|
204
|
-
const existingFields = new Set(currentSchema.fields.map((f: any) => f.name));
|
|
205
|
-
|
|
206
|
-
const typeMap: Record<string, string> = {
|
|
207
|
-
text: 'string',
|
|
208
|
-
integer: 'int',
|
|
209
|
-
bigint: 'bigint',
|
|
210
|
-
timestamp: 'timestamp',
|
|
211
|
-
jsonb: 'string',
|
|
212
|
-
uuid: 'string',
|
|
213
|
-
};
|
|
214
|
-
|
|
215
|
-
// Find columns to add
|
|
216
|
-
const columnsToAdd = ifNotExists
|
|
217
|
-
.filter(col => schema[col] && !existingFields.has(col))
|
|
218
|
-
.map(col => {
|
|
219
|
-
const colDef = schema[col];
|
|
220
|
-
return {
|
|
221
|
-
name: col,
|
|
222
|
-
valueSql: colDef?.nullable
|
|
223
|
-
? `cast(NULL as ${typeMap[colDef.type ?? 'text']})`
|
|
224
|
-
: `cast(${this.getDefaultValue(colDef?.type ?? 'text')} as ${typeMap[colDef?.type ?? 'text']})`,
|
|
225
|
-
};
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
if (columnsToAdd.length > 0) {
|
|
229
|
-
await table.addColumns(columnsToAdd);
|
|
230
|
-
this.logger?.info?.(`Added columns [${columnsToAdd.map(c => c.name).join(', ')}] to table ${tableName}`);
|
|
231
|
-
}
|
|
123
|
+
return this.stores.operations.alterTable({ tableName, schema, ifNotExists });
|
|
232
124
|
}
|
|
233
125
|
|
|
234
126
|
async clearTable({ tableName }: { tableName: TABLE_NAMES }): Promise<void> {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
// delete function always takes a predicate as an argument, so we use '1=1' to delete all records because it is always true.
|
|
238
|
-
await table.delete('1=1');
|
|
127
|
+
return this.stores.operations.clearTable({ tableName });
|
|
239
128
|
}
|
|
240
129
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
* @param tableName The name of the table to insert into.
|
|
244
|
-
* @param record The record to insert.
|
|
245
|
-
*/
|
|
246
|
-
async insert({ tableName, record }: { tableName: string; record: Record<string, any> }): Promise<void> {
|
|
247
|
-
try {
|
|
248
|
-
const table = await this.lanceClient.openTable(tableName);
|
|
249
|
-
|
|
250
|
-
const processedRecord = { ...record };
|
|
251
|
-
|
|
252
|
-
for (const key in processedRecord) {
|
|
253
|
-
if (
|
|
254
|
-
processedRecord[key] !== null &&
|
|
255
|
-
typeof processedRecord[key] === 'object' &&
|
|
256
|
-
!(processedRecord[key] instanceof Date)
|
|
257
|
-
) {
|
|
258
|
-
this.logger.debug('Converting object to JSON string: ', processedRecord[key]);
|
|
259
|
-
processedRecord[key] = JSON.stringify(processedRecord[key]);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
await table.add([processedRecord], { mode: 'overwrite' });
|
|
264
|
-
} catch (error: any) {
|
|
265
|
-
throw new Error(`Failed to insert record: ${error}`);
|
|
266
|
-
}
|
|
130
|
+
async insert({ tableName, record }: { tableName: TABLE_NAMES; record: Record<string, any> }): Promise<void> {
|
|
131
|
+
return this.stores.operations.insert({ tableName, record });
|
|
267
132
|
}
|
|
268
133
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
* @param tableName The name of the table to insert into.
|
|
272
|
-
* @param records The records to insert.
|
|
273
|
-
*/
|
|
274
|
-
async batchInsert({ tableName, records }: { tableName: string; records: Record<string, any>[] }): Promise<void> {
|
|
275
|
-
try {
|
|
276
|
-
const table = await this.lanceClient.openTable(tableName);
|
|
277
|
-
|
|
278
|
-
const processedRecords = records.map(record => {
|
|
279
|
-
const processedRecord = { ...record };
|
|
280
|
-
|
|
281
|
-
// Convert values based on schema type
|
|
282
|
-
for (const key in processedRecord) {
|
|
283
|
-
// Skip null/undefined values
|
|
284
|
-
if (processedRecord[key] == null) continue;
|
|
285
|
-
|
|
286
|
-
if (
|
|
287
|
-
processedRecord[key] !== null &&
|
|
288
|
-
typeof processedRecord[key] === 'object' &&
|
|
289
|
-
!(processedRecord[key] instanceof Date)
|
|
290
|
-
) {
|
|
291
|
-
processedRecord[key] = JSON.stringify(processedRecord[key]);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
return processedRecord;
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
await table.add(processedRecords, { mode: 'overwrite' });
|
|
299
|
-
} catch (error: any) {
|
|
300
|
-
throw new Error(`Failed to batch insert records: ${error}`);
|
|
301
|
-
}
|
|
134
|
+
async batchInsert({ tableName, records }: { tableName: TABLE_NAMES; records: Record<string, any>[] }): Promise<void> {
|
|
135
|
+
return this.stores.operations.batchInsert({ tableName, records });
|
|
302
136
|
}
|
|
303
137
|
|
|
304
|
-
/**
|
|
305
|
-
* Load a record from the database by its key(s)
|
|
306
|
-
* @param tableName The name of the table to query
|
|
307
|
-
* @param keys Record of key-value pairs to use for lookup
|
|
308
|
-
* @throws Error if invalid types are provided for keys
|
|
309
|
-
* @returns The loaded record with proper type conversions, or null if not found
|
|
310
|
-
*/
|
|
311
138
|
async load({ tableName, keys }: { tableName: TABLE_NAMES; keys: Record<string, any> }): Promise<any> {
|
|
312
|
-
|
|
313
|
-
const table = await this.lanceClient.openTable(tableName);
|
|
314
|
-
const tableSchema = await this.getTableSchema(tableName);
|
|
315
|
-
const query = table.query();
|
|
316
|
-
|
|
317
|
-
// Build filter condition with 'and' between all conditions
|
|
318
|
-
if (Object.keys(keys).length > 0) {
|
|
319
|
-
// Validate key types against schema
|
|
320
|
-
this.validateKeyTypes(keys, tableSchema);
|
|
321
|
-
|
|
322
|
-
const filterConditions = Object.entries(keys)
|
|
323
|
-
.map(([key, value]) => {
|
|
324
|
-
// Check if key is in camelCase and wrap it in backticks if it is
|
|
325
|
-
const isCamelCase = /^[a-z][a-zA-Z]*$/.test(key) && /[A-Z]/.test(key);
|
|
326
|
-
const quotedKey = isCamelCase ? `\`${key}\`` : key;
|
|
327
|
-
|
|
328
|
-
// Handle different types appropriately
|
|
329
|
-
if (typeof value === 'string') {
|
|
330
|
-
return `${quotedKey} = '${value}'`;
|
|
331
|
-
} else if (value === null) {
|
|
332
|
-
return `${quotedKey} IS NULL`;
|
|
333
|
-
} else {
|
|
334
|
-
// For numbers, booleans, etc.
|
|
335
|
-
return `${quotedKey} = ${value}`;
|
|
336
|
-
}
|
|
337
|
-
})
|
|
338
|
-
.join(' AND ');
|
|
339
|
-
|
|
340
|
-
this.logger.debug('where clause generated: ' + filterConditions);
|
|
341
|
-
query.where(filterConditions);
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
const result = await query.limit(1).toArray();
|
|
345
|
-
|
|
346
|
-
if (result.length === 0) {
|
|
347
|
-
this.logger.debug('No record found');
|
|
348
|
-
return null;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
// Process the result with type conversions
|
|
352
|
-
return this.processResultWithTypeConversion(result[0], tableSchema);
|
|
353
|
-
} catch (error: any) {
|
|
354
|
-
throw new Error(`Failed to load record: ${error}`);
|
|
355
|
-
}
|
|
139
|
+
return this.stores.operations.load({ tableName, keys });
|
|
356
140
|
}
|
|
357
141
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
* @param keys The keys to validate
|
|
361
|
-
* @param tableSchema The table schema to validate against
|
|
362
|
-
* @throws Error if a key has an incompatible type
|
|
363
|
-
*/
|
|
364
|
-
private validateKeyTypes(keys: Record<string, any>, tableSchema: SchemaLike): void {
|
|
365
|
-
// Create a map of field names to their expected types
|
|
366
|
-
const fieldTypes = new Map(
|
|
367
|
-
tableSchema.fields.map((field: any) => [field.name, field.type?.toString().toLowerCase()]),
|
|
368
|
-
);
|
|
369
|
-
|
|
370
|
-
for (const [key, value] of Object.entries(keys)) {
|
|
371
|
-
const fieldType = fieldTypes.get(key);
|
|
372
|
-
|
|
373
|
-
if (!fieldType) {
|
|
374
|
-
throw new Error(`Field '${key}' does not exist in table schema`);
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
// Type validation
|
|
378
|
-
if (value !== null) {
|
|
379
|
-
if ((fieldType.includes('int') || fieldType.includes('bigint')) && typeof value !== 'number') {
|
|
380
|
-
throw new Error(`Expected numeric value for field '${key}', got ${typeof value}`);
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
if (fieldType.includes('utf8') && typeof value !== 'string') {
|
|
384
|
-
throw new Error(`Expected string value for field '${key}', got ${typeof value}`);
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
if (fieldType.includes('timestamp') && !(value instanceof Date) && typeof value !== 'string') {
|
|
388
|
-
throw new Error(`Expected Date or string value for field '${key}', got ${typeof value}`);
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
/**
|
|
395
|
-
* Process a database result with appropriate type conversions based on the table schema
|
|
396
|
-
* @param rawResult The raw result object from the database
|
|
397
|
-
* @param tableSchema The schema of the table containing type information
|
|
398
|
-
* @returns Processed result with correct data types
|
|
399
|
-
*/
|
|
400
|
-
private processResultWithTypeConversion(
|
|
401
|
-
rawResult: Record<string, any> | Record<string, any>[],
|
|
402
|
-
tableSchema: SchemaLike,
|
|
403
|
-
): Record<string, any> | Record<string, any>[] {
|
|
404
|
-
// Build a map of field names to their schema types
|
|
405
|
-
const fieldTypeMap = new Map();
|
|
406
|
-
tableSchema.fields.forEach((field: any) => {
|
|
407
|
-
const fieldName = field.name;
|
|
408
|
-
const fieldTypeStr = field.type.toString().toLowerCase();
|
|
409
|
-
fieldTypeMap.set(fieldName, fieldTypeStr);
|
|
410
|
-
});
|
|
411
|
-
|
|
412
|
-
// Handle array case
|
|
413
|
-
if (Array.isArray(rawResult)) {
|
|
414
|
-
return rawResult.map(item => this.processResultWithTypeConversion(item, tableSchema));
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
// Handle single record case
|
|
418
|
-
const processedResult = { ...rawResult };
|
|
419
|
-
|
|
420
|
-
// Convert each field according to its schema type
|
|
421
|
-
for (const key in processedResult) {
|
|
422
|
-
const fieldTypeStr = fieldTypeMap.get(key);
|
|
423
|
-
if (!fieldTypeStr) continue;
|
|
424
|
-
|
|
425
|
-
// Skip conversion for ID fields - preserve their original format
|
|
426
|
-
// if (key === 'id') {
|
|
427
|
-
// continue;
|
|
428
|
-
// }
|
|
429
|
-
|
|
430
|
-
// Only try to convert string values
|
|
431
|
-
if (typeof processedResult[key] === 'string') {
|
|
432
|
-
// Numeric types
|
|
433
|
-
if (fieldTypeStr.includes('int32') || fieldTypeStr.includes('float32')) {
|
|
434
|
-
if (!isNaN(Number(processedResult[key]))) {
|
|
435
|
-
processedResult[key] = Number(processedResult[key]);
|
|
436
|
-
}
|
|
437
|
-
} else if (fieldTypeStr.includes('int64')) {
|
|
438
|
-
processedResult[key] = Number(processedResult[key]);
|
|
439
|
-
} else if (fieldTypeStr.includes('utf8')) {
|
|
440
|
-
try {
|
|
441
|
-
processedResult[key] = JSON.parse(processedResult[key]);
|
|
442
|
-
} catch (e) {
|
|
443
|
-
// If JSON parsing fails, keep the original string
|
|
444
|
-
this.logger.debug(`Failed to parse JSON for key ${key}: ${e}`);
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
} else if (typeof processedResult[key] === 'bigint') {
|
|
448
|
-
// Convert BigInt values to regular numbers for application layer
|
|
449
|
-
processedResult[key] = Number(processedResult[key]);
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
return processedResult;
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
getThreadById({ threadId }: { threadId: string }): Promise<StorageThreadType | null> {
|
|
457
|
-
try {
|
|
458
|
-
return this.load({ tableName: TABLE_THREADS, keys: { id: threadId } });
|
|
459
|
-
} catch (error: any) {
|
|
460
|
-
throw new Error(`Failed to get thread by ID: ${error}`);
|
|
461
|
-
}
|
|
142
|
+
async getThreadById({ threadId }: { threadId: string }): Promise<StorageThreadType | null> {
|
|
143
|
+
return this.stores.memory.getThreadById({ threadId });
|
|
462
144
|
}
|
|
463
145
|
|
|
464
146
|
async getThreadsByResourceId({ resourceId }: { resourceId: string }): Promise<StorageThreadType[]> {
|
|
465
|
-
|
|
466
|
-
const table = await this.lanceClient.openTable(TABLE_THREADS);
|
|
467
|
-
// fetches all threads with the given resourceId
|
|
468
|
-
const query = table.query().where(`\`resourceId\` = '${resourceId}'`);
|
|
469
|
-
|
|
470
|
-
const records = await query.toArray();
|
|
471
|
-
return this.processResultWithTypeConversion(
|
|
472
|
-
records,
|
|
473
|
-
await this.getTableSchema(TABLE_THREADS),
|
|
474
|
-
) as StorageThreadType[];
|
|
475
|
-
} catch (error: any) {
|
|
476
|
-
throw new Error(`Failed to get threads by resource ID: ${error}`);
|
|
477
|
-
}
|
|
147
|
+
return this.stores.memory.getThreadsByResourceId({ resourceId });
|
|
478
148
|
}
|
|
479
149
|
|
|
480
150
|
/**
|
|
@@ -483,15 +153,7 @@ export class LanceStorage extends MastraStorage {
|
|
|
483
153
|
* @returns The saved thread
|
|
484
154
|
*/
|
|
485
155
|
async saveThread({ thread }: { thread: StorageThreadType }): Promise<StorageThreadType> {
|
|
486
|
-
|
|
487
|
-
const record = { ...thread, metadata: JSON.stringify(thread.metadata) };
|
|
488
|
-
const table = await this.lanceClient.openTable(TABLE_THREADS);
|
|
489
|
-
await table.add([record], { mode: 'append' });
|
|
490
|
-
|
|
491
|
-
return thread;
|
|
492
|
-
} catch (error: any) {
|
|
493
|
-
throw new Error(`Failed to save thread: ${error}`);
|
|
494
|
-
}
|
|
156
|
+
return this.stores.memory.saveThread({ thread });
|
|
495
157
|
}
|
|
496
158
|
|
|
497
159
|
async updateThread({
|
|
@@ -503,30 +165,41 @@ export class LanceStorage extends MastraStorage {
|
|
|
503
165
|
title: string;
|
|
504
166
|
metadata: Record<string, unknown>;
|
|
505
167
|
}): Promise<StorageThreadType> {
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
const table = await this.lanceClient.openTable(TABLE_THREADS);
|
|
509
|
-
await table.add([record], { mode: 'overwrite' });
|
|
168
|
+
return this.stores.memory.updateThread({ id, title, metadata });
|
|
169
|
+
}
|
|
510
170
|
|
|
511
|
-
|
|
171
|
+
async deleteThread({ threadId }: { threadId: string }): Promise<void> {
|
|
172
|
+
return this.stores.memory.deleteThread({ threadId });
|
|
173
|
+
}
|
|
512
174
|
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
}
|
|
175
|
+
public get supports() {
|
|
176
|
+
return {
|
|
177
|
+
selectByIncludeResourceScope: true,
|
|
178
|
+
resourceWorkingMemory: true,
|
|
179
|
+
hasColumn: true,
|
|
180
|
+
createTable: true,
|
|
181
|
+
deleteMessages: false,
|
|
182
|
+
};
|
|
521
183
|
}
|
|
522
184
|
|
|
523
|
-
async
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
185
|
+
async getResourceById({ resourceId }: { resourceId: string }): Promise<StorageResourceType | null> {
|
|
186
|
+
return this.stores.memory.getResourceById({ resourceId });
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async saveResource({ resource }: { resource: StorageResourceType }): Promise<StorageResourceType> {
|
|
190
|
+
return this.stores.memory.saveResource({ resource });
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async updateResource({
|
|
194
|
+
resourceId,
|
|
195
|
+
workingMemory,
|
|
196
|
+
metadata,
|
|
197
|
+
}: {
|
|
198
|
+
resourceId: string;
|
|
199
|
+
workingMemory?: string;
|
|
200
|
+
metadata?: Record<string, unknown>;
|
|
201
|
+
}): Promise<StorageResourceType> {
|
|
202
|
+
return this.stores.memory.updateResource({ resourceId, workingMemory, metadata });
|
|
530
203
|
}
|
|
531
204
|
|
|
532
205
|
/**
|
|
@@ -556,6 +229,7 @@ export class LanceStorage extends MastraStorage {
|
|
|
556
229
|
|
|
557
230
|
for (const item of messagesWithContext) {
|
|
558
231
|
const messageIndex = messageIndexMap.get(item.id);
|
|
232
|
+
|
|
559
233
|
if (messageIndex !== undefined) {
|
|
560
234
|
// Add previous messages if requested
|
|
561
235
|
if (item.withPreviousMessages) {
|
|
@@ -614,69 +288,7 @@ export class LanceStorage extends MastraStorage {
|
|
|
614
288
|
format,
|
|
615
289
|
threadConfig,
|
|
616
290
|
}: StorageGetMessagesArg & { format?: 'v1' | 'v2' }): Promise<MastraMessageV1[] | MastraMessageV2[]> {
|
|
617
|
-
|
|
618
|
-
if (threadConfig) {
|
|
619
|
-
throw new Error('ThreadConfig is not supported by LanceDB storage');
|
|
620
|
-
}
|
|
621
|
-
const limit = this.resolveMessageLimit({ last: selectBy?.last, defaultLimit: Number.MAX_SAFE_INTEGER });
|
|
622
|
-
const table = await this.lanceClient.openTable(TABLE_MESSAGES);
|
|
623
|
-
let query = table.query().where(`\`threadId\` = '${threadId}'`);
|
|
624
|
-
|
|
625
|
-
// Apply selectBy filters if provided
|
|
626
|
-
if (selectBy) {
|
|
627
|
-
// Handle 'include' to fetch specific messages
|
|
628
|
-
if (selectBy.include && selectBy.include.length > 0) {
|
|
629
|
-
const includeIds = selectBy.include.map(item => item.id);
|
|
630
|
-
// Add additional query to include specific message IDs
|
|
631
|
-
// This will be combined with the threadId filter
|
|
632
|
-
const includeClause = includeIds.map(id => `\`id\` = '${id}'`).join(' OR ');
|
|
633
|
-
query = query.where(`(\`threadId\` = '${threadId}' OR (${includeClause}))`);
|
|
634
|
-
|
|
635
|
-
// Note: The surrounding messages (withPreviousMessages/withNextMessages) will be
|
|
636
|
-
// handled after we retrieve the results
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
// Fetch all records matching the query
|
|
641
|
-
let records = await query.toArray();
|
|
642
|
-
|
|
643
|
-
// Sort the records chronologically
|
|
644
|
-
records.sort((a, b) => {
|
|
645
|
-
const dateA = new Date(a.createdAt).getTime();
|
|
646
|
-
const dateB = new Date(b.createdAt).getTime();
|
|
647
|
-
return dateA - dateB; // Ascending order
|
|
648
|
-
});
|
|
649
|
-
|
|
650
|
-
// Process the include.withPreviousMessages and include.withNextMessages if specified
|
|
651
|
-
if (selectBy?.include && selectBy.include.length > 0) {
|
|
652
|
-
records = this.processMessagesWithContext(records, selectBy.include);
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
// If we're fetching the last N messages, take only the last N after sorting
|
|
656
|
-
if (limit !== Number.MAX_SAFE_INTEGER) {
|
|
657
|
-
records = records.slice(-limit);
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
const messages = this.processResultWithTypeConversion(records, await this.getTableSchema(TABLE_MESSAGES));
|
|
661
|
-
const normalized = messages.map((msg: MastraMessageV2 | MastraMessageV1) => ({
|
|
662
|
-
...msg,
|
|
663
|
-
content:
|
|
664
|
-
typeof msg.content === 'string'
|
|
665
|
-
? (() => {
|
|
666
|
-
try {
|
|
667
|
-
return JSON.parse(msg.content);
|
|
668
|
-
} catch {
|
|
669
|
-
return msg.content;
|
|
670
|
-
}
|
|
671
|
-
})()
|
|
672
|
-
: msg.content,
|
|
673
|
-
}));
|
|
674
|
-
const list = new MessageList({ threadId, resourceId }).add(normalized, 'memory');
|
|
675
|
-
if (format === 'v2') return list.get.all.v2();
|
|
676
|
-
return list.get.all.v1();
|
|
677
|
-
} catch (error: any) {
|
|
678
|
-
throw new Error(`Failed to get messages: ${error}`);
|
|
679
|
-
}
|
|
291
|
+
return this.stores.memory.getMessages({ threadId, resourceId, selectBy, format, threadConfig });
|
|
680
292
|
}
|
|
681
293
|
|
|
682
294
|
async saveMessages(args: { messages: MastraMessageV1[]; format?: undefined | 'v1' }): Promise<MastraMessageV1[]>;
|
|
@@ -684,192 +296,65 @@ export class LanceStorage extends MastraStorage {
|
|
|
684
296
|
async saveMessages(
|
|
685
297
|
args: { messages: MastraMessageV1[]; format?: undefined | 'v1' } | { messages: MastraMessageV2[]; format: 'v2' },
|
|
686
298
|
): Promise<MastraMessageV2[] | MastraMessageV1[]> {
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
if (messages.length === 0) {
|
|
690
|
-
return [];
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
const threadId = messages[0]?.threadId;
|
|
694
|
-
|
|
695
|
-
if (!threadId) {
|
|
696
|
-
throw new Error('Thread ID is required');
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
const transformedMessages = messages.map((message: MastraMessageV2 | MastraMessageV1) => ({
|
|
700
|
-
...message,
|
|
701
|
-
content: JSON.stringify(message.content),
|
|
702
|
-
}));
|
|
299
|
+
return this.stores.memory.saveMessages(args);
|
|
300
|
+
}
|
|
703
301
|
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
throw new Error(`Failed to save messages: ${error}`);
|
|
711
|
-
}
|
|
302
|
+
async getThreadsByResourceIdPaginated(args: {
|
|
303
|
+
resourceId: string;
|
|
304
|
+
page: number;
|
|
305
|
+
perPage: number;
|
|
306
|
+
}): Promise<PaginationInfo & { threads: StorageThreadType[] }> {
|
|
307
|
+
return this.stores.memory.getThreadsByResourceIdPaginated(args);
|
|
712
308
|
}
|
|
713
309
|
|
|
714
|
-
async
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
attributes: JSON.stringify(trace.attributes),
|
|
720
|
-
status: JSON.stringify(trace.status),
|
|
721
|
-
events: JSON.stringify(trace.events),
|
|
722
|
-
links: JSON.stringify(trace.links),
|
|
723
|
-
other: JSON.stringify(trace.other),
|
|
724
|
-
};
|
|
725
|
-
await table.add([record], { mode: 'append' });
|
|
310
|
+
async getMessagesPaginated(
|
|
311
|
+
args: StorageGetMessagesArg & { format?: 'v1' | 'v2' },
|
|
312
|
+
): Promise<PaginationInfo & { messages: MastraMessageV1[] | MastraMessageV2[] }> {
|
|
313
|
+
return this.stores.memory.getMessagesPaginated(args);
|
|
314
|
+
}
|
|
726
315
|
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
316
|
+
async updateMessages(_args: {
|
|
317
|
+
messages: Partial<Omit<MastraMessageV2, 'createdAt'>> &
|
|
318
|
+
{
|
|
319
|
+
id: string;
|
|
320
|
+
content?: { metadata?: MastraMessageContentV2['metadata']; content?: MastraMessageContentV2['content'] };
|
|
321
|
+
}[];
|
|
322
|
+
}): Promise<MastraMessageV2[]> {
|
|
323
|
+
return this.stores.memory.updateMessages(_args);
|
|
731
324
|
}
|
|
732
325
|
|
|
733
|
-
async getTraceById(
|
|
734
|
-
|
|
735
|
-
const table = await this.lanceClient.openTable(TABLE_TRACES);
|
|
736
|
-
const query = table.query().where(`id = '${traceId}'`);
|
|
737
|
-
const records = await query.toArray();
|
|
738
|
-
return this.processResultWithTypeConversion(records[0], await this.getTableSchema(TABLE_TRACES)) as TraceType;
|
|
739
|
-
} catch (error: any) {
|
|
740
|
-
throw new Error(`Failed to get trace by ID: ${error}`);
|
|
741
|
-
}
|
|
326
|
+
async getTraceById(args: { traceId: string }): Promise<TraceType> {
|
|
327
|
+
return (this.stores as any).traces.getTraceById(args);
|
|
742
328
|
}
|
|
743
329
|
|
|
744
|
-
async getTraces({
|
|
745
|
-
name,
|
|
746
|
-
scope,
|
|
747
|
-
page = 1,
|
|
748
|
-
perPage = 10,
|
|
749
|
-
attributes,
|
|
750
|
-
}: {
|
|
330
|
+
async getTraces(args: {
|
|
751
331
|
name?: string;
|
|
752
332
|
scope?: string;
|
|
753
333
|
page: number;
|
|
754
334
|
perPage: number;
|
|
755
335
|
attributes?: Record<string, string>;
|
|
756
|
-
}): Promise<
|
|
757
|
-
|
|
758
|
-
const table = await this.lanceClient.openTable(TABLE_TRACES);
|
|
759
|
-
const query = table.query();
|
|
760
|
-
|
|
761
|
-
if (name) {
|
|
762
|
-
query.where(`name = '${name}'`);
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
if (scope) {
|
|
766
|
-
query.where(`scope = '${scope}'`);
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
if (attributes) {
|
|
770
|
-
query.where(`attributes = '${JSON.stringify(attributes)}'`);
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
// Calculate offset based on page and perPage
|
|
774
|
-
const offset = (page - 1) * perPage;
|
|
775
|
-
|
|
776
|
-
// Apply limit for pagination
|
|
777
|
-
query.limit(perPage);
|
|
778
|
-
|
|
779
|
-
// Apply offset if greater than 0
|
|
780
|
-
if (offset > 0) {
|
|
781
|
-
query.offset(offset);
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
const records = await query.toArray();
|
|
785
|
-
return records.map(record => {
|
|
786
|
-
return {
|
|
787
|
-
...record,
|
|
788
|
-
attributes: JSON.parse(record.attributes),
|
|
789
|
-
status: JSON.parse(record.status),
|
|
790
|
-
events: JSON.parse(record.events),
|
|
791
|
-
links: JSON.parse(record.links),
|
|
792
|
-
other: JSON.parse(record.other),
|
|
793
|
-
startTime: new Date(record.startTime),
|
|
794
|
-
endTime: new Date(record.endTime),
|
|
795
|
-
createdAt: new Date(record.createdAt),
|
|
796
|
-
};
|
|
797
|
-
}) as TraceType[];
|
|
798
|
-
} catch (error: any) {
|
|
799
|
-
throw new Error(`Failed to get traces: ${error}`);
|
|
800
|
-
}
|
|
336
|
+
}): Promise<Trace[]> {
|
|
337
|
+
return (this.stores as any).traces.getTraces(args);
|
|
801
338
|
}
|
|
802
339
|
|
|
803
|
-
async
|
|
804
|
-
|
|
805
|
-
const table = await this.lanceClient.openTable(TABLE_EVALS);
|
|
806
|
-
const transformedEvals = evals.map(evalRecord => ({
|
|
807
|
-
input: evalRecord.input,
|
|
808
|
-
output: evalRecord.output,
|
|
809
|
-
agent_name: evalRecord.agentName,
|
|
810
|
-
metric_name: evalRecord.metricName,
|
|
811
|
-
result: JSON.stringify(evalRecord.result),
|
|
812
|
-
instructions: evalRecord.instructions,
|
|
813
|
-
test_info: JSON.stringify(evalRecord.testInfo),
|
|
814
|
-
global_run_id: evalRecord.globalRunId,
|
|
815
|
-
run_id: evalRecord.runId,
|
|
816
|
-
created_at: new Date(evalRecord.createdAt).getTime(),
|
|
817
|
-
}));
|
|
818
|
-
|
|
819
|
-
await table.add(transformedEvals, { mode: 'append' });
|
|
820
|
-
return evals;
|
|
821
|
-
} catch (error: any) {
|
|
822
|
-
throw new Error(`Failed to save evals: ${error}`);
|
|
823
|
-
}
|
|
340
|
+
async getTracesPaginated(args: StorageGetTracesPaginatedArg): Promise<PaginationInfo & { traces: Trace[] }> {
|
|
341
|
+
return (this.stores as any).traces.getTracesPaginated(args);
|
|
824
342
|
}
|
|
825
343
|
|
|
826
344
|
async getEvalsByAgentName(agentName: string, type?: 'test' | 'live'): Promise<EvalRow[]> {
|
|
827
|
-
|
|
828
|
-
if (type) {
|
|
829
|
-
this.logger.warn('Type is not implemented yet in LanceDB storage');
|
|
830
|
-
}
|
|
831
|
-
const table = await this.lanceClient.openTable(TABLE_EVALS);
|
|
832
|
-
const query = table.query().where(`agent_name = '${agentName}'`);
|
|
833
|
-
const records = await query.toArray();
|
|
834
|
-
return records.map(record => {
|
|
835
|
-
return {
|
|
836
|
-
id: record.id,
|
|
837
|
-
input: record.input,
|
|
838
|
-
output: record.output,
|
|
839
|
-
agentName: record.agent_name,
|
|
840
|
-
metricName: record.metric_name,
|
|
841
|
-
result: JSON.parse(record.result),
|
|
842
|
-
instructions: record.instructions,
|
|
843
|
-
testInfo: JSON.parse(record.test_info),
|
|
844
|
-
globalRunId: record.global_run_id,
|
|
845
|
-
runId: record.run_id,
|
|
846
|
-
createdAt: new Date(record.created_at).toString(),
|
|
847
|
-
};
|
|
848
|
-
}) as EvalRow[];
|
|
849
|
-
} catch (error: any) {
|
|
850
|
-
throw new Error(`Failed to get evals by agent name: ${error}`);
|
|
851
|
-
}
|
|
345
|
+
return this.stores.legacyEvals.getEvalsByAgentName(agentName, type);
|
|
852
346
|
}
|
|
853
347
|
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
return {
|
|
866
|
-
workflowName: row.workflow_name,
|
|
867
|
-
runId: row.run_id,
|
|
868
|
-
snapshot: parsedSnapshot,
|
|
869
|
-
createdAt: this.ensureDate(row.createdAt)!,
|
|
870
|
-
updatedAt: this.ensureDate(row.updatedAt)!,
|
|
871
|
-
resourceId: row.resourceId,
|
|
872
|
-
};
|
|
348
|
+
async getEvals(options: {
|
|
349
|
+
agentName?: string;
|
|
350
|
+
type?: 'test' | 'live';
|
|
351
|
+
page?: number;
|
|
352
|
+
perPage?: number;
|
|
353
|
+
fromDate?: Date;
|
|
354
|
+
toDate?: Date;
|
|
355
|
+
dateRange?: { start?: Date; end?: Date };
|
|
356
|
+
}): Promise<PaginationInfo & { evals: EvalRow[] }> {
|
|
357
|
+
return this.stores.legacyEvals.getEvals(options);
|
|
873
358
|
}
|
|
874
359
|
|
|
875
360
|
async getWorkflowRuns(args?: {
|
|
@@ -880,45 +365,9 @@ export class LanceStorage extends MastraStorage {
|
|
|
880
365
|
limit?: number;
|
|
881
366
|
offset?: number;
|
|
882
367
|
}): Promise<WorkflowRuns> {
|
|
883
|
-
|
|
884
|
-
const table = await this.lanceClient.openTable(TABLE_WORKFLOW_SNAPSHOT);
|
|
885
|
-
const query = table.query();
|
|
886
|
-
|
|
887
|
-
if (args?.workflowName) {
|
|
888
|
-
query.where(`workflow_name = '${args.workflowName}'`);
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
if (args?.fromDate) {
|
|
892
|
-
query.where(`\`createdAt\` >= ${args.fromDate.getTime()}`);
|
|
893
|
-
}
|
|
894
|
-
|
|
895
|
-
if (args?.toDate) {
|
|
896
|
-
query.where(`\`createdAt\` <= ${args.toDate.getTime()}`);
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
if (args?.limit) {
|
|
900
|
-
query.limit(args.limit);
|
|
901
|
-
}
|
|
902
|
-
|
|
903
|
-
if (args?.offset) {
|
|
904
|
-
query.offset(args.offset);
|
|
905
|
-
}
|
|
906
|
-
|
|
907
|
-
const records = await query.toArray();
|
|
908
|
-
return {
|
|
909
|
-
runs: records.map(record => this.parseWorkflowRun(record)),
|
|
910
|
-
total: records.length,
|
|
911
|
-
};
|
|
912
|
-
} catch (error: any) {
|
|
913
|
-
throw new Error(`Failed to get workflow runs: ${error}`);
|
|
914
|
-
}
|
|
368
|
+
return this.stores.workflows.getWorkflowRuns(args);
|
|
915
369
|
}
|
|
916
370
|
|
|
917
|
-
/**
|
|
918
|
-
* Retrieve a single workflow run by its runId.
|
|
919
|
-
* @param args The ID of the workflow run to retrieve
|
|
920
|
-
* @returns The workflow run object or null if not found
|
|
921
|
-
*/
|
|
922
371
|
async getWorkflowRunById(args: { runId: string; workflowName?: string }): Promise<{
|
|
923
372
|
workflowName: string;
|
|
924
373
|
runId: string;
|
|
@@ -926,20 +375,7 @@ export class LanceStorage extends MastraStorage {
|
|
|
926
375
|
createdAt: Date;
|
|
927
376
|
updatedAt: Date;
|
|
928
377
|
} | null> {
|
|
929
|
-
|
|
930
|
-
const table = await this.lanceClient.openTable(TABLE_WORKFLOW_SNAPSHOT);
|
|
931
|
-
let whereClause = `run_id = '${args.runId}'`;
|
|
932
|
-
if (args.workflowName) {
|
|
933
|
-
whereClause += ` AND workflow_name = '${args.workflowName}'`;
|
|
934
|
-
}
|
|
935
|
-
const query = table.query().where(whereClause);
|
|
936
|
-
const records = await query.toArray();
|
|
937
|
-
if (records.length === 0) return null;
|
|
938
|
-
const record = records[0];
|
|
939
|
-
return this.parseWorkflowRun(record);
|
|
940
|
-
} catch (error: any) {
|
|
941
|
-
throw new Error(`Failed to get workflow run by id: ${error}`);
|
|
942
|
-
}
|
|
378
|
+
return this.stores.workflows.getWorkflowRunById(args);
|
|
943
379
|
}
|
|
944
380
|
|
|
945
381
|
async persistWorkflowSnapshot({
|
|
@@ -951,36 +387,9 @@ export class LanceStorage extends MastraStorage {
|
|
|
951
387
|
runId: string;
|
|
952
388
|
snapshot: WorkflowRunState;
|
|
953
389
|
}): Promise<void> {
|
|
954
|
-
|
|
955
|
-
const table = await this.lanceClient.openTable(TABLE_WORKFLOW_SNAPSHOT);
|
|
956
|
-
|
|
957
|
-
// Try to find the existing record
|
|
958
|
-
const query = table.query().where(`workflow_name = '${workflowName}' AND run_id = '${runId}'`);
|
|
959
|
-
const records = await query.toArray();
|
|
960
|
-
let createdAt: number;
|
|
961
|
-
const now = Date.now();
|
|
962
|
-
let mode: 'append' | 'overwrite' = 'append';
|
|
963
|
-
|
|
964
|
-
if (records.length > 0) {
|
|
965
|
-
createdAt = records[0].createdAt ?? now;
|
|
966
|
-
mode = 'overwrite';
|
|
967
|
-
} else {
|
|
968
|
-
createdAt = now;
|
|
969
|
-
}
|
|
970
|
-
|
|
971
|
-
const record = {
|
|
972
|
-
workflow_name: workflowName,
|
|
973
|
-
run_id: runId,
|
|
974
|
-
snapshot: JSON.stringify(snapshot),
|
|
975
|
-
createdAt,
|
|
976
|
-
updatedAt: now,
|
|
977
|
-
};
|
|
978
|
-
|
|
979
|
-
await table.add([record], { mode });
|
|
980
|
-
} catch (error: any) {
|
|
981
|
-
throw new Error(`Failed to persist workflow snapshot: ${error}`);
|
|
982
|
-
}
|
|
390
|
+
return this.stores.workflows.persistWorkflowSnapshot({ workflowName, runId, snapshot });
|
|
983
391
|
}
|
|
392
|
+
|
|
984
393
|
async loadWorkflowSnapshot({
|
|
985
394
|
workflowName,
|
|
986
395
|
runId,
|
|
@@ -988,42 +397,46 @@ export class LanceStorage extends MastraStorage {
|
|
|
988
397
|
workflowName: string;
|
|
989
398
|
runId: string;
|
|
990
399
|
}): Promise<WorkflowRunState | null> {
|
|
991
|
-
|
|
992
|
-
const table = await this.lanceClient.openTable(TABLE_WORKFLOW_SNAPSHOT);
|
|
993
|
-
const query = table.query().where(`workflow_name = '${workflowName}' AND run_id = '${runId}'`);
|
|
994
|
-
const records = await query.toArray();
|
|
995
|
-
return records.length > 0 ? JSON.parse(records[0].snapshot) : null;
|
|
996
|
-
} catch (error: any) {
|
|
997
|
-
throw new Error(`Failed to load workflow snapshot: ${error}`);
|
|
998
|
-
}
|
|
400
|
+
return this.stores.workflows.loadWorkflowSnapshot({ workflowName, runId });
|
|
999
401
|
}
|
|
1000
402
|
|
|
1001
|
-
async
|
|
1002
|
-
|
|
403
|
+
async getScoreById({ id: _id }: { id: string }): Promise<ScoreRowData | null> {
|
|
404
|
+
return this.stores.scores.getScoreById({ id: _id });
|
|
1003
405
|
}
|
|
1004
406
|
|
|
1005
|
-
async
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
407
|
+
async getScoresByScorerId({
|
|
408
|
+
scorerId,
|
|
409
|
+
pagination,
|
|
410
|
+
}: {
|
|
411
|
+
scorerId: string;
|
|
412
|
+
pagination: StoragePagination;
|
|
413
|
+
}): Promise<{ pagination: PaginationInfo; scores: ScoreRowData[] }> {
|
|
414
|
+
return this.stores.scores.getScoresByScorerId({ scorerId, pagination });
|
|
1011
415
|
}
|
|
1012
416
|
|
|
1013
|
-
async
|
|
1014
|
-
|
|
1015
|
-
): Promise<PaginationInfo & { messages: MastraMessageV1[] | MastraMessageV2[] }> {
|
|
1016
|
-
throw new Error('Method not implemented.');
|
|
417
|
+
async saveScore(_score: ScoreRowData): Promise<{ score: ScoreRowData }> {
|
|
418
|
+
return this.stores.scores.saveScore(_score);
|
|
1017
419
|
}
|
|
1018
420
|
|
|
1019
|
-
async
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
}): Promise<
|
|
1026
|
-
this.
|
|
1027
|
-
|
|
421
|
+
async getScoresByRunId({
|
|
422
|
+
runId,
|
|
423
|
+
pagination,
|
|
424
|
+
}: {
|
|
425
|
+
runId: string;
|
|
426
|
+
pagination: StoragePagination;
|
|
427
|
+
}): Promise<{ pagination: PaginationInfo; scores: ScoreRowData[] }> {
|
|
428
|
+
return this.stores.scores.getScoresByRunId({ runId, pagination });
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
async getScoresByEntityId({
|
|
432
|
+
entityId,
|
|
433
|
+
entityType,
|
|
434
|
+
pagination,
|
|
435
|
+
}: {
|
|
436
|
+
pagination: StoragePagination;
|
|
437
|
+
entityId: string;
|
|
438
|
+
entityType: string;
|
|
439
|
+
}): Promise<{ pagination: PaginationInfo; scores: ScoreRowData[] }> {
|
|
440
|
+
return this.stores.scores.getScoresByEntityId({ entityId, entityType, pagination });
|
|
1028
441
|
}
|
|
1029
442
|
}
|