@mastra/clickhouse 0.0.0-trigger-playground-ui-package-20250506151043 → 0.0.0-update-stores-peerDeps-20250723031338

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