@mastra/clickhouse 0.0.0-vnextWorkflows-20250422081019 → 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.
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { createClient } from '@clickhouse/client';
2
+ import { MessageList } from '@mastra/core/agent';
2
3
  import { TABLE_EVALS, TABLE_THREADS, TABLE_TRACES, TABLE_WORKFLOW_SNAPSHOT, TABLE_MESSAGES, MastraStorage, TABLE_SCHEMAS } from '@mastra/core/storage';
3
4
 
4
5
  // src/storage/index.ts
@@ -136,7 +137,9 @@ var ClickhouseStore = class extends MastraStorage {
136
137
  page,
137
138
  perPage,
138
139
  attributes,
139
- filters
140
+ filters,
141
+ fromDate,
142
+ toDate
140
143
  }) {
141
144
  const limit = perPage;
142
145
  const offset = page * perPage;
@@ -164,6 +167,14 @@ var ClickhouseStore = class extends MastraStorage {
164
167
  args[`var_col_${key}`] = value;
165
168
  });
166
169
  }
170
+ if (fromDate) {
171
+ conditions.push(`createdAt >= {var_from_date:DateTime64(3)}`);
172
+ args.var_from_date = fromDate.getTime() / 1e3;
173
+ }
174
+ if (toDate) {
175
+ conditions.push(`createdAt <= {var_to_date:DateTime64(3)}`);
176
+ args.var_to_date = toDate.getTime() / 1e3;
177
+ }
167
178
  const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
168
179
  const result = await this.db.query({
169
180
  query: `SELECT *, toDateTime64(createdAt, 3) as createdAt FROM ${TABLE_TRACES} ${whereClause} ORDER BY "createdAt" DESC LIMIT ${limit} OFFSET ${offset}`,
@@ -225,7 +236,6 @@ var ClickhouseStore = class extends MastraStorage {
225
236
  ${["id String"].concat(columns)}
226
237
  )
227
238
  ENGINE = ${TABLE_ENGINES[tableName]}
228
- PARTITION BY "createdAt"
229
239
  PRIMARY KEY (createdAt, run_id, workflow_name)
230
240
  ORDER BY (createdAt, run_id, workflow_name)
231
241
  ${rowTtl ? `TTL toDateTime(${rowTtl.ttlKey ?? "createdAt"}) + INTERVAL ${rowTtl.interval} ${rowTtl.unit}` : ""}
@@ -235,7 +245,6 @@ var ClickhouseStore = class extends MastraStorage {
235
245
  ${columns}
236
246
  )
237
247
  ENGINE = ${TABLE_ENGINES[tableName]}
238
- PARTITION BY "createdAt"
239
248
  PRIMARY KEY (createdAt, ${tableName === TABLE_EVALS ? "run_id" : "id"})
240
249
  ORDER BY (createdAt, ${tableName === TABLE_EVALS ? "run_id" : "id"})
241
250
  ${this.ttl?.[tableName]?.row ? `TTL toDateTime(createdAt) + INTERVAL ${this.ttl[tableName].row.interval} ${this.ttl[tableName].row.unit}` : ""}
@@ -256,6 +265,61 @@ var ClickhouseStore = class extends MastraStorage {
256
265
  throw error;
257
266
  }
258
267
  }
268
+ getSqlType(type) {
269
+ switch (type) {
270
+ case "text":
271
+ return "String";
272
+ case "timestamp":
273
+ return "DateTime64(3)";
274
+ case "integer":
275
+ case "bigint":
276
+ return "Int64";
277
+ case "jsonb":
278
+ return "String";
279
+ default:
280
+ return super.getSqlType(type);
281
+ }
282
+ }
283
+ /**
284
+ * Alters table schema to add columns if they don't exist
285
+ * @param tableName Name of the table
286
+ * @param schema Schema of the table
287
+ * @param ifNotExists Array of column names to add if they don't exist
288
+ */
289
+ async alterTable({
290
+ tableName,
291
+ schema,
292
+ ifNotExists
293
+ }) {
294
+ try {
295
+ const describeSql = `DESCRIBE TABLE ${tableName}`;
296
+ const result = await this.db.query({
297
+ query: describeSql
298
+ });
299
+ const rows = await result.json();
300
+ const existingColumnNames = new Set(rows.data.map((row) => row.name.toLowerCase()));
301
+ for (const columnName of ifNotExists) {
302
+ if (!existingColumnNames.has(columnName.toLowerCase()) && schema[columnName]) {
303
+ const columnDef = schema[columnName];
304
+ let sqlType = this.getSqlType(columnDef.type);
305
+ if (columnDef.nullable !== false) {
306
+ sqlType = `Nullable(${sqlType})`;
307
+ }
308
+ const defaultValue = columnDef.nullable === false ? this.getDefaultValue(columnDef.type) : "";
309
+ const alterSql = `ALTER TABLE ${tableName} ADD COLUMN IF NOT EXISTS "${columnName}" ${sqlType} ${defaultValue}`.trim();
310
+ await this.db.query({
311
+ query: alterSql
312
+ });
313
+ this.logger?.debug?.(`Added column ${columnName} to table ${tableName}`);
314
+ }
315
+ }
316
+ } catch (error) {
317
+ this.logger?.error?.(
318
+ `Error altering table ${tableName}: ${error instanceof Error ? error.message : String(error)}`
319
+ );
320
+ throw new Error(`Failed to alter table ${tableName}: ${error}`);
321
+ }
322
+ }
259
323
  async clearTable({ tableName }) {
260
324
  try {
261
325
  await this.db.query({
@@ -457,15 +521,18 @@ var ClickhouseStore = class extends MastraStorage {
457
521
  };
458
522
  await this.db.insert({
459
523
  table: TABLE_THREADS,
524
+ format: "JSONEachRow",
460
525
  values: [
461
526
  {
462
- ...updatedThread,
527
+ id: updatedThread.id,
528
+ resourceId: updatedThread.resourceId,
529
+ title: updatedThread.title,
530
+ metadata: updatedThread.metadata,
531
+ createdAt: updatedThread.createdAt,
463
532
  updatedAt: updatedThread.updatedAt.toISOString()
464
533
  }
465
534
  ],
466
- format: "JSONEachRow",
467
535
  clickhouse_settings: {
468
- // Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
469
536
  date_time_input_format: "best_effort",
470
537
  use_client_time_zone: 1,
471
538
  output_format_json_quote_64bit_integers: 0
@@ -480,7 +547,7 @@ var ClickhouseStore = class extends MastraStorage {
480
547
  async deleteThread({ threadId }) {
481
548
  try {
482
549
  await this.db.command({
483
- query: `DELETE FROM "${TABLE_MESSAGES}" WHERE thread_id = '${threadId}';`,
550
+ query: `DELETE FROM "${TABLE_MESSAGES}" WHERE thread_id = {var_thread_id:String};`,
484
551
  query_params: { var_thread_id: threadId },
485
552
  clickhouse_settings: {
486
553
  output_format_json_quote_64bit_integers: 0
@@ -498,7 +565,12 @@ var ClickhouseStore = class extends MastraStorage {
498
565
  throw error;
499
566
  }
500
567
  }
501
- async getMessages({ threadId, selectBy }) {
568
+ async getMessages({
569
+ threadId,
570
+ resourceId,
571
+ selectBy,
572
+ format
573
+ }) {
502
574
  try {
503
575
  const messages = [];
504
576
  const limit = typeof selectBy?.last === `number` ? selectBy.last : 40;
@@ -594,16 +666,20 @@ var ClickhouseStore = class extends MastraStorage {
594
666
  }
595
667
  }
596
668
  });
597
- return messages;
669
+ const list = new MessageList({ threadId, resourceId }).add(messages, "memory");
670
+ if (format === `v2`) return list.get.all.v2();
671
+ return list.get.all.v1();
598
672
  } catch (error) {
599
673
  console.error("Error getting messages:", error);
600
674
  throw error;
601
675
  }
602
676
  }
603
- async saveMessages({ messages }) {
677
+ async saveMessages(args) {
678
+ const { messages, format = "v1" } = args;
604
679
  if (messages.length === 0) return messages;
605
680
  try {
606
681
  const threadId = messages[0]?.threadId;
682
+ const resourceId = messages[0]?.resourceId;
607
683
  if (!threadId) {
608
684
  throw new Error("Thread ID is required");
609
685
  }
@@ -611,25 +687,50 @@ var ClickhouseStore = class extends MastraStorage {
611
687
  if (!thread) {
612
688
  throw new Error(`Thread ${threadId} not found`);
613
689
  }
614
- await this.db.insert({
615
- table: TABLE_MESSAGES,
616
- format: "JSONEachRow",
617
- values: messages.map((message) => ({
618
- id: message.id,
619
- thread_id: threadId,
620
- content: typeof message.content === "string" ? message.content : JSON.stringify(message.content),
621
- createdAt: message.createdAt.toISOString(),
622
- role: message.role,
623
- type: message.type
624
- })),
625
- clickhouse_settings: {
626
- // Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
627
- date_time_input_format: "best_effort",
628
- use_client_time_zone: 1,
629
- output_format_json_quote_64bit_integers: 0
630
- }
631
- });
632
- return messages;
690
+ await Promise.all([
691
+ // Insert messages
692
+ this.db.insert({
693
+ table: TABLE_MESSAGES,
694
+ format: "JSONEachRow",
695
+ values: messages.map((message) => ({
696
+ id: message.id,
697
+ thread_id: threadId,
698
+ content: typeof message.content === "string" ? message.content : JSON.stringify(message.content),
699
+ createdAt: message.createdAt.toISOString(),
700
+ role: message.role,
701
+ type: message.type || "v2"
702
+ })),
703
+ clickhouse_settings: {
704
+ // Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
705
+ date_time_input_format: "best_effort",
706
+ use_client_time_zone: 1,
707
+ output_format_json_quote_64bit_integers: 0
708
+ }
709
+ }),
710
+ // Update thread's updatedAt timestamp
711
+ this.db.insert({
712
+ table: TABLE_THREADS,
713
+ format: "JSONEachRow",
714
+ values: [
715
+ {
716
+ id: thread.id,
717
+ resourceId: thread.resourceId,
718
+ title: thread.title,
719
+ metadata: thread.metadata,
720
+ createdAt: thread.createdAt,
721
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
722
+ }
723
+ ],
724
+ clickhouse_settings: {
725
+ date_time_input_format: "best_effort",
726
+ use_client_time_zone: 1,
727
+ output_format_json_quote_64bit_integers: 0
728
+ }
729
+ })
730
+ ]);
731
+ const list = new MessageList({ threadId, resourceId }).add(messages, "memory");
732
+ if (format === `v2`) return list.get.all.v2();
733
+ return list.get.all.v1();
633
734
  } catch (error) {
634
735
  console.error("Error saving messages:", error);
635
736
  throw error;
@@ -694,12 +795,31 @@ var ClickhouseStore = class extends MastraStorage {
694
795
  throw error;
695
796
  }
696
797
  }
798
+ parseWorkflowRun(row) {
799
+ let parsedSnapshot = row.snapshot;
800
+ if (typeof parsedSnapshot === "string") {
801
+ try {
802
+ parsedSnapshot = JSON.parse(row.snapshot);
803
+ } catch (e) {
804
+ console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
805
+ }
806
+ }
807
+ return {
808
+ workflowName: row.workflow_name,
809
+ runId: row.run_id,
810
+ snapshot: parsedSnapshot,
811
+ createdAt: new Date(row.createdAt),
812
+ updatedAt: new Date(row.updatedAt),
813
+ resourceId: row.resourceId
814
+ };
815
+ }
697
816
  async getWorkflowRuns({
698
817
  workflowName,
699
818
  fromDate,
700
819
  toDate,
701
820
  limit,
702
- offset
821
+ offset,
822
+ resourceId
703
823
  } = {}) {
704
824
  try {
705
825
  const conditions = [];
@@ -708,6 +828,15 @@ var ClickhouseStore = class extends MastraStorage {
708
828
  conditions.push(`workflow_name = {var_workflow_name:String}`);
709
829
  values.var_workflow_name = workflowName;
710
830
  }
831
+ if (resourceId) {
832
+ const hasResourceId = await this.hasColumn(TABLE_WORKFLOW_SNAPSHOT, "resourceId");
833
+ if (hasResourceId) {
834
+ conditions.push(`resourceId = {var_resourceId:String}`);
835
+ values.var_resourceId = resourceId;
836
+ } else {
837
+ console.warn(`[${TABLE_WORKFLOW_SNAPSHOT}] resourceId column not found. Skipping resourceId filter.`);
838
+ }
839
+ }
711
840
  if (fromDate) {
712
841
  conditions.push(`createdAt >= {var_from_date:DateTime64(3)}`);
713
842
  values.var_from_date = fromDate.getTime() / 1e3;
@@ -736,7 +865,8 @@ var ClickhouseStore = class extends MastraStorage {
736
865
  run_id,
737
866
  snapshot,
738
867
  toDateTime64(createdAt, 3) as createdAt,
739
- toDateTime64(updatedAt, 3) as updatedAt
868
+ toDateTime64(updatedAt, 3) as updatedAt,
869
+ resourceId
740
870
  FROM ${TABLE_WORKFLOW_SNAPSHOT} ${TABLE_ENGINES[TABLE_WORKFLOW_SNAPSHOT].startsWith("ReplacingMergeTree") ? "FINAL" : ""}
741
871
  ${whereClause}
742
872
  ORDER BY createdAt DESC
@@ -749,21 +879,7 @@ var ClickhouseStore = class extends MastraStorage {
749
879
  const resultJson = await result.json();
750
880
  const rows = resultJson;
751
881
  const runs = rows.map((row) => {
752
- let parsedSnapshot = row.snapshot;
753
- if (typeof parsedSnapshot === "string") {
754
- try {
755
- parsedSnapshot = JSON.parse(row.snapshot);
756
- } catch (e) {
757
- console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
758
- }
759
- }
760
- return {
761
- workflowName: row.workflow_name,
762
- runId: row.run_id,
763
- snapshot: parsedSnapshot,
764
- createdAt: new Date(row.createdAt),
765
- updatedAt: new Date(row.updatedAt)
766
- };
882
+ return this.parseWorkflowRun(row);
767
883
  });
768
884
  return { runs, total: total || runs.length };
769
885
  } catch (error) {
@@ -771,6 +887,64 @@ var ClickhouseStore = class extends MastraStorage {
771
887
  throw error;
772
888
  }
773
889
  }
890
+ async getWorkflowRunById({
891
+ runId,
892
+ workflowName
893
+ }) {
894
+ try {
895
+ const conditions = [];
896
+ const values = {};
897
+ if (runId) {
898
+ conditions.push(`run_id = {var_runId:String}`);
899
+ values.var_runId = runId;
900
+ }
901
+ if (workflowName) {
902
+ conditions.push(`workflow_name = {var_workflow_name:String}`);
903
+ values.var_workflow_name = workflowName;
904
+ }
905
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
906
+ const result = await this.db.query({
907
+ query: `
908
+ SELECT
909
+ workflow_name,
910
+ run_id,
911
+ snapshot,
912
+ toDateTime64(createdAt, 3) as createdAt,
913
+ toDateTime64(updatedAt, 3) as updatedAt,
914
+ resourceId
915
+ FROM ${TABLE_WORKFLOW_SNAPSHOT} ${TABLE_ENGINES[TABLE_WORKFLOW_SNAPSHOT].startsWith("ReplacingMergeTree") ? "FINAL" : ""}
916
+ ${whereClause}
917
+ `,
918
+ query_params: values,
919
+ format: "JSONEachRow"
920
+ });
921
+ const resultJson = await result.json();
922
+ if (!Array.isArray(resultJson) || resultJson.length === 0) {
923
+ return null;
924
+ }
925
+ return this.parseWorkflowRun(resultJson[0]);
926
+ } catch (error) {
927
+ console.error("Error getting workflow run by ID:", error);
928
+ throw error;
929
+ }
930
+ }
931
+ async hasColumn(table, column) {
932
+ const result = await this.db.query({
933
+ query: `DESCRIBE TABLE ${table}`,
934
+ format: "JSONEachRow"
935
+ });
936
+ const columns = await result.json();
937
+ return columns.some((c) => c.name === column);
938
+ }
939
+ async getTracesPaginated(_args) {
940
+ throw new Error("Method not implemented.");
941
+ }
942
+ async getThreadsByResourceIdPaginated(_args) {
943
+ throw new Error("Method not implemented.");
944
+ }
945
+ async getMessagesPaginated(_args) {
946
+ throw new Error("Method not implemented.");
947
+ }
774
948
  async close() {
775
949
  await this.db.close();
776
950
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mastra/clickhouse",
3
- "version": "0.0.0-vnextWorkflows-20250422081019",
3
+ "version": "0.0.0-workflow-deno-20250616115451",
4
4
  "description": "Clickhouse provider for Mastra - includes db storage capabilities",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -20,17 +20,21 @@
20
20
  },
21
21
  "license": "MIT",
22
22
  "dependencies": {
23
- "@clickhouse/client": "^1.11.0",
24
- "@mastra/core": "0.0.0-vnextWorkflows-20250422081019"
23
+ "@clickhouse/client": "^1.11.2"
25
24
  },
26
25
  "devDependencies": {
27
- "@microsoft/api-extractor": "^7.52.1",
28
- "@types/node": "^20.17.27",
29
- "eslint": "^9.23.0",
30
- "tsup": "^8.4.0",
31
- "typescript": "^5.8.2",
32
- "vitest": "^3.0.9",
33
- "@internal/lint": "0.0.2"
26
+ "@microsoft/api-extractor": "^7.52.8",
27
+ "@types/node": "^20.19.0",
28
+ "eslint": "^9.28.0",
29
+ "tsup": "^8.5.0",
30
+ "typescript": "^5.8.3",
31
+ "vitest": "^3.2.3",
32
+ "@internal/lint": "0.0.0-workflow-deno-20250616115451",
33
+ "@internal/storage-test-utils": "0.0.0-workflow-deno-20250616115451",
34
+ "@mastra/core": "0.0.0-workflow-deno-20250616115451"
35
+ },
36
+ "peerDependencies": {
37
+ "@mastra/core": ">=0.10.4-0 <0.11.0"
34
38
  },
35
39
  "scripts": {
36
40
  "build": "tsup src/index.ts --format esm,cjs --experimental-dts --clean --treeshake=smallest --splitting",