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