@mastra/clickhouse 0.0.0-taofeeqInngest-20250603090617 → 0.0.0-transpile-packages-20250724123433
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 +219 -2
- package/LICENSE.md +12 -4
- package/dist/_tsup-dts-rollup.d.cts +358 -29
- package/dist/_tsup-dts-rollup.d.ts +358 -29
- package/dist/index.cjs +2239 -454
- package/dist/index.d.cts +0 -2
- package/dist/index.d.ts +0 -2
- package/dist/index.js +2222 -435
- package/package.json +12 -11
- 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 +14 -836
- package/src/storage/index.ts +228 -901
- package/.turbo/turbo-build.log +0 -23
package/src/storage/index.ts
CHANGED
|
@@ -1,35 +1,34 @@
|
|
|
1
1
|
import type { ClickHouseClient } from '@clickhouse/client';
|
|
2
2
|
import { createClient } from '@clickhouse/client';
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import type {
|
|
6
|
-
import type {
|
|
7
|
-
import {
|
|
8
|
-
MastraStorage,
|
|
9
|
-
TABLE_EVALS,
|
|
10
|
-
TABLE_MESSAGES,
|
|
11
|
-
TABLE_SCHEMAS,
|
|
12
|
-
TABLE_THREADS,
|
|
13
|
-
TABLE_TRACES,
|
|
14
|
-
TABLE_WORKFLOW_SNAPSHOT,
|
|
15
|
-
} 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';
|
|
16
8
|
import type {
|
|
9
|
+
TABLE_SCHEMAS,
|
|
17
10
|
EvalRow,
|
|
11
|
+
PaginationInfo,
|
|
18
12
|
StorageColumn,
|
|
19
13
|
StorageGetMessagesArg,
|
|
20
14
|
TABLE_NAMES,
|
|
21
15
|
WorkflowRun,
|
|
22
16
|
WorkflowRuns,
|
|
17
|
+
StorageGetTracesArg,
|
|
18
|
+
StorageGetTracesPaginatedArg,
|
|
19
|
+
StoragePagination,
|
|
20
|
+
StorageDomains,
|
|
21
|
+
PaginationArgs,
|
|
22
|
+
StorageResourceType,
|
|
23
23
|
} from '@mastra/core/storage';
|
|
24
|
+
import type { Trace } from '@mastra/core/telemetry';
|
|
24
25
|
import type { WorkflowRunState } from '@mastra/core/workflows';
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
}
|
|
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';
|
|
33
32
|
|
|
34
33
|
type IntervalUnit =
|
|
35
34
|
| 'NANOSECOND'
|
|
@@ -62,47 +61,15 @@ export type ClickhouseConfig = {
|
|
|
62
61
|
};
|
|
63
62
|
};
|
|
64
63
|
|
|
65
|
-
export const TABLE_ENGINES: Record<TABLE_NAMES, string> = {
|
|
66
|
-
[TABLE_MESSAGES]: `MergeTree()`,
|
|
67
|
-
[TABLE_WORKFLOW_SNAPSHOT]: `ReplacingMergeTree()`,
|
|
68
|
-
[TABLE_TRACES]: `MergeTree()`,
|
|
69
|
-
[TABLE_THREADS]: `ReplacingMergeTree()`,
|
|
70
|
-
[TABLE_EVALS]: `MergeTree()`,
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
export const COLUMN_TYPES: Record<StorageColumn['type'], string> = {
|
|
74
|
-
text: 'String',
|
|
75
|
-
timestamp: 'DateTime64(3)',
|
|
76
|
-
uuid: 'String',
|
|
77
|
-
jsonb: 'String',
|
|
78
|
-
integer: 'Int64',
|
|
79
|
-
bigint: 'Int64',
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
function transformRows<R>(rows: any[]): R[] {
|
|
83
|
-
return rows.map((row: any) => transformRow<R>(row));
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function transformRow<R>(row: any): R {
|
|
87
|
-
if (!row) {
|
|
88
|
-
return row;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (row.createdAt) {
|
|
92
|
-
row.createdAt = new Date(row.createdAt);
|
|
93
|
-
}
|
|
94
|
-
if (row.updatedAt) {
|
|
95
|
-
row.updatedAt = new Date(row.updatedAt);
|
|
96
|
-
}
|
|
97
|
-
return row;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
64
|
export class ClickhouseStore extends MastraStorage {
|
|
101
65
|
protected db: ClickHouseClient;
|
|
102
66
|
protected ttl: ClickhouseConfig['ttl'] = {};
|
|
103
67
|
|
|
68
|
+
stores: StorageDomains;
|
|
69
|
+
|
|
104
70
|
constructor(config: ClickhouseConfig) {
|
|
105
71
|
super({ name: 'ClickhouseStore' });
|
|
72
|
+
|
|
106
73
|
this.db = createClient({
|
|
107
74
|
url: config.url,
|
|
108
75
|
username: config.username,
|
|
@@ -115,459 +82,199 @@ export class ClickhouseStore extends MastraStorage {
|
|
|
115
82
|
},
|
|
116
83
|
});
|
|
117
84
|
this.ttl = config.ttl;
|
|
118
|
-
}
|
|
119
85
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
const
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
+
}
|
|
128
102
|
|
|
103
|
+
get supports(): {
|
|
104
|
+
selectByIncludeResourceScope: boolean;
|
|
105
|
+
resourceWorkingMemory: boolean;
|
|
106
|
+
hasColumn: boolean;
|
|
107
|
+
createTable: boolean;
|
|
108
|
+
} {
|
|
129
109
|
return {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
metricName: row.metric_name as string,
|
|
135
|
-
instructions: row.instructions as string,
|
|
136
|
-
testInfo: testInfoValue as TestInfo,
|
|
137
|
-
globalRunId: row.global_run_id as string,
|
|
138
|
-
runId: row.run_id as string,
|
|
139
|
-
createdAt: row.created_at as string,
|
|
110
|
+
selectByIncludeResourceScope: true,
|
|
111
|
+
resourceWorkingMemory: true,
|
|
112
|
+
hasColumn: true,
|
|
113
|
+
createTable: true,
|
|
140
114
|
};
|
|
141
115
|
}
|
|
142
116
|
|
|
143
117
|
async getEvalsByAgentName(agentName: string, type?: 'test' | 'live'): Promise<EvalRow[]> {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
const typeCondition =
|
|
147
|
-
type === 'test'
|
|
148
|
-
? " AND test_info IS NOT NULL AND JSONExtractString(test_info, 'testPath') IS NOT NULL"
|
|
149
|
-
: type === 'live'
|
|
150
|
-
? " AND (test_info IS NULL OR JSONExtractString(test_info, 'testPath') IS NULL)"
|
|
151
|
-
: '';
|
|
152
|
-
|
|
153
|
-
const result = await this.db.query({
|
|
154
|
-
query: `${baseQuery}${typeCondition} ORDER BY createdAt DESC`,
|
|
155
|
-
query_params: { var_agent_name: agentName },
|
|
156
|
-
clickhouse_settings: {
|
|
157
|
-
date_time_input_format: 'best_effort',
|
|
158
|
-
date_time_output_format: 'iso',
|
|
159
|
-
use_client_time_zone: 1,
|
|
160
|
-
output_format_json_quote_64bit_integers: 0,
|
|
161
|
-
},
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
if (!result) {
|
|
165
|
-
return [];
|
|
166
|
-
}
|
|
118
|
+
return this.stores.legacyEvals.getEvalsByAgentName(agentName, type);
|
|
119
|
+
}
|
|
167
120
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
if (error instanceof Error && error.message.includes('no such table')) {
|
|
173
|
-
return [];
|
|
174
|
-
}
|
|
175
|
-
this.logger.error('Failed to get evals for the specified agent: ' + (error as any)?.message);
|
|
176
|
-
throw error;
|
|
177
|
-
}
|
|
121
|
+
async getEvals(
|
|
122
|
+
options: { agentName?: string; type?: 'test' | 'live' } & PaginationArgs,
|
|
123
|
+
): Promise<PaginationInfo & { evals: EvalRow[] }> {
|
|
124
|
+
return this.stores.legacyEvals.getEvals(options);
|
|
178
125
|
}
|
|
179
126
|
|
|
180
127
|
async batchInsert({ tableName, records }: { tableName: TABLE_NAMES; records: Record<string, any>[] }): Promise<void> {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
table: tableName,
|
|
184
|
-
values: records.map(record => ({
|
|
185
|
-
...Object.fromEntries(
|
|
186
|
-
Object.entries(record).map(([key, value]) => [
|
|
187
|
-
key,
|
|
188
|
-
TABLE_SCHEMAS[tableName as TABLE_NAMES]?.[key]?.type === 'timestamp'
|
|
189
|
-
? new Date(value).toISOString()
|
|
190
|
-
: value,
|
|
191
|
-
]),
|
|
192
|
-
),
|
|
193
|
-
})),
|
|
194
|
-
format: 'JSONEachRow',
|
|
195
|
-
clickhouse_settings: {
|
|
196
|
-
// Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
|
|
197
|
-
date_time_input_format: 'best_effort',
|
|
198
|
-
use_client_time_zone: 1,
|
|
199
|
-
output_format_json_quote_64bit_integers: 0,
|
|
200
|
-
},
|
|
201
|
-
});
|
|
202
|
-
} catch (error) {
|
|
203
|
-
console.error(`Error inserting into ${tableName}:`, error);
|
|
204
|
-
throw error;
|
|
205
|
-
}
|
|
128
|
+
await this.stores.operations.batchInsert({ tableName, records });
|
|
129
|
+
// await this.optimizeTable({ tableName });
|
|
206
130
|
}
|
|
207
131
|
|
|
208
|
-
async
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
perPage,
|
|
213
|
-
attributes,
|
|
214
|
-
filters,
|
|
215
|
-
fromDate,
|
|
216
|
-
toDate,
|
|
217
|
-
}: {
|
|
218
|
-
name?: string;
|
|
219
|
-
scope?: string;
|
|
220
|
-
page: number;
|
|
221
|
-
perPage: number;
|
|
222
|
-
attributes?: Record<string, string>;
|
|
223
|
-
filters?: Record<string, any>;
|
|
224
|
-
fromDate?: Date;
|
|
225
|
-
toDate?: Date;
|
|
226
|
-
}): Promise<any[]> {
|
|
227
|
-
const limit = perPage;
|
|
228
|
-
const offset = page * perPage;
|
|
229
|
-
|
|
230
|
-
const args: Record<string, any> = {};
|
|
231
|
-
|
|
232
|
-
const conditions: string[] = [];
|
|
233
|
-
if (name) {
|
|
234
|
-
conditions.push(`name LIKE CONCAT({var_name:String}, '%')`);
|
|
235
|
-
args.var_name = name;
|
|
236
|
-
}
|
|
237
|
-
if (scope) {
|
|
238
|
-
conditions.push(`scope = {var_scope:String}`);
|
|
239
|
-
args.var_scope = scope;
|
|
240
|
-
}
|
|
241
|
-
if (attributes) {
|
|
242
|
-
Object.entries(attributes).forEach(([key, value]) => {
|
|
243
|
-
conditions.push(`JSONExtractString(attributes, '${key}') = {var_attr_${key}:String}`);
|
|
244
|
-
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`,
|
|
245
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
|
+
);
|
|
246
147
|
}
|
|
148
|
+
}
|
|
247
149
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
);
|
|
253
|
-
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;`,
|
|
254
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
|
+
);
|
|
255
165
|
}
|
|
256
|
-
|
|
257
|
-
if (fromDate) {
|
|
258
|
-
conditions.push(`createdAt >= {var_from_date:DateTime64(3)}`);
|
|
259
|
-
args.var_from_date = fromDate.getTime() / 1000; // Convert to Unix timestamp
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
if (toDate) {
|
|
263
|
-
conditions.push(`createdAt <= {var_to_date:DateTime64(3)}`);
|
|
264
|
-
args.var_to_date = toDate.getTime() / 1000; // Convert to Unix timestamp
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
|
|
268
|
-
|
|
269
|
-
const result = await this.db.query({
|
|
270
|
-
query: `SELECT *, toDateTime64(createdAt, 3) as createdAt FROM ${TABLE_TRACES} ${whereClause} ORDER BY "createdAt" DESC LIMIT ${limit} OFFSET ${offset}`,
|
|
271
|
-
query_params: args,
|
|
272
|
-
clickhouse_settings: {
|
|
273
|
-
// Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
|
|
274
|
-
date_time_input_format: 'best_effort',
|
|
275
|
-
date_time_output_format: 'iso',
|
|
276
|
-
use_client_time_zone: 1,
|
|
277
|
-
output_format_json_quote_64bit_integers: 0,
|
|
278
|
-
},
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
if (!result) {
|
|
282
|
-
return [];
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
const resp = await result.json();
|
|
286
|
-
const rows: any[] = resp.data;
|
|
287
|
-
return rows.map(row => ({
|
|
288
|
-
id: row.id,
|
|
289
|
-
parentSpanId: row.parentSpanId,
|
|
290
|
-
traceId: row.traceId,
|
|
291
|
-
name: row.name,
|
|
292
|
-
scope: row.scope,
|
|
293
|
-
kind: row.kind,
|
|
294
|
-
status: safelyParseJSON(row.status as string),
|
|
295
|
-
events: safelyParseJSON(row.events as string),
|
|
296
|
-
links: safelyParseJSON(row.links as string),
|
|
297
|
-
attributes: safelyParseJSON(row.attributes as string),
|
|
298
|
-
startTime: row.startTime,
|
|
299
|
-
endTime: row.endTime,
|
|
300
|
-
other: safelyParseJSON(row.other as string),
|
|
301
|
-
createdAt: row.createdAt,
|
|
302
|
-
}));
|
|
303
166
|
}
|
|
304
167
|
|
|
305
|
-
async
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
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 });
|
|
309
176
|
}
|
|
310
177
|
|
|
311
|
-
async
|
|
312
|
-
|
|
313
|
-
query: `ALTER TABLE ${tableName} MATERIALIZE TTL;`,
|
|
314
|
-
});
|
|
178
|
+
async dropTable({ tableName }: { tableName: TABLE_NAMES }): Promise<void> {
|
|
179
|
+
return this.stores.operations.dropTable({ tableName });
|
|
315
180
|
}
|
|
316
181
|
|
|
317
|
-
async
|
|
182
|
+
async alterTable({
|
|
318
183
|
tableName,
|
|
319
184
|
schema,
|
|
185
|
+
ifNotExists,
|
|
320
186
|
}: {
|
|
321
187
|
tableName: TABLE_NAMES;
|
|
322
188
|
schema: Record<string, StorageColumn>;
|
|
189
|
+
ifNotExists: string[];
|
|
323
190
|
}): Promise<void> {
|
|
324
|
-
|
|
325
|
-
const columns = Object.entries(schema)
|
|
326
|
-
.map(([name, def]) => {
|
|
327
|
-
const constraints = [];
|
|
328
|
-
if (!def.nullable) constraints.push('NOT NULL');
|
|
329
|
-
const columnTtl = this.ttl?.[tableName]?.columns?.[name];
|
|
330
|
-
return `"${name}" ${COLUMN_TYPES[def.type]} ${constraints.join(' ')} ${columnTtl ? `TTL toDateTime(${columnTtl.ttlKey ?? 'createdAt'}) + INTERVAL ${columnTtl.interval} ${columnTtl.unit}` : ''}`;
|
|
331
|
-
})
|
|
332
|
-
.join(',\n');
|
|
333
|
-
|
|
334
|
-
const rowTtl = this.ttl?.[tableName]?.row;
|
|
335
|
-
const sql =
|
|
336
|
-
tableName === TABLE_WORKFLOW_SNAPSHOT
|
|
337
|
-
? `
|
|
338
|
-
CREATE TABLE IF NOT EXISTS ${tableName} (
|
|
339
|
-
${['id String'].concat(columns)}
|
|
340
|
-
)
|
|
341
|
-
ENGINE = ${TABLE_ENGINES[tableName]}
|
|
342
|
-
PRIMARY KEY (createdAt, run_id, workflow_name)
|
|
343
|
-
ORDER BY (createdAt, run_id, workflow_name)
|
|
344
|
-
${rowTtl ? `TTL toDateTime(${rowTtl.ttlKey ?? 'createdAt'}) + INTERVAL ${rowTtl.interval} ${rowTtl.unit}` : ''}
|
|
345
|
-
SETTINGS index_granularity = 8192
|
|
346
|
-
`
|
|
347
|
-
: `
|
|
348
|
-
CREATE TABLE IF NOT EXISTS ${tableName} (
|
|
349
|
-
${columns}
|
|
350
|
-
)
|
|
351
|
-
ENGINE = ${TABLE_ENGINES[tableName]}
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
419
|
-
|
|
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
|
-
}, {});
|
|
203
|
+
return this.stores.operations.load({ tableName, keys });
|
|
204
|
+
}
|
|
429
205
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
206
|
+
async persistWorkflowSnapshot({
|
|
207
|
+
workflowName,
|
|
208
|
+
runId,
|
|
209
|
+
snapshot,
|
|
210
|
+
}: {
|
|
211
|
+
workflowName: string;
|
|
212
|
+
runId: string;
|
|
213
|
+
snapshot: WorkflowRunState;
|
|
214
|
+
}): Promise<void> {
|
|
215
|
+
return this.stores.workflows.persistWorkflowSnapshot({ workflowName, runId, snapshot });
|
|
216
|
+
}
|
|
441
217
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
218
|
+
async loadWorkflowSnapshot({
|
|
219
|
+
workflowName,
|
|
220
|
+
runId,
|
|
221
|
+
}: {
|
|
222
|
+
workflowName: string;
|
|
223
|
+
runId: string;
|
|
224
|
+
}): Promise<WorkflowRunState | null> {
|
|
225
|
+
return this.stores.workflows.loadWorkflowSnapshot({ workflowName, runId });
|
|
226
|
+
}
|
|
445
227
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
228
|
+
async getWorkflowRuns({
|
|
229
|
+
workflowName,
|
|
230
|
+
fromDate,
|
|
231
|
+
toDate,
|
|
232
|
+
limit,
|
|
233
|
+
offset,
|
|
234
|
+
resourceId,
|
|
235
|
+
}: {
|
|
236
|
+
workflowName?: string;
|
|
237
|
+
fromDate?: Date;
|
|
238
|
+
toDate?: Date;
|
|
239
|
+
limit?: number;
|
|
240
|
+
offset?: number;
|
|
241
|
+
resourceId?: string;
|
|
242
|
+
} = {}): Promise<WorkflowRuns> {
|
|
243
|
+
return this.stores.workflows.getWorkflowRuns({ workflowName, fromDate, toDate, limit, offset, resourceId });
|
|
244
|
+
}
|
|
458
245
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
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 });
|
|
465
254
|
}
|
|
466
255
|
|
|
467
|
-
async
|
|
468
|
-
|
|
469
|
-
|
|
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
|
-
});
|
|
256
|
+
async getTraces(args: StorageGetTracesArg): Promise<any[]> {
|
|
257
|
+
return this.stores.traces.getTraces(args);
|
|
258
|
+
}
|
|
489
259
|
|
|
490
|
-
|
|
491
|
-
|
|
260
|
+
async getTracesPaginated(args: StorageGetTracesPaginatedArg): Promise<PaginationInfo & { traces: Trace[] }> {
|
|
261
|
+
return this.stores.traces.getTracesPaginated(args);
|
|
262
|
+
}
|
|
492
263
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
264
|
+
async batchTraceInsert(args: { records: Trace[] }): Promise<void> {
|
|
265
|
+
return this.stores.traces.batchTraceInsert(args);
|
|
266
|
+
}
|
|
496
267
|
|
|
497
|
-
|
|
498
|
-
|
|
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
|
-
}
|
|
268
|
+
async getThreadById({ threadId }: { threadId: string }): Promise<StorageThreadType | null> {
|
|
269
|
+
return this.stores.memory.getThreadById({ threadId });
|
|
507
270
|
}
|
|
508
271
|
|
|
509
272
|
async getThreadsByResourceId({ resourceId }: { resourceId: string }): Promise<StorageThreadType[]> {
|
|
510
|
-
|
|
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
|
-
}
|
|
273
|
+
return this.stores.memory.getThreadsByResourceId({ resourceId });
|
|
544
274
|
}
|
|
545
275
|
|
|
546
276
|
async saveThread({ thread }: { thread: StorageThreadType }): Promise<StorageThreadType> {
|
|
547
|
-
|
|
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
|
-
}
|
|
277
|
+
return this.stores.memory.saveThread({ thread });
|
|
571
278
|
}
|
|
572
279
|
|
|
573
280
|
async updateThread({
|
|
@@ -579,73 +286,19 @@ export class ClickhouseStore extends MastraStorage {
|
|
|
579
286
|
title: string;
|
|
580
287
|
metadata: Record<string, unknown>;
|
|
581
288
|
}): Promise<StorageThreadType> {
|
|
582
|
-
|
|
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
|
-
}
|
|
289
|
+
return this.stores.memory.updateThread({ id, title, metadata });
|
|
624
290
|
}
|
|
625
291
|
|
|
626
292
|
async deleteThread({ threadId }: { threadId: string }): Promise<void> {
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
await this.db.command({
|
|
630
|
-
query: `DELETE FROM "${TABLE_MESSAGES}" WHERE thread_id = {var_thread_id:String};`,
|
|
631
|
-
query_params: { var_thread_id: threadId },
|
|
632
|
-
clickhouse_settings: {
|
|
633
|
-
output_format_json_quote_64bit_integers: 0,
|
|
634
|
-
},
|
|
635
|
-
});
|
|
293
|
+
return this.stores.memory.deleteThread({ threadId });
|
|
294
|
+
}
|
|
636
295
|
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
},
|
|
644
|
-
});
|
|
645
|
-
} catch (error) {
|
|
646
|
-
console.error('Error deleting thread:', error);
|
|
647
|
-
throw error;
|
|
648
|
-
}
|
|
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);
|
|
649
302
|
}
|
|
650
303
|
|
|
651
304
|
public async getMessages(args: StorageGetMessagesArg & { format?: 'v1' }): Promise<MastraMessageV1[]>;
|
|
@@ -656,119 +309,7 @@ export class ClickhouseStore extends MastraStorage {
|
|
|
656
309
|
selectBy,
|
|
657
310
|
format,
|
|
658
311
|
}: StorageGetMessagesArg & { format?: 'v1' | 'v2' }): Promise<MastraMessageV1[] | MastraMessageV2[]> {
|
|
659
|
-
|
|
660
|
-
const messages: any[] = [];
|
|
661
|
-
const limit = typeof selectBy?.last === `number` ? selectBy.last : 40;
|
|
662
|
-
const include = selectBy?.include || [];
|
|
663
|
-
|
|
664
|
-
if (include.length) {
|
|
665
|
-
const includeResult = await this.db.query({
|
|
666
|
-
query: `
|
|
667
|
-
WITH ordered_messages AS (
|
|
668
|
-
SELECT
|
|
669
|
-
*,
|
|
670
|
-
toDateTime64(createdAt, 3) as createdAt,
|
|
671
|
-
toDateTime64(updatedAt, 3) as updatedAt,
|
|
672
|
-
ROW_NUMBER() OVER (ORDER BY "createdAt" DESC) as row_num
|
|
673
|
-
FROM "${TABLE_MESSAGES}"
|
|
674
|
-
WHERE thread_id = {var_thread_id:String}
|
|
675
|
-
)
|
|
676
|
-
SELECT
|
|
677
|
-
m.id AS id,
|
|
678
|
-
m.content as content,
|
|
679
|
-
m.role as role,
|
|
680
|
-
m.type as type,
|
|
681
|
-
m.createdAt as createdAt,
|
|
682
|
-
m.updatedAt as updatedAt,
|
|
683
|
-
m.thread_id AS "threadId"
|
|
684
|
-
FROM ordered_messages m
|
|
685
|
-
WHERE m.id = ANY({var_include:Array(String)})
|
|
686
|
-
OR EXISTS (
|
|
687
|
-
SELECT 1 FROM ordered_messages target
|
|
688
|
-
WHERE target.id = ANY({var_include:Array(String)})
|
|
689
|
-
AND (
|
|
690
|
-
-- Get previous messages based on the max withPreviousMessages
|
|
691
|
-
(m.row_num <= target.row_num + {var_withPreviousMessages:Int64} AND m.row_num > target.row_num)
|
|
692
|
-
OR
|
|
693
|
-
-- Get next messages based on the max withNextMessages
|
|
694
|
-
(m.row_num >= target.row_num - {var_withNextMessages:Int64} AND m.row_num < target.row_num)
|
|
695
|
-
)
|
|
696
|
-
)
|
|
697
|
-
ORDER BY m."createdAt" DESC
|
|
698
|
-
`,
|
|
699
|
-
query_params: {
|
|
700
|
-
var_thread_id: threadId,
|
|
701
|
-
var_include: include.map(i => i.id),
|
|
702
|
-
var_withPreviousMessages: Math.max(...include.map(i => i.withPreviousMessages || 0)),
|
|
703
|
-
var_withNextMessages: Math.max(...include.map(i => i.withNextMessages || 0)),
|
|
704
|
-
},
|
|
705
|
-
clickhouse_settings: {
|
|
706
|
-
// Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
|
|
707
|
-
date_time_input_format: 'best_effort',
|
|
708
|
-
date_time_output_format: 'iso',
|
|
709
|
-
use_client_time_zone: 1,
|
|
710
|
-
output_format_json_quote_64bit_integers: 0,
|
|
711
|
-
},
|
|
712
|
-
});
|
|
713
|
-
|
|
714
|
-
const rows = await includeResult.json();
|
|
715
|
-
messages.push(...transformRows(rows.data));
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
// Then get the remaining messages, excluding the ids we just fetched
|
|
719
|
-
const result = await this.db.query({
|
|
720
|
-
query: `
|
|
721
|
-
SELECT
|
|
722
|
-
id,
|
|
723
|
-
content,
|
|
724
|
-
role,
|
|
725
|
-
type,
|
|
726
|
-
toDateTime64(createdAt, 3) as createdAt,
|
|
727
|
-
thread_id AS "threadId"
|
|
728
|
-
FROM "${TABLE_MESSAGES}"
|
|
729
|
-
WHERE thread_id = {threadId:String}
|
|
730
|
-
AND id NOT IN ({exclude:Array(String)})
|
|
731
|
-
ORDER BY "createdAt" DESC
|
|
732
|
-
LIMIT {limit:Int64}
|
|
733
|
-
`,
|
|
734
|
-
query_params: {
|
|
735
|
-
threadId,
|
|
736
|
-
exclude: messages.map(m => m.id),
|
|
737
|
-
limit,
|
|
738
|
-
},
|
|
739
|
-
clickhouse_settings: {
|
|
740
|
-
// Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
|
|
741
|
-
date_time_input_format: 'best_effort',
|
|
742
|
-
date_time_output_format: 'iso',
|
|
743
|
-
use_client_time_zone: 1,
|
|
744
|
-
output_format_json_quote_64bit_integers: 0,
|
|
745
|
-
},
|
|
746
|
-
});
|
|
747
|
-
|
|
748
|
-
const rows = await result.json();
|
|
749
|
-
messages.push(...transformRows(rows.data));
|
|
750
|
-
|
|
751
|
-
// Sort all messages by creation date
|
|
752
|
-
messages.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
|
|
753
|
-
|
|
754
|
-
// Parse message content
|
|
755
|
-
messages.forEach(message => {
|
|
756
|
-
if (typeof message.content === 'string') {
|
|
757
|
-
try {
|
|
758
|
-
message.content = JSON.parse(message.content);
|
|
759
|
-
} catch {
|
|
760
|
-
// If parsing fails, leave as string
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
|
-
});
|
|
764
|
-
|
|
765
|
-
const list = new MessageList({ threadId, resourceId }).add(messages, 'memory');
|
|
766
|
-
if (format === `v2`) return list.get.all.v2();
|
|
767
|
-
return list.get.all.v1();
|
|
768
|
-
} catch (error) {
|
|
769
|
-
console.error('Error getting messages:', error);
|
|
770
|
-
throw error;
|
|
771
|
-
}
|
|
312
|
+
return this.stores.memory.getMessages({ threadId, resourceId, selectBy, format });
|
|
772
313
|
}
|
|
773
314
|
|
|
774
315
|
async saveMessages(args: { messages: MastraMessageV1[]; format?: undefined | 'v1' }): Promise<MastraMessageV1[]>;
|
|
@@ -776,297 +317,83 @@ export class ClickhouseStore extends MastraStorage {
|
|
|
776
317
|
async saveMessages(
|
|
777
318
|
args: { messages: MastraMessageV1[]; format?: undefined | 'v1' } | { messages: MastraMessageV2[]; format: 'v2' },
|
|
778
319
|
): Promise<MastraMessageV2[] | MastraMessageV1[]> {
|
|
779
|
-
|
|
780
|
-
if (messages.length === 0) return messages;
|
|
781
|
-
|
|
782
|
-
try {
|
|
783
|
-
const threadId = messages[0]?.threadId;
|
|
784
|
-
const resourceId = messages[0]?.resourceId;
|
|
785
|
-
if (!threadId) {
|
|
786
|
-
throw new Error('Thread ID is required');
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
// Check if thread exists
|
|
790
|
-
const thread = await this.getThreadById({ threadId });
|
|
791
|
-
if (!thread) {
|
|
792
|
-
throw new Error(`Thread ${threadId} not found`);
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
await this.db.insert({
|
|
796
|
-
table: TABLE_MESSAGES,
|
|
797
|
-
format: 'JSONEachRow',
|
|
798
|
-
values: messages.map(message => ({
|
|
799
|
-
id: message.id,
|
|
800
|
-
thread_id: threadId,
|
|
801
|
-
content: typeof message.content === 'string' ? message.content : JSON.stringify(message.content),
|
|
802
|
-
createdAt: message.createdAt.toISOString(),
|
|
803
|
-
role: message.role,
|
|
804
|
-
type: message.type || 'v2',
|
|
805
|
-
})),
|
|
806
|
-
clickhouse_settings: {
|
|
807
|
-
// Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
|
|
808
|
-
date_time_input_format: 'best_effort',
|
|
809
|
-
use_client_time_zone: 1,
|
|
810
|
-
output_format_json_quote_64bit_integers: 0,
|
|
811
|
-
},
|
|
812
|
-
});
|
|
813
|
-
|
|
814
|
-
const list = new MessageList({ threadId, resourceId }).add(messages, 'memory');
|
|
815
|
-
if (format === `v2`) return list.get.all.v2();
|
|
816
|
-
return list.get.all.v1();
|
|
817
|
-
} catch (error) {
|
|
818
|
-
console.error('Error saving messages:', error);
|
|
819
|
-
throw error;
|
|
820
|
-
}
|
|
320
|
+
return this.stores.memory.saveMessages(args);
|
|
821
321
|
}
|
|
822
322
|
|
|
823
|
-
async
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
}: {
|
|
828
|
-
workflowName: string;
|
|
829
|
-
runId: string;
|
|
830
|
-
snapshot: WorkflowRunState;
|
|
831
|
-
}): Promise<void> {
|
|
832
|
-
try {
|
|
833
|
-
const currentSnapshot = await this.load({
|
|
834
|
-
tableName: TABLE_WORKFLOW_SNAPSHOT,
|
|
835
|
-
keys: { workflow_name: workflowName, run_id: runId },
|
|
836
|
-
});
|
|
837
|
-
|
|
838
|
-
const now = new Date();
|
|
839
|
-
const persisting = currentSnapshot
|
|
840
|
-
? {
|
|
841
|
-
...currentSnapshot,
|
|
842
|
-
snapshot: JSON.stringify(snapshot),
|
|
843
|
-
updatedAt: now.toISOString(),
|
|
844
|
-
}
|
|
845
|
-
: {
|
|
846
|
-
workflow_name: workflowName,
|
|
847
|
-
run_id: runId,
|
|
848
|
-
snapshot: JSON.stringify(snapshot),
|
|
849
|
-
createdAt: now.toISOString(),
|
|
850
|
-
updatedAt: now.toISOString(),
|
|
851
|
-
};
|
|
852
|
-
|
|
853
|
-
await this.db.insert({
|
|
854
|
-
table: TABLE_WORKFLOW_SNAPSHOT,
|
|
855
|
-
format: 'JSONEachRow',
|
|
856
|
-
values: [persisting],
|
|
857
|
-
clickhouse_settings: {
|
|
858
|
-
// Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
|
|
859
|
-
date_time_input_format: 'best_effort',
|
|
860
|
-
use_client_time_zone: 1,
|
|
861
|
-
output_format_json_quote_64bit_integers: 0,
|
|
862
|
-
},
|
|
863
|
-
});
|
|
864
|
-
} catch (error) {
|
|
865
|
-
console.error('Error persisting workflow snapshot:', error);
|
|
866
|
-
throw error;
|
|
867
|
-
}
|
|
323
|
+
async getMessagesPaginated(
|
|
324
|
+
args: StorageGetMessagesArg & { format?: 'v1' | 'v2' },
|
|
325
|
+
): Promise<PaginationInfo & { messages: MastraMessageV1[] | MastraMessageV2[] }> {
|
|
326
|
+
return this.stores.memory.getMessagesPaginated(args);
|
|
868
327
|
}
|
|
869
328
|
|
|
870
|
-
async
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
}): Promise<
|
|
877
|
-
|
|
878
|
-
const result = await this.load({
|
|
879
|
-
tableName: TABLE_WORKFLOW_SNAPSHOT,
|
|
880
|
-
keys: {
|
|
881
|
-
workflow_name: workflowName,
|
|
882
|
-
run_id: runId,
|
|
883
|
-
},
|
|
884
|
-
});
|
|
885
|
-
|
|
886
|
-
if (!result) {
|
|
887
|
-
return null;
|
|
888
|
-
}
|
|
889
|
-
|
|
890
|
-
return (result as any).snapshot;
|
|
891
|
-
} catch (error) {
|
|
892
|
-
console.error('Error loading workflow snapshot:', error);
|
|
893
|
-
throw error;
|
|
894
|
-
}
|
|
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);
|
|
895
337
|
}
|
|
896
338
|
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
try {
|
|
901
|
-
parsedSnapshot = JSON.parse(row.snapshot as string) as WorkflowRunState;
|
|
902
|
-
} catch (e) {
|
|
903
|
-
// If parsing fails, return the raw snapshot string
|
|
904
|
-
console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
|
|
905
|
-
}
|
|
906
|
-
}
|
|
339
|
+
async getResourceById({ resourceId }: { resourceId: string }): Promise<StorageResourceType | null> {
|
|
340
|
+
return this.stores.memory.getResourceById({ resourceId });
|
|
341
|
+
}
|
|
907
342
|
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
runId: row.run_id,
|
|
911
|
-
snapshot: parsedSnapshot,
|
|
912
|
-
createdAt: new Date(row.createdAt),
|
|
913
|
-
updatedAt: new Date(row.updatedAt),
|
|
914
|
-
resourceId: row.resourceId,
|
|
915
|
-
};
|
|
343
|
+
async saveResource({ resource }: { resource: StorageResourceType }): Promise<StorageResourceType> {
|
|
344
|
+
return this.stores.memory.saveResource({ resource });
|
|
916
345
|
}
|
|
917
346
|
|
|
918
|
-
async
|
|
919
|
-
workflowName,
|
|
920
|
-
fromDate,
|
|
921
|
-
toDate,
|
|
922
|
-
limit,
|
|
923
|
-
offset,
|
|
347
|
+
async updateResource({
|
|
924
348
|
resourceId,
|
|
349
|
+
workingMemory,
|
|
350
|
+
metadata,
|
|
925
351
|
}: {
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
} = {}): Promise<WorkflowRuns> {
|
|
933
|
-
try {
|
|
934
|
-
const conditions: string[] = [];
|
|
935
|
-
const values: Record<string, any> = {};
|
|
936
|
-
|
|
937
|
-
if (workflowName) {
|
|
938
|
-
conditions.push(`workflow_name = {var_workflow_name:String}`);
|
|
939
|
-
values.var_workflow_name = workflowName;
|
|
940
|
-
}
|
|
941
|
-
|
|
942
|
-
if (resourceId) {
|
|
943
|
-
const hasResourceId = await this.hasColumn(TABLE_WORKFLOW_SNAPSHOT, 'resourceId');
|
|
944
|
-
if (hasResourceId) {
|
|
945
|
-
conditions.push(`resourceId = {var_resourceId:String}`);
|
|
946
|
-
values.var_resourceId = resourceId;
|
|
947
|
-
} else {
|
|
948
|
-
console.warn(`[${TABLE_WORKFLOW_SNAPSHOT}] resourceId column not found. Skipping resourceId filter.`);
|
|
949
|
-
}
|
|
950
|
-
}
|
|
951
|
-
|
|
952
|
-
if (fromDate) {
|
|
953
|
-
conditions.push(`createdAt >= {var_from_date:DateTime64(3)}`);
|
|
954
|
-
values.var_from_date = fromDate.getTime() / 1000; // Convert to Unix timestamp
|
|
955
|
-
}
|
|
956
|
-
|
|
957
|
-
if (toDate) {
|
|
958
|
-
conditions.push(`createdAt <= {var_to_date:DateTime64(3)}`);
|
|
959
|
-
values.var_to_date = toDate.getTime() / 1000; // Convert to Unix timestamp
|
|
960
|
-
}
|
|
961
|
-
|
|
962
|
-
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
|
|
963
|
-
const limitClause = limit !== undefined ? `LIMIT ${limit}` : '';
|
|
964
|
-
const offsetClause = offset !== undefined ? `OFFSET ${offset}` : '';
|
|
965
|
-
|
|
966
|
-
let total = 0;
|
|
967
|
-
// Only get total count when using pagination
|
|
968
|
-
if (limit !== undefined && offset !== undefined) {
|
|
969
|
-
const countResult = await this.db.query({
|
|
970
|
-
query: `SELECT COUNT(*) as count FROM ${TABLE_WORKFLOW_SNAPSHOT} ${TABLE_ENGINES[TABLE_WORKFLOW_SNAPSHOT].startsWith('ReplacingMergeTree') ? 'FINAL' : ''} ${whereClause}`,
|
|
971
|
-
query_params: values,
|
|
972
|
-
format: 'JSONEachRow',
|
|
973
|
-
});
|
|
974
|
-
const countRows = await countResult.json();
|
|
975
|
-
total = Number((countRows as Array<{ count: string | number }>)[0]?.count ?? 0);
|
|
976
|
-
}
|
|
977
|
-
|
|
978
|
-
// Get results
|
|
979
|
-
const result = await this.db.query({
|
|
980
|
-
query: `
|
|
981
|
-
SELECT
|
|
982
|
-
workflow_name,
|
|
983
|
-
run_id,
|
|
984
|
-
snapshot,
|
|
985
|
-
toDateTime64(createdAt, 3) as createdAt,
|
|
986
|
-
toDateTime64(updatedAt, 3) as updatedAt,
|
|
987
|
-
resourceId
|
|
988
|
-
FROM ${TABLE_WORKFLOW_SNAPSHOT} ${TABLE_ENGINES[TABLE_WORKFLOW_SNAPSHOT].startsWith('ReplacingMergeTree') ? 'FINAL' : ''}
|
|
989
|
-
${whereClause}
|
|
990
|
-
ORDER BY createdAt DESC
|
|
991
|
-
${limitClause}
|
|
992
|
-
${offsetClause}
|
|
993
|
-
`,
|
|
994
|
-
query_params: values,
|
|
995
|
-
format: 'JSONEachRow',
|
|
996
|
-
});
|
|
352
|
+
resourceId: string;
|
|
353
|
+
workingMemory?: string;
|
|
354
|
+
metadata?: Record<string, unknown>;
|
|
355
|
+
}): Promise<StorageResourceType> {
|
|
356
|
+
return this.stores.memory.updateResource({ resourceId, workingMemory, metadata });
|
|
357
|
+
}
|
|
997
358
|
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
return this.parseWorkflowRun(row);
|
|
1002
|
-
});
|
|
359
|
+
async getScoreById({ id }: { id: string }): Promise<ScoreRowData | null> {
|
|
360
|
+
return this.stores.scores.getScoreById({ id });
|
|
361
|
+
}
|
|
1003
362
|
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
} catch (error) {
|
|
1007
|
-
console.error('Error getting workflow runs:', error);
|
|
1008
|
-
throw error;
|
|
1009
|
-
}
|
|
363
|
+
async saveScore(_score: ScoreRowData): Promise<{ score: ScoreRowData }> {
|
|
364
|
+
return this.stores.scores.saveScore(_score);
|
|
1010
365
|
}
|
|
1011
366
|
|
|
1012
|
-
async
|
|
367
|
+
async getScoresByRunId({
|
|
1013
368
|
runId,
|
|
1014
|
-
|
|
369
|
+
pagination,
|
|
1015
370
|
}: {
|
|
1016
371
|
runId: string;
|
|
1017
|
-
|
|
1018
|
-
}): Promise<
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
const values: Record<string, any> = {};
|
|
1022
|
-
|
|
1023
|
-
if (runId) {
|
|
1024
|
-
conditions.push(`run_id = {var_runId:String}`);
|
|
1025
|
-
values.var_runId = runId;
|
|
1026
|
-
}
|
|
1027
|
-
|
|
1028
|
-
if (workflowName) {
|
|
1029
|
-
conditions.push(`workflow_name = {var_workflow_name:String}`);
|
|
1030
|
-
values.var_workflow_name = workflowName;
|
|
1031
|
-
}
|
|
1032
|
-
|
|
1033
|
-
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
|
|
1034
|
-
|
|
1035
|
-
// Get results
|
|
1036
|
-
const result = await this.db.query({
|
|
1037
|
-
query: `
|
|
1038
|
-
SELECT
|
|
1039
|
-
workflow_name,
|
|
1040
|
-
run_id,
|
|
1041
|
-
snapshot,
|
|
1042
|
-
toDateTime64(createdAt, 3) as createdAt,
|
|
1043
|
-
toDateTime64(updatedAt, 3) as updatedAt,
|
|
1044
|
-
resourceId
|
|
1045
|
-
FROM ${TABLE_WORKFLOW_SNAPSHOT} ${TABLE_ENGINES[TABLE_WORKFLOW_SNAPSHOT].startsWith('ReplacingMergeTree') ? 'FINAL' : ''}
|
|
1046
|
-
${whereClause}
|
|
1047
|
-
`,
|
|
1048
|
-
query_params: values,
|
|
1049
|
-
format: 'JSONEachRow',
|
|
1050
|
-
});
|
|
372
|
+
pagination: StoragePagination;
|
|
373
|
+
}): Promise<{ pagination: PaginationInfo; scores: ScoreRowData[] }> {
|
|
374
|
+
return this.stores.scores.getScoresByRunId({ runId, pagination });
|
|
375
|
+
}
|
|
1051
376
|
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
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 });
|
|
1061
387
|
}
|
|
1062
388
|
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
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 });
|
|
1070
397
|
}
|
|
1071
398
|
|
|
1072
399
|
async close(): Promise<void> {
|