@mastra/clickhouse 1.0.0-beta.11 → 1.0.0-beta.12
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 +58 -0
- package/dist/docs/README.md +1 -1
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/SOURCE_MAP.json +1 -1
- package/dist/docs/storage/01-reference.md +15 -15
- package/dist/index.cjs +314 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +315 -11
- package/dist/index.js.map +1 -1
- package/dist/storage/db/index.d.ts +50 -0
- package/dist/storage/db/index.d.ts.map +1 -1
- package/dist/storage/db/utils.d.ts.map +1 -1
- package/dist/storage/domains/observability/index.d.ts +23 -0
- package/dist/storage/domains/observability/index.d.ts.map +1 -1
- package/dist/storage/index.d.ts +2 -2
- package/dist/storage/index.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createClient } from '@clickhouse/client';
|
|
2
2
|
import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
|
|
3
|
-
import { TABLE_SPANS, TABLE_RESOURCES, TABLE_SCORERS, TABLE_THREADS, TABLE_TRACES, TABLE_WORKFLOW_SNAPSHOT, TABLE_MESSAGES, MemoryStorage, TABLE_SCHEMAS, createStorageErrorId, normalizePerPage, calculatePagination, ObservabilityStorage, SPAN_SCHEMA, listTracesArgsSchema, ScoresStorage, transformScoreRow, SCORERS_SCHEMA, WorkflowsStorage,
|
|
3
|
+
import { TABLE_SPANS, TABLE_RESOURCES, TABLE_SCORERS, TABLE_THREADS, TABLE_TRACES, TABLE_WORKFLOW_SNAPSHOT, TABLE_MESSAGES, MemoryStorage, TABLE_SCHEMAS, createStorageErrorId, normalizePerPage, calculatePagination, ObservabilityStorage, SPAN_SCHEMA, listTracesArgsSchema, ScoresStorage, transformScoreRow, SCORERS_SCHEMA, WorkflowsStorage, MastraCompositeStore, TraceStatus, getSqlType, getDefaultValue, safelyParseJSON } from '@mastra/core/storage';
|
|
4
4
|
import { MessageList } from '@mastra/core/agent';
|
|
5
5
|
import { MastraBase } from '@mastra/core/base';
|
|
6
6
|
import { saveScorePayloadSchema } from '@mastra/core/evals';
|
|
@@ -13,8 +13,10 @@ var TABLE_ENGINES = {
|
|
|
13
13
|
[TABLE_THREADS]: `ReplacingMergeTree()`,
|
|
14
14
|
[TABLE_SCORERS]: `MergeTree()`,
|
|
15
15
|
[TABLE_RESOURCES]: `ReplacingMergeTree()`,
|
|
16
|
-
//
|
|
17
|
-
|
|
16
|
+
// ReplacingMergeTree(updatedAt) deduplicates rows with the same (traceId, spanId) sorting key,
|
|
17
|
+
// keeping the row with the highest updatedAt value. Combined with ORDER BY (traceId, spanId),
|
|
18
|
+
// this provides eventual uniqueness for the (traceId, spanId) composite key.
|
|
19
|
+
[TABLE_SPANS]: `ReplacingMergeTree(updatedAt)`,
|
|
18
20
|
mastra_agents: `ReplacingMergeTree()`
|
|
19
21
|
};
|
|
20
22
|
var COLUMN_TYPES = {
|
|
@@ -97,6 +99,213 @@ var ClickhouseDB = class extends MastraBase {
|
|
|
97
99
|
const columns = await result.json();
|
|
98
100
|
return columns.some((c) => c.name === column);
|
|
99
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* Checks if a table exists in the database.
|
|
104
|
+
*/
|
|
105
|
+
async tableExists(tableName) {
|
|
106
|
+
try {
|
|
107
|
+
const result = await this.client.query({
|
|
108
|
+
query: `EXISTS TABLE ${tableName}`,
|
|
109
|
+
format: "JSONEachRow"
|
|
110
|
+
});
|
|
111
|
+
const rows = await result.json();
|
|
112
|
+
return rows[0]?.result === 1;
|
|
113
|
+
} catch {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Gets the sorting key (ORDER BY columns) for a table.
|
|
119
|
+
* Returns null if the table doesn't exist.
|
|
120
|
+
*/
|
|
121
|
+
async getTableSortingKey(tableName) {
|
|
122
|
+
try {
|
|
123
|
+
const result = await this.client.query({
|
|
124
|
+
query: `SELECT sorting_key FROM system.tables WHERE database = currentDatabase() AND name = {tableName:String}`,
|
|
125
|
+
query_params: { tableName },
|
|
126
|
+
format: "JSONEachRow"
|
|
127
|
+
});
|
|
128
|
+
const rows = await result.json();
|
|
129
|
+
return rows[0]?.sorting_key ?? null;
|
|
130
|
+
} catch {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Checks if migration is needed for the spans table.
|
|
136
|
+
* Returns information about the current state.
|
|
137
|
+
*/
|
|
138
|
+
async checkSpansMigrationStatus(tableName) {
|
|
139
|
+
const exists = await this.tableExists(tableName);
|
|
140
|
+
if (!exists) {
|
|
141
|
+
return { needsMigration: false, currentSortingKey: null };
|
|
142
|
+
}
|
|
143
|
+
const currentSortingKey = await this.getTableSortingKey(tableName);
|
|
144
|
+
if (!currentSortingKey) {
|
|
145
|
+
return { needsMigration: false, currentSortingKey: null };
|
|
146
|
+
}
|
|
147
|
+
const needsMigration = currentSortingKey.toLowerCase().startsWith("createdat");
|
|
148
|
+
return { needsMigration, currentSortingKey };
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Checks for duplicate (traceId, spanId) combinations in the spans table.
|
|
152
|
+
* Returns information about duplicates for logging/CLI purposes.
|
|
153
|
+
*/
|
|
154
|
+
async checkForDuplicateSpans(tableName) {
|
|
155
|
+
try {
|
|
156
|
+
const result = await this.client.query({
|
|
157
|
+
query: `
|
|
158
|
+
SELECT count() as duplicate_count
|
|
159
|
+
FROM (
|
|
160
|
+
SELECT traceId, spanId
|
|
161
|
+
FROM ${tableName}
|
|
162
|
+
GROUP BY traceId, spanId
|
|
163
|
+
HAVING count() > 1
|
|
164
|
+
)
|
|
165
|
+
`,
|
|
166
|
+
format: "JSONEachRow"
|
|
167
|
+
});
|
|
168
|
+
const rows = await result.json();
|
|
169
|
+
const duplicateCount = parseInt(rows[0]?.duplicate_count ?? "0", 10);
|
|
170
|
+
return {
|
|
171
|
+
hasDuplicates: duplicateCount > 0,
|
|
172
|
+
duplicateCount
|
|
173
|
+
};
|
|
174
|
+
} catch (error) {
|
|
175
|
+
this.logger?.debug?.(`Could not check for duplicates: ${error}`);
|
|
176
|
+
return { hasDuplicates: false, duplicateCount: 0 };
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Migrates the spans table from the old sorting key (createdAt, traceId, spanId)
|
|
181
|
+
* to the new sorting key (traceId, spanId) for proper uniqueness enforcement.
|
|
182
|
+
*
|
|
183
|
+
* This migration:
|
|
184
|
+
* 1. Renames the old table to a backup
|
|
185
|
+
* 2. Creates a new table with the correct sorting key
|
|
186
|
+
* 3. Copies all data from the backup to the new table, deduplicating by (traceId, spanId)
|
|
187
|
+
* using priority-based selection:
|
|
188
|
+
* - First, prefer completed spans (those with endedAt set)
|
|
189
|
+
* - Then prefer the most recently updated span (highest updatedAt)
|
|
190
|
+
* - Finally use creation time as tiebreaker (highest createdAt)
|
|
191
|
+
* 4. Drops the backup table
|
|
192
|
+
*
|
|
193
|
+
* The deduplication strategy matches the PostgreSQL migration (PR #12073) to ensure
|
|
194
|
+
* consistent behavior across storage backends.
|
|
195
|
+
*
|
|
196
|
+
* The migration is idempotent - it only runs if the old sorting key is detected.
|
|
197
|
+
*
|
|
198
|
+
* @returns true if migration was performed, false if not needed
|
|
199
|
+
*/
|
|
200
|
+
async migrateSpansTableSortingKey({
|
|
201
|
+
tableName,
|
|
202
|
+
schema
|
|
203
|
+
}) {
|
|
204
|
+
if (tableName !== TABLE_SPANS) {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
const exists = await this.tableExists(tableName);
|
|
208
|
+
if (!exists) {
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
const currentSortingKey = await this.getTableSortingKey(tableName);
|
|
212
|
+
if (!currentSortingKey) {
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
const needsMigration = currentSortingKey.toLowerCase().startsWith("createdat");
|
|
216
|
+
if (!needsMigration) {
|
|
217
|
+
this.logger?.debug?.(`Spans table already has correct sorting key: ${currentSortingKey}`);
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
this.logger?.info?.(`Migrating spans table from sorting key "${currentSortingKey}" to "(traceId, spanId)"`);
|
|
221
|
+
const backupTableName = `${tableName}_backup_${Date.now()}`;
|
|
222
|
+
const rowTtl = this.ttl?.[tableName]?.row;
|
|
223
|
+
try {
|
|
224
|
+
await this.client.command({
|
|
225
|
+
query: `RENAME TABLE ${tableName} TO ${backupTableName}`
|
|
226
|
+
});
|
|
227
|
+
const columns = Object.entries(schema).map(([name, def]) => {
|
|
228
|
+
let sqlType = this.getSqlType(def.type);
|
|
229
|
+
let isNullable = def.nullable === true;
|
|
230
|
+
if (tableName === TABLE_SPANS && name === "updatedAt") {
|
|
231
|
+
isNullable = false;
|
|
232
|
+
}
|
|
233
|
+
if (isNullable) {
|
|
234
|
+
sqlType = `Nullable(${sqlType})`;
|
|
235
|
+
}
|
|
236
|
+
const constraints = [];
|
|
237
|
+
if (name === "metadata" && (def.type === "text" || def.type === "jsonb") && isNullable) {
|
|
238
|
+
constraints.push("DEFAULT '{}'");
|
|
239
|
+
}
|
|
240
|
+
const columnTtl = this.ttl?.[tableName]?.columns?.[name];
|
|
241
|
+
return `"${name}" ${sqlType} ${constraints.join(" ")} ${columnTtl ? `TTL toDateTime(${columnTtl.ttlKey ?? "createdAt"}) + INTERVAL ${columnTtl.interval} ${columnTtl.unit}` : ""}`;
|
|
242
|
+
}).join(",\n");
|
|
243
|
+
const createSql = `
|
|
244
|
+
CREATE TABLE ${tableName} (
|
|
245
|
+
${columns}
|
|
246
|
+
)
|
|
247
|
+
ENGINE = ${TABLE_ENGINES[tableName] ?? "MergeTree()"}
|
|
248
|
+
PRIMARY KEY (traceId, spanId)
|
|
249
|
+
ORDER BY (traceId, spanId)
|
|
250
|
+
${rowTtl ? `TTL toDateTime(${rowTtl.ttlKey ?? "createdAt"}) + INTERVAL ${rowTtl.interval} ${rowTtl.unit}` : ""}
|
|
251
|
+
SETTINGS index_granularity = 8192
|
|
252
|
+
`;
|
|
253
|
+
await this.client.command({
|
|
254
|
+
query: createSql
|
|
255
|
+
});
|
|
256
|
+
const describeResult = await this.client.query({
|
|
257
|
+
query: `DESCRIBE TABLE ${backupTableName}`,
|
|
258
|
+
format: "JSONEachRow"
|
|
259
|
+
});
|
|
260
|
+
const backupColumns = await describeResult.json();
|
|
261
|
+
const backupColumnNames = new Set(backupColumns.map((c) => c.name));
|
|
262
|
+
const columnsToInsert = Object.keys(schema).filter((col) => backupColumnNames.has(col));
|
|
263
|
+
const columnList = columnsToInsert.map((c) => `"${c}"`).join(", ");
|
|
264
|
+
const selectExpressions = columnsToInsert.map((c) => c === "updatedAt" ? `COALESCE("updatedAt", "createdAt") as "updatedAt"` : `"${c}"`).join(", ");
|
|
265
|
+
await this.client.command({
|
|
266
|
+
query: `INSERT INTO ${tableName} (${columnList})
|
|
267
|
+
SELECT ${selectExpressions}
|
|
268
|
+
FROM ${backupTableName}
|
|
269
|
+
ORDER BY traceId, spanId,
|
|
270
|
+
(endedAt IS NOT NULL AND endedAt != '') DESC,
|
|
271
|
+
COALESCE(updatedAt, createdAt) DESC,
|
|
272
|
+
createdAt DESC
|
|
273
|
+
LIMIT 1 BY traceId, spanId`
|
|
274
|
+
});
|
|
275
|
+
await this.client.command({
|
|
276
|
+
query: `DROP TABLE ${backupTableName}`
|
|
277
|
+
});
|
|
278
|
+
this.logger?.info?.(`Successfully migrated spans table to new sorting key`);
|
|
279
|
+
return true;
|
|
280
|
+
} catch (error) {
|
|
281
|
+
this.logger?.error?.(`Migration failed: ${error.message}`);
|
|
282
|
+
try {
|
|
283
|
+
const originalExists = await this.tableExists(tableName);
|
|
284
|
+
const backupExists = await this.tableExists(backupTableName);
|
|
285
|
+
if (!originalExists && backupExists) {
|
|
286
|
+
this.logger?.info?.(`Restoring spans table from backup`);
|
|
287
|
+
await this.client.command({
|
|
288
|
+
query: `RENAME TABLE ${backupTableName} TO ${tableName}`
|
|
289
|
+
});
|
|
290
|
+
} else if (originalExists && backupExists) {
|
|
291
|
+
await this.client.command({
|
|
292
|
+
query: `DROP TABLE IF EXISTS ${backupTableName}`
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
} catch (restoreError) {
|
|
296
|
+
this.logger?.error?.(`Failed to restore from backup: ${restoreError}`);
|
|
297
|
+
}
|
|
298
|
+
throw new MastraError(
|
|
299
|
+
{
|
|
300
|
+
id: createStorageErrorId("CLICKHOUSE", "MIGRATE_SPANS_SORTING_KEY", "FAILED"),
|
|
301
|
+
domain: ErrorDomain.STORAGE,
|
|
302
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
303
|
+
details: { tableName, currentSortingKey }
|
|
304
|
+
},
|
|
305
|
+
error
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
100
309
|
getSqlType(type) {
|
|
101
310
|
switch (type) {
|
|
102
311
|
case "text":
|
|
@@ -123,7 +332,10 @@ var ClickhouseDB = class extends MastraBase {
|
|
|
123
332
|
try {
|
|
124
333
|
const columns = Object.entries(schema).map(([name, def]) => {
|
|
125
334
|
let sqlType = this.getSqlType(def.type);
|
|
126
|
-
|
|
335
|
+
let isNullable = def.nullable === true;
|
|
336
|
+
if (tableName === TABLE_SPANS && name === "updatedAt") {
|
|
337
|
+
isNullable = false;
|
|
338
|
+
}
|
|
127
339
|
if (isNullable) {
|
|
128
340
|
sqlType = `Nullable(${sqlType})`;
|
|
129
341
|
}
|
|
@@ -153,8 +365,8 @@ var ClickhouseDB = class extends MastraBase {
|
|
|
153
365
|
${columns}
|
|
154
366
|
)
|
|
155
367
|
ENGINE = ${TABLE_ENGINES[tableName] ?? "MergeTree()"}
|
|
156
|
-
PRIMARY KEY (
|
|
157
|
-
ORDER BY (
|
|
368
|
+
PRIMARY KEY (traceId, spanId)
|
|
369
|
+
ORDER BY (traceId, spanId)
|
|
158
370
|
${rowTtl ? `TTL toDateTime(${rowTtl.ttlKey ?? "createdAt"}) + INTERVAL ${rowTtl.interval} ${rowTtl.unit}` : ""}
|
|
159
371
|
SETTINGS index_granularity = 8192
|
|
160
372
|
`;
|
|
@@ -300,7 +512,9 @@ var ClickhouseDB = class extends MastraBase {
|
|
|
300
512
|
...Object.fromEntries(
|
|
301
513
|
Object.entries(record).map(([key, value]) => [
|
|
302
514
|
key,
|
|
303
|
-
|
|
515
|
+
// Only convert to Date if it's a timestamp column AND value is not null/undefined
|
|
516
|
+
// new Date(null) returns epoch date, not null, so we must check first
|
|
517
|
+
TABLE_SCHEMAS[tableName]?.[key]?.type === "timestamp" && value != null ? new Date(value).toISOString() : value
|
|
304
518
|
])
|
|
305
519
|
)
|
|
306
520
|
}));
|
|
@@ -1642,11 +1856,101 @@ var ObservabilityStorageClickhouse = class extends ObservabilityStorage {
|
|
|
1642
1856
|
this.#db = new ClickhouseDB({ client, ttl });
|
|
1643
1857
|
}
|
|
1644
1858
|
async init() {
|
|
1859
|
+
const migrationStatus = await this.#db.checkSpansMigrationStatus(TABLE_SPANS);
|
|
1860
|
+
if (migrationStatus.needsMigration) {
|
|
1861
|
+
const duplicateInfo = await this.#db.checkForDuplicateSpans(TABLE_SPANS);
|
|
1862
|
+
const duplicateMessage = duplicateInfo.hasDuplicates ? `
|
|
1863
|
+
Found ${duplicateInfo.duplicateCount} duplicate (traceId, spanId) combinations that will be removed.
|
|
1864
|
+
` : "";
|
|
1865
|
+
const errorMessage = `
|
|
1866
|
+
===========================================================================
|
|
1867
|
+
MIGRATION REQUIRED: ClickHouse spans table needs sorting key update
|
|
1868
|
+
===========================================================================
|
|
1869
|
+
|
|
1870
|
+
The spans table structure has changed. ClickHouse requires a table recreation
|
|
1871
|
+
to update the sorting key from (traceId) to (traceId, spanId).
|
|
1872
|
+
` + duplicateMessage + `
|
|
1873
|
+
To fix this, run the manual migration command:
|
|
1874
|
+
|
|
1875
|
+
npx mastra migrate
|
|
1876
|
+
|
|
1877
|
+
This command will:
|
|
1878
|
+
1. Create a new table with the correct sorting key
|
|
1879
|
+
2. Copy data from the old table (deduplicating if needed)
|
|
1880
|
+
3. Replace the old table with the new one
|
|
1881
|
+
|
|
1882
|
+
WARNING: This migration involves table recreation and may take significant
|
|
1883
|
+
time for large tables. Please ensure you have a backup before proceeding.
|
|
1884
|
+
===========================================================================
|
|
1885
|
+
`;
|
|
1886
|
+
throw new MastraError({
|
|
1887
|
+
id: createStorageErrorId("CLICKHOUSE", "MIGRATION_REQUIRED", "SORTING_KEY_CHANGE"),
|
|
1888
|
+
domain: ErrorDomain.STORAGE,
|
|
1889
|
+
category: ErrorCategory.USER,
|
|
1890
|
+
text: errorMessage
|
|
1891
|
+
});
|
|
1892
|
+
}
|
|
1645
1893
|
await this.#db.createTable({ tableName: TABLE_SPANS, schema: SPAN_SCHEMA });
|
|
1646
1894
|
}
|
|
1647
1895
|
async dangerouslyClearAll() {
|
|
1648
1896
|
await this.#db.clearTable({ tableName: TABLE_SPANS });
|
|
1649
1897
|
}
|
|
1898
|
+
/**
|
|
1899
|
+
* Manually run the spans migration to deduplicate and update the sorting key.
|
|
1900
|
+
* This is intended to be called from the CLI when duplicates are detected.
|
|
1901
|
+
*
|
|
1902
|
+
* @returns Migration result with status and details
|
|
1903
|
+
*/
|
|
1904
|
+
async migrateSpans() {
|
|
1905
|
+
const migrationStatus = await this.#db.checkSpansMigrationStatus(TABLE_SPANS);
|
|
1906
|
+
if (!migrationStatus.needsMigration) {
|
|
1907
|
+
return {
|
|
1908
|
+
success: true,
|
|
1909
|
+
alreadyMigrated: true,
|
|
1910
|
+
duplicatesRemoved: 0,
|
|
1911
|
+
message: `Migration already complete. Spans table has correct sorting key.`
|
|
1912
|
+
};
|
|
1913
|
+
}
|
|
1914
|
+
const duplicateInfo = await this.#db.checkForDuplicateSpans(TABLE_SPANS);
|
|
1915
|
+
if (duplicateInfo.hasDuplicates) {
|
|
1916
|
+
this.logger?.info?.(
|
|
1917
|
+
`Found ${duplicateInfo.duplicateCount} duplicate (traceId, spanId) combinations. Starting migration with deduplication...`
|
|
1918
|
+
);
|
|
1919
|
+
} else {
|
|
1920
|
+
this.logger?.info?.(`No duplicate spans found. Starting sorting key migration...`);
|
|
1921
|
+
}
|
|
1922
|
+
await this.#db.migrateSpansTableSortingKey({ tableName: TABLE_SPANS, schema: SPAN_SCHEMA });
|
|
1923
|
+
return {
|
|
1924
|
+
success: true,
|
|
1925
|
+
alreadyMigrated: false,
|
|
1926
|
+
duplicatesRemoved: duplicateInfo.duplicateCount,
|
|
1927
|
+
message: duplicateInfo.hasDuplicates ? `Migration complete. Removed duplicates and updated sorting key for ${TABLE_SPANS}.` : `Migration complete. Updated sorting key for ${TABLE_SPANS}.`
|
|
1928
|
+
};
|
|
1929
|
+
}
|
|
1930
|
+
/**
|
|
1931
|
+
* Check migration status for the spans table.
|
|
1932
|
+
* Returns information about whether migration is needed.
|
|
1933
|
+
*/
|
|
1934
|
+
async checkSpansMigrationStatus() {
|
|
1935
|
+
const migrationStatus = await this.#db.checkSpansMigrationStatus(TABLE_SPANS);
|
|
1936
|
+
if (!migrationStatus.needsMigration) {
|
|
1937
|
+
return {
|
|
1938
|
+
needsMigration: false,
|
|
1939
|
+
hasDuplicates: false,
|
|
1940
|
+
duplicateCount: 0,
|
|
1941
|
+
constraintExists: true,
|
|
1942
|
+
tableName: TABLE_SPANS
|
|
1943
|
+
};
|
|
1944
|
+
}
|
|
1945
|
+
const duplicateInfo = await this.#db.checkForDuplicateSpans(TABLE_SPANS);
|
|
1946
|
+
return {
|
|
1947
|
+
needsMigration: true,
|
|
1948
|
+
hasDuplicates: duplicateInfo.hasDuplicates,
|
|
1949
|
+
duplicateCount: duplicateInfo.duplicateCount,
|
|
1950
|
+
constraintExists: false,
|
|
1951
|
+
tableName: TABLE_SPANS
|
|
1952
|
+
};
|
|
1953
|
+
}
|
|
1650
1954
|
get tracingStrategy() {
|
|
1651
1955
|
return {
|
|
1652
1956
|
preferred: "insert-only",
|
|
@@ -1980,10 +2284,10 @@ var ObservabilityStorageClickhouse = class extends ObservabilityStorage {
|
|
|
1980
2284
|
conditions.push(`(error IS NOT NULL AND error != '')`);
|
|
1981
2285
|
break;
|
|
1982
2286
|
case TraceStatus.RUNNING:
|
|
1983
|
-
conditions.push(`
|
|
2287
|
+
conditions.push(`endedAt IS NULL AND (error IS NULL OR error = '')`);
|
|
1984
2288
|
break;
|
|
1985
2289
|
case TraceStatus.SUCCESS:
|
|
1986
|
-
conditions.push(`
|
|
2290
|
+
conditions.push(`endedAt IS NOT NULL AND (error IS NULL OR error = '')`);
|
|
1987
2291
|
break;
|
|
1988
2292
|
}
|
|
1989
2293
|
}
|
|
@@ -2012,7 +2316,7 @@ var ObservabilityStorageClickhouse = class extends ObservabilityStorage {
|
|
|
2012
2316
|
if (sortField === "endedAt") {
|
|
2013
2317
|
const nullSortValue = sortDirection === "DESC" ? 0 : 1;
|
|
2014
2318
|
const nonNullSortValue = sortDirection === "DESC" ? 1 : 0;
|
|
2015
|
-
orderClause = `ORDER BY CASE WHEN ${sortField} IS NULL
|
|
2319
|
+
orderClause = `ORDER BY CASE WHEN ${sortField} IS NULL THEN ${nullSortValue} ELSE ${nonNullSortValue} END, ${sortField} ${sortDirection}`;
|
|
2016
2320
|
} else {
|
|
2017
2321
|
orderClause = `ORDER BY ${sortField} ${sortDirection}`;
|
|
2018
2322
|
}
|
|
@@ -2918,7 +3222,7 @@ var WorkflowsStorageClickhouse = class extends WorkflowsStorage {
|
|
|
2918
3222
|
var isClientConfig = (config) => {
|
|
2919
3223
|
return "client" in config;
|
|
2920
3224
|
};
|
|
2921
|
-
var ClickhouseStore = class extends
|
|
3225
|
+
var ClickhouseStore = class extends MastraCompositeStore {
|
|
2922
3226
|
db;
|
|
2923
3227
|
ttl = {};
|
|
2924
3228
|
stores;
|