@mastra/clickhouse 0.0.0-vector-query-sources-20250516172905 → 0.0.0-vector-query-tool-provider-options-20250828222356

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.
Files changed (41) hide show
  1. package/CHANGELOG.md +603 -2
  2. package/LICENSE.md +12 -4
  3. package/dist/index.cjs +2366 -442
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.ts +3 -4
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +2349 -425
  8. package/dist/index.js.map +1 -0
  9. package/dist/storage/domains/legacy-evals/index.d.ts +21 -0
  10. package/dist/storage/domains/legacy-evals/index.d.ts.map +1 -0
  11. package/dist/storage/domains/memory/index.d.ts +87 -0
  12. package/dist/storage/domains/memory/index.d.ts.map +1 -0
  13. package/dist/storage/domains/operations/index.d.ts +42 -0
  14. package/dist/storage/domains/operations/index.d.ts.map +1 -0
  15. package/dist/storage/domains/scores/index.d.ts +46 -0
  16. package/dist/storage/domains/scores/index.d.ts.map +1 -0
  17. package/dist/storage/domains/traces/index.d.ts +21 -0
  18. package/dist/storage/domains/traces/index.d.ts.map +1 -0
  19. package/dist/storage/domains/utils.d.ts +28 -0
  20. package/dist/storage/domains/utils.d.ts.map +1 -0
  21. package/dist/storage/domains/workflows/index.d.ts +54 -0
  22. package/dist/storage/domains/workflows/index.d.ts.map +1 -0
  23. package/dist/storage/index.d.ts +235 -0
  24. package/dist/storage/index.d.ts.map +1 -0
  25. package/package.json +18 -13
  26. package/src/index.ts +1 -0
  27. package/src/storage/domains/legacy-evals/index.ts +246 -0
  28. package/src/storage/domains/memory/index.ts +1473 -0
  29. package/src/storage/domains/operations/index.ts +319 -0
  30. package/src/storage/domains/scores/index.ts +351 -0
  31. package/src/storage/domains/traces/index.ts +275 -0
  32. package/src/storage/domains/utils.ts +90 -0
  33. package/src/storage/domains/workflows/index.ts +323 -0
  34. package/src/storage/index.test.ts +14 -844
  35. package/src/storage/index.ts +290 -890
  36. package/tsconfig.build.json +9 -0
  37. package/tsconfig.json +1 -1
  38. package/tsup.config.ts +17 -0
  39. package/dist/_tsup-dts-rollup.d.cts +0 -138
  40. package/dist/_tsup-dts-rollup.d.ts +0 -138
  41. package/dist/index.d.cts +0 -4
@@ -1,33 +1,34 @@
1
1
  import type { ClickHouseClient } from '@clickhouse/client';
2
2
  import { createClient } from '@clickhouse/client';
3
- import type { MetricResult, TestInfo } from '@mastra/core/eval';
4
- import type { MessageType, StorageThreadType } from '@mastra/core/memory';
5
- import {
6
- MastraStorage,
7
- TABLE_EVALS,
8
- TABLE_MESSAGES,
9
- TABLE_SCHEMAS,
10
- TABLE_THREADS,
11
- TABLE_TRACES,
12
- TABLE_WORKFLOW_SNAPSHOT,
13
- } from '@mastra/core/storage';
3
+ import type { MastraMessageContentV2 } from '@mastra/core/agent';
4
+ import { MastraError, ErrorDomain, ErrorCategory } from '@mastra/core/error';
5
+ import type { MastraMessageV1, MastraMessageV2, StorageThreadType } from '@mastra/core/memory';
6
+ import type { ScoreRowData, ScoringSource } from '@mastra/core/scores';
7
+ import { MastraStorage } from '@mastra/core/storage';
14
8
  import type {
9
+ TABLE_SCHEMAS,
15
10
  EvalRow,
11
+ PaginationInfo,
16
12
  StorageColumn,
17
13
  StorageGetMessagesArg,
18
14
  TABLE_NAMES,
19
15
  WorkflowRun,
20
16
  WorkflowRuns,
17
+ StorageGetTracesArg,
18
+ StorageGetTracesPaginatedArg,
19
+ StoragePagination,
20
+ StorageDomains,
21
+ PaginationArgs,
22
+ StorageResourceType,
21
23
  } from '@mastra/core/storage';
22
- import type { WorkflowRunState } from '@mastra/core/workflows';
23
-
24
- function safelyParseJSON(jsonString: string): any {
25
- try {
26
- return JSON.parse(jsonString);
27
- } catch {
28
- return {};
29
- }
30
- }
24
+ import type { Trace } from '@mastra/core/telemetry';
25
+ import type { StepResult, WorkflowRunState } from '@mastra/core/workflows';
26
+ import { LegacyEvalsStorageClickhouse } from './domains/legacy-evals';
27
+ import { MemoryStorageClickhouse } from './domains/memory';
28
+ import { StoreOperationsClickhouse } from './domains/operations';
29
+ import { ScoresStorageClickhouse } from './domains/scores';
30
+ import { TracesStorageClickhouse } from './domains/traces';
31
+ import { WorkflowsStorageClickhouse } from './domains/workflows';
31
32
 
32
33
  type IntervalUnit =
33
34
  | 'NANOSECOND'
@@ -60,47 +61,15 @@ export type ClickhouseConfig = {
60
61
  };
61
62
  };
62
63
 
63
- export const TABLE_ENGINES: Record<TABLE_NAMES, string> = {
64
- [TABLE_MESSAGES]: `MergeTree()`,
65
- [TABLE_WORKFLOW_SNAPSHOT]: `ReplacingMergeTree()`,
66
- [TABLE_TRACES]: `MergeTree()`,
67
- [TABLE_THREADS]: `ReplacingMergeTree()`,
68
- [TABLE_EVALS]: `MergeTree()`,
69
- };
70
-
71
- export const COLUMN_TYPES: Record<StorageColumn['type'], string> = {
72
- text: 'String',
73
- timestamp: 'DateTime64(3)',
74
- uuid: 'String',
75
- jsonb: 'String',
76
- integer: 'Int64',
77
- bigint: 'Int64',
78
- };
79
-
80
- function transformRows<R>(rows: any[]): R[] {
81
- return rows.map((row: any) => transformRow<R>(row));
82
- }
83
-
84
- function transformRow<R>(row: any): R {
85
- if (!row) {
86
- return row;
87
- }
88
-
89
- if (row.createdAt) {
90
- row.createdAt = new Date(row.createdAt);
91
- }
92
- if (row.updatedAt) {
93
- row.updatedAt = new Date(row.updatedAt);
94
- }
95
- return row;
96
- }
97
-
98
64
  export class ClickhouseStore extends MastraStorage {
99
65
  protected db: ClickHouseClient;
100
66
  protected ttl: ClickhouseConfig['ttl'] = {};
101
67
 
68
+ stores: StorageDomains;
69
+
102
70
  constructor(config: ClickhouseConfig) {
103
71
  super({ name: 'ClickhouseStore' });
72
+
104
73
  this.db = createClient({
105
74
  url: config.url,
106
75
  username: config.username,
@@ -113,692 +82,161 @@ export class ClickhouseStore extends MastraStorage {
113
82
  },
114
83
  });
115
84
  this.ttl = config.ttl;
116
- }
117
85
 
118
- private transformEvalRow(row: Record<string, any>): EvalRow {
119
- row = transformRow(row);
120
- const resultValue = JSON.parse(row.result as string);
121
- const testInfoValue = row.test_info ? JSON.parse(row.test_info as string) : undefined;
122
-
123
- if (!resultValue || typeof resultValue !== 'object' || !('score' in resultValue)) {
124
- throw new Error(`Invalid MetricResult format: ${JSON.stringify(resultValue)}`);
125
- }
86
+ const operations = new StoreOperationsClickhouse({ client: this.db, ttl: this.ttl });
87
+ const workflows = new WorkflowsStorageClickhouse({ client: this.db, operations });
88
+ const scores = new ScoresStorageClickhouse({ client: this.db, operations });
89
+ const legacyEvals = new LegacyEvalsStorageClickhouse({ client: this.db, operations });
90
+ const traces = new TracesStorageClickhouse({ client: this.db, operations });
91
+ const memory = new MemoryStorageClickhouse({ client: this.db, operations });
92
+
93
+ this.stores = {
94
+ operations,
95
+ workflows,
96
+ scores,
97
+ legacyEvals,
98
+ traces,
99
+ memory,
100
+ };
101
+ }
126
102
 
103
+ get supports(): {
104
+ selectByIncludeResourceScope: boolean;
105
+ resourceWorkingMemory: boolean;
106
+ hasColumn: boolean;
107
+ createTable: boolean;
108
+ deleteMessages: boolean;
109
+ } {
127
110
  return {
128
- input: row.input as string,
129
- output: row.output as string,
130
- result: resultValue as MetricResult,
131
- agentName: row.agent_name as string,
132
- metricName: row.metric_name as string,
133
- instructions: row.instructions as string,
134
- testInfo: testInfoValue as TestInfo,
135
- globalRunId: row.global_run_id as string,
136
- runId: row.run_id as string,
137
- createdAt: row.created_at as string,
111
+ selectByIncludeResourceScope: true,
112
+ resourceWorkingMemory: true,
113
+ hasColumn: true,
114
+ createTable: true,
115
+ deleteMessages: false,
138
116
  };
139
117
  }
140
118
 
141
119
  async getEvalsByAgentName(agentName: string, type?: 'test' | 'live'): Promise<EvalRow[]> {
142
- try {
143
- const baseQuery = `SELECT *, toDateTime64(createdAt, 3) as createdAt FROM ${TABLE_EVALS} WHERE agent_name = {var_agent_name:String}`;
144
- const typeCondition =
145
- type === 'test'
146
- ? " AND test_info IS NOT NULL AND JSONExtractString(test_info, 'testPath') IS NOT NULL"
147
- : type === 'live'
148
- ? " AND (test_info IS NULL OR JSONExtractString(test_info, 'testPath') IS NULL)"
149
- : '';
150
-
151
- const result = await this.db.query({
152
- query: `${baseQuery}${typeCondition} ORDER BY createdAt DESC`,
153
- query_params: { var_agent_name: agentName },
154
- clickhouse_settings: {
155
- date_time_input_format: 'best_effort',
156
- date_time_output_format: 'iso',
157
- use_client_time_zone: 1,
158
- output_format_json_quote_64bit_integers: 0,
159
- },
160
- });
161
-
162
- if (!result) {
163
- return [];
164
- }
120
+ return this.stores.legacyEvals.getEvalsByAgentName(agentName, type);
121
+ }
165
122
 
166
- const rows = await result.json();
167
- return rows.data.map((row: any) => this.transformEvalRow(row));
168
- } catch (error) {
169
- // Handle case where table doesn't exist yet
170
- if (error instanceof Error && error.message.includes('no such table')) {
171
- return [];
172
- }
173
- this.logger.error('Failed to get evals for the specified agent: ' + (error as any)?.message);
174
- throw error;
175
- }
123
+ async getEvals(
124
+ options: { agentName?: string; type?: 'test' | 'live' } & PaginationArgs,
125
+ ): Promise<PaginationInfo & { evals: EvalRow[] }> {
126
+ return this.stores.legacyEvals.getEvals(options);
176
127
  }
177
128
 
178
129
  async batchInsert({ tableName, records }: { tableName: TABLE_NAMES; records: Record<string, any>[] }): Promise<void> {
179
- try {
180
- await this.db.insert({
181
- table: tableName,
182
- values: records.map(record => ({
183
- ...Object.fromEntries(
184
- Object.entries(record).map(([key, value]) => [
185
- key,
186
- TABLE_SCHEMAS[tableName as TABLE_NAMES]?.[key]?.type === 'timestamp'
187
- ? new Date(value).toISOString()
188
- : value,
189
- ]),
190
- ),
191
- })),
192
- format: 'JSONEachRow',
193
- clickhouse_settings: {
194
- // Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
195
- date_time_input_format: 'best_effort',
196
- use_client_time_zone: 1,
197
- output_format_json_quote_64bit_integers: 0,
198
- },
199
- });
200
- } catch (error) {
201
- console.error(`Error inserting into ${tableName}:`, error);
202
- throw error;
203
- }
130
+ await this.stores.operations.batchInsert({ tableName, records });
131
+ // await this.optimizeTable({ tableName });
204
132
  }
205
133
 
206
- async getTraces({
207
- name,
208
- scope,
209
- page,
210
- perPage,
211
- attributes,
212
- filters,
213
- fromDate,
214
- toDate,
215
- }: {
216
- name?: string;
217
- scope?: string;
218
- page: number;
219
- perPage: number;
220
- attributes?: Record<string, string>;
221
- filters?: Record<string, any>;
222
- fromDate?: Date;
223
- toDate?: Date;
224
- }): Promise<any[]> {
225
- const limit = perPage;
226
- const offset = page * perPage;
227
-
228
- const args: Record<string, any> = {};
229
-
230
- const conditions: string[] = [];
231
- if (name) {
232
- conditions.push(`name LIKE CONCAT({var_name:String}, '%')`);
233
- args.var_name = name;
234
- }
235
- if (scope) {
236
- conditions.push(`scope = {var_scope:String}`);
237
- args.var_scope = scope;
238
- }
239
- if (attributes) {
240
- Object.entries(attributes).forEach(([key, value]) => {
241
- conditions.push(`JSONExtractString(attributes, '${key}') = {var_attr_${key}:String}`);
242
- args[`var_attr_${key}`] = value;
134
+ async optimizeTable({ tableName }: { tableName: TABLE_NAMES }): Promise<void> {
135
+ try {
136
+ await this.db.command({
137
+ query: `OPTIMIZE TABLE ${tableName} FINAL`,
243
138
  });
139
+ } catch (error: any) {
140
+ throw new MastraError(
141
+ {
142
+ id: 'CLICKHOUSE_STORAGE_OPTIMIZE_TABLE_FAILED',
143
+ domain: ErrorDomain.STORAGE,
144
+ category: ErrorCategory.THIRD_PARTY,
145
+ details: { tableName },
146
+ },
147
+ error,
148
+ );
244
149
  }
150
+ }
245
151
 
246
- if (filters) {
247
- Object.entries(filters).forEach(([key, value]) => {
248
- conditions.push(
249
- `${key} = {var_col_${key}:${COLUMN_TYPES[TABLE_SCHEMAS.mastra_traces?.[key]?.type ?? 'text']}}`,
250
- );
251
- args[`var_col_${key}`] = value;
152
+ async materializeTtl({ tableName }: { tableName: TABLE_NAMES }): Promise<void> {
153
+ try {
154
+ await this.db.command({
155
+ query: `ALTER TABLE ${tableName} MATERIALIZE TTL;`,
252
156
  });
157
+ } catch (error: any) {
158
+ throw new MastraError(
159
+ {
160
+ id: 'CLICKHOUSE_STORAGE_MATERIALIZE_TTL_FAILED',
161
+ domain: ErrorDomain.STORAGE,
162
+ category: ErrorCategory.THIRD_PARTY,
163
+ details: { tableName },
164
+ },
165
+ error,
166
+ );
253
167
  }
254
-
255
- if (fromDate) {
256
- conditions.push(`createdAt >= {var_from_date:DateTime64(3)}`);
257
- args.var_from_date = fromDate.getTime() / 1000; // Convert to Unix timestamp
258
- }
259
-
260
- if (toDate) {
261
- conditions.push(`createdAt <= {var_to_date:DateTime64(3)}`);
262
- args.var_to_date = toDate.getTime() / 1000; // Convert to Unix timestamp
263
- }
264
-
265
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
266
-
267
- const result = await this.db.query({
268
- query: `SELECT *, toDateTime64(createdAt, 3) as createdAt FROM ${TABLE_TRACES} ${whereClause} ORDER BY "createdAt" DESC LIMIT ${limit} OFFSET ${offset}`,
269
- query_params: args,
270
- clickhouse_settings: {
271
- // Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
272
- date_time_input_format: 'best_effort',
273
- date_time_output_format: 'iso',
274
- use_client_time_zone: 1,
275
- output_format_json_quote_64bit_integers: 0,
276
- },
277
- });
278
-
279
- if (!result) {
280
- return [];
281
- }
282
-
283
- const resp = await result.json();
284
- const rows: any[] = resp.data;
285
- return rows.map(row => ({
286
- id: row.id,
287
- parentSpanId: row.parentSpanId,
288
- traceId: row.traceId,
289
- name: row.name,
290
- scope: row.scope,
291
- kind: row.kind,
292
- status: safelyParseJSON(row.status as string),
293
- events: safelyParseJSON(row.events as string),
294
- links: safelyParseJSON(row.links as string),
295
- attributes: safelyParseJSON(row.attributes as string),
296
- startTime: row.startTime,
297
- endTime: row.endTime,
298
- other: safelyParseJSON(row.other as string),
299
- createdAt: row.createdAt,
300
- }));
301
168
  }
302
169
 
303
- async optimizeTable({ tableName }: { tableName: TABLE_NAMES }): Promise<void> {
304
- await this.db.command({
305
- query: `OPTIMIZE TABLE ${tableName} FINAL`,
306
- });
170
+ async createTable({
171
+ tableName,
172
+ schema,
173
+ }: {
174
+ tableName: TABLE_NAMES;
175
+ schema: Record<string, StorageColumn>;
176
+ }): Promise<void> {
177
+ return this.stores.operations.createTable({ tableName, schema });
307
178
  }
308
179
 
309
- async materializeTtl({ tableName }: { tableName: TABLE_NAMES }): Promise<void> {
310
- await this.db.command({
311
- query: `ALTER TABLE ${tableName} MATERIALIZE TTL;`,
312
- });
180
+ async dropTable({ tableName }: { tableName: TABLE_NAMES }): Promise<void> {
181
+ return this.stores.operations.dropTable({ tableName });
313
182
  }
314
183
 
315
- async createTable({
184
+ async alterTable({
316
185
  tableName,
317
186
  schema,
187
+ ifNotExists,
318
188
  }: {
319
189
  tableName: TABLE_NAMES;
320
190
  schema: Record<string, StorageColumn>;
191
+ ifNotExists: string[];
321
192
  }): Promise<void> {
322
- try {
323
- const columns = Object.entries(schema)
324
- .map(([name, def]) => {
325
- const constraints = [];
326
- if (!def.nullable) constraints.push('NOT NULL');
327
- const columnTtl = this.ttl?.[tableName]?.columns?.[name];
328
- return `"${name}" ${COLUMN_TYPES[def.type]} ${constraints.join(' ')} ${columnTtl ? `TTL toDateTime(${columnTtl.ttlKey ?? 'createdAt'}) + INTERVAL ${columnTtl.interval} ${columnTtl.unit}` : ''}`;
329
- })
330
- .join(',\n');
331
-
332
- const rowTtl = this.ttl?.[tableName]?.row;
333
- const sql =
334
- tableName === TABLE_WORKFLOW_SNAPSHOT
335
- ? `
336
- CREATE TABLE IF NOT EXISTS ${tableName} (
337
- ${['id String'].concat(columns)}
338
- )
339
- ENGINE = ${TABLE_ENGINES[tableName]}
340
- PRIMARY KEY (createdAt, run_id, workflow_name)
341
- ORDER BY (createdAt, run_id, workflow_name)
342
- ${rowTtl ? `TTL toDateTime(${rowTtl.ttlKey ?? 'createdAt'}) + INTERVAL ${rowTtl.interval} ${rowTtl.unit}` : ''}
343
- SETTINGS index_granularity = 8192
344
- `
345
- : `
346
- CREATE TABLE IF NOT EXISTS ${tableName} (
347
- ${columns}
348
- )
349
- ENGINE = ${TABLE_ENGINES[tableName]}
350
- PRIMARY KEY (createdAt, ${tableName === TABLE_EVALS ? 'run_id' : 'id'})
351
- ORDER BY (createdAt, ${tableName === TABLE_EVALS ? 'run_id' : 'id'})
352
- ${this.ttl?.[tableName]?.row ? `TTL toDateTime(createdAt) + INTERVAL ${this.ttl[tableName].row.interval} ${this.ttl[tableName].row.unit}` : ''}
353
- SETTINGS index_granularity = 8192
354
- `;
355
-
356
- await this.db.query({
357
- query: sql,
358
- clickhouse_settings: {
359
- // Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
360
- date_time_input_format: 'best_effort',
361
- date_time_output_format: 'iso',
362
- use_client_time_zone: 1,
363
- output_format_json_quote_64bit_integers: 0,
364
- },
365
- });
366
- } catch (error) {
367
- console.error(`Error creating table ${tableName}:`, error);
368
- throw error;
369
- }
193
+ return this.stores.operations.alterTable({ tableName, schema, ifNotExists });
370
194
  }
371
195
 
372
196
  async clearTable({ tableName }: { tableName: TABLE_NAMES }): Promise<void> {
373
- try {
374
- await this.db.query({
375
- query: `TRUNCATE TABLE ${tableName}`,
376
- clickhouse_settings: {
377
- // Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
378
- date_time_input_format: 'best_effort',
379
- date_time_output_format: 'iso',
380
- use_client_time_zone: 1,
381
- output_format_json_quote_64bit_integers: 0,
382
- },
383
- });
384
- } catch (error) {
385
- console.error(`Error clearing table ${tableName}:`, error);
386
- throw error;
387
- }
197
+ return this.stores.operations.clearTable({ tableName });
388
198
  }
389
199
 
390
200
  async insert({ tableName, record }: { tableName: TABLE_NAMES; record: Record<string, any> }): Promise<void> {
391
- try {
392
- await this.db.insert({
393
- table: tableName,
394
- values: [
395
- {
396
- ...record,
397
- createdAt: record.createdAt.toISOString(),
398
- updatedAt: record.updatedAt.toISOString(),
399
- },
400
- ],
401
- format: 'JSONEachRow',
402
- clickhouse_settings: {
403
- // Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
404
- output_format_json_quote_64bit_integers: 0,
405
- date_time_input_format: 'best_effort',
406
- use_client_time_zone: 1,
407
- },
408
- });
409
- } catch (error) {
410
- console.error(`Error inserting into ${tableName}:`, error);
411
- throw error;
412
- }
201
+ return this.stores.operations.insert({ tableName, record });
413
202
  }
414
203
 
415
204
  async load<R>({ tableName, keys }: { tableName: TABLE_NAMES; keys: Record<string, string> }): Promise<R | null> {
416
- try {
417
- const keyEntries = Object.entries(keys);
418
- const conditions = keyEntries
419
- .map(
420
- ([key]) =>
421
- `"${key}" = {var_${key}:${COLUMN_TYPES[TABLE_SCHEMAS[tableName as TABLE_NAMES]?.[key]?.type ?? 'text']}}`,
422
- )
423
- .join(' AND ');
424
- const values = keyEntries.reduce((acc, [key, value]) => {
425
- return { ...acc, [`var_${key}`]: value };
426
- }, {});
427
-
428
- const result = await this.db.query({
429
- query: `SELECT *, toDateTime64(createdAt, 3) as createdAt, toDateTime64(updatedAt, 3) as updatedAt FROM ${tableName} ${TABLE_ENGINES[tableName as TABLE_NAMES].startsWith('ReplacingMergeTree') ? 'FINAL' : ''} WHERE ${conditions}`,
430
- query_params: values,
431
- clickhouse_settings: {
432
- // Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
433
- date_time_input_format: 'best_effort',
434
- date_time_output_format: 'iso',
435
- use_client_time_zone: 1,
436
- output_format_json_quote_64bit_integers: 0,
437
- },
438
- });
439
-
440
- if (!result) {
441
- return null;
442
- }
443
-
444
- const rows = await result.json();
445
- // If this is a workflow snapshot, parse the snapshot field
446
- if (tableName === TABLE_WORKFLOW_SNAPSHOT) {
447
- const snapshot = rows.data[0] as any;
448
- if (!snapshot) {
449
- return null;
450
- }
451
- if (typeof snapshot.snapshot === 'string') {
452
- snapshot.snapshot = JSON.parse(snapshot.snapshot);
453
- }
454
- return transformRow(snapshot);
455
- }
456
-
457
- const data: R = transformRow(rows.data[0]);
458
- return data;
459
- } catch (error) {
460
- console.error(`Error loading from ${tableName}:`, error);
461
- throw error;
462
- }
205
+ return this.stores.operations.load({ tableName, keys });
463
206
  }
464
207
 
465
- async getThreadById({ threadId }: { threadId: string }): Promise<StorageThreadType | null> {
466
- try {
467
- const result = await this.db.query({
468
- query: `SELECT
469
- id,
470
- "resourceId",
471
- title,
472
- metadata,
473
- toDateTime64(createdAt, 3) as createdAt,
474
- toDateTime64(updatedAt, 3) as updatedAt
475
- FROM "${TABLE_THREADS}"
476
- FINAL
477
- WHERE id = {var_id:String}`,
478
- query_params: { var_id: threadId },
479
- clickhouse_settings: {
480
- // Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
481
- date_time_input_format: 'best_effort',
482
- date_time_output_format: 'iso',
483
- use_client_time_zone: 1,
484
- output_format_json_quote_64bit_integers: 0,
485
- },
486
- });
487
-
488
- const rows = await result.json();
489
- const thread = transformRow(rows.data[0]) as StorageThreadType;
490
-
491
- if (!thread) {
492
- return null;
493
- }
494
-
495
- return {
496
- ...thread,
497
- metadata: typeof thread.metadata === 'string' ? JSON.parse(thread.metadata) : thread.metadata,
498
- createdAt: thread.createdAt,
499
- updatedAt: thread.updatedAt,
500
- };
501
- } catch (error) {
502
- console.error(`Error getting thread ${threadId}:`, error);
503
- throw error;
504
- }
505
- }
506
-
507
- async getThreadsByResourceId({ resourceId }: { resourceId: string }): Promise<StorageThreadType[]> {
508
- try {
509
- const result = await this.db.query({
510
- query: `SELECT
511
- id,
512
- "resourceId",
513
- title,
514
- metadata,
515
- toDateTime64(createdAt, 3) as createdAt,
516
- toDateTime64(updatedAt, 3) as updatedAt
517
- FROM "${TABLE_THREADS}"
518
- WHERE "resourceId" = {var_resourceId:String}`,
519
- query_params: { var_resourceId: resourceId },
520
- clickhouse_settings: {
521
- // Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
522
- date_time_input_format: 'best_effort',
523
- date_time_output_format: 'iso',
524
- use_client_time_zone: 1,
525
- output_format_json_quote_64bit_integers: 0,
526
- },
527
- });
528
-
529
- const rows = await result.json();
530
- const threads = transformRows(rows.data) as StorageThreadType[];
531
-
532
- return threads.map((thread: StorageThreadType) => ({
533
- ...thread,
534
- metadata: typeof thread.metadata === 'string' ? JSON.parse(thread.metadata) : thread.metadata,
535
- createdAt: thread.createdAt,
536
- updatedAt: thread.updatedAt,
537
- }));
538
- } catch (error) {
539
- console.error(`Error getting threads for resource ${resourceId}:`, error);
540
- throw error;
541
- }
542
- }
543
-
544
- async saveThread({ thread }: { thread: StorageThreadType }): Promise<StorageThreadType> {
545
- try {
546
- await this.db.insert({
547
- table: TABLE_THREADS,
548
- values: [
549
- {
550
- ...thread,
551
- createdAt: thread.createdAt.toISOString(),
552
- updatedAt: thread.updatedAt.toISOString(),
553
- },
554
- ],
555
- format: 'JSONEachRow',
556
- clickhouse_settings: {
557
- // Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
558
- date_time_input_format: 'best_effort',
559
- use_client_time_zone: 1,
560
- output_format_json_quote_64bit_integers: 0,
561
- },
562
- });
563
-
564
- return thread;
565
- } catch (error) {
566
- console.error('Error saving thread:', error);
567
- throw error;
568
- }
569
- }
570
-
571
- async updateThread({
572
- id,
573
- title,
574
- metadata,
208
+ async updateWorkflowResults({
209
+ workflowName,
210
+ runId,
211
+ stepId,
212
+ result,
213
+ runtimeContext,
575
214
  }: {
576
- id: string;
577
- title: string;
578
- metadata: Record<string, unknown>;
579
- }): Promise<StorageThreadType> {
580
- try {
581
- // First get the existing thread to merge metadata
582
- const existingThread = await this.getThreadById({ threadId: id });
583
- if (!existingThread) {
584
- throw new Error(`Thread ${id} not found`);
585
- }
586
-
587
- // Merge the existing metadata with the new metadata
588
- const mergedMetadata = {
589
- ...existingThread.metadata,
590
- ...metadata,
591
- };
592
-
593
- const updatedThread = {
594
- ...existingThread,
595
- title,
596
- metadata: mergedMetadata,
597
- updatedAt: new Date(),
598
- };
599
-
600
- await this.db.insert({
601
- table: TABLE_THREADS,
602
- values: [
603
- {
604
- ...updatedThread,
605
- updatedAt: updatedThread.updatedAt.toISOString(),
606
- },
607
- ],
608
- format: 'JSONEachRow',
609
- clickhouse_settings: {
610
- // Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
611
- date_time_input_format: 'best_effort',
612
- use_client_time_zone: 1,
613
- output_format_json_quote_64bit_integers: 0,
614
- },
615
- });
616
-
617
- return updatedThread;
618
- } catch (error) {
619
- console.error('Error updating thread:', error);
620
- throw error;
621
- }
622
- }
623
-
624
- async deleteThread({ threadId }: { threadId: string }): Promise<void> {
625
- try {
626
- // First delete all messages associated with this thread
627
- await this.db.command({
628
- query: `DELETE FROM "${TABLE_MESSAGES}" WHERE thread_id = {var_thread_id:String};`,
629
- query_params: { var_thread_id: threadId },
630
- clickhouse_settings: {
631
- output_format_json_quote_64bit_integers: 0,
632
- },
633
- });
634
-
635
- // Then delete the thread
636
- await this.db.command({
637
- query: `DELETE FROM "${TABLE_THREADS}" WHERE id = {var_id:String};`,
638
- query_params: { var_id: threadId },
639
- clickhouse_settings: {
640
- output_format_json_quote_64bit_integers: 0,
641
- },
642
- });
643
- } catch (error) {
644
- console.error('Error deleting thread:', error);
645
- throw error;
646
- }
647
- }
648
-
649
- async getMessages<T = unknown>({ threadId, selectBy }: StorageGetMessagesArg): Promise<T[]> {
650
- try {
651
- const messages: any[] = [];
652
- const limit = typeof selectBy?.last === `number` ? selectBy.last : 40;
653
- const include = selectBy?.include || [];
654
-
655
- if (include.length) {
656
- const includeResult = await this.db.query({
657
- query: `
658
- WITH ordered_messages AS (
659
- SELECT
660
- *,
661
- toDateTime64(createdAt, 3) as createdAt,
662
- toDateTime64(updatedAt, 3) as updatedAt,
663
- ROW_NUMBER() OVER (ORDER BY "createdAt" DESC) as row_num
664
- FROM "${TABLE_MESSAGES}"
665
- WHERE thread_id = {var_thread_id:String}
666
- )
667
- SELECT
668
- m.id AS id,
669
- m.content as content,
670
- m.role as role,
671
- m.type as type,
672
- m.createdAt as createdAt,
673
- m.updatedAt as updatedAt,
674
- m.thread_id AS "threadId"
675
- FROM ordered_messages m
676
- WHERE m.id = ANY({var_include:Array(String)})
677
- OR EXISTS (
678
- SELECT 1 FROM ordered_messages target
679
- WHERE target.id = ANY({var_include:Array(String)})
680
- AND (
681
- -- Get previous messages based on the max withPreviousMessages
682
- (m.row_num <= target.row_num + {var_withPreviousMessages:Int64} AND m.row_num > target.row_num)
683
- OR
684
- -- Get next messages based on the max withNextMessages
685
- (m.row_num >= target.row_num - {var_withNextMessages:Int64} AND m.row_num < target.row_num)
686
- )
687
- )
688
- ORDER BY m."createdAt" DESC
689
- `,
690
- query_params: {
691
- var_thread_id: threadId,
692
- var_include: include.map(i => i.id),
693
- var_withPreviousMessages: Math.max(...include.map(i => i.withPreviousMessages || 0)),
694
- var_withNextMessages: Math.max(...include.map(i => i.withNextMessages || 0)),
695
- },
696
- clickhouse_settings: {
697
- // Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
698
- date_time_input_format: 'best_effort',
699
- date_time_output_format: 'iso',
700
- use_client_time_zone: 1,
701
- output_format_json_quote_64bit_integers: 0,
702
- },
703
- });
704
-
705
- const rows = await includeResult.json();
706
- messages.push(...transformRows(rows.data));
707
- }
708
-
709
- // Then get the remaining messages, excluding the ids we just fetched
710
- const result = await this.db.query({
711
- query: `
712
- SELECT
713
- id,
714
- content,
715
- role,
716
- type,
717
- toDateTime64(createdAt, 3) as createdAt,
718
- thread_id AS "threadId"
719
- FROM "${TABLE_MESSAGES}"
720
- WHERE thread_id = {threadId:String}
721
- AND id NOT IN ({exclude:Array(String)})
722
- ORDER BY "createdAt" DESC
723
- LIMIT {limit:Int64}
724
- `,
725
- query_params: {
726
- threadId,
727
- exclude: messages.map(m => m.id),
728
- limit,
729
- },
730
- clickhouse_settings: {
731
- // Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
732
- date_time_input_format: 'best_effort',
733
- date_time_output_format: 'iso',
734
- use_client_time_zone: 1,
735
- output_format_json_quote_64bit_integers: 0,
736
- },
737
- });
738
-
739
- const rows = await result.json();
740
- messages.push(...transformRows(rows.data));
741
-
742
- // Sort all messages by creation date
743
- messages.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
744
-
745
- // Parse message content
746
- messages.forEach(message => {
747
- if (typeof message.content === 'string') {
748
- try {
749
- message.content = JSON.parse(message.content);
750
- } catch {
751
- // If parsing fails, leave as string
752
- }
753
- }
754
- });
755
-
756
- return messages as T[];
757
- } catch (error) {
758
- console.error('Error getting messages:', error);
759
- throw error;
760
- }
215
+ workflowName: string;
216
+ runId: string;
217
+ stepId: string;
218
+ result: StepResult<any, any, any, any>;
219
+ runtimeContext: Record<string, any>;
220
+ }): Promise<Record<string, StepResult<any, any, any, any>>> {
221
+ return this.stores.workflows.updateWorkflowResults({ workflowName, runId, stepId, result, runtimeContext });
761
222
  }
762
223
 
763
- async saveMessages({ messages }: { messages: MessageType[] }): Promise<MessageType[]> {
764
- if (messages.length === 0) return messages;
765
-
766
- try {
767
- const threadId = messages[0]?.threadId;
768
- if (!threadId) {
769
- throw new Error('Thread ID is required');
770
- }
771
-
772
- // Check if thread exists
773
- const thread = await this.getThreadById({ threadId });
774
- if (!thread) {
775
- throw new Error(`Thread ${threadId} not found`);
776
- }
777
-
778
- await this.db.insert({
779
- table: TABLE_MESSAGES,
780
- format: 'JSONEachRow',
781
- values: messages.map(message => ({
782
- id: message.id,
783
- thread_id: threadId,
784
- content: typeof message.content === 'string' ? message.content : JSON.stringify(message.content),
785
- createdAt: message.createdAt.toISOString(),
786
- role: message.role,
787
- type: message.type,
788
- })),
789
- clickhouse_settings: {
790
- // Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
791
- date_time_input_format: 'best_effort',
792
- use_client_time_zone: 1,
793
- output_format_json_quote_64bit_integers: 0,
794
- },
795
- });
796
-
797
- return messages;
798
- } catch (error) {
799
- console.error('Error saving messages:', error);
800
- throw error;
801
- }
224
+ async updateWorkflowState({
225
+ workflowName,
226
+ runId,
227
+ opts,
228
+ }: {
229
+ workflowName: string;
230
+ runId: string;
231
+ opts: {
232
+ status: string;
233
+ result?: StepResult<any, any, any, any>;
234
+ error?: string;
235
+ suspendedPaths?: Record<string, number[]>;
236
+ waitingPaths?: Record<string, number[]>;
237
+ };
238
+ }): Promise<WorkflowRunState | undefined> {
239
+ return this.stores.workflows.updateWorkflowState({ workflowName, runId, opts });
802
240
  }
803
241
 
804
242
  async persistWorkflowSnapshot({
@@ -810,42 +248,7 @@ export class ClickhouseStore extends MastraStorage {
810
248
  runId: string;
811
249
  snapshot: WorkflowRunState;
812
250
  }): Promise<void> {
813
- try {
814
- const currentSnapshot = await this.load({
815
- tableName: TABLE_WORKFLOW_SNAPSHOT,
816
- keys: { workflow_name: workflowName, run_id: runId },
817
- });
818
-
819
- const now = new Date();
820
- const persisting = currentSnapshot
821
- ? {
822
- ...currentSnapshot,
823
- snapshot: JSON.stringify(snapshot),
824
- updatedAt: now.toISOString(),
825
- }
826
- : {
827
- workflow_name: workflowName,
828
- run_id: runId,
829
- snapshot: JSON.stringify(snapshot),
830
- createdAt: now.toISOString(),
831
- updatedAt: now.toISOString(),
832
- };
833
-
834
- await this.db.insert({
835
- table: TABLE_WORKFLOW_SNAPSHOT,
836
- format: 'JSONEachRow',
837
- values: [persisting],
838
- clickhouse_settings: {
839
- // Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
840
- date_time_input_format: 'best_effort',
841
- use_client_time_zone: 1,
842
- output_format_json_quote_64bit_integers: 0,
843
- },
844
- });
845
- } catch (error) {
846
- console.error('Error persisting workflow snapshot:', error);
847
- throw error;
848
- }
251
+ return this.stores.workflows.persistWorkflowSnapshot({ workflowName, runId, snapshot });
849
252
  }
850
253
 
851
254
  async loadWorkflowSnapshot({
@@ -855,45 +258,7 @@ export class ClickhouseStore extends MastraStorage {
855
258
  workflowName: string;
856
259
  runId: string;
857
260
  }): Promise<WorkflowRunState | null> {
858
- try {
859
- const result = await this.load({
860
- tableName: TABLE_WORKFLOW_SNAPSHOT,
861
- keys: {
862
- workflow_name: workflowName,
863
- run_id: runId,
864
- },
865
- });
866
-
867
- if (!result) {
868
- return null;
869
- }
870
-
871
- return (result as any).snapshot;
872
- } catch (error) {
873
- console.error('Error loading workflow snapshot:', error);
874
- throw error;
875
- }
876
- }
877
-
878
- private parseWorkflowRun(row: any): WorkflowRun {
879
- let parsedSnapshot: WorkflowRunState | string = row.snapshot as string;
880
- if (typeof parsedSnapshot === 'string') {
881
- try {
882
- parsedSnapshot = JSON.parse(row.snapshot as string) as WorkflowRunState;
883
- } catch (e) {
884
- // If parsing fails, return the raw snapshot string
885
- console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
886
- }
887
- }
888
-
889
- return {
890
- workflowName: row.workflow_name,
891
- runId: row.run_id,
892
- snapshot: parsedSnapshot,
893
- createdAt: new Date(row.createdAt),
894
- updatedAt: new Date(row.updatedAt),
895
- resourceId: row.resourceId,
896
- };
261
+ return this.stores.workflows.loadWorkflowSnapshot({ workflowName, runId });
897
262
  }
898
263
 
899
264
  async getWorkflowRuns({
@@ -911,143 +276,178 @@ export class ClickhouseStore extends MastraStorage {
911
276
  offset?: number;
912
277
  resourceId?: string;
913
278
  } = {}): Promise<WorkflowRuns> {
914
- try {
915
- const conditions: string[] = [];
916
- const values: Record<string, any> = {};
279
+ return this.stores.workflows.getWorkflowRuns({ workflowName, fromDate, toDate, limit, offset, resourceId });
280
+ }
917
281
 
918
- if (workflowName) {
919
- conditions.push(`workflow_name = {var_workflow_name:String}`);
920
- values.var_workflow_name = workflowName;
921
- }
282
+ async getWorkflowRunById({
283
+ runId,
284
+ workflowName,
285
+ }: {
286
+ runId: string;
287
+ workflowName?: string;
288
+ }): Promise<WorkflowRun | null> {
289
+ return this.stores.workflows.getWorkflowRunById({ runId, workflowName });
290
+ }
922
291
 
923
- if (resourceId) {
924
- const hasResourceId = await this.hasColumn(TABLE_WORKFLOW_SNAPSHOT, 'resourceId');
925
- if (hasResourceId) {
926
- conditions.push(`resourceId = {var_resourceId:String}`);
927
- values.var_resourceId = resourceId;
928
- } else {
929
- console.warn(`[${TABLE_WORKFLOW_SNAPSHOT}] resourceId column not found. Skipping resourceId filter.`);
930
- }
931
- }
292
+ async getTraces(args: StorageGetTracesArg): Promise<any[]> {
293
+ return this.stores.traces.getTraces(args);
294
+ }
932
295
 
933
- if (fromDate) {
934
- conditions.push(`createdAt >= {var_from_date:DateTime64(3)}`);
935
- values.var_from_date = fromDate.getTime() / 1000; // Convert to Unix timestamp
936
- }
296
+ async getTracesPaginated(args: StorageGetTracesPaginatedArg): Promise<PaginationInfo & { traces: Trace[] }> {
297
+ return this.stores.traces.getTracesPaginated(args);
298
+ }
937
299
 
938
- if (toDate) {
939
- conditions.push(`createdAt <= {var_to_date:DateTime64(3)}`);
940
- values.var_to_date = toDate.getTime() / 1000; // Convert to Unix timestamp
941
- }
300
+ async batchTraceInsert(args: { records: Trace[] }): Promise<void> {
301
+ return this.stores.traces.batchTraceInsert(args);
302
+ }
942
303
 
943
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
944
- const limitClause = limit !== undefined ? `LIMIT ${limit}` : '';
945
- const offsetClause = offset !== undefined ? `OFFSET ${offset}` : '';
304
+ async getThreadById({ threadId }: { threadId: string }): Promise<StorageThreadType | null> {
305
+ return this.stores.memory.getThreadById({ threadId });
306
+ }
946
307
 
947
- let total = 0;
948
- // Only get total count when using pagination
949
- if (limit !== undefined && offset !== undefined) {
950
- const countResult = await this.db.query({
951
- query: `SELECT COUNT(*) as count FROM ${TABLE_WORKFLOW_SNAPSHOT} ${TABLE_ENGINES[TABLE_WORKFLOW_SNAPSHOT].startsWith('ReplacingMergeTree') ? 'FINAL' : ''} ${whereClause}`,
952
- query_params: values,
953
- format: 'JSONEachRow',
954
- });
955
- const countRows = await countResult.json();
956
- total = Number((countRows as Array<{ count: string | number }>)[0]?.count ?? 0);
957
- }
308
+ async getThreadsByResourceId({ resourceId }: { resourceId: string }): Promise<StorageThreadType[]> {
309
+ return this.stores.memory.getThreadsByResourceId({ resourceId });
310
+ }
958
311
 
959
- // Get results
960
- const result = await this.db.query({
961
- query: `
962
- SELECT
963
- workflow_name,
964
- run_id,
965
- snapshot,
966
- toDateTime64(createdAt, 3) as createdAt,
967
- toDateTime64(updatedAt, 3) as updatedAt,
968
- resourceId
969
- FROM ${TABLE_WORKFLOW_SNAPSHOT} ${TABLE_ENGINES[TABLE_WORKFLOW_SNAPSHOT].startsWith('ReplacingMergeTree') ? 'FINAL' : ''}
970
- ${whereClause}
971
- ORDER BY createdAt DESC
972
- ${limitClause}
973
- ${offsetClause}
974
- `,
975
- query_params: values,
976
- format: 'JSONEachRow',
977
- });
312
+ async saveThread({ thread }: { thread: StorageThreadType }): Promise<StorageThreadType> {
313
+ return this.stores.memory.saveThread({ thread });
314
+ }
978
315
 
979
- const resultJson = await result.json();
980
- const rows = resultJson as any[];
981
- const runs = rows.map(row => {
982
- return this.parseWorkflowRun(row);
983
- });
316
+ async updateThread({
317
+ id,
318
+ title,
319
+ metadata,
320
+ }: {
321
+ id: string;
322
+ title: string;
323
+ metadata: Record<string, unknown>;
324
+ }): Promise<StorageThreadType> {
325
+ return this.stores.memory.updateThread({ id, title, metadata });
326
+ }
984
327
 
985
- // Use runs.length as total when not paginating
986
- return { runs, total: total || runs.length };
987
- } catch (error) {
988
- console.error('Error getting workflow runs:', error);
989
- throw error;
990
- }
328
+ async deleteThread({ threadId }: { threadId: string }): Promise<void> {
329
+ return this.stores.memory.deleteThread({ threadId });
991
330
  }
992
331
 
993
- async getWorkflowRunById({
994
- runId,
995
- workflowName,
332
+ async getThreadsByResourceIdPaginated(args: {
333
+ resourceId: string;
334
+ page: number;
335
+ perPage: number;
336
+ }): Promise<PaginationInfo & { threads: StorageThreadType[] }> {
337
+ return this.stores.memory.getThreadsByResourceIdPaginated(args);
338
+ }
339
+
340
+ public async getMessages(args: StorageGetMessagesArg & { format?: 'v1' }): Promise<MastraMessageV1[]>;
341
+ public async getMessages(args: StorageGetMessagesArg & { format: 'v2' }): Promise<MastraMessageV2[]>;
342
+ public async getMessages({
343
+ threadId,
344
+ resourceId,
345
+ selectBy,
346
+ format,
347
+ }: StorageGetMessagesArg & { format?: 'v1' | 'v2' }): Promise<MastraMessageV1[] | MastraMessageV2[]> {
348
+ return this.stores.memory.getMessages({ threadId, resourceId, selectBy, format });
349
+ }
350
+
351
+ async getMessagesById({ messageIds, format }: { messageIds: string[]; format: 'v1' }): Promise<MastraMessageV1[]>;
352
+ async getMessagesById({ messageIds, format }: { messageIds: string[]; format?: 'v2' }): Promise<MastraMessageV2[]>;
353
+ async getMessagesById({
354
+ messageIds,
355
+ format,
996
356
  }: {
997
- runId: string;
998
- workflowName?: string;
999
- }): Promise<WorkflowRun | null> {
1000
- try {
1001
- const conditions: string[] = [];
1002
- const values: Record<string, any> = {};
357
+ messageIds: string[];
358
+ format?: 'v1' | 'v2';
359
+ }): Promise<MastraMessageV1[] | MastraMessageV2[]> {
360
+ return this.stores.memory.getMessagesById({ messageIds, format });
361
+ }
1003
362
 
1004
- if (runId) {
1005
- conditions.push(`run_id = {var_runId:String}`);
1006
- values.var_runId = runId;
1007
- }
363
+ async saveMessages(args: { messages: MastraMessageV1[]; format?: undefined | 'v1' }): Promise<MastraMessageV1[]>;
364
+ async saveMessages(args: { messages: MastraMessageV2[]; format: 'v2' }): Promise<MastraMessageV2[]>;
365
+ async saveMessages(
366
+ args: { messages: MastraMessageV1[]; format?: undefined | 'v1' } | { messages: MastraMessageV2[]; format: 'v2' },
367
+ ): Promise<MastraMessageV2[] | MastraMessageV1[]> {
368
+ return this.stores.memory.saveMessages(args);
369
+ }
1008
370
 
1009
- if (workflowName) {
1010
- conditions.push(`workflow_name = {var_workflow_name:String}`);
1011
- values.var_workflow_name = workflowName;
1012
- }
371
+ async getMessagesPaginated(
372
+ args: StorageGetMessagesArg & { format?: 'v1' | 'v2' },
373
+ ): Promise<PaginationInfo & { messages: MastraMessageV1[] | MastraMessageV2[] }> {
374
+ return this.stores.memory.getMessagesPaginated(args);
375
+ }
1013
376
 
1014
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
377
+ async updateMessages(args: {
378
+ messages: (Partial<Omit<MastraMessageV2, 'createdAt'>> & {
379
+ id: string;
380
+ threadId?: string;
381
+ content?: { metadata?: MastraMessageContentV2['metadata']; content?: MastraMessageContentV2['content'] };
382
+ })[];
383
+ }): Promise<MastraMessageV2[]> {
384
+ return this.stores.memory.updateMessages(args);
385
+ }
1015
386
 
1016
- // Get results
1017
- const result = await this.db.query({
1018
- query: `
1019
- SELECT
1020
- workflow_name,
1021
- run_id,
1022
- snapshot,
1023
- toDateTime64(createdAt, 3) as createdAt,
1024
- toDateTime64(updatedAt, 3) as updatedAt,
1025
- resourceId
1026
- FROM ${TABLE_WORKFLOW_SNAPSHOT} ${TABLE_ENGINES[TABLE_WORKFLOW_SNAPSHOT].startsWith('ReplacingMergeTree') ? 'FINAL' : ''}
1027
- ${whereClause}
1028
- `,
1029
- query_params: values,
1030
- format: 'JSONEachRow',
1031
- });
387
+ async getResourceById({ resourceId }: { resourceId: string }): Promise<StorageResourceType | null> {
388
+ return this.stores.memory.getResourceById({ resourceId });
389
+ }
1032
390
 
1033
- const resultJson = await result.json();
1034
- if (!Array.isArray(resultJson) || resultJson.length === 0) {
1035
- return null;
1036
- }
1037
- return this.parseWorkflowRun(resultJson[0]);
1038
- } catch (error) {
1039
- console.error('Error getting workflow run by ID:', error);
1040
- throw error;
1041
- }
391
+ async saveResource({ resource }: { resource: StorageResourceType }): Promise<StorageResourceType> {
392
+ return this.stores.memory.saveResource({ resource });
1042
393
  }
1043
394
 
1044
- private async hasColumn(table: string, column: string): Promise<boolean> {
1045
- const result = await this.db.query({
1046
- query: `DESCRIBE TABLE ${table}`,
1047
- format: 'JSONEachRow',
1048
- });
1049
- const columns = (await result.json()) as { name: string }[];
1050
- return columns.some(c => c.name === column);
395
+ async updateResource({
396
+ resourceId,
397
+ workingMemory,
398
+ metadata,
399
+ }: {
400
+ resourceId: string;
401
+ workingMemory?: string;
402
+ metadata?: Record<string, unknown>;
403
+ }): Promise<StorageResourceType> {
404
+ return this.stores.memory.updateResource({ resourceId, workingMemory, metadata });
405
+ }
406
+
407
+ async getScoreById({ id }: { id: string }): Promise<ScoreRowData | null> {
408
+ return this.stores.scores.getScoreById({ id });
409
+ }
410
+
411
+ async saveScore(_score: ScoreRowData): Promise<{ score: ScoreRowData }> {
412
+ return this.stores.scores.saveScore(_score);
413
+ }
414
+
415
+ async getScoresByRunId({
416
+ runId,
417
+ pagination,
418
+ }: {
419
+ runId: string;
420
+ pagination: StoragePagination;
421
+ }): Promise<{ pagination: PaginationInfo; scores: ScoreRowData[] }> {
422
+ return this.stores.scores.getScoresByRunId({ runId, pagination });
423
+ }
424
+
425
+ async getScoresByEntityId({
426
+ entityId,
427
+ entityType,
428
+ pagination,
429
+ }: {
430
+ pagination: StoragePagination;
431
+ entityId: string;
432
+ entityType: string;
433
+ }): Promise<{ pagination: PaginationInfo; scores: ScoreRowData[] }> {
434
+ return this.stores.scores.getScoresByEntityId({ entityId, entityType, pagination });
435
+ }
436
+
437
+ async getScoresByScorerId({
438
+ scorerId,
439
+ pagination,
440
+ entityId,
441
+ entityType,
442
+ source,
443
+ }: {
444
+ scorerId: string;
445
+ pagination: StoragePagination;
446
+ entityId?: string;
447
+ entityType?: string;
448
+ source?: ScoringSource;
449
+ }): Promise<{ pagination: PaginationInfo; scores: ScoreRowData[] }> {
450
+ return this.stores.scores.getScoresByScorerId({ scorerId, pagination, entityId, entityType, source });
1051
451
  }
1052
452
 
1053
453
  async close(): Promise<void> {