@mastra/clickhouse 1.27.0 → 1.28.0-alpha.1
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/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/index.cjs +50 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +50 -19
- package/dist/index.js.map +1 -1
- package/dist/storage/db/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/index.d.ts +1 -1
- package/dist/storage/domains/observability/v-next/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/preview-compat.d.ts +1 -0
- package/dist/storage/domains/observability/v-next/preview-compat.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/span-query.d.ts +1 -0
- package/dist/storage/domains/observability/v-next/span-query.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/thread-query.d.ts +1 -0
- package/dist/storage/domains/observability/v-next/thread-query.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/trace-query.d.ts +3 -1
- package/dist/storage/domains/observability/v-next/trace-query.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -759,6 +759,7 @@ var ClickhouseDB = class extends MastraBase {
|
|
|
759
759
|
const rawUpdatedAt = filteredRecord.updatedAt || /* @__PURE__ */ new Date();
|
|
760
760
|
if ("createdAt" in filteredRecord || (await this.getTableColumns(tableName)).has("createdAt")) filteredRecord.createdAt = rawCreatedAt instanceof Date ? rawCreatedAt.toISOString() : rawCreatedAt;
|
|
761
761
|
if ("updatedAt" in filteredRecord || (await this.getTableColumns(tableName)).has("updatedAt")) filteredRecord.updatedAt = rawUpdatedAt instanceof Date ? rawUpdatedAt.toISOString() : rawUpdatedAt;
|
|
762
|
+
for (const [key, value] of Object.entries(filteredRecord)) if (TABLE_SCHEMAS[tableName]?.[key]?.type === "timestamp" && typeof value === "number") filteredRecord[key] = new Date(value).toISOString();
|
|
762
763
|
await this.client.insert({
|
|
763
764
|
table: tableName,
|
|
764
765
|
values: [filteredRecord],
|
|
@@ -2271,12 +2272,12 @@ var ObservabilityStorageClickhouse = class extends ObservabilityStorage {
|
|
|
2271
2272
|
}
|
|
2272
2273
|
});
|
|
2273
2274
|
const data = { ...updates };
|
|
2274
|
-
if (data.endedAt instanceof Date) data.endedAt = data.endedAt.
|
|
2275
|
-
if (data.startedAt instanceof Date) data.startedAt = data.startedAt.
|
|
2275
|
+
if (data.endedAt instanceof Date) data.endedAt = data.endedAt.toISOString();
|
|
2276
|
+
if (data.startedAt instanceof Date) data.startedAt = data.startedAt.toISOString();
|
|
2276
2277
|
const updated = {
|
|
2277
2278
|
...existing,
|
|
2278
2279
|
...data,
|
|
2279
|
-
updatedAt: Date.
|
|
2280
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2280
2281
|
};
|
|
2281
2282
|
await this.client.insert({
|
|
2282
2283
|
table: TABLE_SPANS,
|
|
@@ -2514,7 +2515,7 @@ var ObservabilityStorageClickhouse = class extends ObservabilityStorage {
|
|
|
2514
2515
|
}
|
|
2515
2516
|
async batchUpdateSpans(args) {
|
|
2516
2517
|
try {
|
|
2517
|
-
const now = Date.
|
|
2518
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2518
2519
|
if (args.records.length === 0) return;
|
|
2519
2520
|
const keyParams = {};
|
|
2520
2521
|
const keyTuples = args.records.map((record, i) => {
|
|
@@ -2547,8 +2548,8 @@ var ObservabilityStorageClickhouse = class extends ObservabilityStorage {
|
|
|
2547
2548
|
const existing = current.get(key);
|
|
2548
2549
|
if (!existing) continue;
|
|
2549
2550
|
const updates = { ...record.updates };
|
|
2550
|
-
if (updates.startedAt instanceof Date) updates.startedAt = updates.startedAt.
|
|
2551
|
-
if (updates.endedAt instanceof Date) updates.endedAt = updates.endedAt.
|
|
2551
|
+
if (updates.startedAt instanceof Date) updates.startedAt = updates.startedAt.toISOString();
|
|
2552
|
+
if (updates.endedAt instanceof Date) updates.endedAt = updates.endedAt.toISOString();
|
|
2552
2553
|
current.set(key, {
|
|
2553
2554
|
...existing,
|
|
2554
2555
|
...updates,
|
|
@@ -6906,7 +6907,17 @@ const TRACE_STATUS_SQL = `if(isNotNull(r.error), 'error', 'success')`;
|
|
|
6906
6907
|
const timeToFirstChunkSupported = "timeToFirstChunkMs" in (coreStorage.TRACE_QUERY_FIELD_REGISTRY?.trace ?? {});
|
|
6907
6908
|
/** Core before the sort rejects `orderBy` on the field, so the sort is only declared when core plans it. */
|
|
6908
6909
|
const timeToFirstChunkSortSupported = coreStorage.TRACE_QUERY_ORDER_FIELDS?.includes("timeToFirstChunkMs") === true;
|
|
6909
|
-
|
|
6910
|
+
/** Same rule for time to first token: returned and sorted only when core knows the field. */
|
|
6911
|
+
const timeToFirstTokenSupported = "timeToFirstTokenMs" in (coreStorage.TRACE_QUERY_FIELD_REGISTRY?.trace ?? {});
|
|
6912
|
+
const timeToFirstTokenSortSupported = coreStorage.TRACE_QUERY_ORDER_FIELDS?.includes("timeToFirstTokenMs") === true;
|
|
6913
|
+
const timingFeatures = [
|
|
6914
|
+
...timeToFirstChunkSupported ? ["trace-query-time-to-first-chunk"] : [],
|
|
6915
|
+
...timeToFirstChunkSupported && timeToFirstChunkSortSupported ? ["trace-query-time-to-first-chunk-sort"] : [],
|
|
6916
|
+
...timeToFirstTokenSupported ? ["trace-query-time-to-first-token"] : [],
|
|
6917
|
+
...timeToFirstTokenSupported && timeToFirstTokenSortSupported ? ["trace-query-time-to-first-token-sort"] : []
|
|
6918
|
+
];
|
|
6919
|
+
/** Order fields holding numbers that can be empty; the rest are timestamps. */
|
|
6920
|
+
const NUMERIC_ORDER_FIELDS = /* @__PURE__ */ new Set(["timeToFirstChunkMs", "timeToFirstTokenMs"]);
|
|
6910
6921
|
function durationMsSql(startedAt, endedAt) {
|
|
6911
6922
|
return `dateDiff('millisecond', ${startedAt}, ${endedAt})`;
|
|
6912
6923
|
}
|
|
@@ -6959,6 +6970,10 @@ const TRACE_FIELDS = {
|
|
|
6959
6970
|
sql: "r.timeToFirstChunkMs",
|
|
6960
6971
|
parameterType: "Float64"
|
|
6961
6972
|
},
|
|
6973
|
+
timeToFirstTokenMs: {
|
|
6974
|
+
sql: "r.timeToFirstTokenMs",
|
|
6975
|
+
parameterType: "Float64"
|
|
6976
|
+
},
|
|
6962
6977
|
entityId: {
|
|
6963
6978
|
sql: "r.entityId",
|
|
6964
6979
|
parameterType: "String"
|
|
@@ -7025,6 +7040,10 @@ const SPAN_FIELDS = {
|
|
|
7025
7040
|
sql: "s.timeToFirstChunkMs",
|
|
7026
7041
|
parameterType: "Float64"
|
|
7027
7042
|
},
|
|
7043
|
+
timeToFirstTokenMs: {
|
|
7044
|
+
sql: "s.timeToFirstTokenMs",
|
|
7045
|
+
parameterType: "Float64"
|
|
7046
|
+
},
|
|
7028
7047
|
status: {
|
|
7029
7048
|
sql: "s.status",
|
|
7030
7049
|
parameterType: "String"
|
|
@@ -7237,6 +7256,7 @@ const TRACE_SELECT = `
|
|
|
7237
7256
|
r.entityType AS entityType,
|
|
7238
7257
|
r.environment AS environment,
|
|
7239
7258
|
r.timeToFirstChunkMs AS timeToFirstChunkMs,
|
|
7259
|
+
r.timeToFirstTokenMs AS timeToFirstTokenMs,
|
|
7240
7260
|
${TRACE_STATUS_SQL} AS status`;
|
|
7241
7261
|
var ParameterBuilder = class {
|
|
7242
7262
|
params = {};
|
|
@@ -7253,7 +7273,7 @@ function fieldDefinition(registry, field) {
|
|
|
7253
7273
|
return definition;
|
|
7254
7274
|
}
|
|
7255
7275
|
function resolveOrderField(field) {
|
|
7256
|
-
if (field === "startedAt" || field === "endedAt" || field
|
|
7276
|
+
if (field === "startedAt" || field === "endedAt" || NUMERIC_ORDER_FIELDS.has(field)) return field;
|
|
7257
7277
|
throw new Error(`Unsupported trusted trace-query field: ${field}`);
|
|
7258
7278
|
}
|
|
7259
7279
|
function isMetadataField(field) {
|
|
@@ -7443,6 +7463,7 @@ function compileClickHouseTraceScope(selection, relationCollections, parameters,
|
|
|
7443
7463
|
endedAt,
|
|
7444
7464
|
${durationMsSql("startedAt", "endedAt")} AS durationMs,
|
|
7445
7465
|
timeToFirstChunkMs,
|
|
7466
|
+
timeToFirstTokenMs,
|
|
7446
7467
|
if(isNotNull(error), 'error', 'success') AS status,
|
|
7447
7468
|
error,
|
|
7448
7469
|
entityType,
|
|
@@ -7558,7 +7579,7 @@ function traceOrder(plan) {
|
|
|
7558
7579
|
function keysetCursorCondition(plan, order, parameters) {
|
|
7559
7580
|
if (plan.paginationMode !== "keyset" || !plan.cursor) return "";
|
|
7560
7581
|
if (plan.cursor.sortValue === null) return `(isNull(sortValue) AND traceId > ${parameters.add(plan.cursor.traceId, "String")})`;
|
|
7561
|
-
const numeric = order.field
|
|
7582
|
+
const numeric = NUMERIC_ORDER_FIELDS.has(order.field);
|
|
7562
7583
|
const comparison = order.direction === "ASC" ? ">" : "<";
|
|
7563
7584
|
const sortValue = parameters.add(plan.cursor.sortValue, numeric ? "Float64" : "DateTime64(3, 'UTC')");
|
|
7564
7585
|
return `(sortValue ${comparison} ${sortValue} OR (sortValue = ${sortValue} AND traceId > ${parameters.add(plan.cursor.traceId, "String")})${numeric ? " OR isNull(sortValue)" : ""})`;
|
|
@@ -7572,7 +7593,7 @@ function compileClickHouseTraceQueryKeys(plan) {
|
|
|
7572
7593
|
if (plan.paginationMode === "delta") throw new Error("Delta trace queries have no keys statement");
|
|
7573
7594
|
const parameters = new ParameterBuilder();
|
|
7574
7595
|
const order = traceOrder(plan);
|
|
7575
|
-
const ctes = compileClickHouseTraceCandidates(plan, "r.traceId AS traceId, r.dedupeKey AS dedupeKey, r.startedAt AS startedAt, r.endedAt AS endedAt, r.timeToFirstChunkMs AS timeToFirstChunkMs", parameters, QUERY_TRACES_SCOPE);
|
|
7596
|
+
const ctes = compileClickHouseTraceCandidates(plan, "r.traceId AS traceId, r.dedupeKey AS dedupeKey, r.startedAt AS startedAt, r.endedAt AS endedAt, r.timeToFirstChunkMs AS timeToFirstChunkMs, r.timeToFirstTokenMs AS timeToFirstTokenMs", parameters, QUERY_TRACES_SCOPE);
|
|
7576
7597
|
const cursor = keysetCursorCondition(plan, order, parameters);
|
|
7577
7598
|
const limit = plan.paginationMode === "page" ? `LIMIT ${parameters.add(plan.perPage, "UInt64")} OFFSET ${parameters.add(plan.page * plan.perPage, "UInt64")}` : `LIMIT ${parameters.add(plan.limit + 1, "UInt64")}`;
|
|
7578
7599
|
return {
|
|
@@ -7973,7 +7994,8 @@ async function queryTraces(client, plan, timeoutMs, strategy = null) {
|
|
|
7973
7994
|
entityType: row.entityType == null ? null : String(row.entityType),
|
|
7974
7995
|
environment: row.environment == null ? null : String(row.environment),
|
|
7975
7996
|
status: row.status,
|
|
7976
|
-
...timeToFirstChunkSupported && { timeToFirstChunkMs: row.timeToFirstChunkMs == null ? null : Number(row.timeToFirstChunkMs) }
|
|
7997
|
+
...timeToFirstChunkSupported && { timeToFirstChunkMs: row.timeToFirstChunkMs == null ? null : Number(row.timeToFirstChunkMs) },
|
|
7998
|
+
...timeToFirstTokenSupported && { timeToFirstTokenMs: row.timeToFirstTokenMs == null ? null : Number(row.timeToFirstTokenMs) }
|
|
7977
7999
|
});
|
|
7978
8000
|
if (plan.paginationMode === "delta") {
|
|
7979
8001
|
const rows = await run(compileClickHouseTraceQuery(plan, deltaHead));
|
|
@@ -7996,7 +8018,7 @@ async function queryTraces(client, plan, timeoutMs, strategy = null) {
|
|
|
7996
8018
|
});
|
|
7997
8019
|
}
|
|
7998
8020
|
const keys = await run(compileClickHouseTraceQueryKeys(plan));
|
|
7999
|
-
const sortValue = (value) => plan.orderBy.field
|
|
8021
|
+
const sortValue = (value) => !NUMERIC_ORDER_FIELDS.has(plan.orderBy.field) ? asIsoTimestamp(value) : value == null ? null : Number(value);
|
|
8000
8022
|
let total = Number(keys[0]?.__total ?? 0);
|
|
8001
8023
|
if (plan.paginationMode === "page" && keys.length === 0 && plan.page > 0) {
|
|
8002
8024
|
const [stats] = await run(compileClickHouseTraceQueryTotal(plan));
|
|
@@ -8099,6 +8121,7 @@ const displayFields = [
|
|
|
8099
8121
|
"provider",
|
|
8100
8122
|
"durationMs",
|
|
8101
8123
|
"timeToFirstChunkMs",
|
|
8124
|
+
"timeToFirstTokenMs",
|
|
8102
8125
|
"status"
|
|
8103
8126
|
];
|
|
8104
8127
|
var Parameters = class {
|
|
@@ -8127,7 +8150,7 @@ function afterCursor$1(plan, parameters) {
|
|
|
8127
8150
|
const equal = [];
|
|
8128
8151
|
if (cursor.sortValue === null) equal.push(`isNull(${field})`);
|
|
8129
8152
|
else {
|
|
8130
|
-
const numeric = plan.orderBy.field === "timeToFirstChunkMs";
|
|
8153
|
+
const numeric = plan.orderBy.field === "timeToFirstChunkMs" || plan.orderBy.field === "timeToFirstTokenMs";
|
|
8131
8154
|
const value = parameters.add(cursor.sortValue, numeric ? "Float64" : "DateTime64(3, 'UTC')");
|
|
8132
8155
|
terms.push(`${field} ${plan.orderBy.direction === "asc" ? ">" : "<"} ${value}`);
|
|
8133
8156
|
if (numeric) terms.push(`isNull(${field})`);
|
|
@@ -8223,6 +8246,7 @@ const TRACE_BRANCHES_SPAN_FIELD_COLUMNS = {
|
|
|
8223
8246
|
"startedAt",
|
|
8224
8247
|
"endedAt",
|
|
8225
8248
|
"timeToFirstChunkMs",
|
|
8249
|
+
"timeToFirstTokenMs",
|
|
8226
8250
|
"threadId",
|
|
8227
8251
|
"runId",
|
|
8228
8252
|
"sessionId",
|
|
@@ -8383,12 +8407,13 @@ async function querySpans(client, plan, timeoutMs) {
|
|
|
8383
8407
|
const hydrated = page.flatMap((identity) => {
|
|
8384
8408
|
const row = byIdentity.get(coreStorage.spanQueryIdentityKey(identity));
|
|
8385
8409
|
if (!row) return [];
|
|
8386
|
-
const { inputPreview: _input, outputPreview: _output, errorPreview: _error, timeToFirstChunkMs, ...metadata } = row;
|
|
8410
|
+
const { inputPreview: _input, outputPreview: _output, errorPreview: _error, timeToFirstChunkMs, timeToFirstTokenMs, ...metadata } = row;
|
|
8387
8411
|
return [{
|
|
8388
8412
|
...metadata,
|
|
8389
8413
|
...identity,
|
|
8390
8414
|
durationMs: Number(row.durationMs),
|
|
8391
|
-
...timeToFirstChunkSupported && { timeToFirstChunkMs: timeToFirstChunkMs == null ? null : Number(timeToFirstChunkMs) }
|
|
8415
|
+
...timeToFirstChunkSupported && { timeToFirstChunkMs: timeToFirstChunkMs == null ? null : Number(timeToFirstChunkMs) },
|
|
8416
|
+
...timeToFirstTokenSupported && { timeToFirstTokenMs: timeToFirstTokenMs == null ? null : Number(timeToFirstTokenMs) }
|
|
8392
8417
|
}];
|
|
8393
8418
|
});
|
|
8394
8419
|
let metrics = [];
|
|
@@ -8511,7 +8536,11 @@ function compileClickHouseThreadRowsQuery(plan, strategy = "full") {
|
|
|
8511
8536
|
const after = afterCursor(plan, parameters);
|
|
8512
8537
|
const qualify = plan.timeRange.mode === "traces" ? "s.inWindow > 0" : `s.${plan.timeRange.field} >= ${from} AND s.${plan.timeRange.field} < ${to}`;
|
|
8513
8538
|
const filtered = predicateFields(plan.where);
|
|
8514
|
-
const firstContext = [
|
|
8539
|
+
const firstContext = [
|
|
8540
|
+
...contextFields,
|
|
8541
|
+
"timeToFirstChunkMs",
|
|
8542
|
+
"timeToFirstTokenMs"
|
|
8543
|
+
].filter((field) => field === "resourceId" || filtered.has(field));
|
|
8515
8544
|
const firstFields = [
|
|
8516
8545
|
"startedAt",
|
|
8517
8546
|
"traceId",
|
|
@@ -8535,7 +8564,7 @@ function compileClickHouseThreadRowsQuery(plan, strategy = "full") {
|
|
|
8535
8564
|
...firstContext,
|
|
8536
8565
|
...filtered.has("status") ? ["status"] : []
|
|
8537
8566
|
];
|
|
8538
|
-
const rootExpression = (field) => field === "timeToFirstChunkMs" ?
|
|
8567
|
+
const rootExpression = (field) => field === "timeToFirstChunkMs" || field === "timeToFirstTokenMs" ? `if(spanType = 'agent_run', ${field}, NULL)` : field === "status" ? "if(isNull(error), 'success', 'error')" : field;
|
|
8539
8568
|
const sortedRoots = `roots AS (
|
|
8540
8569
|
SELECT organizationId, traceId, ${rootDataFields.map((field) => `${rootExpression(field)} AS ${field}`).join(", ")}
|
|
8541
8570
|
FROM ${TABLE_TRACE_ROOTS} ${scope.length || strategy === "candidates" ? `WHERE ${[...scope, ...strategy === "candidates" ? [candidateTracesFilter] : []].join(" AND ")}` : ""}
|
|
@@ -8639,6 +8668,7 @@ async function queryThreadRows(client, plan, timeoutMs, strategy = "auto") {
|
|
|
8639
8668
|
for (const batch of batches([...roots.values()])) stored.push(...await run({
|
|
8640
8669
|
query: `SELECT organizationId, traceId, spanId, startedAt, endedAt,
|
|
8641
8670
|
${contextFields.join(", ")}, if(spanType = 'agent_run', timeToFirstChunkMs, NULL) AS timeToFirstChunkMs,
|
|
8671
|
+
if(spanType = 'agent_run', timeToFirstTokenMs, NULL) AS timeToFirstTokenMs,
|
|
8642
8672
|
if(isNull(error), 'success', 'error') AS status, ${PREVIEW_COLUMNS.join(", ")}
|
|
8643
8673
|
FROM ${TABLE_TRACE_ROOTS} WHERE ${rootWhere}
|
|
8644
8674
|
ORDER BY ${ROOT_VERSION_ORDER} LIMIT 1 BY organizationId, traceId, spanId`,
|
|
@@ -8672,6 +8702,7 @@ async function queryThreadRows(client, plan, timeoutMs, strategy = "auto") {
|
|
|
8672
8702
|
endedAt: iso(row.endedAt),
|
|
8673
8703
|
traceCount: Number(row.traceCount),
|
|
8674
8704
|
timeToFirstChunkMs: first.timeToFirstChunkMs,
|
|
8705
|
+
...timeToFirstTokenSupported && { timeToFirstTokenMs: first.timeToFirstTokenMs },
|
|
8675
8706
|
inputPreview: resolveStoredPreviews(first).inputPreview,
|
|
8676
8707
|
outputPreview: resolveStoredPreviews(last).outputPreview,
|
|
8677
8708
|
errorPreview: resolveStoredPreviews(last).errorPreview
|
|
@@ -10328,7 +10359,7 @@ var ObservabilityStorageClickhouseVNext = class extends ObservabilityStorage {
|
|
|
10328
10359
|
"trace-query-tenant-scope",
|
|
10329
10360
|
"feedback",
|
|
10330
10361
|
"trace-query-context-ids",
|
|
10331
|
-
...
|
|
10362
|
+
...timingFeatures,
|
|
10332
10363
|
"trace-query-root-span-fields",
|
|
10333
10364
|
"trace-query-root-entity-id",
|
|
10334
10365
|
"trace-aggregate",
|
|
@@ -10353,7 +10384,7 @@ var ObservabilityStorageClickhouseVNext = class extends ObservabilityStorage {
|
|
|
10353
10384
|
"trace-query-tenant-scope",
|
|
10354
10385
|
"feedback",
|
|
10355
10386
|
"trace-query-context-ids",
|
|
10356
|
-
...
|
|
10387
|
+
...timingFeatures,
|
|
10357
10388
|
"trace-query-root-span-fields",
|
|
10358
10389
|
"trace-query-root-entity-id",
|
|
10359
10390
|
"trace-aggregate",
|