@mastra/clickhouse 0.0.0-trigger-playground-ui-package-20250506151043 → 0.0.0-tsconfig-compile-20250703214351
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 +426 -2
- package/dist/_tsup-dts-rollup.d.cts +61 -8
- package/dist/_tsup-dts-rollup.d.ts +61 -8
- package/dist/index.cjs +466 -114
- package/dist/index.js +449 -97
- package/package.json +14 -10
- package/src/storage/index.test.ts +417 -119
- package/src/storage/index.ts +520 -114
package/src/storage/index.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import type { ClickHouseClient } from '@clickhouse/client';
|
|
2
2
|
import { createClient } from '@clickhouse/client';
|
|
3
|
+
import { MessageList } from '@mastra/core/agent';
|
|
4
|
+
import type { MastraMessageContentV2 } from '@mastra/core/agent';
|
|
5
|
+
import { MastraError, ErrorDomain, ErrorCategory } from '@mastra/core/error';
|
|
3
6
|
import type { MetricResult, TestInfo } from '@mastra/core/eval';
|
|
4
|
-
import type {
|
|
7
|
+
import type { MastraMessageV1, MastraMessageV2, StorageThreadType } from '@mastra/core/memory';
|
|
5
8
|
import {
|
|
6
9
|
MastraStorage,
|
|
7
10
|
TABLE_EVALS,
|
|
@@ -13,14 +16,20 @@ import {
|
|
|
13
16
|
} from '@mastra/core/storage';
|
|
14
17
|
import type {
|
|
15
18
|
EvalRow,
|
|
19
|
+
PaginationInfo,
|
|
16
20
|
StorageColumn,
|
|
17
21
|
StorageGetMessagesArg,
|
|
18
22
|
TABLE_NAMES,
|
|
19
23
|
WorkflowRun,
|
|
20
24
|
WorkflowRuns,
|
|
25
|
+
StorageGetTracesArg,
|
|
26
|
+
TABLE_RESOURCES,
|
|
21
27
|
} from '@mastra/core/storage';
|
|
28
|
+
import type { Trace } from '@mastra/core/telemetry';
|
|
22
29
|
import type { WorkflowRunState } from '@mastra/core/workflows';
|
|
23
30
|
|
|
31
|
+
type SUPPORTED_TABLE_NAMES = Exclude<TABLE_NAMES, typeof TABLE_RESOURCES>;
|
|
32
|
+
|
|
24
33
|
function safelyParseJSON(jsonString: string): any {
|
|
25
34
|
try {
|
|
26
35
|
return JSON.parse(jsonString);
|
|
@@ -60,7 +69,7 @@ export type ClickhouseConfig = {
|
|
|
60
69
|
};
|
|
61
70
|
};
|
|
62
71
|
|
|
63
|
-
export const TABLE_ENGINES: Record<
|
|
72
|
+
export const TABLE_ENGINES: Record<SUPPORTED_TABLE_NAMES, string> = {
|
|
64
73
|
[TABLE_MESSAGES]: `MergeTree()`,
|
|
65
74
|
[TABLE_WORKFLOW_SNAPSHOT]: `ReplacingMergeTree()`,
|
|
66
75
|
[TABLE_TRACES]: `MergeTree()`,
|
|
@@ -121,7 +130,12 @@ export class ClickhouseStore extends MastraStorage {
|
|
|
121
130
|
const testInfoValue = row.test_info ? JSON.parse(row.test_info as string) : undefined;
|
|
122
131
|
|
|
123
132
|
if (!resultValue || typeof resultValue !== 'object' || !('score' in resultValue)) {
|
|
124
|
-
throw new
|
|
133
|
+
throw new MastraError({
|
|
134
|
+
id: 'CLICKHOUSE_STORAGE_INVALID_METRIC_FORMAT',
|
|
135
|
+
text: `Invalid MetricResult format: ${JSON.stringify(resultValue)}`,
|
|
136
|
+
domain: ErrorDomain.STORAGE,
|
|
137
|
+
category: ErrorCategory.USER,
|
|
138
|
+
});
|
|
125
139
|
}
|
|
126
140
|
|
|
127
141
|
return {
|
|
@@ -138,6 +152,19 @@ export class ClickhouseStore extends MastraStorage {
|
|
|
138
152
|
};
|
|
139
153
|
}
|
|
140
154
|
|
|
155
|
+
private escape(value: any): string {
|
|
156
|
+
if (typeof value === 'string') {
|
|
157
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
158
|
+
}
|
|
159
|
+
if (value instanceof Date) {
|
|
160
|
+
return `'${value.toISOString()}'`;
|
|
161
|
+
}
|
|
162
|
+
if (value === null || value === undefined) {
|
|
163
|
+
return 'NULL';
|
|
164
|
+
}
|
|
165
|
+
return value.toString();
|
|
166
|
+
}
|
|
167
|
+
|
|
141
168
|
async getEvalsByAgentName(agentName: string, type?: 'test' | 'live'): Promise<EvalRow[]> {
|
|
142
169
|
try {
|
|
143
170
|
const baseQuery = `SELECT *, toDateTime64(createdAt, 3) as createdAt FROM ${TABLE_EVALS} WHERE agent_name = {var_agent_name:String}`;
|
|
@@ -165,13 +192,19 @@ export class ClickhouseStore extends MastraStorage {
|
|
|
165
192
|
|
|
166
193
|
const rows = await result.json();
|
|
167
194
|
return rows.data.map((row: any) => this.transformEvalRow(row));
|
|
168
|
-
} catch (error) {
|
|
169
|
-
|
|
170
|
-
if (error instanceof Error && error.message.includes('no such table')) {
|
|
195
|
+
} catch (error: any) {
|
|
196
|
+
if (error?.message?.includes('no such table') || error?.message?.includes('does not exist')) {
|
|
171
197
|
return [];
|
|
172
198
|
}
|
|
173
|
-
|
|
174
|
-
|
|
199
|
+
throw new MastraError(
|
|
200
|
+
{
|
|
201
|
+
id: 'CLICKHOUSE_STORAGE_GET_EVALS_BY_AGENT_FAILED',
|
|
202
|
+
domain: ErrorDomain.STORAGE,
|
|
203
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
204
|
+
details: { agentName, type: type ?? null },
|
|
205
|
+
},
|
|
206
|
+
error,
|
|
207
|
+
);
|
|
175
208
|
}
|
|
176
209
|
}
|
|
177
210
|
|
|
@@ -197,9 +230,16 @@ export class ClickhouseStore extends MastraStorage {
|
|
|
197
230
|
output_format_json_quote_64bit_integers: 0,
|
|
198
231
|
},
|
|
199
232
|
});
|
|
200
|
-
} catch (error) {
|
|
201
|
-
|
|
202
|
-
|
|
233
|
+
} catch (error: any) {
|
|
234
|
+
throw new MastraError(
|
|
235
|
+
{
|
|
236
|
+
id: 'CLICKHOUSE_STORAGE_BATCH_INSERT_FAILED',
|
|
237
|
+
domain: ErrorDomain.STORAGE,
|
|
238
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
239
|
+
details: { tableName },
|
|
240
|
+
},
|
|
241
|
+
error,
|
|
242
|
+
);
|
|
203
243
|
}
|
|
204
244
|
}
|
|
205
245
|
|
|
@@ -264,59 +304,107 @@ export class ClickhouseStore extends MastraStorage {
|
|
|
264
304
|
|
|
265
305
|
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
|
|
266
306
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
307
|
+
try {
|
|
308
|
+
const result = await this.db.query({
|
|
309
|
+
query: `SELECT *, toDateTime64(createdAt, 3) as createdAt FROM ${TABLE_TRACES} ${whereClause} ORDER BY "createdAt" DESC LIMIT ${limit} OFFSET ${offset}`,
|
|
310
|
+
query_params: args,
|
|
311
|
+
clickhouse_settings: {
|
|
312
|
+
// Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
|
|
313
|
+
date_time_input_format: 'best_effort',
|
|
314
|
+
date_time_output_format: 'iso',
|
|
315
|
+
use_client_time_zone: 1,
|
|
316
|
+
output_format_json_quote_64bit_integers: 0,
|
|
317
|
+
},
|
|
318
|
+
});
|
|
278
319
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
320
|
+
if (!result) {
|
|
321
|
+
return [];
|
|
322
|
+
}
|
|
282
323
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
324
|
+
const resp = await result.json();
|
|
325
|
+
const rows: any[] = resp.data;
|
|
326
|
+
return rows.map(row => ({
|
|
327
|
+
id: row.id,
|
|
328
|
+
parentSpanId: row.parentSpanId,
|
|
329
|
+
traceId: row.traceId,
|
|
330
|
+
name: row.name,
|
|
331
|
+
scope: row.scope,
|
|
332
|
+
kind: row.kind,
|
|
333
|
+
status: safelyParseJSON(row.status as string),
|
|
334
|
+
events: safelyParseJSON(row.events as string),
|
|
335
|
+
links: safelyParseJSON(row.links as string),
|
|
336
|
+
attributes: safelyParseJSON(row.attributes as string),
|
|
337
|
+
startTime: row.startTime,
|
|
338
|
+
endTime: row.endTime,
|
|
339
|
+
other: safelyParseJSON(row.other as string),
|
|
340
|
+
createdAt: row.createdAt,
|
|
341
|
+
}));
|
|
342
|
+
} catch (error: any) {
|
|
343
|
+
if (error?.message?.includes('no such table') || error?.message?.includes('does not exist')) {
|
|
344
|
+
return [];
|
|
345
|
+
}
|
|
346
|
+
throw new MastraError(
|
|
347
|
+
{
|
|
348
|
+
id: 'CLICKHOUSE_STORAGE_GET_TRACES_FAILED',
|
|
349
|
+
domain: ErrorDomain.STORAGE,
|
|
350
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
351
|
+
details: {
|
|
352
|
+
name: name ?? null,
|
|
353
|
+
scope: scope ?? null,
|
|
354
|
+
page,
|
|
355
|
+
perPage,
|
|
356
|
+
attributes: attributes ? JSON.stringify(attributes) : null,
|
|
357
|
+
filters: filters ? JSON.stringify(filters) : null,
|
|
358
|
+
fromDate: fromDate?.toISOString() ?? null,
|
|
359
|
+
toDate: toDate?.toISOString() ?? null,
|
|
360
|
+
},
|
|
361
|
+
},
|
|
362
|
+
error,
|
|
363
|
+
);
|
|
364
|
+
}
|
|
301
365
|
}
|
|
302
366
|
|
|
303
367
|
async optimizeTable({ tableName }: { tableName: TABLE_NAMES }): Promise<void> {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
368
|
+
try {
|
|
369
|
+
await this.db.command({
|
|
370
|
+
query: `OPTIMIZE TABLE ${tableName} FINAL`,
|
|
371
|
+
});
|
|
372
|
+
} catch (error: any) {
|
|
373
|
+
throw new MastraError(
|
|
374
|
+
{
|
|
375
|
+
id: 'CLICKHOUSE_STORAGE_OPTIMIZE_TABLE_FAILED',
|
|
376
|
+
domain: ErrorDomain.STORAGE,
|
|
377
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
378
|
+
details: { tableName },
|
|
379
|
+
},
|
|
380
|
+
error,
|
|
381
|
+
);
|
|
382
|
+
}
|
|
307
383
|
}
|
|
308
384
|
|
|
309
385
|
async materializeTtl({ tableName }: { tableName: TABLE_NAMES }): Promise<void> {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
386
|
+
try {
|
|
387
|
+
await this.db.command({
|
|
388
|
+
query: `ALTER TABLE ${tableName} MATERIALIZE TTL;`,
|
|
389
|
+
});
|
|
390
|
+
} catch (error: any) {
|
|
391
|
+
throw new MastraError(
|
|
392
|
+
{
|
|
393
|
+
id: 'CLICKHOUSE_STORAGE_MATERIALIZE_TTL_FAILED',
|
|
394
|
+
domain: ErrorDomain.STORAGE,
|
|
395
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
396
|
+
details: { tableName },
|
|
397
|
+
},
|
|
398
|
+
error,
|
|
399
|
+
);
|
|
400
|
+
}
|
|
313
401
|
}
|
|
314
402
|
|
|
315
403
|
async createTable({
|
|
316
404
|
tableName,
|
|
317
405
|
schema,
|
|
318
406
|
}: {
|
|
319
|
-
tableName:
|
|
407
|
+
tableName: SUPPORTED_TABLE_NAMES;
|
|
320
408
|
schema: Record<string, StorageColumn>;
|
|
321
409
|
}): Promise<void> {
|
|
322
410
|
try {
|
|
@@ -337,7 +425,6 @@ export class ClickhouseStore extends MastraStorage {
|
|
|
337
425
|
${['id String'].concat(columns)}
|
|
338
426
|
)
|
|
339
427
|
ENGINE = ${TABLE_ENGINES[tableName]}
|
|
340
|
-
PARTITION BY "createdAt"
|
|
341
428
|
PRIMARY KEY (createdAt, run_id, workflow_name)
|
|
342
429
|
ORDER BY (createdAt, run_id, workflow_name)
|
|
343
430
|
${rowTtl ? `TTL toDateTime(${rowTtl.ttlKey ?? 'createdAt'}) + INTERVAL ${rowTtl.interval} ${rowTtl.unit}` : ''}
|
|
@@ -348,7 +435,6 @@ export class ClickhouseStore extends MastraStorage {
|
|
|
348
435
|
${columns}
|
|
349
436
|
)
|
|
350
437
|
ENGINE = ${TABLE_ENGINES[tableName]}
|
|
351
|
-
PARTITION BY "createdAt"
|
|
352
438
|
PRIMARY KEY (createdAt, ${tableName === TABLE_EVALS ? 'run_id' : 'id'})
|
|
353
439
|
ORDER BY (createdAt, ${tableName === TABLE_EVALS ? 'run_id' : 'id'})
|
|
354
440
|
${this.ttl?.[tableName]?.row ? `TTL toDateTime(createdAt) + INTERVAL ${this.ttl[tableName].row.interval} ${this.ttl[tableName].row.unit}` : ''}
|
|
@@ -365,9 +451,88 @@ export class ClickhouseStore extends MastraStorage {
|
|
|
365
451
|
output_format_json_quote_64bit_integers: 0,
|
|
366
452
|
},
|
|
367
453
|
});
|
|
368
|
-
} catch (error) {
|
|
369
|
-
|
|
370
|
-
|
|
454
|
+
} catch (error: any) {
|
|
455
|
+
throw new MastraError(
|
|
456
|
+
{
|
|
457
|
+
id: 'CLICKHOUSE_STORAGE_CREATE_TABLE_FAILED',
|
|
458
|
+
domain: ErrorDomain.STORAGE,
|
|
459
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
460
|
+
details: { tableName },
|
|
461
|
+
},
|
|
462
|
+
error,
|
|
463
|
+
);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
protected getSqlType(type: StorageColumn['type']): string {
|
|
468
|
+
switch (type) {
|
|
469
|
+
case 'text':
|
|
470
|
+
return 'String';
|
|
471
|
+
case 'timestamp':
|
|
472
|
+
return 'DateTime64(3)';
|
|
473
|
+
case 'integer':
|
|
474
|
+
case 'bigint':
|
|
475
|
+
return 'Int64';
|
|
476
|
+
case 'jsonb':
|
|
477
|
+
return 'String';
|
|
478
|
+
default:
|
|
479
|
+
return super.getSqlType(type); // fallback to base implementation
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Alters table schema to add columns if they don't exist
|
|
485
|
+
* @param tableName Name of the table
|
|
486
|
+
* @param schema Schema of the table
|
|
487
|
+
* @param ifNotExists Array of column names to add if they don't exist
|
|
488
|
+
*/
|
|
489
|
+
async alterTable({
|
|
490
|
+
tableName,
|
|
491
|
+
schema,
|
|
492
|
+
ifNotExists,
|
|
493
|
+
}: {
|
|
494
|
+
tableName: TABLE_NAMES;
|
|
495
|
+
schema: Record<string, StorageColumn>;
|
|
496
|
+
ifNotExists: string[];
|
|
497
|
+
}): Promise<void> {
|
|
498
|
+
try {
|
|
499
|
+
// 1. Get existing columns
|
|
500
|
+
const describeSql = `DESCRIBE TABLE ${tableName}`;
|
|
501
|
+
const result = await this.db.query({
|
|
502
|
+
query: describeSql,
|
|
503
|
+
});
|
|
504
|
+
const rows = await result.json();
|
|
505
|
+
const existingColumnNames = new Set(rows.data.map((row: any) => row.name.toLowerCase()));
|
|
506
|
+
|
|
507
|
+
// 2. Add missing columns
|
|
508
|
+
for (const columnName of ifNotExists) {
|
|
509
|
+
if (!existingColumnNames.has(columnName.toLowerCase()) && schema[columnName]) {
|
|
510
|
+
const columnDef = schema[columnName];
|
|
511
|
+
let sqlType = this.getSqlType(columnDef.type);
|
|
512
|
+
if (columnDef.nullable !== false) {
|
|
513
|
+
sqlType = `Nullable(${sqlType})`;
|
|
514
|
+
}
|
|
515
|
+
const defaultValue = columnDef.nullable === false ? this.getDefaultValue(columnDef.type) : '';
|
|
516
|
+
// Use backticks or double quotes as needed for identifiers
|
|
517
|
+
const alterSql =
|
|
518
|
+
`ALTER TABLE ${tableName} ADD COLUMN IF NOT EXISTS "${columnName}" ${sqlType} ${defaultValue}`.trim();
|
|
519
|
+
|
|
520
|
+
await this.db.query({
|
|
521
|
+
query: alterSql,
|
|
522
|
+
});
|
|
523
|
+
this.logger?.debug?.(`Added column ${columnName} to table ${tableName}`);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
} catch (error: any) {
|
|
527
|
+
throw new MastraError(
|
|
528
|
+
{
|
|
529
|
+
id: 'CLICKHOUSE_STORAGE_ALTER_TABLE_FAILED',
|
|
530
|
+
domain: ErrorDomain.STORAGE,
|
|
531
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
532
|
+
details: { tableName },
|
|
533
|
+
},
|
|
534
|
+
error,
|
|
535
|
+
);
|
|
371
536
|
}
|
|
372
537
|
}
|
|
373
538
|
|
|
@@ -383,9 +548,16 @@ export class ClickhouseStore extends MastraStorage {
|
|
|
383
548
|
output_format_json_quote_64bit_integers: 0,
|
|
384
549
|
},
|
|
385
550
|
});
|
|
386
|
-
} catch (error) {
|
|
387
|
-
|
|
388
|
-
|
|
551
|
+
} catch (error: any) {
|
|
552
|
+
throw new MastraError(
|
|
553
|
+
{
|
|
554
|
+
id: 'CLICKHOUSE_STORAGE_CLEAR_TABLE_FAILED',
|
|
555
|
+
domain: ErrorDomain.STORAGE,
|
|
556
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
557
|
+
details: { tableName },
|
|
558
|
+
},
|
|
559
|
+
error,
|
|
560
|
+
);
|
|
389
561
|
}
|
|
390
562
|
}
|
|
391
563
|
|
|
@@ -408,13 +580,26 @@ export class ClickhouseStore extends MastraStorage {
|
|
|
408
580
|
use_client_time_zone: 1,
|
|
409
581
|
},
|
|
410
582
|
});
|
|
411
|
-
} catch (error) {
|
|
412
|
-
|
|
413
|
-
|
|
583
|
+
} catch (error: any) {
|
|
584
|
+
throw new MastraError(
|
|
585
|
+
{
|
|
586
|
+
id: 'CLICKHOUSE_STORAGE_INSERT_FAILED',
|
|
587
|
+
domain: ErrorDomain.STORAGE,
|
|
588
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
589
|
+
details: { tableName },
|
|
590
|
+
},
|
|
591
|
+
error,
|
|
592
|
+
);
|
|
414
593
|
}
|
|
415
594
|
}
|
|
416
595
|
|
|
417
|
-
async load<R>({
|
|
596
|
+
async load<R>({
|
|
597
|
+
tableName,
|
|
598
|
+
keys,
|
|
599
|
+
}: {
|
|
600
|
+
tableName: SUPPORTED_TABLE_NAMES;
|
|
601
|
+
keys: Record<string, string>;
|
|
602
|
+
}): Promise<R | null> {
|
|
418
603
|
try {
|
|
419
604
|
const keyEntries = Object.entries(keys);
|
|
420
605
|
const conditions = keyEntries
|
|
@@ -428,7 +613,7 @@ export class ClickhouseStore extends MastraStorage {
|
|
|
428
613
|
}, {});
|
|
429
614
|
|
|
430
615
|
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
|
|
616
|
+
query: `SELECT *, toDateTime64(createdAt, 3) as createdAt, toDateTime64(updatedAt, 3) as updatedAt FROM ${tableName} ${TABLE_ENGINES[tableName as SUPPORTED_TABLE_NAMES].startsWith('ReplacingMergeTree') ? 'FINAL' : ''} WHERE ${conditions}`,
|
|
432
617
|
query_params: values,
|
|
433
618
|
clickhouse_settings: {
|
|
434
619
|
// Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
|
|
@@ -459,8 +644,15 @@ export class ClickhouseStore extends MastraStorage {
|
|
|
459
644
|
const data: R = transformRow(rows.data[0]);
|
|
460
645
|
return data;
|
|
461
646
|
} catch (error) {
|
|
462
|
-
|
|
463
|
-
|
|
647
|
+
throw new MastraError(
|
|
648
|
+
{
|
|
649
|
+
id: 'CLICKHOUSE_STORAGE_LOAD_FAILED',
|
|
650
|
+
domain: ErrorDomain.STORAGE,
|
|
651
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
652
|
+
details: { tableName },
|
|
653
|
+
},
|
|
654
|
+
error,
|
|
655
|
+
);
|
|
464
656
|
}
|
|
465
657
|
}
|
|
466
658
|
|
|
@@ -500,9 +692,16 @@ export class ClickhouseStore extends MastraStorage {
|
|
|
500
692
|
createdAt: thread.createdAt,
|
|
501
693
|
updatedAt: thread.updatedAt,
|
|
502
694
|
};
|
|
503
|
-
} catch (error) {
|
|
504
|
-
|
|
505
|
-
|
|
695
|
+
} catch (error: any) {
|
|
696
|
+
throw new MastraError(
|
|
697
|
+
{
|
|
698
|
+
id: 'CLICKHOUSE_STORAGE_GET_THREAD_BY_ID_FAILED',
|
|
699
|
+
domain: ErrorDomain.STORAGE,
|
|
700
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
701
|
+
details: { threadId },
|
|
702
|
+
},
|
|
703
|
+
error,
|
|
704
|
+
);
|
|
506
705
|
}
|
|
507
706
|
}
|
|
508
707
|
|
|
@@ -538,8 +737,15 @@ export class ClickhouseStore extends MastraStorage {
|
|
|
538
737
|
updatedAt: thread.updatedAt,
|
|
539
738
|
}));
|
|
540
739
|
} catch (error) {
|
|
541
|
-
|
|
542
|
-
|
|
740
|
+
throw new MastraError(
|
|
741
|
+
{
|
|
742
|
+
id: 'CLICKHOUSE_STORAGE_GET_THREADS_BY_RESOURCE_ID_FAILED',
|
|
743
|
+
domain: ErrorDomain.STORAGE,
|
|
744
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
745
|
+
details: { resourceId },
|
|
746
|
+
},
|
|
747
|
+
error,
|
|
748
|
+
);
|
|
543
749
|
}
|
|
544
750
|
}
|
|
545
751
|
|
|
@@ -565,8 +771,15 @@ export class ClickhouseStore extends MastraStorage {
|
|
|
565
771
|
|
|
566
772
|
return thread;
|
|
567
773
|
} catch (error) {
|
|
568
|
-
|
|
569
|
-
|
|
774
|
+
throw new MastraError(
|
|
775
|
+
{
|
|
776
|
+
id: 'CLICKHOUSE_STORAGE_SAVE_THREAD_FAILED',
|
|
777
|
+
domain: ErrorDomain.STORAGE,
|
|
778
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
779
|
+
details: { threadId: thread.id },
|
|
780
|
+
},
|
|
781
|
+
error,
|
|
782
|
+
);
|
|
570
783
|
}
|
|
571
784
|
}
|
|
572
785
|
|
|
@@ -601,15 +814,18 @@ export class ClickhouseStore extends MastraStorage {
|
|
|
601
814
|
|
|
602
815
|
await this.db.insert({
|
|
603
816
|
table: TABLE_THREADS,
|
|
817
|
+
format: 'JSONEachRow',
|
|
604
818
|
values: [
|
|
605
819
|
{
|
|
606
|
-
|
|
820
|
+
id: updatedThread.id,
|
|
821
|
+
resourceId: updatedThread.resourceId,
|
|
822
|
+
title: updatedThread.title,
|
|
823
|
+
metadata: updatedThread.metadata,
|
|
824
|
+
createdAt: updatedThread.createdAt,
|
|
607
825
|
updatedAt: updatedThread.updatedAt.toISOString(),
|
|
608
826
|
},
|
|
609
827
|
],
|
|
610
|
-
format: 'JSONEachRow',
|
|
611
828
|
clickhouse_settings: {
|
|
612
|
-
// Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
|
|
613
829
|
date_time_input_format: 'best_effort',
|
|
614
830
|
use_client_time_zone: 1,
|
|
615
831
|
output_format_json_quote_64bit_integers: 0,
|
|
@@ -618,8 +834,15 @@ export class ClickhouseStore extends MastraStorage {
|
|
|
618
834
|
|
|
619
835
|
return updatedThread;
|
|
620
836
|
} catch (error) {
|
|
621
|
-
|
|
622
|
-
|
|
837
|
+
throw new MastraError(
|
|
838
|
+
{
|
|
839
|
+
id: 'CLICKHOUSE_STORAGE_UPDATE_THREAD_FAILED',
|
|
840
|
+
domain: ErrorDomain.STORAGE,
|
|
841
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
842
|
+
details: { threadId: id, title },
|
|
843
|
+
},
|
|
844
|
+
error,
|
|
845
|
+
);
|
|
623
846
|
}
|
|
624
847
|
}
|
|
625
848
|
|
|
@@ -627,7 +850,7 @@ export class ClickhouseStore extends MastraStorage {
|
|
|
627
850
|
try {
|
|
628
851
|
// First delete all messages associated with this thread
|
|
629
852
|
await this.db.command({
|
|
630
|
-
query: `DELETE FROM "${TABLE_MESSAGES}" WHERE thread_id =
|
|
853
|
+
query: `DELETE FROM "${TABLE_MESSAGES}" WHERE thread_id = {var_thread_id:String};`,
|
|
631
854
|
query_params: { var_thread_id: threadId },
|
|
632
855
|
clickhouse_settings: {
|
|
633
856
|
output_format_json_quote_64bit_integers: 0,
|
|
@@ -643,15 +866,29 @@ export class ClickhouseStore extends MastraStorage {
|
|
|
643
866
|
},
|
|
644
867
|
});
|
|
645
868
|
} catch (error) {
|
|
646
|
-
|
|
647
|
-
|
|
869
|
+
throw new MastraError(
|
|
870
|
+
{
|
|
871
|
+
id: 'CLICKHOUSE_STORAGE_DELETE_THREAD_FAILED',
|
|
872
|
+
domain: ErrorDomain.STORAGE,
|
|
873
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
874
|
+
details: { threadId },
|
|
875
|
+
},
|
|
876
|
+
error,
|
|
877
|
+
);
|
|
648
878
|
}
|
|
649
879
|
}
|
|
650
880
|
|
|
651
|
-
async getMessages
|
|
881
|
+
public async getMessages(args: StorageGetMessagesArg & { format?: 'v1' }): Promise<MastraMessageV1[]>;
|
|
882
|
+
public async getMessages(args: StorageGetMessagesArg & { format: 'v2' }): Promise<MastraMessageV2[]>;
|
|
883
|
+
public async getMessages({
|
|
884
|
+
threadId,
|
|
885
|
+
resourceId,
|
|
886
|
+
selectBy,
|
|
887
|
+
format,
|
|
888
|
+
}: StorageGetMessagesArg & { format?: 'v1' | 'v2' }): Promise<MastraMessageV1[] | MastraMessageV2[]> {
|
|
652
889
|
try {
|
|
653
890
|
const messages: any[] = [];
|
|
654
|
-
const limit =
|
|
891
|
+
const limit = this.resolveMessageLimit({ last: selectBy?.last, defaultLimit: 40 });
|
|
655
892
|
const include = selectBy?.include || [];
|
|
656
893
|
|
|
657
894
|
if (include.length) {
|
|
@@ -755,18 +992,33 @@ export class ClickhouseStore extends MastraStorage {
|
|
|
755
992
|
}
|
|
756
993
|
});
|
|
757
994
|
|
|
758
|
-
|
|
995
|
+
const list = new MessageList({ threadId, resourceId }).add(messages, 'memory');
|
|
996
|
+
if (format === `v2`) return list.get.all.v2();
|
|
997
|
+
return list.get.all.v1();
|
|
759
998
|
} catch (error) {
|
|
760
|
-
|
|
761
|
-
|
|
999
|
+
throw new MastraError(
|
|
1000
|
+
{
|
|
1001
|
+
id: 'CLICKHOUSE_STORAGE_GET_MESSAGES_FAILED',
|
|
1002
|
+
domain: ErrorDomain.STORAGE,
|
|
1003
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1004
|
+
details: { threadId, resourceId: resourceId ?? '' },
|
|
1005
|
+
},
|
|
1006
|
+
error,
|
|
1007
|
+
);
|
|
762
1008
|
}
|
|
763
1009
|
}
|
|
764
1010
|
|
|
765
|
-
async saveMessages(
|
|
1011
|
+
async saveMessages(args: { messages: MastraMessageV1[]; format?: undefined | 'v1' }): Promise<MastraMessageV1[]>;
|
|
1012
|
+
async saveMessages(args: { messages: MastraMessageV2[]; format: 'v2' }): Promise<MastraMessageV2[]>;
|
|
1013
|
+
async saveMessages(
|
|
1014
|
+
args: { messages: MastraMessageV1[]; format?: undefined | 'v1' } | { messages: MastraMessageV2[]; format: 'v2' },
|
|
1015
|
+
): Promise<MastraMessageV2[] | MastraMessageV1[]> {
|
|
1016
|
+
const { messages, format = 'v1' } = args;
|
|
766
1017
|
if (messages.length === 0) return messages;
|
|
767
1018
|
|
|
768
1019
|
try {
|
|
769
1020
|
const threadId = messages[0]?.threadId;
|
|
1021
|
+
const resourceId = messages[0]?.resourceId;
|
|
770
1022
|
if (!threadId) {
|
|
771
1023
|
throw new Error('Thread ID is required');
|
|
772
1024
|
}
|
|
@@ -777,29 +1029,111 @@ export class ClickhouseStore extends MastraStorage {
|
|
|
777
1029
|
throw new Error(`Thread ${threadId} not found`);
|
|
778
1030
|
}
|
|
779
1031
|
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
type: message.type,
|
|
790
|
-
})),
|
|
1032
|
+
// Clickhouse's MergeTree engine does not support native upserts or unique constraints on (id, thread_id).
|
|
1033
|
+
// Note: We cannot switch to ReplacingMergeTree without a schema migration,
|
|
1034
|
+
// as it would require altering the table engine.
|
|
1035
|
+
// To ensure correct upsert behavior, we first fetch existing (id, thread_id) pairs for the incoming messages.
|
|
1036
|
+
const existingResult = await this.db.query({
|
|
1037
|
+
query: `SELECT id, thread_id FROM ${TABLE_MESSAGES} WHERE id IN ({ids:Array(String)})`,
|
|
1038
|
+
query_params: {
|
|
1039
|
+
ids: messages.map(m => m.id),
|
|
1040
|
+
},
|
|
791
1041
|
clickhouse_settings: {
|
|
792
1042
|
// Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
|
|
793
1043
|
date_time_input_format: 'best_effort',
|
|
1044
|
+
date_time_output_format: 'iso',
|
|
794
1045
|
use_client_time_zone: 1,
|
|
795
1046
|
output_format_json_quote_64bit_integers: 0,
|
|
796
1047
|
},
|
|
1048
|
+
format: 'JSONEachRow',
|
|
797
1049
|
});
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
1050
|
+
const existingRows: Array<{ id: string; thread_id: string }> = await existingResult.json();
|
|
1051
|
+
|
|
1052
|
+
const existingSet = new Set(existingRows.map(row => `${row.id}::${row.thread_id}`));
|
|
1053
|
+
// Partition the batch into new inserts and updates:
|
|
1054
|
+
// New messages are inserted in bulk.
|
|
1055
|
+
const toInsert = messages.filter(m => !existingSet.has(`${m.id}::${threadId}`));
|
|
1056
|
+
// Existing messages are updated via ALTER TABLE ... UPDATE.
|
|
1057
|
+
const toUpdate = messages.filter(m => existingSet.has(`${m.id}::${threadId}`));
|
|
1058
|
+
const updatePromises = toUpdate.map(message =>
|
|
1059
|
+
this.db.command({
|
|
1060
|
+
query: `
|
|
1061
|
+
ALTER TABLE ${TABLE_MESSAGES}
|
|
1062
|
+
UPDATE content = {var_content:String}, role = {var_role:String}, type = {var_type:String}
|
|
1063
|
+
WHERE id = {var_id:String} AND thread_id = {var_thread_id:String}
|
|
1064
|
+
`,
|
|
1065
|
+
query_params: {
|
|
1066
|
+
var_content: typeof message.content === 'string' ? message.content : JSON.stringify(message.content),
|
|
1067
|
+
var_role: message.role,
|
|
1068
|
+
var_type: message.type || 'v2',
|
|
1069
|
+
var_id: message.id,
|
|
1070
|
+
var_thread_id: threadId,
|
|
1071
|
+
},
|
|
1072
|
+
clickhouse_settings: {
|
|
1073
|
+
// Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
|
|
1074
|
+
date_time_input_format: 'best_effort',
|
|
1075
|
+
use_client_time_zone: 1,
|
|
1076
|
+
output_format_json_quote_64bit_integers: 0,
|
|
1077
|
+
},
|
|
1078
|
+
}),
|
|
1079
|
+
);
|
|
1080
|
+
|
|
1081
|
+
// Execute message inserts and thread update in parallel for better performance
|
|
1082
|
+
await Promise.all([
|
|
1083
|
+
// Insert messages
|
|
1084
|
+
this.db.insert({
|
|
1085
|
+
table: TABLE_MESSAGES,
|
|
1086
|
+
format: 'JSONEachRow',
|
|
1087
|
+
values: toInsert.map(message => ({
|
|
1088
|
+
id: message.id,
|
|
1089
|
+
thread_id: threadId,
|
|
1090
|
+
content: typeof message.content === 'string' ? message.content : JSON.stringify(message.content),
|
|
1091
|
+
createdAt: message.createdAt.toISOString(),
|
|
1092
|
+
role: message.role,
|
|
1093
|
+
type: message.type || 'v2',
|
|
1094
|
+
})),
|
|
1095
|
+
clickhouse_settings: {
|
|
1096
|
+
// Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
|
|
1097
|
+
date_time_input_format: 'best_effort',
|
|
1098
|
+
use_client_time_zone: 1,
|
|
1099
|
+
output_format_json_quote_64bit_integers: 0,
|
|
1100
|
+
},
|
|
1101
|
+
}),
|
|
1102
|
+
...updatePromises,
|
|
1103
|
+
// Update thread's updatedAt timestamp
|
|
1104
|
+
this.db.insert({
|
|
1105
|
+
table: TABLE_THREADS,
|
|
1106
|
+
format: 'JSONEachRow',
|
|
1107
|
+
values: [
|
|
1108
|
+
{
|
|
1109
|
+
id: thread.id,
|
|
1110
|
+
resourceId: thread.resourceId,
|
|
1111
|
+
title: thread.title,
|
|
1112
|
+
metadata: thread.metadata,
|
|
1113
|
+
createdAt: thread.createdAt,
|
|
1114
|
+
updatedAt: new Date().toISOString(),
|
|
1115
|
+
},
|
|
1116
|
+
],
|
|
1117
|
+
clickhouse_settings: {
|
|
1118
|
+
date_time_input_format: 'best_effort',
|
|
1119
|
+
use_client_time_zone: 1,
|
|
1120
|
+
output_format_json_quote_64bit_integers: 0,
|
|
1121
|
+
},
|
|
1122
|
+
}),
|
|
1123
|
+
]);
|
|
1124
|
+
|
|
1125
|
+
const list = new MessageList({ threadId, resourceId }).add(messages, 'memory');
|
|
1126
|
+
if (format === `v2`) return list.get.all.v2();
|
|
1127
|
+
return list.get.all.v1();
|
|
1128
|
+
} catch (error: any) {
|
|
1129
|
+
throw new MastraError(
|
|
1130
|
+
{
|
|
1131
|
+
id: 'CLICKHOUSE_STORAGE_SAVE_MESSAGES_FAILED',
|
|
1132
|
+
domain: ErrorDomain.STORAGE,
|
|
1133
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1134
|
+
},
|
|
1135
|
+
error,
|
|
1136
|
+
);
|
|
803
1137
|
}
|
|
804
1138
|
}
|
|
805
1139
|
|
|
@@ -844,9 +1178,16 @@ export class ClickhouseStore extends MastraStorage {
|
|
|
844
1178
|
output_format_json_quote_64bit_integers: 0,
|
|
845
1179
|
},
|
|
846
1180
|
});
|
|
847
|
-
} catch (error) {
|
|
848
|
-
|
|
849
|
-
|
|
1181
|
+
} catch (error: any) {
|
|
1182
|
+
throw new MastraError(
|
|
1183
|
+
{
|
|
1184
|
+
id: 'CLICKHOUSE_STORAGE_PERSIST_WORKFLOW_SNAPSHOT_FAILED',
|
|
1185
|
+
domain: ErrorDomain.STORAGE,
|
|
1186
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1187
|
+
details: { workflowName, runId },
|
|
1188
|
+
},
|
|
1189
|
+
error,
|
|
1190
|
+
);
|
|
850
1191
|
}
|
|
851
1192
|
}
|
|
852
1193
|
|
|
@@ -871,9 +1212,16 @@ export class ClickhouseStore extends MastraStorage {
|
|
|
871
1212
|
}
|
|
872
1213
|
|
|
873
1214
|
return (result as any).snapshot;
|
|
874
|
-
} catch (error) {
|
|
875
|
-
|
|
876
|
-
|
|
1215
|
+
} catch (error: any) {
|
|
1216
|
+
throw new MastraError(
|
|
1217
|
+
{
|
|
1218
|
+
id: 'CLICKHOUSE_STORAGE_LOAD_WORKFLOW_SNAPSHOT_FAILED',
|
|
1219
|
+
domain: ErrorDomain.STORAGE,
|
|
1220
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1221
|
+
details: { workflowName, runId },
|
|
1222
|
+
},
|
|
1223
|
+
error,
|
|
1224
|
+
);
|
|
877
1225
|
}
|
|
878
1226
|
}
|
|
879
1227
|
|
|
@@ -986,9 +1334,16 @@ export class ClickhouseStore extends MastraStorage {
|
|
|
986
1334
|
|
|
987
1335
|
// Use runs.length as total when not paginating
|
|
988
1336
|
return { runs, total: total || runs.length };
|
|
989
|
-
} catch (error) {
|
|
990
|
-
|
|
991
|
-
|
|
1337
|
+
} catch (error: any) {
|
|
1338
|
+
throw new MastraError(
|
|
1339
|
+
{
|
|
1340
|
+
id: 'CLICKHOUSE_STORAGE_GET_WORKFLOW_RUNS_FAILED',
|
|
1341
|
+
domain: ErrorDomain.STORAGE,
|
|
1342
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1343
|
+
details: { workflowName: workflowName ?? '', resourceId: resourceId ?? '' },
|
|
1344
|
+
},
|
|
1345
|
+
error,
|
|
1346
|
+
);
|
|
992
1347
|
}
|
|
993
1348
|
}
|
|
994
1349
|
|
|
@@ -1037,9 +1392,16 @@ export class ClickhouseStore extends MastraStorage {
|
|
|
1037
1392
|
return null;
|
|
1038
1393
|
}
|
|
1039
1394
|
return this.parseWorkflowRun(resultJson[0]);
|
|
1040
|
-
} catch (error) {
|
|
1041
|
-
|
|
1042
|
-
|
|
1395
|
+
} catch (error: any) {
|
|
1396
|
+
throw new MastraError(
|
|
1397
|
+
{
|
|
1398
|
+
id: 'CLICKHOUSE_STORAGE_GET_WORKFLOW_RUN_BY_ID_FAILED',
|
|
1399
|
+
domain: ErrorDomain.STORAGE,
|
|
1400
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1401
|
+
details: { runId: runId ?? '', workflowName: workflowName ?? '' },
|
|
1402
|
+
},
|
|
1403
|
+
error,
|
|
1404
|
+
);
|
|
1043
1405
|
}
|
|
1044
1406
|
}
|
|
1045
1407
|
|
|
@@ -1052,7 +1414,51 @@ export class ClickhouseStore extends MastraStorage {
|
|
|
1052
1414
|
return columns.some(c => c.name === column);
|
|
1053
1415
|
}
|
|
1054
1416
|
|
|
1417
|
+
async getTracesPaginated(_args: StorageGetTracesArg): Promise<PaginationInfo & { traces: Trace[] }> {
|
|
1418
|
+
throw new MastraError({
|
|
1419
|
+
id: 'CLICKHOUSE_STORAGE_GET_TRACES_PAGINATED_FAILED',
|
|
1420
|
+
domain: ErrorDomain.STORAGE,
|
|
1421
|
+
category: ErrorCategory.USER,
|
|
1422
|
+
text: 'Method not implemented.',
|
|
1423
|
+
});
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
async getThreadsByResourceIdPaginated(_args: {
|
|
1427
|
+
resourceId: string;
|
|
1428
|
+
page?: number;
|
|
1429
|
+
perPage?: number;
|
|
1430
|
+
}): Promise<PaginationInfo & { threads: StorageThreadType[] }> {
|
|
1431
|
+
throw new MastraError({
|
|
1432
|
+
id: 'CLICKHOUSE_STORAGE_GET_THREADS_BY_RESOURCE_ID_PAGINATED_FAILED',
|
|
1433
|
+
domain: ErrorDomain.STORAGE,
|
|
1434
|
+
category: ErrorCategory.USER,
|
|
1435
|
+
text: 'Method not implemented.',
|
|
1436
|
+
});
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
async getMessagesPaginated(
|
|
1440
|
+
_args: StorageGetMessagesArg,
|
|
1441
|
+
): Promise<PaginationInfo & { messages: MastraMessageV1[] | MastraMessageV2[] }> {
|
|
1442
|
+
throw new MastraError({
|
|
1443
|
+
id: 'CLICKHOUSE_STORAGE_GET_MESSAGES_PAGINATED_FAILED',
|
|
1444
|
+
domain: ErrorDomain.STORAGE,
|
|
1445
|
+
category: ErrorCategory.USER,
|
|
1446
|
+
text: 'Method not implemented.',
|
|
1447
|
+
});
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1055
1450
|
async close(): Promise<void> {
|
|
1056
1451
|
await this.db.close();
|
|
1057
1452
|
}
|
|
1453
|
+
|
|
1454
|
+
async updateMessages(_args: {
|
|
1455
|
+
messages: Partial<Omit<MastraMessageV2, 'createdAt'>> &
|
|
1456
|
+
{
|
|
1457
|
+
id: string;
|
|
1458
|
+
content?: { metadata?: MastraMessageContentV2['metadata']; content?: MastraMessageContentV2['content'] };
|
|
1459
|
+
}[];
|
|
1460
|
+
}): Promise<MastraMessageV2[]> {
|
|
1461
|
+
this.logger.error('updateMessages is not yet implemented in ClickhouseStore');
|
|
1462
|
+
throw new Error('Method not implemented');
|
|
1463
|
+
}
|
|
1058
1464
|
}
|