@mastra/clickhouse 1.26.0-alpha.2 → 1.26.0-alpha.4
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/docs/references/integrations-databases-clickhouse.md +1 -1
- package/dist/index.cjs +401 -38
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +401 -38
- package/dist/index.js.map +1 -1
- package/dist/storage/domains/observability/v-next/index.d.ts +4 -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 +55 -0
- package/dist/storage/domains/observability/v-next/thread-query.d.ts.map +1 -0
- package/dist/storage/domains/observability/v-next/trace-aggregate.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/trace-query.d.ts +13 -2
- package/dist/storage/domains/observability/v-next/trace-query.d.ts.map +1 -1
- package/package.json +5 -6
package/dist/index.cjs
CHANGED
|
@@ -6864,6 +6864,14 @@ async function getScorePercentiles(client, args) {
|
|
|
6864
6864
|
//#endregion
|
|
6865
6865
|
//#region src/storage/domains/observability/v-next/trace-query.ts
|
|
6866
6866
|
const TRACE_STATUS_SQL = `if(isNotNull(r.error), 'error', 'success')`;
|
|
6867
|
+
/**
|
|
6868
|
+
* Core versions without the field reject it in strict query results, so it is only returned when core knows it.
|
|
6869
|
+
* Core before 1.68 has no field registry at all.
|
|
6870
|
+
*/
|
|
6871
|
+
const timeToFirstChunkSupported = "timeToFirstChunkMs" in (_mastra_core_storage.TRACE_QUERY_FIELD_REGISTRY?.trace ?? {});
|
|
6872
|
+
/** Core before the sort rejects `orderBy` on the field, so the sort is only declared when core plans it. */
|
|
6873
|
+
const timeToFirstChunkSortSupported = _mastra_core_storage.TRACE_QUERY_ORDER_FIELDS?.includes("timeToFirstChunkMs") === true;
|
|
6874
|
+
const timeToFirstChunkFeatures = timeToFirstChunkSupported ? timeToFirstChunkSortSupported ? ["trace-query-time-to-first-chunk", "trace-query-time-to-first-chunk-sort"] : ["trace-query-time-to-first-chunk"] : [];
|
|
6867
6875
|
function durationMsSql(startedAt, endedAt) {
|
|
6868
6876
|
return `dateDiff('millisecond', ${startedAt}, ${endedAt})`;
|
|
6869
6877
|
}
|
|
@@ -6912,6 +6920,10 @@ const TRACE_FIELDS = {
|
|
|
6912
6920
|
sql: durationMsSql("r.startedAt", "r.endedAt"),
|
|
6913
6921
|
parameterType: "Float64"
|
|
6914
6922
|
},
|
|
6923
|
+
timeToFirstChunkMs: {
|
|
6924
|
+
sql: "r.timeToFirstChunkMs",
|
|
6925
|
+
parameterType: "Float64"
|
|
6926
|
+
},
|
|
6915
6927
|
entityName: {
|
|
6916
6928
|
sql: "r.entityName",
|
|
6917
6929
|
parameterType: "String"
|
|
@@ -6920,6 +6932,14 @@ const TRACE_FIELDS = {
|
|
|
6920
6932
|
sql: "r.entityType",
|
|
6921
6933
|
parameterType: "String"
|
|
6922
6934
|
},
|
|
6935
|
+
name: {
|
|
6936
|
+
sql: "r.name",
|
|
6937
|
+
parameterType: "String"
|
|
6938
|
+
},
|
|
6939
|
+
spanType: {
|
|
6940
|
+
sql: "r.spanType",
|
|
6941
|
+
parameterType: "String"
|
|
6942
|
+
},
|
|
6923
6943
|
environment: {
|
|
6924
6944
|
sql: "r.environment",
|
|
6925
6945
|
parameterType: "String"
|
|
@@ -6962,6 +6982,10 @@ const SPAN_FIELDS = {
|
|
|
6962
6982
|
sql: "s.durationMs",
|
|
6963
6983
|
parameterType: "Float64"
|
|
6964
6984
|
},
|
|
6985
|
+
timeToFirstChunkMs: {
|
|
6986
|
+
sql: "s.timeToFirstChunkMs",
|
|
6987
|
+
parameterType: "Float64"
|
|
6988
|
+
},
|
|
6965
6989
|
status: {
|
|
6966
6990
|
sql: "s.status",
|
|
6967
6991
|
parameterType: "String"
|
|
@@ -7173,6 +7197,7 @@ const TRACE_SELECT = `
|
|
|
7173
7197
|
r.entityName AS entityName,
|
|
7174
7198
|
r.entityType AS entityType,
|
|
7175
7199
|
r.environment AS environment,
|
|
7200
|
+
r.timeToFirstChunkMs AS timeToFirstChunkMs,
|
|
7176
7201
|
${TRACE_STATUS_SQL} AS status`;
|
|
7177
7202
|
var ParameterBuilder = class {
|
|
7178
7203
|
params = {};
|
|
@@ -7189,7 +7214,7 @@ function fieldDefinition(registry, field) {
|
|
|
7189
7214
|
return definition;
|
|
7190
7215
|
}
|
|
7191
7216
|
function resolveOrderField(field) {
|
|
7192
|
-
if (field === "startedAt" || field === "endedAt") return field;
|
|
7217
|
+
if (field === "startedAt" || field === "endedAt" || field === "timeToFirstChunkMs") return field;
|
|
7193
7218
|
throw new Error(`Unsupported trusted trace-query field: ${field}`);
|
|
7194
7219
|
}
|
|
7195
7220
|
function isMetadataField(field) {
|
|
@@ -7348,6 +7373,7 @@ function compileClickHouseTraceScope(selection, relationCollections, parameters,
|
|
|
7348
7373
|
startedAt,
|
|
7349
7374
|
endedAt,
|
|
7350
7375
|
${durationMsSql("startedAt", "endedAt")} AS durationMs,
|
|
7376
|
+
timeToFirstChunkMs,
|
|
7351
7377
|
if(isNotNull(error), 'error', 'success') AS status,
|
|
7352
7378
|
error,
|
|
7353
7379
|
entityType,
|
|
@@ -7466,9 +7492,11 @@ function traceOrder(plan) {
|
|
|
7466
7492
|
*/
|
|
7467
7493
|
function keysetCursorCondition(plan, order, parameters) {
|
|
7468
7494
|
if (plan.paginationMode !== "keyset" || !plan.cursor) return "";
|
|
7495
|
+
if (plan.cursor.sortValue === null) return `(isNull(sortValue) AND traceId > ${parameters.add(plan.cursor.traceId, "String")})`;
|
|
7496
|
+
const numeric = order.field === "timeToFirstChunkMs";
|
|
7469
7497
|
const comparison = order.direction === "ASC" ? ">" : "<";
|
|
7470
|
-
const sortValue = parameters.add(plan.cursor.sortValue, "DateTime64(3, 'UTC')");
|
|
7471
|
-
return `(sortValue ${comparison} ${sortValue} OR (sortValue = ${sortValue} AND traceId > ${parameters.add(plan.cursor.traceId, "String")}))`;
|
|
7498
|
+
const sortValue = parameters.add(plan.cursor.sortValue, numeric ? "Float64" : "DateTime64(3, 'UTC')");
|
|
7499
|
+
return `(sortValue ${comparison} ${sortValue} OR (sortValue = ${sortValue} AND traceId > ${parameters.add(plan.cursor.traceId, "String")})${numeric ? " OR isNull(sortValue)" : ""})`;
|
|
7472
7500
|
}
|
|
7473
7501
|
/**
|
|
7474
7502
|
* First statement of keyset and page mode: the page's traces (after the keyset cursor, `limit + 1`
|
|
@@ -7480,21 +7508,22 @@ function compileClickHouseTraceQueryKeys(plan) {
|
|
|
7480
7508
|
if (plan.paginationMode === "delta") throw new Error("Delta trace queries have no keys statement");
|
|
7481
7509
|
const parameters = new ParameterBuilder();
|
|
7482
7510
|
const order = traceOrder(plan);
|
|
7483
|
-
const ctes = compileClickHouseTraceCandidates(plan, "r.traceId AS traceId, r.dedupeKey AS dedupeKey, r.startedAt AS startedAt, r.endedAt AS endedAt", parameters, QUERY_TRACES_SCOPE);
|
|
7511
|
+
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);
|
|
7484
7512
|
const cursor = keysetCursorCondition(plan, order, parameters);
|
|
7485
7513
|
const pick = order.direction === "DESC" ? "max" : "min";
|
|
7514
|
+
const rootOrder = order.field === "timeToFirstChunkMs" ? `ifNull(${order.field}, ${pick === "max" ? "-inf" : "inf"})` : order.field;
|
|
7486
7515
|
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")}`;
|
|
7487
7516
|
return {
|
|
7488
7517
|
query: `WITH ${ctes.join(",\n")},
|
|
7489
7518
|
per_trace AS (
|
|
7490
7519
|
SELECT traceId, ${pick}(${order.field}) AS sortValue,
|
|
7491
|
-
${pick === "max" ? "argMax" : "argMin"}((dedupeKey, startedAt), ${
|
|
7520
|
+
${pick === "max" ? "argMax" : "argMin"}((dedupeKey, startedAt), ${rootOrder}) AS root
|
|
7492
7521
|
FROM candidates
|
|
7493
7522
|
GROUP BY traceId${cursor ? `\n HAVING ${cursor}` : ""}
|
|
7494
7523
|
)
|
|
7495
7524
|
SELECT traceId, root.1 AS dedupeKey, sortValue, root.2 AS rootStartedAt, count() OVER () AS __total
|
|
7496
7525
|
FROM per_trace
|
|
7497
|
-
ORDER BY sortValue ${order.direction}, traceId ASC
|
|
7526
|
+
ORDER BY sortValue ${order.direction} NULLS LAST, traceId ASC
|
|
7498
7527
|
${limit}`,
|
|
7499
7528
|
query_params: parameters.params,
|
|
7500
7529
|
sharedSnapshot: true
|
|
@@ -7512,16 +7541,16 @@ function compileClickHouseTraceRowsByKey(plan, keys) {
|
|
|
7512
7541
|
const ts = (value) => parameters.add(value, "DateTime64(3, 'UTC')");
|
|
7513
7542
|
const byEnd = order.field === "endedAt";
|
|
7514
7543
|
const tuples = keys.map((key) => {
|
|
7515
|
-
const started = ts(
|
|
7544
|
+
const started = ts(key.rootStartedAt);
|
|
7516
7545
|
const id = `${parameters.add(key.traceId, "String")}, ${parameters.add(key.dedupeKey, "String")}`;
|
|
7517
|
-
return byEnd ? `(${started}, ${id}, ${ts(key.sortValue)})` : `(${started}, ${id})`;
|
|
7546
|
+
return byEnd ? `(${started}, ${id}, ${ts(String(key.sortValue))})` : `(${started}, ${id})`;
|
|
7518
7547
|
});
|
|
7519
7548
|
const predicate = (plan.where ? (plan.where.type === "boolean" && plan.where.operator === "and" ? plan.where.args : [plan.where]).filter((conjunct) => collectRelationCollections(conjunct).size === 0) : []).map((conjunct) => `\n AND (${compilePredicate(conjunct, parameters)})`).join("");
|
|
7520
7549
|
return {
|
|
7521
7550
|
query: `SELECT ${TRACE_SELECT}
|
|
7522
7551
|
FROM ${TABLE_TRACE_ROOTS} r
|
|
7523
7552
|
WHERE (r.startedAt, r.traceId, r.dedupeKey${byEnd ? ", r.endedAt" : ""}) IN (${tuples.join(", ")})${compileTenantScope(plan.scope, parameters)}${predicate}
|
|
7524
|
-
ORDER BY ${order.field} ${order.direction}, traceId ASC
|
|
7553
|
+
ORDER BY ${order.field} ${order.direction} NULLS LAST, traceId ASC
|
|
7525
7554
|
LIMIT 1 BY traceId`,
|
|
7526
7555
|
query_params: parameters.params
|
|
7527
7556
|
};
|
|
@@ -7760,6 +7789,12 @@ async function runWithClickHouseTraceQueryTimeout(client, limits, compiled, quer
|
|
|
7760
7789
|
...CH_SETTINGS,
|
|
7761
7790
|
max_execution_time: resolvedTimeoutMs / 1e3,
|
|
7762
7791
|
...limits.memoryLimitBytes === void 0 ? {} : { max_memory_usage: String(limits.memoryLimitBytes) },
|
|
7792
|
+
...limits.maxThreads === void 0 ? {} : { max_threads: limits.maxThreads },
|
|
7793
|
+
...limits.maxBlockSize === void 0 ? {} : { max_block_size: String(limits.maxBlockSize) },
|
|
7794
|
+
...limits.spillThresholdBytes === void 0 ? {} : {
|
|
7795
|
+
max_bytes_before_external_sort: String(limits.spillThresholdBytes),
|
|
7796
|
+
max_bytes_before_external_group_by: String(limits.spillThresholdBytes)
|
|
7797
|
+
},
|
|
7763
7798
|
...compiled.sharedSnapshot ? { enable_shared_storage_snapshot_in_query: 1 } : {}
|
|
7764
7799
|
}
|
|
7765
7800
|
})).json();
|
|
@@ -7882,7 +7917,8 @@ async function queryTraces(client, plan, timeoutMs, strategy = null) {
|
|
|
7882
7917
|
entityName: row.entityName == null ? null : String(row.entityName),
|
|
7883
7918
|
entityType: row.entityType == null ? null : String(row.entityType),
|
|
7884
7919
|
environment: row.environment == null ? null : String(row.environment),
|
|
7885
|
-
status: row.status
|
|
7920
|
+
status: row.status,
|
|
7921
|
+
...timeToFirstChunkSupported && { timeToFirstChunkMs: row.timeToFirstChunkMs == null ? null : Number(row.timeToFirstChunkMs) }
|
|
7886
7922
|
});
|
|
7887
7923
|
if (plan.paginationMode === "delta") {
|
|
7888
7924
|
const rows = await run(compileClickHouseTraceQuery(plan, deltaHead));
|
|
@@ -7905,6 +7941,7 @@ async function queryTraces(client, plan, timeoutMs, strategy = null) {
|
|
|
7905
7941
|
});
|
|
7906
7942
|
}
|
|
7907
7943
|
const keys = await run(compileClickHouseTraceQueryKeys(plan));
|
|
7944
|
+
const sortValue = (value) => plan.orderBy.field !== "timeToFirstChunkMs" ? asIsoTimestamp(value) : value == null ? null : Number(value);
|
|
7908
7945
|
let total = Number(keys[0]?.__total ?? 0);
|
|
7909
7946
|
if (plan.paginationMode === "page" && keys.length === 0 && plan.page > 0) {
|
|
7910
7947
|
const [stats] = await run(compileClickHouseTraceQueryTotal(plan));
|
|
@@ -7914,7 +7951,7 @@ async function queryTraces(client, plan, timeoutMs, strategy = null) {
|
|
|
7914
7951
|
const rows = visible.length > 0 ? await run(compileClickHouseTraceRowsByKey(plan, visible.map((key) => ({
|
|
7915
7952
|
traceId: String(key.traceId),
|
|
7916
7953
|
dedupeKey: String(key.dedupeKey),
|
|
7917
|
-
sortValue:
|
|
7954
|
+
sortValue: sortValue(key.sortValue),
|
|
7918
7955
|
rootStartedAt: asIsoTimestamp(key.rootStartedAt)
|
|
7919
7956
|
})))) : [];
|
|
7920
7957
|
if (plan.paginationMode === "page") return _mastra_core_storage.traceQueryResponseSchema.parse({
|
|
@@ -7932,7 +7969,7 @@ async function queryTraces(client, plan, timeoutMs, strategy = null) {
|
|
|
7932
7969
|
traces: await toTraceRecords(rows),
|
|
7933
7970
|
page: { next: keys.length > plan.limit && lastKey ? _mastra_core_storage.encodeTraceQueryCursor(plan, {
|
|
7934
7971
|
result: "traces",
|
|
7935
|
-
sortValue:
|
|
7972
|
+
sortValue: sortValue(lastKey.sortValue),
|
|
7936
7973
|
traceId: String(lastKey.traceId)
|
|
7937
7974
|
}) : null }
|
|
7938
7975
|
});
|
|
@@ -7957,6 +7994,16 @@ function compileSpanQueryPredicate(predicate) {
|
|
|
7957
7994
|
params: parameters.params
|
|
7958
7995
|
};
|
|
7959
7996
|
}
|
|
7997
|
+
/** Thread filters see full-history representative values, never individual member traces. */
|
|
7998
|
+
function compileThreadQueryPredicate(predicate, parameters = new ParameterBuilder()) {
|
|
7999
|
+
return {
|
|
8000
|
+
sql: compileScalarPredicate(predicate, Object.fromEntries(Object.entries(_mastra_core_storage.THREAD_QUERY_FIELD_REGISTRY).map(([field, rule]) => [field, {
|
|
8001
|
+
sql: `s.${field}`,
|
|
8002
|
+
parameterType: rule.valueKind === "timestamp" ? "DateTime64(3, 'UTC')" : rule.valueKind === "number" ? "Float64" : "String"
|
|
8003
|
+
}])), parameters),
|
|
8004
|
+
params: parameters.params
|
|
8005
|
+
};
|
|
8006
|
+
}
|
|
7960
8007
|
//#endregion
|
|
7961
8008
|
//#region src/storage/domains/observability/v-next/span-query.ts
|
|
7962
8009
|
const identityFields = [
|
|
@@ -7997,14 +8044,15 @@ const displayFields = [
|
|
|
7997
8044
|
"model",
|
|
7998
8045
|
"provider",
|
|
7999
8046
|
"durationMs",
|
|
8047
|
+
"timeToFirstChunkMs",
|
|
8000
8048
|
"status"
|
|
8001
8049
|
];
|
|
8002
8050
|
var Parameters = class {
|
|
8003
8051
|
values = {};
|
|
8004
|
-
add(value,
|
|
8052
|
+
add(value, type = typeof value === "number" ? "UInt64" : "String") {
|
|
8005
8053
|
const name = `span_${Object.keys(this.values).length}`;
|
|
8006
|
-
this.values[name] =
|
|
8007
|
-
return `{${name}:${
|
|
8054
|
+
this.values[name] = type === "DateTime64(3, 'UTC')" ? new Date(value).toISOString().replace("T", " ").replace(/Z$/, "") : value;
|
|
8055
|
+
return `{${name}:${type}}`;
|
|
8008
8056
|
}
|
|
8009
8057
|
};
|
|
8010
8058
|
function identityValues(row) {
|
|
@@ -8017,13 +8065,20 @@ function identityValues(row) {
|
|
|
8017
8065
|
row.spanId
|
|
8018
8066
|
];
|
|
8019
8067
|
}
|
|
8020
|
-
function afterCursor(plan, parameters) {
|
|
8068
|
+
function afterCursor$1(plan, parameters) {
|
|
8021
8069
|
if (!plan.cursor) return "1";
|
|
8022
8070
|
const cursor = plan.cursor;
|
|
8023
|
-
const timestamp = parameters.add(cursor.sortValue, true);
|
|
8024
8071
|
const field = `s.${plan.orderBy.field}`;
|
|
8025
|
-
const terms = [
|
|
8026
|
-
const equal = [
|
|
8072
|
+
const terms = [];
|
|
8073
|
+
const equal = [];
|
|
8074
|
+
if (cursor.sortValue === null) equal.push(`isNull(${field})`);
|
|
8075
|
+
else {
|
|
8076
|
+
const numeric = plan.orderBy.field === "timeToFirstChunkMs";
|
|
8077
|
+
const value = parameters.add(cursor.sortValue, numeric ? "Float64" : "DateTime64(3, 'UTC')");
|
|
8078
|
+
terms.push(`${field} ${plan.orderBy.direction === "asc" ? ">" : "<"} ${value}`);
|
|
8079
|
+
if (numeric) terms.push(`isNull(${field})`);
|
|
8080
|
+
equal.push(`${field} = ${value}`);
|
|
8081
|
+
}
|
|
8027
8082
|
for (const key of identityFields) {
|
|
8028
8083
|
const value = cursor[key];
|
|
8029
8084
|
if (value === null) equal.push(`isNull(s.${key})`);
|
|
@@ -8035,10 +8090,10 @@ function afterCursor(plan, parameters) {
|
|
|
8035
8090
|
}
|
|
8036
8091
|
return `(${terms.join(" OR ")})`;
|
|
8037
8092
|
}
|
|
8038
|
-
function predicateFields(predicate, fields = /* @__PURE__ */ new Set()) {
|
|
8093
|
+
function predicateFields$1(predicate, fields = /* @__PURE__ */ new Set()) {
|
|
8039
8094
|
if (!predicate) return fields;
|
|
8040
|
-
if (predicate.type === "boolean") for (const arg of predicate.args) predicateFields(arg, fields);
|
|
8041
|
-
else if (predicate.type === "not") predicateFields(predicate.arg, fields);
|
|
8095
|
+
if (predicate.type === "boolean") for (const arg of predicate.args) predicateFields$1(arg, fields);
|
|
8096
|
+
else if (predicate.type === "not") predicateFields$1(predicate.arg, fields);
|
|
8042
8097
|
else fields.add(predicate.field);
|
|
8043
8098
|
return fields;
|
|
8044
8099
|
}
|
|
@@ -8051,8 +8106,8 @@ function fieldExpression(field) {
|
|
|
8051
8106
|
}
|
|
8052
8107
|
function compileClickHouseSpanQuery(plan) {
|
|
8053
8108
|
const parameters = new Parameters();
|
|
8054
|
-
const from = parameters.add(plan.timeRange.from,
|
|
8055
|
-
const to = parameters.add(plan.timeRange.to,
|
|
8109
|
+
const from = parameters.add(plan.timeRange.from, "DateTime64(3, 'UTC')");
|
|
8110
|
+
const to = parameters.add(plan.timeRange.to, "DateTime64(3, 'UTC')");
|
|
8056
8111
|
const scope = [];
|
|
8057
8112
|
if (plan.scope) {
|
|
8058
8113
|
scope.push(`organizationId = ${parameters.add(plan.scope.organizationId)}`);
|
|
@@ -8065,10 +8120,11 @@ function compileClickHouseSpanQuery(plan) {
|
|
|
8065
8120
|
const selectionFields = [.../* @__PURE__ */ new Set([
|
|
8066
8121
|
"startedAt",
|
|
8067
8122
|
"endedAt",
|
|
8068
|
-
|
|
8123
|
+
plan.orderBy.field,
|
|
8124
|
+
...predicateFields$1(plan.where)
|
|
8069
8125
|
])].filter((field) => !new Set(identityFields).has(field));
|
|
8070
8126
|
const expressions = selectionFields.map(fieldExpression);
|
|
8071
|
-
const after = afterCursor(plan, parameters);
|
|
8127
|
+
const after = afterCursor$1(plan, parameters);
|
|
8072
8128
|
return {
|
|
8073
8129
|
query: `WITH candidates AS (
|
|
8074
8130
|
SELECT ${identities}, ${selectionFields.map((field, index) => `${expressions[index]} AS ${field}`).join(", ")}
|
|
@@ -8078,13 +8134,14 @@ function compileClickHouseSpanQuery(plan) {
|
|
|
8078
8134
|
ORDER BY ${identities}, e.endedAt DESC
|
|
8079
8135
|
LIMIT 1 BY ${identities}
|
|
8080
8136
|
)
|
|
8081
|
-
SELECT ${[
|
|
8137
|
+
SELECT ${[.../* @__PURE__ */ new Set([
|
|
8082
8138
|
...identityFields,
|
|
8083
8139
|
"startedAt",
|
|
8084
|
-
"endedAt"
|
|
8085
|
-
|
|
8140
|
+
"endedAt",
|
|
8141
|
+
plan.orderBy.field
|
|
8142
|
+
])].map((field) => `s.${field}`).join(", ")}
|
|
8086
8143
|
FROM candidates s WHERE s.startedAt >= ${from} AND s.startedAt < ${to} AND (${predicate.sql}) AND ${after}
|
|
8087
|
-
ORDER BY s.${plan.orderBy.field} ${plan.orderBy.direction === "asc" ? "ASC" : "DESC"}, ${identityFields.map((field) => `s.${field} ASC NULLS LAST`).join(", ")}
|
|
8144
|
+
ORDER BY s.${plan.orderBy.field} ${plan.orderBy.direction === "asc" ? "ASC" : "DESC"} NULLS LAST, ${identityFields.map((field) => `s.${field} ASC NULLS LAST`).join(", ")}
|
|
8088
8145
|
LIMIT ${parameters.add(plan.limit + 1)}`,
|
|
8089
8146
|
query_params: {
|
|
8090
8147
|
...parameters.values,
|
|
@@ -8093,7 +8150,7 @@ function compileClickHouseSpanQuery(plan) {
|
|
|
8093
8150
|
};
|
|
8094
8151
|
}
|
|
8095
8152
|
const LEGACY_PAYLOAD_BATCH_ROWS = 32;
|
|
8096
|
-
function iso(value) {
|
|
8153
|
+
function iso$1(value) {
|
|
8097
8154
|
return new Date(value.includes("T") ? value : `${value.replace(" ", "T")}Z`).toISOString();
|
|
8098
8155
|
}
|
|
8099
8156
|
async function querySpans(client, plan, timeoutMs) {
|
|
@@ -8108,8 +8165,8 @@ async function querySpans(client, plan, timeoutMs) {
|
|
|
8108
8165
|
};
|
|
8109
8166
|
const selected = (await run(compileClickHouseSpanQuery(plan))).map((row) => ({
|
|
8110
8167
|
...row,
|
|
8111
|
-
startedAt: iso(row.startedAt),
|
|
8112
|
-
endedAt: iso(row.endedAt)
|
|
8168
|
+
startedAt: iso$1(row.startedAt),
|
|
8169
|
+
endedAt: iso$1(row.endedAt)
|
|
8113
8170
|
}));
|
|
8114
8171
|
const page = selected.slice(0, plan.limit);
|
|
8115
8172
|
if (!page.length) return {
|
|
@@ -8147,11 +8204,12 @@ async function querySpans(client, plan, timeoutMs) {
|
|
|
8147
8204
|
const hydrated = page.flatMap((identity) => {
|
|
8148
8205
|
const row = byIdentity.get(_mastra_core_storage.spanQueryIdentityKey(identity));
|
|
8149
8206
|
if (!row) return [];
|
|
8150
|
-
const { inputPreview: _input, outputPreview: _output, errorPreview: _error, ...metadata } = row;
|
|
8207
|
+
const { inputPreview: _input, outputPreview: _output, errorPreview: _error, timeToFirstChunkMs, ...metadata } = row;
|
|
8151
8208
|
return [{
|
|
8152
8209
|
...metadata,
|
|
8153
8210
|
...identity,
|
|
8154
|
-
durationMs: Number(row.durationMs)
|
|
8211
|
+
durationMs: Number(row.durationMs),
|
|
8212
|
+
...timeToFirstChunkSupported && { timeToFirstChunkMs: timeToFirstChunkMs == null ? null : Number(timeToFirstChunkMs) }
|
|
8155
8213
|
}];
|
|
8156
8214
|
});
|
|
8157
8215
|
let metrics = [];
|
|
@@ -8175,16 +8233,313 @@ async function querySpans(client, plan, timeoutMs) {
|
|
|
8175
8233
|
resourceId: last.resourceId,
|
|
8176
8234
|
traceId: last.traceId,
|
|
8177
8235
|
spanId: last.spanId,
|
|
8178
|
-
sortValue: last[plan.orderBy.field]
|
|
8236
|
+
sortValue: last[plan.orderBy.field] ?? null
|
|
8179
8237
|
}) : null;
|
|
8180
8238
|
return result;
|
|
8181
8239
|
}
|
|
8182
8240
|
//#endregion
|
|
8241
|
+
//#region src/storage/domains/observability/v-next/thread-query.ts
|
|
8242
|
+
const contextFields = [
|
|
8243
|
+
"resourceId",
|
|
8244
|
+
"userId",
|
|
8245
|
+
"entityType",
|
|
8246
|
+
"entityId",
|
|
8247
|
+
"entityName",
|
|
8248
|
+
"entityVersionId",
|
|
8249
|
+
"runId",
|
|
8250
|
+
"sessionId",
|
|
8251
|
+
"requestId",
|
|
8252
|
+
"serviceName",
|
|
8253
|
+
"experimentId",
|
|
8254
|
+
"environment",
|
|
8255
|
+
"executionSource"
|
|
8256
|
+
];
|
|
8257
|
+
function predicateFields(predicate, fields = /* @__PURE__ */ new Set()) {
|
|
8258
|
+
if (!predicate) return fields;
|
|
8259
|
+
if (predicate.type === "boolean") for (const arg of predicate.args) predicateFields(arg, fields);
|
|
8260
|
+
else if (predicate.type === "not") predicateFields(predicate.arg, fields);
|
|
8261
|
+
else fields.add(predicate.field);
|
|
8262
|
+
return fields;
|
|
8263
|
+
}
|
|
8264
|
+
function afterCursor(plan, parameters) {
|
|
8265
|
+
if (!plan.cursor) return "1";
|
|
8266
|
+
const { cursor } = plan;
|
|
8267
|
+
const stamp = parameters.add(cursor.sortValue, "DateTime64(3, 'UTC')");
|
|
8268
|
+
const field = `s.${plan.orderBy.field}`;
|
|
8269
|
+
const terms = [`${field} ${plan.orderBy.direction === "asc" ? ">" : "<"} ${stamp}`];
|
|
8270
|
+
const equal = [`${field} = ${stamp}`];
|
|
8271
|
+
for (const key of ["organizationId", "threadId"]) if (cursor[key] === null) equal.push(`isNull(s.${key})`);
|
|
8272
|
+
else {
|
|
8273
|
+
const value = parameters.add(cursor[key], "String");
|
|
8274
|
+
terms.push(`(${equal.join(" AND ")} AND (s.${key} > ${value} OR isNull(s.${key})))`);
|
|
8275
|
+
equal.push(`s.${key} = ${value}`);
|
|
8276
|
+
}
|
|
8277
|
+
return `(${terms.join(" OR ")})`;
|
|
8278
|
+
}
|
|
8279
|
+
const THREAD_QUERY_EXECUTION_LIMITS = {
|
|
8280
|
+
memoryLimitBytes: 512 * 1024 * 1024,
|
|
8281
|
+
maxThreads: 2,
|
|
8282
|
+
maxBlockSize: 4096,
|
|
8283
|
+
spillThresholdBytes: 32 * 1024 * 1024
|
|
8284
|
+
};
|
|
8285
|
+
const MAX_CANDIDATE_THREADS = 16384;
|
|
8286
|
+
const MAX_CANDIDATE_TRACES = 1e5;
|
|
8287
|
+
const MAX_CANDIDATE_ID_BYTES = 4 * 1024 * 1024;
|
|
8288
|
+
const MAX_FULL_HISTORY_ROOTS = 25e4;
|
|
8289
|
+
function canUseThreadCandidates(probe) {
|
|
8290
|
+
return Number(probe.threadCount) <= MAX_CANDIDATE_THREADS && Number(probe.traceCount) <= MAX_CANDIDATE_TRACES && Number(probe.threadBytes) + Number(probe.traceBytes) <= MAX_CANDIDATE_ID_BYTES;
|
|
8291
|
+
}
|
|
8292
|
+
const identity = (field) => `tuple(isNull(organizationId), ifNull(organizationId, ''), ${field})`;
|
|
8293
|
+
const candidateThreadsFilter = `${identity("threadId")} IN (SELECT ${identity("threadId")} FROM candidate_threads)`;
|
|
8294
|
+
const candidateTracesFilter = `${identity("traceId")} IN (SELECT ${identity("traceId")} FROM candidate_traces)`;
|
|
8295
|
+
function candidateCtes(scope, from, to, plan, probe = false) {
|
|
8296
|
+
const timestamp = plan.timeRange.mode === "threads" ? plan.timeRange.field : "startedAt";
|
|
8297
|
+
const scoped = scope.length ? `${scope.join(" AND ")} AND ` : "";
|
|
8298
|
+
return `candidate_threads AS (
|
|
8299
|
+
SELECT DISTINCT organizationId, threadId FROM ${TABLE_TRACE_ROOTS}
|
|
8300
|
+
WHERE ${scoped}threadId IS NOT NULL AND ${timestamp} >= ${from} AND ${timestamp} < ${to}
|
|
8301
|
+
${probe ? `LIMIT 16385` : ""}
|
|
8302
|
+
), candidate_traces AS (
|
|
8303
|
+
SELECT DISTINCT organizationId, traceId FROM ${TABLE_TRACE_ROOTS}
|
|
8304
|
+
WHERE ${scoped}${candidateThreadsFilter}
|
|
8305
|
+
${probe ? `LIMIT 100001` : ""}
|
|
8306
|
+
)`;
|
|
8307
|
+
}
|
|
8308
|
+
function compileClickHouseThreadRowsQuery(plan, strategy = "full") {
|
|
8309
|
+
const parameters = new ParameterBuilder();
|
|
8310
|
+
const from = parameters.add(plan.timeRange.from, "DateTime64(3, 'UTC')");
|
|
8311
|
+
const to = parameters.add(plan.timeRange.to, "DateTime64(3, 'UTC')");
|
|
8312
|
+
const scope = [];
|
|
8313
|
+
if (plan.scope) {
|
|
8314
|
+
scope.push(`organizationId = ${parameters.add(plan.scope.organizationId, "String")}`);
|
|
8315
|
+
if (plan.scope.resourceId !== void 0) scope.push(`resourceId = ${parameters.add(plan.scope.resourceId, "String")}`);
|
|
8316
|
+
}
|
|
8317
|
+
if (strategy === "historyBudget") return {
|
|
8318
|
+
query: `SELECT count() AS rootCount FROM (
|
|
8319
|
+
SELECT 1 FROM ${TABLE_TRACE_ROOTS} ${scope.length ? `WHERE ${scope.join(" AND ")}` : ""}
|
|
8320
|
+
LIMIT 250001
|
|
8321
|
+
)`,
|
|
8322
|
+
query_params: parameters.params
|
|
8323
|
+
};
|
|
8324
|
+
if (strategy === "probe") return {
|
|
8325
|
+
query: `WITH ${candidateCtes(scope, from, to, plan, true)}
|
|
8326
|
+
SELECT (SELECT count() FROM candidate_threads) AS threadCount,
|
|
8327
|
+
(SELECT sum(length(ifNull(organizationId, '')) + length(threadId)) FROM candidate_threads) AS threadBytes,
|
|
8328
|
+
count() AS traceCount, sum(length(ifNull(organizationId, '')) + length(traceId)) AS traceBytes
|
|
8329
|
+
FROM candidate_traces`,
|
|
8330
|
+
query_params: parameters.params
|
|
8331
|
+
};
|
|
8332
|
+
const predicate = plan.where ? compileThreadQueryPredicate(plan.where, parameters).sql : "1";
|
|
8333
|
+
const after = afterCursor(plan, parameters);
|
|
8334
|
+
const qualify = plan.timeRange.mode === "traces" ? "s.inWindow > 0" : `s.${plan.timeRange.field} >= ${from} AND s.${plan.timeRange.field} < ${to}`;
|
|
8335
|
+
const filtered = predicateFields(plan.where);
|
|
8336
|
+
const firstContext = [...contextFields, "timeToFirstChunkMs"].filter((field) => field === "resourceId" || filtered.has(field));
|
|
8337
|
+
const firstFields = [
|
|
8338
|
+
"startedAt",
|
|
8339
|
+
"traceId",
|
|
8340
|
+
"spanId",
|
|
8341
|
+
"endedAt",
|
|
8342
|
+
...firstContext
|
|
8343
|
+
];
|
|
8344
|
+
const lastFields = [
|
|
8345
|
+
"startedAt",
|
|
8346
|
+
"traceId",
|
|
8347
|
+
"spanId",
|
|
8348
|
+
"endedAt",
|
|
8349
|
+
"resourceId",
|
|
8350
|
+
...filtered.has("status") ? ["status"] : []
|
|
8351
|
+
];
|
|
8352
|
+
const rootDataFields = [
|
|
8353
|
+
"threadId",
|
|
8354
|
+
"spanId",
|
|
8355
|
+
"startedAt",
|
|
8356
|
+
"endedAt",
|
|
8357
|
+
...firstContext,
|
|
8358
|
+
...filtered.has("status") ? ["status"] : []
|
|
8359
|
+
];
|
|
8360
|
+
const rootExpression = (field) => field === "timeToFirstChunkMs" ? "if(spanType = 'agent_run', timeToFirstChunkMs, NULL)" : field === "status" ? "if(isNull(error), 'success', 'error')" : field;
|
|
8361
|
+
const sortedRoots = `roots AS (
|
|
8362
|
+
SELECT organizationId, traceId, ${rootDataFields.map((field) => `${rootExpression(field)} AS ${field}`).join(", ")}
|
|
8363
|
+
FROM ${TABLE_TRACE_ROOTS} ${scope.length || strategy === "candidates" ? `WHERE ${[...scope, ...strategy === "candidates" ? [candidateTracesFilter] : []].join(" AND ")}` : ""}
|
|
8364
|
+
ORDER BY organizationId, traceId, startedAt ASC, spanId, endedAt DESC, isNotNull(threadId) DESC, dedupeKey
|
|
8365
|
+
LIMIT 1 BY organizationId, traceId
|
|
8366
|
+
)`;
|
|
8367
|
+
return {
|
|
8368
|
+
query: `WITH ${strategy === "candidates" ? `${candidateCtes(scope, from, to, plan)}, ` : ""}${sortedRoots}, grouped AS (
|
|
8369
|
+
SELECT organizationId, threadId, min(startedAt) AS firstActivity, max(endedAt) AS lastActivity,
|
|
8370
|
+
count() AS traceCount, countIf(startedAt >= ${from} AND startedAt < ${to}) AS inWindow,
|
|
8371
|
+
min(tuple(${firstFields.join(", ")})) AS first,
|
|
8372
|
+
max(tuple(${lastFields.join(", ")})) AS last
|
|
8373
|
+
FROM roots WHERE threadId IS NOT NULL ${strategy === "candidates" ? `AND ${candidateThreadsFilter}` : ""} GROUP BY organizationId, threadId
|
|
8374
|
+
), rows AS (
|
|
8375
|
+
SELECT organizationId, threadId, firstActivity AS startedAt, lastActivity AS endedAt, traceCount, inWindow,
|
|
8376
|
+
${firstFields.map((field, i) => `first.${i + 1} AS ${i < 4 ? `first${field[0].toUpperCase()}${field.slice(1)}` : field}`).join(", ")},
|
|
8377
|
+
${lastFields.map((field, i) => `last.${i + 1} AS ${field === "status" ? "status" : `last${field[0].toUpperCase()}${field.slice(1)}`}`).join(", ")}
|
|
8378
|
+
FROM grouped
|
|
8379
|
+
) SELECT * FROM rows s WHERE ${qualify} AND (${predicate}) AND ${after}
|
|
8380
|
+
ORDER BY s.${plan.orderBy.field} ${plan.orderBy.direction === "asc" ? "ASC" : "DESC"}, s.organizationId ASC NULLS LAST, s.threadId ASC
|
|
8381
|
+
LIMIT ${parameters.add(plan.limit + 1, "UInt64")}`,
|
|
8382
|
+
query_params: parameters.params
|
|
8383
|
+
};
|
|
8384
|
+
}
|
|
8385
|
+
const rootKey = (row) => JSON.stringify([
|
|
8386
|
+
row.organizationId,
|
|
8387
|
+
row.traceId,
|
|
8388
|
+
row.spanId
|
|
8389
|
+
]);
|
|
8390
|
+
const iso = (value) => new Date(value.includes("T") ? value : `${value.replace(" ", "T")}Z`).toISOString();
|
|
8391
|
+
/** Bound HTTP form parameter size as well as payload rows. Long IDs must not break large pages. */
|
|
8392
|
+
function* batches(rows, maxRows = 256) {
|
|
8393
|
+
let batch = [];
|
|
8394
|
+
let bytes = 0;
|
|
8395
|
+
for (const row of rows) {
|
|
8396
|
+
const size = Buffer.byteLength(JSON.stringify(row), "utf8") * 2;
|
|
8397
|
+
if (batch.length && (batch.length >= maxRows || bytes + size > 48 * 1024)) {
|
|
8398
|
+
yield batch;
|
|
8399
|
+
batch = [];
|
|
8400
|
+
bytes = 0;
|
|
8401
|
+
}
|
|
8402
|
+
batch.push(row);
|
|
8403
|
+
bytes += size;
|
|
8404
|
+
}
|
|
8405
|
+
if (batch.length) yield batch;
|
|
8406
|
+
}
|
|
8407
|
+
async function queryThreadRows(client, plan, timeoutMs, strategy = "auto") {
|
|
8408
|
+
const deadline = performance.now() + _mastra_core_storage.resolveTraceQueryTimeoutMs(timeoutMs);
|
|
8409
|
+
const run = async (compiled) => {
|
|
8410
|
+
const remaining = Math.floor(deadline - performance.now());
|
|
8411
|
+
if (remaining <= 0) throw new _mastra_core_storage.TraceQueryExecutionError();
|
|
8412
|
+
return await runWithClickHouseTraceQueryTimeout(client, {
|
|
8413
|
+
timeoutMs: remaining,
|
|
8414
|
+
...THREAD_QUERY_EXECUTION_LIMITS
|
|
8415
|
+
}, compiled);
|
|
8416
|
+
};
|
|
8417
|
+
if (strategy === "auto") {
|
|
8418
|
+
const [probe] = await run(compileClickHouseThreadRowsQuery(plan, "probe"));
|
|
8419
|
+
if (Number(probe.threadCount) === 0) return {
|
|
8420
|
+
threads: [],
|
|
8421
|
+
page: { next: null }
|
|
8422
|
+
};
|
|
8423
|
+
strategy = canUseThreadCandidates(probe) ? "candidates" : "full";
|
|
8424
|
+
}
|
|
8425
|
+
if (strategy === "full") {
|
|
8426
|
+
const [budget] = await run(compileClickHouseThreadRowsQuery(plan, "historyBudget"));
|
|
8427
|
+
if (Number(budget.rootCount) > MAX_FULL_HISTORY_ROOTS) throw new _mastra_core_storage.TraceQueryResourceLimitError();
|
|
8428
|
+
}
|
|
8429
|
+
const selected = await run(compileClickHouseThreadRowsQuery(plan, strategy));
|
|
8430
|
+
const page = selected.slice(0, plan.limit);
|
|
8431
|
+
if (!page.length) return {
|
|
8432
|
+
threads: [],
|
|
8433
|
+
page: { next: null }
|
|
8434
|
+
};
|
|
8435
|
+
const roots = /* @__PURE__ */ new Map();
|
|
8436
|
+
const representative = (row, which) => ({
|
|
8437
|
+
organizationId: row.organizationId,
|
|
8438
|
+
resourceId: which === "first" ? row.resourceId : row.lastResourceId,
|
|
8439
|
+
traceId: row[`${which}TraceId`],
|
|
8440
|
+
spanId: row[`${which}SpanId`],
|
|
8441
|
+
startedAt: row[`${which}StartedAt`],
|
|
8442
|
+
endedAt: row[`${which}EndedAt`]
|
|
8443
|
+
});
|
|
8444
|
+
for (const row of page) for (const which of ["first", "last"]) {
|
|
8445
|
+
const root = representative(row, which);
|
|
8446
|
+
roots.set(rootKey(root), root);
|
|
8447
|
+
}
|
|
8448
|
+
const keys = (batch) => batch.map((root) => new _clickhouse_client.TupleParam([
|
|
8449
|
+
root.organizationId === null ? 1 : 0,
|
|
8450
|
+
root.organizationId ?? "",
|
|
8451
|
+
root.resourceId === null ? 1 : 0,
|
|
8452
|
+
root.resourceId ?? "",
|
|
8453
|
+
root.traceId,
|
|
8454
|
+
root.spanId,
|
|
8455
|
+
iso(root.startedAt).replace("T", " ").replace(/Z$/, ""),
|
|
8456
|
+
iso(root.endedAt).replace("T", " ").replace(/Z$/, "")
|
|
8457
|
+
]));
|
|
8458
|
+
const rootWhere = `tuple(isNull(organizationId), ifNull(organizationId, ''), isNull(resourceId), ifNull(resourceId, ''),
|
|
8459
|
+
traceId, spanId, startedAt, endedAt) IN {roots:Array(Tuple(UInt8, String, UInt8, String, String, String, DateTime64(3, 'UTC'), DateTime64(3, 'UTC')))}`;
|
|
8460
|
+
const stored = [];
|
|
8461
|
+
for (const batch of batches([...roots.values()])) stored.push(...await run({
|
|
8462
|
+
query: `SELECT organizationId, traceId, spanId, startedAt, endedAt, spanType,
|
|
8463
|
+
${contextFields.join(", ")}, if(spanType = 'agent_run', timeToFirstChunkMs, NULL) AS timeToFirstChunkMs,
|
|
8464
|
+
if(isNull(error), 'success', 'error') AS status, ${PREVIEW_COLUMNS.join(", ")}
|
|
8465
|
+
FROM ${TABLE_TRACE_ROOTS} WHERE ${rootWhere}
|
|
8466
|
+
ORDER BY isNotNull(threadId) DESC LIMIT 1 BY organizationId, traceId, spanId`,
|
|
8467
|
+
query_params: { roots: keys(batch) }
|
|
8468
|
+
}));
|
|
8469
|
+
const byRoot = new Map(stored.map((row) => [rootKey(row), row]));
|
|
8470
|
+
const missing = stored.filter(needsPreviewPayloads);
|
|
8471
|
+
for (const batch of batches(missing, 32)) {
|
|
8472
|
+
const payloads = await run({
|
|
8473
|
+
query: `SELECT organizationId, traceId, spanId, input, output, error FROM ${TABLE_TRACE_ROOTS}
|
|
8474
|
+
WHERE ${rootWhere} ORDER BY isNotNull(threadId) DESC LIMIT 1 BY organizationId, traceId, spanId`,
|
|
8475
|
+
query_params: { roots: keys(batch) }
|
|
8476
|
+
});
|
|
8477
|
+
for (const payload of payloads) {
|
|
8478
|
+
const row = byRoot.get(rootKey(payload));
|
|
8479
|
+
if (row) Object.assign(row, resolveStoredPreviews(row, payload));
|
|
8480
|
+
}
|
|
8481
|
+
}
|
|
8482
|
+
const firstRoots = page.map((row) => representative(row, "first")).filter((root) => byRoot.get(rootKey(root))?.spanType === "agent_run");
|
|
8483
|
+
const models = [];
|
|
8484
|
+
for (const batch of batches(firstRoots)) models.push(...await run({
|
|
8485
|
+
query: `SELECT organizationId, traceId,
|
|
8486
|
+
if(JSONType(attributes, 'model') = 'String', JSONExtractString(attributes, 'model'), NULL) AS model,
|
|
8487
|
+
if(JSONType(attributes, 'provider') = 'String', JSONExtractString(attributes, 'provider'), NULL) AS provider
|
|
8488
|
+
FROM ${TABLE_SPAN_EVENTS} WHERE spanType = 'model_generation' AND
|
|
8489
|
+
tuple(isNull(organizationId), ifNull(organizationId, ''), isNull(resourceId), ifNull(resourceId, ''), traceId, parentSpanId)
|
|
8490
|
+
IN {agents:Array(Tuple(UInt8, String, UInt8, String, String, String))}
|
|
8491
|
+
ORDER BY organizationId, traceId, startedAt, spanId LIMIT 1 BY organizationId, traceId`,
|
|
8492
|
+
query_params: { agents: batch.map((root) => new _clickhouse_client.TupleParam([
|
|
8493
|
+
root.organizationId === null ? 1 : 0,
|
|
8494
|
+
root.organizationId ?? "",
|
|
8495
|
+
root.resourceId === null ? 1 : 0,
|
|
8496
|
+
root.resourceId ?? "",
|
|
8497
|
+
root.traceId,
|
|
8498
|
+
root.spanId
|
|
8499
|
+
])) }
|
|
8500
|
+
}));
|
|
8501
|
+
const byTrace = new Map(models.map((row) => [JSON.stringify([row.organizationId, row.traceId]), row]));
|
|
8502
|
+
const threads = page.flatMap((row) => {
|
|
8503
|
+
const first = byRoot.get(rootKey(representative(row, "first")));
|
|
8504
|
+
const last = byRoot.get(rootKey(representative(row, "last")));
|
|
8505
|
+
if (!first || !last) return [];
|
|
8506
|
+
const model = byTrace.get(JSON.stringify([row.organizationId, row.firstTraceId]));
|
|
8507
|
+
return [_mastra_core_storage.threadQueryRowSchema.parse({
|
|
8508
|
+
organizationId: row.organizationId,
|
|
8509
|
+
threadId: row.threadId,
|
|
8510
|
+
firstTraceId: row.firstTraceId,
|
|
8511
|
+
lastTraceId: row.lastTraceId,
|
|
8512
|
+
...Object.fromEntries(contextFields.map((field) => [field, first[field]])),
|
|
8513
|
+
status: last.status,
|
|
8514
|
+
startedAt: iso(row.startedAt),
|
|
8515
|
+
endedAt: iso(row.endedAt),
|
|
8516
|
+
traceCount: Number(row.traceCount),
|
|
8517
|
+
timeToFirstChunkMs: first.timeToFirstChunkMs,
|
|
8518
|
+
model: model?.model ?? null,
|
|
8519
|
+
provider: model?.provider ?? null,
|
|
8520
|
+
inputPreview: resolveStoredPreviews(first).inputPreview,
|
|
8521
|
+
outputPreview: resolveStoredPreviews(last).outputPreview,
|
|
8522
|
+
errorPreview: resolveStoredPreviews(last).errorPreview
|
|
8523
|
+
})];
|
|
8524
|
+
});
|
|
8525
|
+
const last = page.at(-1);
|
|
8526
|
+
return {
|
|
8527
|
+
threads,
|
|
8528
|
+
page: { next: selected.length > plan.limit ? _mastra_core_storage.encodeThreadQueryCursor(plan, {
|
|
8529
|
+
organizationId: last.organizationId,
|
|
8530
|
+
threadId: last.threadId,
|
|
8531
|
+
sortValue: iso(last[plan.orderBy.field])
|
|
8532
|
+
}) : null }
|
|
8533
|
+
};
|
|
8534
|
+
}
|
|
8535
|
+
//#endregion
|
|
8183
8536
|
//#region src/storage/domains/observability/v-next/trace-aggregate.ts
|
|
8184
8537
|
/** Trace-root column expressions (alias `r`) for each groupable dimension; same values `where` sees. */
|
|
8185
8538
|
const DIMENSION_SQL = {
|
|
8186
8539
|
entityType: "r.entityType",
|
|
8187
8540
|
entityName: "r.entityName",
|
|
8541
|
+
name: "r.name",
|
|
8542
|
+
spanType: "r.spanType",
|
|
8188
8543
|
environment: "r.environment",
|
|
8189
8544
|
status: TRACE_STATUS_SQL,
|
|
8190
8545
|
serviceName: "r.serviceName",
|
|
@@ -9328,6 +9683,7 @@ function buildBranchCursor(row) {
|
|
|
9328
9683
|
//#endregion
|
|
9329
9684
|
//#region src/storage/domains/observability/v-next/index.ts
|
|
9330
9685
|
const spanQueryFeatures = typeof _mastra_core_storage.planSpanQuery === "function" ? ["span-query", "span-query-context-fields"] : [];
|
|
9686
|
+
const threadQueryFeatures = typeof _mastra_core_storage.planThreadRowsQuery === "function" ? ["thread-query-rows"] : [];
|
|
9331
9687
|
function buildSignalMigrationRequiredMessage(args) {
|
|
9332
9688
|
const tableList = args.tables.map((table) => ` - ${table.table} (${table.engine})`).join("\n");
|
|
9333
9689
|
return `
|
|
@@ -9765,8 +10121,11 @@ var ObservabilityStorageClickhouseVNext = class extends _mastra_core_storage.Obs
|
|
|
9765
10121
|
"trace-query-tenant-scope",
|
|
9766
10122
|
"feedback",
|
|
9767
10123
|
"trace-query-context-ids",
|
|
10124
|
+
...timeToFirstChunkFeatures,
|
|
10125
|
+
"trace-query-root-span-fields",
|
|
9768
10126
|
"trace-aggregate",
|
|
9769
|
-
...spanQueryFeatures
|
|
10127
|
+
...spanQueryFeatures,
|
|
10128
|
+
...threadQueryFeatures
|
|
9770
10129
|
];
|
|
9771
10130
|
return [
|
|
9772
10131
|
"metrics",
|
|
@@ -9785,8 +10144,11 @@ var ObservabilityStorageClickhouseVNext = class extends _mastra_core_storage.Obs
|
|
|
9785
10144
|
"trace-query-tenant-scope",
|
|
9786
10145
|
"feedback",
|
|
9787
10146
|
"trace-query-context-ids",
|
|
10147
|
+
...timeToFirstChunkFeatures,
|
|
10148
|
+
"trace-query-root-span-fields",
|
|
9788
10149
|
"trace-aggregate",
|
|
9789
|
-
...spanQueryFeatures
|
|
10150
|
+
...spanQueryFeatures,
|
|
10151
|
+
...threadQueryFeatures
|
|
9790
10152
|
];
|
|
9791
10153
|
}
|
|
9792
10154
|
async createSpan(args) {
|
|
@@ -9963,6 +10325,7 @@ var ObservabilityStorageClickhouseVNext = class extends _mastra_core_storage.Obs
|
|
|
9963
10325
|
}
|
|
9964
10326
|
async queryThreads(plan) {
|
|
9965
10327
|
try {
|
|
10328
|
+
if (plan.result === "threadRows") return await queryThreadRows(this.#client, plan, this.#traceQueryTimeoutMs);
|
|
9966
10329
|
return await queryThreads(this.#client, plan, this.#traceQueryTimeoutMs);
|
|
9967
10330
|
} catch (error) {
|
|
9968
10331
|
if (error instanceof _mastra_core_error.MastraError || error instanceof _mastra_core_storage.TraceQueryExecutionError || error instanceof _mastra_core_storage.TraceQueryResourceLimitError) throw error;
|