@mastra/clickhouse 0.0.0-vnext-20251104230439 → 0.0.0-vnext-20251119160359

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.cjs CHANGED
@@ -14,8 +14,8 @@ var TABLE_ENGINES = {
14
14
  [storage.TABLE_THREADS]: `ReplacingMergeTree()`,
15
15
  [storage.TABLE_SCORERS]: `MergeTree()`,
16
16
  [storage.TABLE_RESOURCES]: `ReplacingMergeTree()`,
17
- // TODO: verify this is the correct engine for ai spans when implementing clickhouse storage
18
- [storage.TABLE_AI_SPANS]: `ReplacingMergeTree()`
17
+ // TODO: verify this is the correct engine for Spans when implementing clickhouse storage
18
+ [storage.TABLE_SPANS]: `ReplacingMergeTree()`
19
19
  };
20
20
  var COLUMN_TYPES = {
21
21
  text: "String",
@@ -47,6 +47,24 @@ function transformRows(rows) {
47
47
  }
48
48
 
49
49
  // src/storage/domains/memory/index.ts
50
+ function serializeMetadata(metadata) {
51
+ if (!metadata || Object.keys(metadata).length === 0) {
52
+ return "{}";
53
+ }
54
+ return JSON.stringify(metadata);
55
+ }
56
+ function parseMetadata(metadata) {
57
+ if (!metadata) return {};
58
+ if (typeof metadata === "object") return metadata;
59
+ if (typeof metadata !== "string") return {};
60
+ const trimmed = metadata.trim();
61
+ if (trimmed === "" || trimmed === "null") return {};
62
+ try {
63
+ return JSON.parse(trimmed);
64
+ } catch {
65
+ return {};
66
+ }
67
+ }
50
68
  var MemoryStorageClickhouse = class extends storage.MemoryStorage {
51
69
  client;
52
70
  operations;
@@ -55,135 +73,6 @@ var MemoryStorageClickhouse = class extends storage.MemoryStorage {
55
73
  this.client = client;
56
74
  this.operations = operations;
57
75
  }
58
- async getMessages({
59
- threadId,
60
- resourceId,
61
- selectBy
62
- }) {
63
- try {
64
- if (!threadId.trim()) throw new Error("threadId must be a non-empty string");
65
- const messages = [];
66
- const limit = storage.resolveMessageLimit({ last: selectBy?.last, defaultLimit: 40 });
67
- const include = selectBy?.include || [];
68
- if (include.length) {
69
- const unionQueries = [];
70
- const params = [];
71
- let paramIdx = 1;
72
- for (const inc of include) {
73
- const { id, withPreviousMessages = 0, withNextMessages = 0 } = inc;
74
- const searchId = inc.threadId || threadId;
75
- unionQueries.push(`
76
- SELECT * FROM (
77
- WITH numbered_messages AS (
78
- SELECT
79
- id, content, role, type, "createdAt", thread_id, "resourceId",
80
- ROW_NUMBER() OVER (ORDER BY "createdAt" ASC) as row_num
81
- FROM "${storage.TABLE_MESSAGES}"
82
- WHERE thread_id = {var_thread_id_${paramIdx}:String}
83
- ),
84
- target_positions AS (
85
- SELECT row_num as target_pos
86
- FROM numbered_messages
87
- WHERE id = {var_include_id_${paramIdx}:String}
88
- )
89
- SELECT DISTINCT m.id, m.content, m.role, m.type, m."createdAt", m.thread_id AS "threadId"
90
- FROM numbered_messages m
91
- CROSS JOIN target_positions t
92
- WHERE m.row_num BETWEEN (t.target_pos - {var_withPreviousMessages_${paramIdx}:Int64}) AND (t.target_pos + {var_withNextMessages_${paramIdx}:Int64})
93
- ) AS query_${paramIdx}
94
- `);
95
- params.push(
96
- { [`var_thread_id_${paramIdx}`]: searchId },
97
- { [`var_include_id_${paramIdx}`]: id },
98
- { [`var_withPreviousMessages_${paramIdx}`]: withPreviousMessages },
99
- { [`var_withNextMessages_${paramIdx}`]: withNextMessages }
100
- );
101
- paramIdx++;
102
- }
103
- const finalQuery = unionQueries.join(" UNION ALL ") + ' ORDER BY "createdAt" DESC';
104
- const mergedParams = params.reduce((acc, paramObj) => ({ ...acc, ...paramObj }), {});
105
- const includeResult = await this.client.query({
106
- query: finalQuery,
107
- query_params: mergedParams,
108
- clickhouse_settings: {
109
- date_time_input_format: "best_effort",
110
- date_time_output_format: "iso",
111
- use_client_time_zone: 1,
112
- output_format_json_quote_64bit_integers: 0
113
- }
114
- });
115
- const rows2 = await includeResult.json();
116
- const includedMessages = transformRows(rows2.data);
117
- const seen = /* @__PURE__ */ new Set();
118
- const dedupedMessages = includedMessages.filter((message) => {
119
- if (seen.has(message.id)) return false;
120
- seen.add(message.id);
121
- return true;
122
- });
123
- messages.push(...dedupedMessages);
124
- }
125
- let whereClause = "WHERE thread_id = {threadId:String}";
126
- const queryParams = {
127
- threadId,
128
- exclude: messages.map((m) => m.id),
129
- limit
130
- };
131
- if (resourceId) {
132
- whereClause += ' AND "resourceId" = {resourceId:String}';
133
- queryParams.resourceId = resourceId;
134
- }
135
- const result = await this.client.query({
136
- query: `
137
- SELECT
138
- id,
139
- content,
140
- role,
141
- type,
142
- toDateTime64(createdAt, 3) as createdAt,
143
- thread_id AS "threadId"
144
- FROM "${storage.TABLE_MESSAGES}"
145
- ${whereClause}
146
- AND id NOT IN ({exclude:Array(String)})
147
- ORDER BY "createdAt" DESC
148
- LIMIT {limit:Int64}
149
- `,
150
- query_params: queryParams,
151
- clickhouse_settings: {
152
- // Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
153
- date_time_input_format: "best_effort",
154
- date_time_output_format: "iso",
155
- use_client_time_zone: 1,
156
- output_format_json_quote_64bit_integers: 0
157
- }
158
- });
159
- const rows = await result.json();
160
- messages.push(...transformRows(rows.data));
161
- messages.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
162
- messages.forEach((message) => {
163
- if (typeof message.content === "string") {
164
- try {
165
- message.content = JSON.parse(message.content);
166
- } catch {
167
- }
168
- }
169
- });
170
- const list = new agent.MessageList({ threadId, resourceId }).add(
171
- messages,
172
- "memory"
173
- );
174
- return { messages: list.get.all.db() };
175
- } catch (error$1) {
176
- throw new error.MastraError(
177
- {
178
- id: "CLICKHOUSE_STORAGE_GET_MESSAGES_FAILED",
179
- domain: error.ErrorDomain.STORAGE,
180
- category: error.ErrorCategory.THIRD_PARTY,
181
- details: { threadId, resourceId: resourceId ?? "" }
182
- },
183
- error$1
184
- );
185
- }
186
- }
187
76
  async listMessagesById({ messageIds }) {
188
77
  if (messageIds.length === 0) return { messages: [] };
189
78
  try {
@@ -290,7 +179,7 @@ var MemoryStorageClickhouse = class extends storage.MemoryStorage {
290
179
  dataQuery += ` AND createdAt <= parseDateTime64BestEffort({toDate:String}, 3)`;
291
180
  dataParams.toDate = endDate;
292
181
  }
293
- const { field, direction } = this.parseOrderBy(orderBy);
182
+ const { field, direction } = this.parseOrderBy(orderBy, "ASC");
294
183
  dataQuery += ` ORDER BY "${field}" ${direction}`;
295
184
  if (perPageForResponse === false) ; else {
296
185
  dataQuery += ` LIMIT {limit:Int64} OFFSET {offset:Int64}`;
@@ -579,7 +468,7 @@ var MemoryStorageClickhouse = class extends storage.MemoryStorage {
579
468
  id: thread.id,
580
469
  resourceId: thread.resourceId,
581
470
  title: thread.title,
582
- metadata: thread.metadata,
471
+ metadata: serializeMetadata(thread.metadata),
583
472
  createdAt: thread.createdAt,
584
473
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
585
474
  })),
@@ -614,8 +503,9 @@ var MemoryStorageClickhouse = class extends storage.MemoryStorage {
614
503
  toDateTime64(createdAt, 3) as createdAt,
615
504
  toDateTime64(updatedAt, 3) as updatedAt
616
505
  FROM "${storage.TABLE_THREADS}"
617
- FINAL
618
- WHERE id = {var_id:String}`,
506
+ WHERE id = {var_id:String}
507
+ ORDER BY updatedAt DESC
508
+ LIMIT 1`,
619
509
  query_params: { var_id: threadId },
620
510
  clickhouse_settings: {
621
511
  // Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
@@ -632,7 +522,7 @@ var MemoryStorageClickhouse = class extends storage.MemoryStorage {
632
522
  }
633
523
  return {
634
524
  ...thread,
635
- metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
525
+ metadata: parseMetadata(thread.metadata),
636
526
  createdAt: thread.createdAt,
637
527
  updatedAt: thread.updatedAt
638
528
  };
@@ -655,6 +545,7 @@ var MemoryStorageClickhouse = class extends storage.MemoryStorage {
655
545
  values: [
656
546
  {
657
547
  ...thread,
548
+ metadata: serializeMetadata(thread.metadata),
658
549
  createdAt: thread.createdAt.toISOString(),
659
550
  updatedAt: thread.updatedAt.toISOString()
660
551
  }
@@ -708,7 +599,7 @@ var MemoryStorageClickhouse = class extends storage.MemoryStorage {
708
599
  id: updatedThread.id,
709
600
  resourceId: updatedThread.resourceId,
710
601
  title: updatedThread.title,
711
- metadata: updatedThread.metadata,
602
+ metadata: serializeMetadata(updatedThread.metadata),
712
603
  createdAt: updatedThread.createdAt,
713
604
  updatedAt: updatedThread.updatedAt.toISOString()
714
605
  }
@@ -778,7 +669,7 @@ var MemoryStorageClickhouse = class extends storage.MemoryStorage {
778
669
  const { field, direction } = this.parseOrderBy(orderBy);
779
670
  try {
780
671
  const countResult = await this.client.query({
781
- query: `SELECT count() as total FROM ${storage.TABLE_THREADS} WHERE resourceId = {resourceId:String}`,
672
+ query: `SELECT count(DISTINCT id) as total FROM ${storage.TABLE_THREADS} WHERE resourceId = {resourceId:String}`,
782
673
  query_params: { resourceId },
783
674
  clickhouse_settings: {
784
675
  date_time_input_format: "best_effort",
@@ -800,15 +691,27 @@ var MemoryStorageClickhouse = class extends storage.MemoryStorage {
800
691
  }
801
692
  const dataResult = await this.client.query({
802
693
  query: `
694
+ WITH ranked_threads AS (
695
+ SELECT
696
+ id,
697
+ resourceId,
698
+ title,
699
+ metadata,
700
+ toDateTime64(createdAt, 3) as createdAt,
701
+ toDateTime64(updatedAt, 3) as updatedAt,
702
+ ROW_NUMBER() OVER (PARTITION BY id ORDER BY updatedAt DESC) as row_num
703
+ FROM ${storage.TABLE_THREADS}
704
+ WHERE resourceId = {resourceId:String}
705
+ )
803
706
  SELECT
804
707
  id,
805
708
  resourceId,
806
709
  title,
807
710
  metadata,
808
- toDateTime64(createdAt, 3) as createdAt,
809
- toDateTime64(updatedAt, 3) as updatedAt
810
- FROM ${storage.TABLE_THREADS}
811
- WHERE resourceId = {resourceId:String}
711
+ createdAt,
712
+ updatedAt
713
+ FROM ranked_threads
714
+ WHERE row_num = 1
812
715
  ORDER BY "${field}" ${direction === "DESC" ? "DESC" : "ASC"}
813
716
  LIMIT {perPage:Int64} OFFSET {offset:Int64}
814
717
  `,
@@ -825,7 +728,10 @@ var MemoryStorageClickhouse = class extends storage.MemoryStorage {
825
728
  }
826
729
  });
827
730
  const rows = await dataResult.json();
828
- const threads = transformRows(rows.data);
731
+ const threads = transformRows(rows.data).map((thread) => ({
732
+ ...thread,
733
+ metadata: parseMetadata(thread.metadata)
734
+ }));
829
735
  return {
830
736
  threads,
831
737
  total,
@@ -1042,7 +948,7 @@ var MemoryStorageClickhouse = class extends storage.MemoryStorage {
1042
948
  const now = (/* @__PURE__ */ new Date()).toISOString().replace("Z", "");
1043
949
  const threadUpdatePromises = Array.from(threadIdsToUpdate).map(async (threadId) => {
1044
950
  const threadResult = await this.client.query({
1045
- query: `SELECT id, resourceId, title, metadata, createdAt FROM ${storage.TABLE_THREADS} WHERE id = {threadId:String}`,
951
+ query: `SELECT id, resourceId, title, metadata, createdAt FROM ${storage.TABLE_THREADS} WHERE id = {threadId:String} ORDER BY updatedAt DESC LIMIT 1`,
1046
952
  query_params: { threadId },
1047
953
  clickhouse_settings: {
1048
954
  date_time_input_format: "best_effort",
@@ -1071,7 +977,7 @@ var MemoryStorageClickhouse = class extends storage.MemoryStorage {
1071
977
  id: existingThread.id,
1072
978
  resourceId: existingThread.resourceId,
1073
979
  title: existingThread.title,
1074
- metadata: existingThread.metadata,
980
+ metadata: typeof existingThread.metadata === "string" ? existingThread.metadata : serializeMetadata(existingThread.metadata),
1075
981
  createdAt: existingThread.createdAt,
1076
982
  updatedAt: now
1077
983
  }
@@ -1130,7 +1036,7 @@ var MemoryStorageClickhouse = class extends storage.MemoryStorage {
1130
1036
  async getResourceById({ resourceId }) {
1131
1037
  try {
1132
1038
  const result = await this.client.query({
1133
- query: `SELECT id, workingMemory, metadata, createdAt, updatedAt FROM ${storage.TABLE_RESOURCES} WHERE id = {resourceId:String}`,
1039
+ query: `SELECT id, workingMemory, metadata, createdAt, updatedAt FROM ${storage.TABLE_RESOURCES} WHERE id = {resourceId:String} ORDER BY updatedAt DESC LIMIT 1`,
1134
1040
  query_params: { resourceId },
1135
1041
  clickhouse_settings: {
1136
1042
  date_time_input_format: "best_effort",
@@ -1302,6 +1208,9 @@ var StoreOperationsClickhouse = class extends storage.StoreOperations {
1302
1208
  const columns = Object.entries(schema).map(([name, def]) => {
1303
1209
  const constraints = [];
1304
1210
  if (!def.nullable) constraints.push("NOT NULL");
1211
+ if (name === "metadata" && def.type === "text" && def.nullable) {
1212
+ constraints.push("DEFAULT '{}'");
1213
+ }
1305
1214
  const columnTtl = this.ttl?.[tableName]?.columns?.[name];
1306
1215
  return `"${name}" ${COLUMN_TYPES[def.type]} ${constraints.join(" ")} ${columnTtl ? `TTL toDateTime(${columnTtl.ttlKey ?? "createdAt"}) + INTERVAL ${columnTtl.interval} ${columnTtl.unit}` : ""}`;
1307
1216
  }).join(",\n");
@@ -2073,7 +1982,8 @@ var WorkflowsStorageClickhouse = class extends storage.WorkflowsStorage {
2073
1982
  toDate,
2074
1983
  page,
2075
1984
  perPage,
2076
- resourceId
1985
+ resourceId,
1986
+ status
2077
1987
  } = {}) {
2078
1988
  try {
2079
1989
  const conditions = [];
@@ -2082,6 +1992,10 @@ var WorkflowsStorageClickhouse = class extends storage.WorkflowsStorage {
2082
1992
  conditions.push(`workflow_name = {var_workflow_name:String}`);
2083
1993
  values.var_workflow_name = workflowName;
2084
1994
  }
1995
+ if (status) {
1996
+ conditions.push(`JSONExtractString(snapshot, 'status') = {var_status:String}`);
1997
+ values.var_status = status;
1998
+ }
2085
1999
  if (resourceId) {
2086
2000
  const hasResourceId = await this.operations.hasColumn(storage.TABLE_WORKFLOW_SNAPSHOT, "resourceId");
2087
2001
  if (hasResourceId) {
@@ -2208,7 +2122,7 @@ var ClickhouseStore = class extends storage.MastraStorage {
2208
2122
  ttl = {};
2209
2123
  stores;
2210
2124
  constructor(config) {
2211
- super({ name: "ClickhouseStore" });
2125
+ super({ id: config.id, name: "ClickhouseStore" });
2212
2126
  this.db = client.createClient({
2213
2127
  url: config.url,
2214
2128
  username: config.username,
@@ -2335,15 +2249,8 @@ var ClickhouseStore = class extends storage.MastraStorage {
2335
2249
  }) {
2336
2250
  return this.stores.workflows.loadWorkflowSnapshot({ workflowName, runId });
2337
2251
  }
2338
- async listWorkflowRuns({
2339
- workflowName,
2340
- fromDate,
2341
- toDate,
2342
- perPage,
2343
- page,
2344
- resourceId
2345
- } = {}) {
2346
- return this.stores.workflows.listWorkflowRuns({ workflowName, fromDate, toDate, perPage, page, resourceId });
2252
+ async listWorkflowRuns(args = {}) {
2253
+ return this.stores.workflows.listWorkflowRuns(args);
2347
2254
  }
2348
2255
  async getWorkflowRunById({
2349
2256
  runId,
@@ -2367,13 +2274,6 @@ var ClickhouseStore = class extends storage.MastraStorage {
2367
2274
  async deleteThread({ threadId }) {
2368
2275
  return this.stores.memory.deleteThread({ threadId });
2369
2276
  }
2370
- async getMessages({
2371
- threadId,
2372
- resourceId,
2373
- selectBy
2374
- }) {
2375
- return this.stores.memory.getMessages({ threadId, resourceId, selectBy });
2376
- }
2377
2277
  async saveMessages(args) {
2378
2278
  return this.stores.memory.saveMessages(args);
2379
2279
  }