@mastra/clickhouse 0.0.0-vnextWorkflows-20250422142014 → 0.0.0-workflow-deno-20250616115451

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,12 +1,18 @@
1
1
  import type { ClickHouseClient } from '@clickhouse/client';
2
2
  import type { EvalRow } from '@mastra/core/storage';
3
+ import type { MastraMessageV1 } from '@mastra/core/memory';
4
+ import type { MastraMessageV2 } from '@mastra/core/memory';
3
5
  import { MastraStorage } from '@mastra/core/storage';
4
- import type { MessageType } from '@mastra/core/memory';
6
+ import type { PaginationInfo } from '@mastra/core/storage';
5
7
  import type { StorageColumn } from '@mastra/core/storage';
6
8
  import type { StorageGetMessagesArg } from '@mastra/core/storage';
9
+ import type { StorageGetTracesArg } from '@mastra/core/storage';
7
10
  import type { StorageThreadType } from '@mastra/core/memory';
8
11
  import type { TABLE_NAMES } from '@mastra/core/storage';
9
12
  import { TABLE_SCHEMAS } from '@mastra/core/storage';
13
+ import type { Trace } from '@mastra/core/telemetry';
14
+ import type { WorkflowRun } from '@mastra/core/storage';
15
+ import type { WorkflowRuns } from '@mastra/core/storage';
10
16
  import type { WorkflowRunState } from '@mastra/core/workflows';
11
17
 
12
18
  declare type ClickhouseConfig = {
@@ -43,13 +49,15 @@ declare class ClickhouseStore extends MastraStorage {
43
49
  tableName: TABLE_NAMES;
44
50
  records: Record<string, any>[];
45
51
  }): Promise<void>;
46
- getTraces({ name, scope, page, perPage, attributes, filters, }: {
52
+ getTraces({ name, scope, page, perPage, attributes, filters, fromDate, toDate, }: {
47
53
  name?: string;
48
54
  scope?: string;
49
55
  page: number;
50
56
  perPage: number;
51
57
  attributes?: Record<string, string>;
52
58
  filters?: Record<string, any>;
59
+ fromDate?: Date;
60
+ toDate?: Date;
53
61
  }): Promise<any[]>;
54
62
  optimizeTable({ tableName }: {
55
63
  tableName: TABLE_NAMES;
@@ -61,6 +69,18 @@ declare class ClickhouseStore extends MastraStorage {
61
69
  tableName: TABLE_NAMES;
62
70
  schema: Record<string, StorageColumn>;
63
71
  }): Promise<void>;
72
+ protected getSqlType(type: StorageColumn['type']): string;
73
+ /**
74
+ * Alters table schema to add columns if they don't exist
75
+ * @param tableName Name of the table
76
+ * @param schema Schema of the table
77
+ * @param ifNotExists Array of column names to add if they don't exist
78
+ */
79
+ alterTable({ tableName, schema, ifNotExists, }: {
80
+ tableName: TABLE_NAMES;
81
+ schema: Record<string, StorageColumn>;
82
+ ifNotExists: string[];
83
+ }): Promise<void>;
64
84
  clearTable({ tableName }: {
65
85
  tableName: TABLE_NAMES;
66
86
  }): Promise<void>;
@@ -89,10 +109,20 @@ declare class ClickhouseStore extends MastraStorage {
89
109
  deleteThread({ threadId }: {
90
110
  threadId: string;
91
111
  }): Promise<void>;
92
- getMessages<T = unknown>({ threadId, selectBy }: StorageGetMessagesArg): Promise<T>;
93
- saveMessages({ messages }: {
94
- messages: MessageType[];
95
- }): Promise<MessageType[]>;
112
+ getMessages(args: StorageGetMessagesArg & {
113
+ format?: 'v1';
114
+ }): Promise<MastraMessageV1[]>;
115
+ getMessages(args: StorageGetMessagesArg & {
116
+ format: 'v2';
117
+ }): Promise<MastraMessageV2[]>;
118
+ saveMessages(args: {
119
+ messages: MastraMessageV1[];
120
+ format?: undefined | 'v1';
121
+ }): Promise<MastraMessageV1[]>;
122
+ saveMessages(args: {
123
+ messages: MastraMessageV2[];
124
+ format: 'v2';
125
+ }): Promise<MastraMessageV2[]>;
96
126
  persistWorkflowSnapshot({ workflowName, runId, snapshot, }: {
97
127
  workflowName: string;
98
128
  runId: string;
@@ -102,21 +132,32 @@ declare class ClickhouseStore extends MastraStorage {
102
132
  workflowName: string;
103
133
  runId: string;
104
134
  }): Promise<WorkflowRunState | null>;
105
- getWorkflowRuns({ workflowName, fromDate, toDate, limit, offset, }?: {
135
+ private parseWorkflowRun;
136
+ getWorkflowRuns({ workflowName, fromDate, toDate, limit, offset, resourceId, }?: {
106
137
  workflowName?: string;
107
138
  fromDate?: Date;
108
139
  toDate?: Date;
109
140
  limit?: number;
110
141
  offset?: number;
111
- }): Promise<{
112
- runs: Array<{
113
- workflowName: string;
114
- runId: string;
115
- snapshot: WorkflowRunState | string;
116
- createdAt: Date;
117
- updatedAt: Date;
118
- }>;
119
- total: number;
142
+ resourceId?: string;
143
+ }): Promise<WorkflowRuns>;
144
+ getWorkflowRunById({ runId, workflowName, }: {
145
+ runId: string;
146
+ workflowName?: string;
147
+ }): Promise<WorkflowRun | null>;
148
+ private hasColumn;
149
+ getTracesPaginated(_args: StorageGetTracesArg): Promise<PaginationInfo & {
150
+ traces: Trace[];
151
+ }>;
152
+ getThreadsByResourceIdPaginated(_args: {
153
+ resourceId: string;
154
+ page?: number;
155
+ perPage?: number;
156
+ }): Promise<PaginationInfo & {
157
+ threads: StorageThreadType[];
158
+ }>;
159
+ getMessagesPaginated(_args: StorageGetMessagesArg): Promise<PaginationInfo & {
160
+ messages: MastraMessageV1[] | MastraMessageV2[];
120
161
  }>;
121
162
  close(): Promise<void>;
122
163
  }
package/dist/index.cjs CHANGED
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var client = require('@clickhouse/client');
4
+ var agent = require('@mastra/core/agent');
4
5
  var storage = require('@mastra/core/storage');
5
6
 
6
7
  // src/storage/index.ts
@@ -138,7 +139,9 @@ var ClickhouseStore = class extends storage.MastraStorage {
138
139
  page,
139
140
  perPage,
140
141
  attributes,
141
- filters
142
+ filters,
143
+ fromDate,
144
+ toDate
142
145
  }) {
143
146
  const limit = perPage;
144
147
  const offset = page * perPage;
@@ -166,6 +169,14 @@ var ClickhouseStore = class extends storage.MastraStorage {
166
169
  args[`var_col_${key}`] = value;
167
170
  });
168
171
  }
172
+ if (fromDate) {
173
+ conditions.push(`createdAt >= {var_from_date:DateTime64(3)}`);
174
+ args.var_from_date = fromDate.getTime() / 1e3;
175
+ }
176
+ if (toDate) {
177
+ conditions.push(`createdAt <= {var_to_date:DateTime64(3)}`);
178
+ args.var_to_date = toDate.getTime() / 1e3;
179
+ }
169
180
  const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
170
181
  const result = await this.db.query({
171
182
  query: `SELECT *, toDateTime64(createdAt, 3) as createdAt FROM ${storage.TABLE_TRACES} ${whereClause} ORDER BY "createdAt" DESC LIMIT ${limit} OFFSET ${offset}`,
@@ -227,7 +238,6 @@ var ClickhouseStore = class extends storage.MastraStorage {
227
238
  ${["id String"].concat(columns)}
228
239
  )
229
240
  ENGINE = ${TABLE_ENGINES[tableName]}
230
- PARTITION BY "createdAt"
231
241
  PRIMARY KEY (createdAt, run_id, workflow_name)
232
242
  ORDER BY (createdAt, run_id, workflow_name)
233
243
  ${rowTtl ? `TTL toDateTime(${rowTtl.ttlKey ?? "createdAt"}) + INTERVAL ${rowTtl.interval} ${rowTtl.unit}` : ""}
@@ -237,7 +247,6 @@ var ClickhouseStore = class extends storage.MastraStorage {
237
247
  ${columns}
238
248
  )
239
249
  ENGINE = ${TABLE_ENGINES[tableName]}
240
- PARTITION BY "createdAt"
241
250
  PRIMARY KEY (createdAt, ${tableName === storage.TABLE_EVALS ? "run_id" : "id"})
242
251
  ORDER BY (createdAt, ${tableName === storage.TABLE_EVALS ? "run_id" : "id"})
243
252
  ${this.ttl?.[tableName]?.row ? `TTL toDateTime(createdAt) + INTERVAL ${this.ttl[tableName].row.interval} ${this.ttl[tableName].row.unit}` : ""}
@@ -258,6 +267,61 @@ var ClickhouseStore = class extends storage.MastraStorage {
258
267
  throw error;
259
268
  }
260
269
  }
270
+ getSqlType(type) {
271
+ switch (type) {
272
+ case "text":
273
+ return "String";
274
+ case "timestamp":
275
+ return "DateTime64(3)";
276
+ case "integer":
277
+ case "bigint":
278
+ return "Int64";
279
+ case "jsonb":
280
+ return "String";
281
+ default:
282
+ return super.getSqlType(type);
283
+ }
284
+ }
285
+ /**
286
+ * Alters table schema to add columns if they don't exist
287
+ * @param tableName Name of the table
288
+ * @param schema Schema of the table
289
+ * @param ifNotExists Array of column names to add if they don't exist
290
+ */
291
+ async alterTable({
292
+ tableName,
293
+ schema,
294
+ ifNotExists
295
+ }) {
296
+ try {
297
+ const describeSql = `DESCRIBE TABLE ${tableName}`;
298
+ const result = await this.db.query({
299
+ query: describeSql
300
+ });
301
+ const rows = await result.json();
302
+ const existingColumnNames = new Set(rows.data.map((row) => row.name.toLowerCase()));
303
+ for (const columnName of ifNotExists) {
304
+ if (!existingColumnNames.has(columnName.toLowerCase()) && schema[columnName]) {
305
+ const columnDef = schema[columnName];
306
+ let sqlType = this.getSqlType(columnDef.type);
307
+ if (columnDef.nullable !== false) {
308
+ sqlType = `Nullable(${sqlType})`;
309
+ }
310
+ const defaultValue = columnDef.nullable === false ? this.getDefaultValue(columnDef.type) : "";
311
+ const alterSql = `ALTER TABLE ${tableName} ADD COLUMN IF NOT EXISTS "${columnName}" ${sqlType} ${defaultValue}`.trim();
312
+ await this.db.query({
313
+ query: alterSql
314
+ });
315
+ this.logger?.debug?.(`Added column ${columnName} to table ${tableName}`);
316
+ }
317
+ }
318
+ } catch (error) {
319
+ this.logger?.error?.(
320
+ `Error altering table ${tableName}: ${error instanceof Error ? error.message : String(error)}`
321
+ );
322
+ throw new Error(`Failed to alter table ${tableName}: ${error}`);
323
+ }
324
+ }
261
325
  async clearTable({ tableName }) {
262
326
  try {
263
327
  await this.db.query({
@@ -459,15 +523,18 @@ var ClickhouseStore = class extends storage.MastraStorage {
459
523
  };
460
524
  await this.db.insert({
461
525
  table: storage.TABLE_THREADS,
526
+ format: "JSONEachRow",
462
527
  values: [
463
528
  {
464
- ...updatedThread,
529
+ id: updatedThread.id,
530
+ resourceId: updatedThread.resourceId,
531
+ title: updatedThread.title,
532
+ metadata: updatedThread.metadata,
533
+ createdAt: updatedThread.createdAt,
465
534
  updatedAt: updatedThread.updatedAt.toISOString()
466
535
  }
467
536
  ],
468
- format: "JSONEachRow",
469
537
  clickhouse_settings: {
470
- // Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
471
538
  date_time_input_format: "best_effort",
472
539
  use_client_time_zone: 1,
473
540
  output_format_json_quote_64bit_integers: 0
@@ -482,7 +549,7 @@ var ClickhouseStore = class extends storage.MastraStorage {
482
549
  async deleteThread({ threadId }) {
483
550
  try {
484
551
  await this.db.command({
485
- query: `DELETE FROM "${storage.TABLE_MESSAGES}" WHERE thread_id = '${threadId}';`,
552
+ query: `DELETE FROM "${storage.TABLE_MESSAGES}" WHERE thread_id = {var_thread_id:String};`,
486
553
  query_params: { var_thread_id: threadId },
487
554
  clickhouse_settings: {
488
555
  output_format_json_quote_64bit_integers: 0
@@ -500,7 +567,12 @@ var ClickhouseStore = class extends storage.MastraStorage {
500
567
  throw error;
501
568
  }
502
569
  }
503
- async getMessages({ threadId, selectBy }) {
570
+ async getMessages({
571
+ threadId,
572
+ resourceId,
573
+ selectBy,
574
+ format
575
+ }) {
504
576
  try {
505
577
  const messages = [];
506
578
  const limit = typeof selectBy?.last === `number` ? selectBy.last : 40;
@@ -596,16 +668,20 @@ var ClickhouseStore = class extends storage.MastraStorage {
596
668
  }
597
669
  }
598
670
  });
599
- return messages;
671
+ const list = new agent.MessageList({ threadId, resourceId }).add(messages, "memory");
672
+ if (format === `v2`) return list.get.all.v2();
673
+ return list.get.all.v1();
600
674
  } catch (error) {
601
675
  console.error("Error getting messages:", error);
602
676
  throw error;
603
677
  }
604
678
  }
605
- async saveMessages({ messages }) {
679
+ async saveMessages(args) {
680
+ const { messages, format = "v1" } = args;
606
681
  if (messages.length === 0) return messages;
607
682
  try {
608
683
  const threadId = messages[0]?.threadId;
684
+ const resourceId = messages[0]?.resourceId;
609
685
  if (!threadId) {
610
686
  throw new Error("Thread ID is required");
611
687
  }
@@ -613,25 +689,50 @@ var ClickhouseStore = class extends storage.MastraStorage {
613
689
  if (!thread) {
614
690
  throw new Error(`Thread ${threadId} not found`);
615
691
  }
616
- await this.db.insert({
617
- table: storage.TABLE_MESSAGES,
618
- format: "JSONEachRow",
619
- values: messages.map((message) => ({
620
- id: message.id,
621
- thread_id: threadId,
622
- content: typeof message.content === "string" ? message.content : JSON.stringify(message.content),
623
- createdAt: message.createdAt.toISOString(),
624
- role: message.role,
625
- type: message.type
626
- })),
627
- clickhouse_settings: {
628
- // Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
629
- date_time_input_format: "best_effort",
630
- use_client_time_zone: 1,
631
- output_format_json_quote_64bit_integers: 0
632
- }
633
- });
634
- return messages;
692
+ await Promise.all([
693
+ // Insert messages
694
+ this.db.insert({
695
+ table: storage.TABLE_MESSAGES,
696
+ format: "JSONEachRow",
697
+ values: messages.map((message) => ({
698
+ id: message.id,
699
+ thread_id: threadId,
700
+ content: typeof message.content === "string" ? message.content : JSON.stringify(message.content),
701
+ createdAt: message.createdAt.toISOString(),
702
+ role: message.role,
703
+ type: message.type || "v2"
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
+ use_client_time_zone: 1,
709
+ output_format_json_quote_64bit_integers: 0
710
+ }
711
+ }),
712
+ // Update thread's updatedAt timestamp
713
+ this.db.insert({
714
+ table: storage.TABLE_THREADS,
715
+ format: "JSONEachRow",
716
+ values: [
717
+ {
718
+ id: thread.id,
719
+ resourceId: thread.resourceId,
720
+ title: thread.title,
721
+ metadata: thread.metadata,
722
+ createdAt: thread.createdAt,
723
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
724
+ }
725
+ ],
726
+ clickhouse_settings: {
727
+ date_time_input_format: "best_effort",
728
+ use_client_time_zone: 1,
729
+ output_format_json_quote_64bit_integers: 0
730
+ }
731
+ })
732
+ ]);
733
+ const list = new agent.MessageList({ threadId, resourceId }).add(messages, "memory");
734
+ if (format === `v2`) return list.get.all.v2();
735
+ return list.get.all.v1();
635
736
  } catch (error) {
636
737
  console.error("Error saving messages:", error);
637
738
  throw error;
@@ -696,12 +797,31 @@ var ClickhouseStore = class extends storage.MastraStorage {
696
797
  throw error;
697
798
  }
698
799
  }
800
+ parseWorkflowRun(row) {
801
+ let parsedSnapshot = row.snapshot;
802
+ if (typeof parsedSnapshot === "string") {
803
+ try {
804
+ parsedSnapshot = JSON.parse(row.snapshot);
805
+ } catch (e) {
806
+ console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
807
+ }
808
+ }
809
+ return {
810
+ workflowName: row.workflow_name,
811
+ runId: row.run_id,
812
+ snapshot: parsedSnapshot,
813
+ createdAt: new Date(row.createdAt),
814
+ updatedAt: new Date(row.updatedAt),
815
+ resourceId: row.resourceId
816
+ };
817
+ }
699
818
  async getWorkflowRuns({
700
819
  workflowName,
701
820
  fromDate,
702
821
  toDate,
703
822
  limit,
704
- offset
823
+ offset,
824
+ resourceId
705
825
  } = {}) {
706
826
  try {
707
827
  const conditions = [];
@@ -710,6 +830,15 @@ var ClickhouseStore = class extends storage.MastraStorage {
710
830
  conditions.push(`workflow_name = {var_workflow_name:String}`);
711
831
  values.var_workflow_name = workflowName;
712
832
  }
833
+ if (resourceId) {
834
+ const hasResourceId = await this.hasColumn(storage.TABLE_WORKFLOW_SNAPSHOT, "resourceId");
835
+ if (hasResourceId) {
836
+ conditions.push(`resourceId = {var_resourceId:String}`);
837
+ values.var_resourceId = resourceId;
838
+ } else {
839
+ console.warn(`[${storage.TABLE_WORKFLOW_SNAPSHOT}] resourceId column not found. Skipping resourceId filter.`);
840
+ }
841
+ }
713
842
  if (fromDate) {
714
843
  conditions.push(`createdAt >= {var_from_date:DateTime64(3)}`);
715
844
  values.var_from_date = fromDate.getTime() / 1e3;
@@ -738,7 +867,8 @@ var ClickhouseStore = class extends storage.MastraStorage {
738
867
  run_id,
739
868
  snapshot,
740
869
  toDateTime64(createdAt, 3) as createdAt,
741
- toDateTime64(updatedAt, 3) as updatedAt
870
+ toDateTime64(updatedAt, 3) as updatedAt,
871
+ resourceId
742
872
  FROM ${storage.TABLE_WORKFLOW_SNAPSHOT} ${TABLE_ENGINES[storage.TABLE_WORKFLOW_SNAPSHOT].startsWith("ReplacingMergeTree") ? "FINAL" : ""}
743
873
  ${whereClause}
744
874
  ORDER BY createdAt DESC
@@ -751,21 +881,7 @@ var ClickhouseStore = class extends storage.MastraStorage {
751
881
  const resultJson = await result.json();
752
882
  const rows = resultJson;
753
883
  const runs = rows.map((row) => {
754
- let parsedSnapshot = row.snapshot;
755
- if (typeof parsedSnapshot === "string") {
756
- try {
757
- parsedSnapshot = JSON.parse(row.snapshot);
758
- } catch (e) {
759
- console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
760
- }
761
- }
762
- return {
763
- workflowName: row.workflow_name,
764
- runId: row.run_id,
765
- snapshot: parsedSnapshot,
766
- createdAt: new Date(row.createdAt),
767
- updatedAt: new Date(row.updatedAt)
768
- };
884
+ return this.parseWorkflowRun(row);
769
885
  });
770
886
  return { runs, total: total || runs.length };
771
887
  } catch (error) {
@@ -773,6 +889,64 @@ var ClickhouseStore = class extends storage.MastraStorage {
773
889
  throw error;
774
890
  }
775
891
  }
892
+ async getWorkflowRunById({
893
+ runId,
894
+ workflowName
895
+ }) {
896
+ try {
897
+ const conditions = [];
898
+ const values = {};
899
+ if (runId) {
900
+ conditions.push(`run_id = {var_runId:String}`);
901
+ values.var_runId = runId;
902
+ }
903
+ if (workflowName) {
904
+ conditions.push(`workflow_name = {var_workflow_name:String}`);
905
+ values.var_workflow_name = workflowName;
906
+ }
907
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
908
+ const result = await this.db.query({
909
+ query: `
910
+ SELECT
911
+ workflow_name,
912
+ run_id,
913
+ snapshot,
914
+ toDateTime64(createdAt, 3) as createdAt,
915
+ toDateTime64(updatedAt, 3) as updatedAt,
916
+ resourceId
917
+ FROM ${storage.TABLE_WORKFLOW_SNAPSHOT} ${TABLE_ENGINES[storage.TABLE_WORKFLOW_SNAPSHOT].startsWith("ReplacingMergeTree") ? "FINAL" : ""}
918
+ ${whereClause}
919
+ `,
920
+ query_params: values,
921
+ format: "JSONEachRow"
922
+ });
923
+ const resultJson = await result.json();
924
+ if (!Array.isArray(resultJson) || resultJson.length === 0) {
925
+ return null;
926
+ }
927
+ return this.parseWorkflowRun(resultJson[0]);
928
+ } catch (error) {
929
+ console.error("Error getting workflow run by ID:", error);
930
+ throw error;
931
+ }
932
+ }
933
+ async hasColumn(table, column) {
934
+ const result = await this.db.query({
935
+ query: `DESCRIBE TABLE ${table}`,
936
+ format: "JSONEachRow"
937
+ });
938
+ const columns = await result.json();
939
+ return columns.some((c) => c.name === column);
940
+ }
941
+ async getTracesPaginated(_args) {
942
+ throw new Error("Method not implemented.");
943
+ }
944
+ async getThreadsByResourceIdPaginated(_args) {
945
+ throw new Error("Method not implemented.");
946
+ }
947
+ async getMessagesPaginated(_args) {
948
+ throw new Error("Method not implemented.");
949
+ }
776
950
  async close() {
777
951
  await this.db.close();
778
952
  }