@mastra/clickhouse 1.23.0-alpha.2 → 1.23.0
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 +101 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +101 -7
- package/dist/index.js.map +1 -1
- package/dist/storage/domains/memory/index.d.ts.map +1 -1
- 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 +5 -0
- package/dist/storage/domains/observability/v-next/trace-query.d.ts.map +1 -1
- package/package.json +5 -5
package/dist/docs/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: mastra-clickhouse
|
|
|
3
3
|
description: Documentation for @mastra/clickhouse. Use when working with @mastra/clickhouse APIs, configuration, or implementation.
|
|
4
4
|
metadata:
|
|
5
5
|
package: "@mastra/clickhouse"
|
|
6
|
-
version: "1.23.0
|
|
6
|
+
version: "1.23.0"
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
## When to use
|
package/dist/index.cjs
CHANGED
|
@@ -1712,6 +1712,7 @@ var MemoryStorageClickhouse = class extends _mastra_core_storage.MemoryStorage {
|
|
|
1712
1712
|
});
|
|
1713
1713
|
const threadIdsToUpdate = /* @__PURE__ */ new Set();
|
|
1714
1714
|
const updatePromises = [];
|
|
1715
|
+
const writtenContent = /* @__PURE__ */ new Map();
|
|
1715
1716
|
for (const existingMessage of parsedExistingMessages) {
|
|
1716
1717
|
const updatePayload = messages.find((m) => m.id === existingMessage.id);
|
|
1717
1718
|
if (!updatePayload) continue;
|
|
@@ -1738,6 +1739,7 @@ var MemoryStorageClickhouse = class extends _mastra_core_storage.MemoryStorage {
|
|
|
1738
1739
|
};
|
|
1739
1740
|
setClauses.push(`content = {var_content_${paramIdx}:String}`);
|
|
1740
1741
|
values[`var_content_${paramIdx}`] = JSON.stringify(newContent);
|
|
1742
|
+
writtenContent.set(id, values[`var_content_${paramIdx}`]);
|
|
1741
1743
|
paramIdx++;
|
|
1742
1744
|
delete updatableFields.content;
|
|
1743
1745
|
}
|
|
@@ -1754,7 +1756,7 @@ var MemoryStorageClickhouse = class extends _mastra_core_storage.MemoryStorage {
|
|
|
1754
1756
|
UPDATE ${setClauses.join(", ")}
|
|
1755
1757
|
WHERE id = {var_id_${paramIdx}:String}
|
|
1756
1758
|
`;
|
|
1757
|
-
|
|
1759
|
+
this.logger?.debug?.("Updating message", { id });
|
|
1758
1760
|
updatePromises.push(this.client.command({
|
|
1759
1761
|
query: updateQuery,
|
|
1760
1762
|
query_params: values,
|
|
@@ -1790,7 +1792,7 @@ var MemoryStorageClickhouse = class extends _mastra_core_storage.MemoryStorage {
|
|
|
1790
1792
|
if (updatedMessage) {
|
|
1791
1793
|
let needsRetry = false;
|
|
1792
1794
|
for (const [key, value] of Object.entries(fieldsToUpdate)) if (key === "content") {
|
|
1793
|
-
const expectedContent =
|
|
1795
|
+
const expectedContent = writtenContent.get(id);
|
|
1794
1796
|
if ((typeof updatedMessage.content === "string" ? updatedMessage.content : JSON.stringify(updatedMessage.content)) !== expectedContent) {
|
|
1795
1797
|
needsRetry = true;
|
|
1796
1798
|
break;
|
|
@@ -1800,7 +1802,7 @@ var MemoryStorageClickhouse = class extends _mastra_core_storage.MemoryStorage {
|
|
|
1800
1802
|
break;
|
|
1801
1803
|
}
|
|
1802
1804
|
if (needsRetry) {
|
|
1803
|
-
|
|
1805
|
+
this.logger?.warn?.("Update not applied correctly, retrying with DELETE + INSERT", { id });
|
|
1804
1806
|
await this.client.command({
|
|
1805
1807
|
query: `DELETE FROM ${_mastra_core_storage.TABLE_MESSAGES} WHERE id = {messageId:String}`,
|
|
1806
1808
|
query_params: { messageId: id },
|
|
@@ -2435,7 +2437,7 @@ var ObservabilityStorageClickhouse = class extends _mastra_core_storage.Observab
|
|
|
2435
2437
|
if (filters.hasChildError !== void 0) {
|
|
2436
2438
|
const finalClause = (TABLE_ENGINES[_mastra_core_storage.TABLE_SPANS] ?? "MergeTree()").startsWith("ReplacingMergeTree") ? "FINAL" : "";
|
|
2437
2439
|
conditions.push(`traceId ${filters.hasChildError ? "IN" : "NOT IN"} (
|
|
2438
|
-
SELECT c.traceId FROM ${_mastra_core_storage.TABLE_SPANS} ${finalClause}
|
|
2440
|
+
SELECT c.traceId FROM ${_mastra_core_storage.TABLE_SPANS} c ${finalClause}
|
|
2439
2441
|
WHERE c.error IS NOT NULL AND c.error != ''
|
|
2440
2442
|
)`);
|
|
2441
2443
|
}
|
|
@@ -7884,12 +7886,31 @@ function compileClickHouseTraceAggregate(plan) {
|
|
|
7884
7886
|
if (intervalMs === void 0) throw new Error(`Unsupported trusted trace-aggregate interval: ${plan.interval}`);
|
|
7885
7887
|
factColumns.push(`fromUnixTimestamp64Milli(intDiv(toUnixTimestamp64Milli(r.startedAt), ${intervalMs}) * ${intervalMs}, 'UTC') AS bucket`);
|
|
7886
7888
|
}
|
|
7889
|
+
const measureKinds = new Set(plan.measures.map((measure) => measureRule(measure.name)?.kind));
|
|
7890
|
+
const hasCost = measureKinds.has("cost");
|
|
7891
|
+
let usageJoin = "";
|
|
7892
|
+
if (hasCost || measureKinds.has("tokens")) {
|
|
7893
|
+
ctes.push(compileUsageCte(plan, parameters));
|
|
7894
|
+
usageJoin = "\n LEFT JOIN usage u ON u.traceId = r.traceId";
|
|
7895
|
+
USAGE_METRIC_NAMES.forEach((_, index) => factColumns.push(`if(u.hasUsage = 1, u.t${index}, NULL) AS t${index}`));
|
|
7896
|
+
factColumns.push(`u.hasUsage = 1 AS usageBearing`, `if(u.hasUsage = 1 AND u.priced = 1, u.cost, NULL) AS traceCost`, `u.hasUsage = 1 AND u.priced = 1 AND u.pricingFailure = 0 AS covered`, `if(u.hasUsage = 1 AND u.priced = 1, u.unitMin, NULL) AS unitMin`, `if(u.hasUsage = 1 AND u.priced = 1, u.unitMax, NULL) AS unitMax`);
|
|
7897
|
+
}
|
|
7887
7898
|
ctes.push(`facts AS (
|
|
7888
7899
|
SELECT ${factColumns.join(",\n ")}
|
|
7889
|
-
FROM candidates r
|
|
7900
|
+
FROM candidates r${usageJoin}
|
|
7890
7901
|
)`);
|
|
7902
|
+
const mixedUnitsSql = "minOrNull(unitMin) != maxOrNull(unitMax)";
|
|
7891
7903
|
const measureSql = (name) => {
|
|
7892
7904
|
if (name === "count") return "toFloat64(count())";
|
|
7905
|
+
const rule = measureRule(name);
|
|
7906
|
+
if (rule?.kind === "tokens" && rule.metricNames) {
|
|
7907
|
+
const value = rule.metricNames.map((metricName) => `t${usageMetricIndex(metricName)}`).join(" + ");
|
|
7908
|
+
return `toFloat64(${rule.statistic === "avg" ? "avgOrNull" : "sumOrNull"}(${value}))`;
|
|
7909
|
+
}
|
|
7910
|
+
if (rule?.kind === "cost") {
|
|
7911
|
+
const aggregate = rule.statistic === "avg" ? "avgOrNull" : "sumOrNull";
|
|
7912
|
+
return `if(${mixedUnitsSql}, NULL, toFloat64(${aggregate}(traceCost)))`;
|
|
7913
|
+
}
|
|
7893
7914
|
if (name === "errorCount") return "toFloat64(countIf(isError))";
|
|
7894
7915
|
if (name === "errorRate") return "countIf(isError) / count()";
|
|
7895
7916
|
if (name === "duration.avg") return "avg(durationMs)";
|
|
@@ -7927,6 +7948,11 @@ function compileClickHouseTraceAggregate(plan) {
|
|
|
7927
7948
|
const tiebreakSql = dimensionColumns.map((column) => `${column} ASC NULLS LAST`);
|
|
7928
7949
|
const measureColumns = plan.measures.map((measure, index) => `${measureSql(measure.name)} AS m${index}`);
|
|
7929
7950
|
const measureNames = plan.measures.map((_, index) => `m${index}`);
|
|
7951
|
+
if (hasCost) {
|
|
7952
|
+
const mixedUnit = parameters.add(_mastra_core_storage.TRACE_AGGREGATE_MIXED_COST_UNIT, "String");
|
|
7953
|
+
measureColumns.push(`countIf(covered) / nullIf(countIf(usageBearing), 0) AS costCoverage`, `if(${mixedUnitsSql}, ${mixedUnit}, minOrNull(unitMin)) AS costUnit`);
|
|
7954
|
+
measureNames.push("costCoverage", "costUnit");
|
|
7955
|
+
}
|
|
7930
7956
|
if (plan.interval === void 0) {
|
|
7931
7957
|
const limit = parameters.add(plan.limit + 1, "UInt64");
|
|
7932
7958
|
return {
|
|
@@ -7948,7 +7974,7 @@ LIMIT ${limit}`,
|
|
|
7948
7974
|
"bucket",
|
|
7949
7975
|
"grouping(bucket) = 1 AS __isGroup",
|
|
7950
7976
|
...measureColumns,
|
|
7951
|
-
`(${havingSql}) AS __keep`,
|
|
7977
|
+
`ifNull(${havingSql}, 0) AS __keep`,
|
|
7952
7978
|
`${orderTarget} AS __order`
|
|
7953
7979
|
].join(", ")}
|
|
7954
7980
|
FROM facts
|
|
@@ -7976,6 +8002,70 @@ ORDER BY __rank ASC, bucket ASC`,
|
|
|
7976
8002
|
query_params: parameters.params
|
|
7977
8003
|
};
|
|
7978
8004
|
}
|
|
8005
|
+
const USAGE_METRIC_NAMES = _mastra_core_storage.TRACE_AGGREGATE_USAGE_METRIC_NAMES;
|
|
8006
|
+
function measureRule(name) {
|
|
8007
|
+
return _mastra_core_storage.isTraceAggregateCanonicalMeasure(name) ? _mastra_core_storage.TRACE_AGGREGATE_MEASURE_REGISTRY[name] : void 0;
|
|
8008
|
+
}
|
|
8009
|
+
function usageMetricIndex(name) {
|
|
8010
|
+
const index = USAGE_METRIC_NAMES.indexOf(name);
|
|
8011
|
+
if (index === -1) throw new Error(`Unsupported trusted trace-aggregate token metric: ${name}`);
|
|
8012
|
+
return index;
|
|
8013
|
+
}
|
|
8014
|
+
/**
|
|
8015
|
+
* Builds `usage`: one row per candidate trace that has at least one token metric row, holding
|
|
8016
|
+
* per-name token sums (`t<i>`, indexed by `TRACE_AGGREGATE_USAGE_METRIC_NAMES`) and the trace's
|
|
8017
|
+
* pricing state. Metric rows are matched by traceId, usage metric name, tenant scope, and
|
|
8018
|
+
* `timestamp >= from` only: a trace that starts in the window can emit metrics after `to`, and
|
|
8019
|
+
* the candidates semi-join already bounds the scan.
|
|
8020
|
+
*
|
|
8021
|
+
* Rows are deduplicated with `LIMIT 1 BY metricId` at query time. `ReplacingMergeTree` only
|
|
8022
|
+
* collapses rows with equal sort keys `(name, timestamp, metricId)`, so retried copies with
|
|
8023
|
+
* different timestamps never merge, and copies with equal keys stay separate until a background
|
|
8024
|
+
* merge runs.
|
|
8025
|
+
*/
|
|
8026
|
+
function compileUsageCte(plan, parameters) {
|
|
8027
|
+
const names = USAGE_METRIC_NAMES.map((name) => parameters.add(name, "String"));
|
|
8028
|
+
const costRow = `name IN (${_mastra_core_storage.TRACE_AGGREGATE_COST_METRIC_NAMES.map((name) => names[usageMetricIndex(name)]).join(", ")})`;
|
|
8029
|
+
const from = parameters.add(plan.timeRange.from, "DateTime64(3, 'UTC')");
|
|
8030
|
+
const tenant = compileTenantScope(plan.scope, parameters);
|
|
8031
|
+
const priced = `${costRow} AND isNotNull(estimatedCost) AND isNotNull(costUnit) AND NOT hasError`;
|
|
8032
|
+
const failed = `${costRow} AND (hasError OR (isNotNull(estimatedCost) AND isNull(costUnit)))`;
|
|
8033
|
+
return `usage AS (
|
|
8034
|
+
SELECT traceId,
|
|
8035
|
+
toUInt8(1) AS hasUsage,
|
|
8036
|
+
${names.map((name, index) => `sumIf(value, name = ${name}) AS t${index}`).join(",\n ")},
|
|
8037
|
+
sumIf(assumeNotNull(estimatedCost), ${priced}) AS cost,
|
|
8038
|
+
toUInt8(countIf(${priced}) > 0) AS priced,
|
|
8039
|
+
toUInt8(countIf(${failed}) > 0) AS pricingFailure,
|
|
8040
|
+
minIf(assumeNotNull(costUnit), ${priced}) AS unitMin,
|
|
8041
|
+
maxIf(assumeNotNull(costUnit), ${priced}) AS unitMax
|
|
8042
|
+
FROM (
|
|
8043
|
+
SELECT traceId,
|
|
8044
|
+
latest.1 AS name,
|
|
8045
|
+
latest.2 AS value,
|
|
8046
|
+
latest.3 AS estimatedCost,
|
|
8047
|
+
latest.4 AS costUnit,
|
|
8048
|
+
latest.5 AS hasError
|
|
8049
|
+
FROM (
|
|
8050
|
+
SELECT traceId,
|
|
8051
|
+
argMax(tuple(name, value, estimatedCost, costUnit, hasError), timestamp) AS latest
|
|
8052
|
+
FROM (
|
|
8053
|
+
SELECT traceId, metricId, timestamp, name, value, estimatedCost, costUnit,
|
|
8054
|
+
if(${costRow}, ifNull(JSONHas(costMetadata, 'error') AND JSONType(costMetadata, 'error') != 'Null', 0), 0) AS hasError
|
|
8055
|
+
FROM ${TABLE_METRIC_EVENTS}
|
|
8056
|
+
WHERE traceId IN (SELECT traceId FROM candidates)
|
|
8057
|
+
AND name IN (${names.join(", ")})
|
|
8058
|
+
AND timestamp >= ${from}${tenant}
|
|
8059
|
+
)
|
|
8060
|
+
GROUP BY traceId, metricId
|
|
8061
|
+
)
|
|
8062
|
+
)
|
|
8063
|
+
GROUP BY traceId
|
|
8064
|
+
)`;
|
|
8065
|
+
}
|
|
8066
|
+
function nullableNumber(value) {
|
|
8067
|
+
return value === null || value === void 0 ? null : Number(value);
|
|
8068
|
+
}
|
|
7979
8069
|
async function aggregateTraces(client, plan, timeoutMs) {
|
|
7980
8070
|
const result = await runWithClickHouseTraceQueryTimeout(client, { timeoutMs }, compileClickHouseTraceAggregate(plan));
|
|
7981
8071
|
let truncated;
|
|
@@ -7989,7 +8079,11 @@ async function aggregateTraces(client, plan, timeoutMs) {
|
|
|
7989
8079
|
}
|
|
7990
8080
|
return {
|
|
7991
8081
|
rows: resultRows.map((row) => {
|
|
7992
|
-
const shaped = { measures: Object.fromEntries(plan.measures.map((measure, index) => [measure.name,
|
|
8082
|
+
const shaped = { measures: Object.fromEntries(plan.measures.map((measure, index) => [measure.name, nullableNumber(row[`m${index}`])])) };
|
|
8083
|
+
if ("costCoverage" in row) shaped.cost = {
|
|
8084
|
+
coverage: nullableNumber(row.costCoverage),
|
|
8085
|
+
unit: row.costUnit ?? null
|
|
8086
|
+
};
|
|
7993
8087
|
if (plan.dimensions.length > 0) shaped.dimensions = Object.fromEntries(plan.dimensions.map((dimension, index) => [dimension, row[`d${index}`] ?? null]));
|
|
7994
8088
|
if (plan.interval !== void 0) shaped.bucket = new Date(row.bucket).toISOString();
|
|
7995
8089
|
return shaped;
|