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