@mastra/clickhouse 1.4.1 → 1.5.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/CHANGELOG.md +24 -0
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/index.cjs +458 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +459 -15
- package/dist/index.js.map +1 -1
- package/dist/storage/db/utils.d.ts.map +1 -1
- package/dist/storage/domains/background-tasks/index.d.ts +20 -0
- package/dist/storage/domains/background-tasks/index.d.ts.map +1 -0
- package/dist/storage/domains/observability/index.d.ts +2 -1
- package/dist/storage/domains/observability/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/ddl.d.ts +4 -4
- package/dist/storage/domains/observability/v-next/ddl.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/helpers.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/index.d.ts +15 -4
- package/dist/storage/domains/observability/v-next/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/migration.d.ts +20 -0
- package/dist/storage/domains/observability/v-next/migration.d.ts.map +1 -0
- package/dist/storage/domains/observability/v-next/tracing.d.ts +5 -1
- package/dist/storage/domains/observability/v-next/tracing.d.ts.map +1 -1
- package/dist/storage/index.d.ts +2 -1
- package/dist/storage/index.d.ts.map +1 -1
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { createClient } from '@clickhouse/client';
|
|
2
2
|
import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
|
|
3
|
-
import { TABLE_SKILL_BLOBS, TABLE_SKILL_VERSIONS, TABLE_SKILLS, TABLE_WORKSPACE_VERSIONS, TABLE_WORKSPACES, TABLE_MCP_SERVER_VERSIONS, TABLE_MCP_SERVERS, TABLE_MCP_CLIENT_VERSIONS, TABLE_MCP_CLIENTS, TABLE_SCORER_DEFINITION_VERSIONS, TABLE_SCORER_DEFINITIONS, TABLE_PROMPT_BLOCK_VERSIONS, TABLE_PROMPT_BLOCKS, TABLE_EXPERIMENT_RESULTS, TABLE_EXPERIMENTS, TABLE_DATASET_VERSIONS, TABLE_DATASET_ITEMS, TABLE_DATASETS, TABLE_AGENT_VERSIONS, TABLE_SPANS, TABLE_RESOURCES, TABLE_SCORERS, TABLE_THREADS, TABLE_TRACES, TABLE_WORKFLOW_SNAPSHOT, TABLE_MESSAGES,
|
|
4
|
-
import { MessageList } from '@mastra/core/agent';
|
|
3
|
+
import { TABLE_SKILL_BLOBS, TABLE_SKILL_VERSIONS, TABLE_SKILLS, TABLE_WORKSPACE_VERSIONS, TABLE_WORKSPACES, TABLE_MCP_SERVER_VERSIONS, TABLE_MCP_SERVERS, TABLE_MCP_CLIENT_VERSIONS, TABLE_MCP_CLIENTS, TABLE_SCORER_DEFINITION_VERSIONS, TABLE_SCORER_DEFINITIONS, TABLE_PROMPT_BLOCK_VERSIONS, TABLE_PROMPT_BLOCKS, TABLE_EXPERIMENT_RESULTS, TABLE_EXPERIMENTS, TABLE_DATASET_VERSIONS, TABLE_DATASET_ITEMS, TABLE_DATASETS, TABLE_AGENT_VERSIONS, TABLE_SPANS, TABLE_RESOURCES, TABLE_SCORERS, TABLE_THREADS, TABLE_TRACES, TABLE_WORKFLOW_SNAPSHOT, TABLE_MESSAGES, BackgroundTasksStorage, TABLE_SCHEMAS, TABLE_BACKGROUND_TASKS, MemoryStorage, createStorageErrorId, normalizePerPage, calculatePagination, ObservabilityStorage, SPAN_SCHEMA, listTracesArgsSchema, toTraceSpans, ScoresStorage, transformScoreRow, SCORERS_SCHEMA, WorkflowsStorage, MastraCompositeStore, getSqlType, getDefaultValue, safelyParseJSON, listLogsArgsSchema, listMetricsArgsSchema, listScoresArgsSchema, listFeedbackArgsSchema, EntityType, TraceStatus } from '@mastra/core/storage';
|
|
5
4
|
import { MastraBase } from '@mastra/core/base';
|
|
5
|
+
import { MessageList } from '@mastra/core/agent';
|
|
6
6
|
import { parseFieldKey } from '@mastra/core/utils';
|
|
7
7
|
import { saveScorePayloadSchema } from '@mastra/core/evals';
|
|
8
8
|
|
|
@@ -37,7 +37,8 @@ var TABLE_ENGINES = {
|
|
|
37
37
|
[TABLE_WORKSPACE_VERSIONS]: `MergeTree()`,
|
|
38
38
|
[TABLE_SKILLS]: `ReplacingMergeTree()`,
|
|
39
39
|
[TABLE_SKILL_VERSIONS]: `MergeTree()`,
|
|
40
|
-
[TABLE_SKILL_BLOBS]: `ReplacingMergeTree()
|
|
40
|
+
[TABLE_SKILL_BLOBS]: `ReplacingMergeTree()`,
|
|
41
|
+
mastra_background_tasks: `ReplacingMergeTree()`
|
|
41
42
|
};
|
|
42
43
|
var COLUMN_TYPES = {
|
|
43
44
|
text: "String",
|
|
@@ -682,7 +683,203 @@ var ClickhouseDB = class extends MastraBase {
|
|
|
682
683
|
}
|
|
683
684
|
};
|
|
684
685
|
|
|
685
|
-
// src/storage/domains/
|
|
686
|
+
// src/storage/domains/background-tasks/index.ts
|
|
687
|
+
function serializeJson(v) {
|
|
688
|
+
if (typeof v === "object" && v != null) return JSON.stringify(v);
|
|
689
|
+
return v ?? "";
|
|
690
|
+
}
|
|
691
|
+
function rowToTask(row) {
|
|
692
|
+
const parseJson2 = (val) => {
|
|
693
|
+
if (val == null || val === "") return void 0;
|
|
694
|
+
if (typeof val === "string") {
|
|
695
|
+
try {
|
|
696
|
+
return JSON.parse(val);
|
|
697
|
+
} catch {
|
|
698
|
+
return val;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
return val;
|
|
702
|
+
};
|
|
703
|
+
return {
|
|
704
|
+
id: row.id,
|
|
705
|
+
status: row.status,
|
|
706
|
+
toolName: row.tool_name,
|
|
707
|
+
toolCallId: row.tool_call_id,
|
|
708
|
+
args: parseJson2(row.args) ?? {},
|
|
709
|
+
agentId: row.agent_id,
|
|
710
|
+
threadId: row.thread_id || void 0,
|
|
711
|
+
resourceId: row.resource_id || void 0,
|
|
712
|
+
runId: row.run_id ?? "",
|
|
713
|
+
result: parseJson2(row.result),
|
|
714
|
+
error: parseJson2(row.error),
|
|
715
|
+
retryCount: Number(row.retry_count ?? 0),
|
|
716
|
+
maxRetries: Number(row.max_retries ?? 0),
|
|
717
|
+
timeoutMs: Number(row.timeout_ms ?? 3e5),
|
|
718
|
+
createdAt: new Date(row.createdAt),
|
|
719
|
+
startedAt: row.startedAt ? new Date(row.startedAt) : void 0,
|
|
720
|
+
completedAt: row.completedAt ? new Date(row.completedAt) : void 0
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
var BackgroundTasksStorageClickhouse = class extends BackgroundTasksStorage {
|
|
724
|
+
client;
|
|
725
|
+
#db;
|
|
726
|
+
constructor(config) {
|
|
727
|
+
super();
|
|
728
|
+
const { client, ttl } = resolveClickhouseConfig(config);
|
|
729
|
+
this.client = client;
|
|
730
|
+
this.#db = new ClickhouseDB({ client, ttl });
|
|
731
|
+
}
|
|
732
|
+
async init() {
|
|
733
|
+
await this.#db.createTable({ tableName: TABLE_BACKGROUND_TASKS, schema: TABLE_SCHEMAS[TABLE_BACKGROUND_TASKS] });
|
|
734
|
+
}
|
|
735
|
+
async dangerouslyClearAll() {
|
|
736
|
+
await this.#db.clearTable({ tableName: TABLE_BACKGROUND_TASKS });
|
|
737
|
+
}
|
|
738
|
+
async createTask(task) {
|
|
739
|
+
await this.client.insert({
|
|
740
|
+
table: TABLE_BACKGROUND_TASKS,
|
|
741
|
+
values: [
|
|
742
|
+
{
|
|
743
|
+
id: task.id,
|
|
744
|
+
tool_call_id: task.toolCallId,
|
|
745
|
+
tool_name: task.toolName,
|
|
746
|
+
agent_id: task.agentId,
|
|
747
|
+
thread_id: task.threadId ?? "",
|
|
748
|
+
resource_id: task.resourceId ?? "",
|
|
749
|
+
run_id: task.runId,
|
|
750
|
+
status: task.status,
|
|
751
|
+
args: serializeJson(task.args),
|
|
752
|
+
result: serializeJson(task.result),
|
|
753
|
+
error: serializeJson(task.error),
|
|
754
|
+
retry_count: task.retryCount,
|
|
755
|
+
max_retries: task.maxRetries,
|
|
756
|
+
timeout_ms: task.timeoutMs,
|
|
757
|
+
createdAt: task.createdAt.toISOString(),
|
|
758
|
+
startedAt: task.startedAt?.toISOString() ?? "1970-01-01T00:00:00.000Z",
|
|
759
|
+
completedAt: task.completedAt?.toISOString() ?? "1970-01-01T00:00:00.000Z"
|
|
760
|
+
}
|
|
761
|
+
],
|
|
762
|
+
format: "JSONEachRow",
|
|
763
|
+
clickhouse_settings: { date_time_input_format: "best_effort" }
|
|
764
|
+
});
|
|
765
|
+
}
|
|
766
|
+
async updateTask(taskId, update) {
|
|
767
|
+
const existing = await this.getTask(taskId);
|
|
768
|
+
if (!existing) return;
|
|
769
|
+
const merged = { ...existing };
|
|
770
|
+
if ("status" in update) merged.status = update.status;
|
|
771
|
+
if ("result" in update) merged.result = update.result;
|
|
772
|
+
if ("error" in update) merged.error = update.error;
|
|
773
|
+
if ("retryCount" in update) merged.retryCount = update.retryCount;
|
|
774
|
+
if ("startedAt" in update) merged.startedAt = update.startedAt;
|
|
775
|
+
if ("completedAt" in update) merged.completedAt = update.completedAt;
|
|
776
|
+
await this.createTask(merged);
|
|
777
|
+
}
|
|
778
|
+
async getTask(taskId) {
|
|
779
|
+
const result = await this.client.query({
|
|
780
|
+
query: `SELECT * FROM ${TABLE_BACKGROUND_TASKS} FINAL WHERE id = {var_id:String} LIMIT 1`,
|
|
781
|
+
query_params: { var_id: taskId },
|
|
782
|
+
format: "JSONEachRow",
|
|
783
|
+
clickhouse_settings: { date_time_input_format: "best_effort" }
|
|
784
|
+
});
|
|
785
|
+
const rows = await result.json();
|
|
786
|
+
return rows.length > 0 ? rowToTask(rows[0]) : null;
|
|
787
|
+
}
|
|
788
|
+
async listTasks(filter) {
|
|
789
|
+
const conditions = [];
|
|
790
|
+
const params = {};
|
|
791
|
+
if (filter.status) {
|
|
792
|
+
const statuses = Array.isArray(filter.status) ? filter.status : [filter.status];
|
|
793
|
+
conditions.push(`status IN ({var_statuses:Array(String)})`);
|
|
794
|
+
params.var_statuses = statuses;
|
|
795
|
+
}
|
|
796
|
+
if (filter.agentId) {
|
|
797
|
+
conditions.push(`agent_id = {var_agent:String}`);
|
|
798
|
+
params.var_agent = filter.agentId;
|
|
799
|
+
}
|
|
800
|
+
if (filter.threadId) {
|
|
801
|
+
conditions.push(`thread_id = {var_thread:String}`);
|
|
802
|
+
params.var_thread = filter.threadId;
|
|
803
|
+
}
|
|
804
|
+
if (filter.runId) {
|
|
805
|
+
conditions.push(`run_id = {var_run:String}`);
|
|
806
|
+
params.var_run = filter.runId;
|
|
807
|
+
}
|
|
808
|
+
if (filter.toolName) {
|
|
809
|
+
conditions.push(`tool_name = {var_tool:String}`);
|
|
810
|
+
params.var_tool = filter.toolName;
|
|
811
|
+
}
|
|
812
|
+
const dateCol = filter.dateFilterBy === "startedAt" ? "startedAt" : filter.dateFilterBy === "completedAt" ? "completedAt" : "createdAt";
|
|
813
|
+
if (filter.fromDate) {
|
|
814
|
+
conditions.push(`${dateCol} >= parseDateTimeBestEffort({var_from_date:String})`);
|
|
815
|
+
params.var_from_date = filter.fromDate.toISOString();
|
|
816
|
+
}
|
|
817
|
+
if (filter.toDate) {
|
|
818
|
+
conditions.push(`${dateCol} < parseDateTimeBestEffort({var_to_date:String})`);
|
|
819
|
+
params.var_to_date = filter.toDate.toISOString();
|
|
820
|
+
}
|
|
821
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
822
|
+
const countResult = await this.client.query({
|
|
823
|
+
query: `SELECT count() as count FROM ${TABLE_BACKGROUND_TASKS} FINAL ${where}`,
|
|
824
|
+
query_params: params,
|
|
825
|
+
format: "JSONEachRow",
|
|
826
|
+
clickhouse_settings: { date_time_input_format: "best_effort" }
|
|
827
|
+
});
|
|
828
|
+
const countRows = await countResult.json();
|
|
829
|
+
const total = Number(countRows[0]?.count ?? 0);
|
|
830
|
+
const orderCol = filter.orderBy === "startedAt" ? "startedAt" : filter.orderBy === "completedAt" ? "completedAt" : "createdAt";
|
|
831
|
+
const direction = filter.orderDirection === "desc" ? "DESC" : "ASC";
|
|
832
|
+
let sql = `SELECT * FROM ${TABLE_BACKGROUND_TASKS} FINAL ${where} ORDER BY ${orderCol} ${direction}`;
|
|
833
|
+
if (filter.perPage != null) {
|
|
834
|
+
sql += ` LIMIT {var_limit:UInt32}`;
|
|
835
|
+
params.var_limit = filter.perPage;
|
|
836
|
+
if (filter.page != null) {
|
|
837
|
+
sql += ` OFFSET {var_offset:UInt32}`;
|
|
838
|
+
params.var_offset = filter.page * filter.perPage;
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
const result = await this.client.query({
|
|
842
|
+
query: sql,
|
|
843
|
+
query_params: params,
|
|
844
|
+
format: "JSONEachRow",
|
|
845
|
+
clickhouse_settings: { date_time_input_format: "best_effort" }
|
|
846
|
+
});
|
|
847
|
+
const tasks = (await result.json()).map(rowToTask);
|
|
848
|
+
return { tasks, total };
|
|
849
|
+
}
|
|
850
|
+
async deleteTask(taskId) {
|
|
851
|
+
await this.client.query({
|
|
852
|
+
query: `ALTER TABLE ${TABLE_BACKGROUND_TASKS} DELETE WHERE id = {var_id:String}`,
|
|
853
|
+
query_params: { var_id: taskId }
|
|
854
|
+
});
|
|
855
|
+
}
|
|
856
|
+
async deleteTasks(filter) {
|
|
857
|
+
const { tasks } = await this.listTasks(filter);
|
|
858
|
+
for (const task of tasks) {
|
|
859
|
+
await this.client.query({
|
|
860
|
+
query: `ALTER TABLE ${TABLE_BACKGROUND_TASKS} DELETE WHERE id = {var_id:String}`,
|
|
861
|
+
query_params: { var_id: task.id }
|
|
862
|
+
});
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
async getRunningCount() {
|
|
866
|
+
const result = await this.client.query({
|
|
867
|
+
query: `SELECT count() as count FROM ${TABLE_BACKGROUND_TASKS} FINAL WHERE status = 'running'`,
|
|
868
|
+
format: "JSONEachRow"
|
|
869
|
+
});
|
|
870
|
+
const rows = await result.json();
|
|
871
|
+
return Number(rows[0]?.count ?? 0);
|
|
872
|
+
}
|
|
873
|
+
async getRunningCountByAgent(agentId) {
|
|
874
|
+
const result = await this.client.query({
|
|
875
|
+
query: `SELECT count() as count FROM ${TABLE_BACKGROUND_TASKS} FINAL WHERE status = 'running' AND agent_id = {var_agent:String}`,
|
|
876
|
+
query_params: { var_agent: agentId },
|
|
877
|
+
format: "JSONEachRow"
|
|
878
|
+
});
|
|
879
|
+
const rows = await result.json();
|
|
880
|
+
return Number(rows[0]?.count ?? 0);
|
|
881
|
+
}
|
|
882
|
+
};
|
|
686
883
|
function serializeMetadata(metadata) {
|
|
687
884
|
if (!metadata || Object.keys(metadata).length === 0) {
|
|
688
885
|
return "{}";
|
|
@@ -2231,6 +2428,49 @@ time for large tables. Please ensure you have a backup before proceeding.
|
|
|
2231
2428
|
);
|
|
2232
2429
|
}
|
|
2233
2430
|
}
|
|
2431
|
+
async getTraceLight(args) {
|
|
2432
|
+
const { traceId } = args;
|
|
2433
|
+
try {
|
|
2434
|
+
const engine = TABLE_ENGINES[TABLE_SPANS] ?? "MergeTree()";
|
|
2435
|
+
const result = await this.client.query({
|
|
2436
|
+
query: `
|
|
2437
|
+
SELECT traceId, spanId, parentSpanId, name,
|
|
2438
|
+
entityType, entityId, entityName,
|
|
2439
|
+
spanType, error, isEvent,
|
|
2440
|
+
startedAt, endedAt, createdAt, updatedAt
|
|
2441
|
+
FROM ${TABLE_SPANS} ${engine.startsWith("ReplacingMergeTree") ? "FINAL" : ""}
|
|
2442
|
+
WHERE traceId = {traceId:String}
|
|
2443
|
+
ORDER BY startedAt ASC
|
|
2444
|
+
`,
|
|
2445
|
+
query_params: { traceId },
|
|
2446
|
+
format: "JSONEachRow",
|
|
2447
|
+
clickhouse_settings: {
|
|
2448
|
+
date_time_input_format: "best_effort",
|
|
2449
|
+
date_time_output_format: "iso",
|
|
2450
|
+
use_client_time_zone: 1,
|
|
2451
|
+
output_format_json_quote_64bit_integers: 0
|
|
2452
|
+
}
|
|
2453
|
+
});
|
|
2454
|
+
const rows = await result.json();
|
|
2455
|
+
if (!rows || rows.length === 0) {
|
|
2456
|
+
return null;
|
|
2457
|
+
}
|
|
2458
|
+
return {
|
|
2459
|
+
traceId,
|
|
2460
|
+
spans: transformRows(rows)
|
|
2461
|
+
};
|
|
2462
|
+
} catch (error) {
|
|
2463
|
+
throw new MastraError(
|
|
2464
|
+
{
|
|
2465
|
+
id: createStorageErrorId("CLICKHOUSE", "GET_TRACE_LIGHT", "FAILED"),
|
|
2466
|
+
domain: ErrorDomain.STORAGE,
|
|
2467
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2468
|
+
details: { traceId }
|
|
2469
|
+
},
|
|
2470
|
+
error
|
|
2471
|
+
);
|
|
2472
|
+
}
|
|
2473
|
+
}
|
|
2234
2474
|
async updateSpan(args) {
|
|
2235
2475
|
const { traceId, spanId, updates } = args;
|
|
2236
2476
|
try {
|
|
@@ -2745,6 +2985,7 @@ CREATE TABLE IF NOT EXISTS ${TABLE_METRIC_EVENTS} (
|
|
|
2745
2985
|
timestamp DateTime64(3, 'UTC'),
|
|
2746
2986
|
|
|
2747
2987
|
-- IDs
|
|
2988
|
+
metricId String,
|
|
2748
2989
|
traceId Nullable(String),
|
|
2749
2990
|
spanId Nullable(String),
|
|
2750
2991
|
experimentId Nullable(String),
|
|
@@ -2792,9 +3033,9 @@ CREATE TABLE IF NOT EXISTS ${TABLE_METRIC_EVENTS} (
|
|
|
2792
3033
|
metadata Nullable(String),
|
|
2793
3034
|
scope Nullable(String)
|
|
2794
3035
|
)
|
|
2795
|
-
ENGINE =
|
|
3036
|
+
ENGINE = ReplacingMergeTree
|
|
2796
3037
|
PARTITION BY toDate(timestamp)
|
|
2797
|
-
ORDER BY (name, timestamp)
|
|
3038
|
+
ORDER BY (name, timestamp, metricId)
|
|
2798
3039
|
`;
|
|
2799
3040
|
var LOG_EVENTS_DDL = `
|
|
2800
3041
|
CREATE TABLE IF NOT EXISTS ${TABLE_LOG_EVENTS} (
|
|
@@ -2802,6 +3043,7 @@ CREATE TABLE IF NOT EXISTS ${TABLE_LOG_EVENTS} (
|
|
|
2802
3043
|
timestamp DateTime64(3, 'UTC'),
|
|
2803
3044
|
|
|
2804
3045
|
-- IDs
|
|
3046
|
+
logId String,
|
|
2805
3047
|
traceId Nullable(String),
|
|
2806
3048
|
spanId Nullable(String),
|
|
2807
3049
|
experimentId Nullable(String),
|
|
@@ -2844,10 +3086,9 @@ CREATE TABLE IF NOT EXISTS ${TABLE_LOG_EVENTS} (
|
|
|
2844
3086
|
metadata Nullable(String),
|
|
2845
3087
|
scope Nullable(String)
|
|
2846
3088
|
)
|
|
2847
|
-
ENGINE =
|
|
3089
|
+
ENGINE = ReplacingMergeTree
|
|
2848
3090
|
PARTITION BY toDate(timestamp)
|
|
2849
|
-
ORDER BY (timestamp,
|
|
2850
|
-
SETTINGS allow_nullable_key = 1
|
|
3091
|
+
ORDER BY (timestamp, logId)
|
|
2851
3092
|
`;
|
|
2852
3093
|
var SCORE_EVENTS_DDL = `
|
|
2853
3094
|
CREATE TABLE IF NOT EXISTS ${TABLE_SCORE_EVENTS} (
|
|
@@ -2855,6 +3096,7 @@ CREATE TABLE IF NOT EXISTS ${TABLE_SCORE_EVENTS} (
|
|
|
2855
3096
|
timestamp DateTime64(3, 'UTC'),
|
|
2856
3097
|
|
|
2857
3098
|
-- IDs
|
|
3099
|
+
scoreId String,
|
|
2858
3100
|
traceId Nullable(String),
|
|
2859
3101
|
spanId Nullable(String),
|
|
2860
3102
|
experimentId Nullable(String),
|
|
@@ -2904,9 +3146,9 @@ CREATE TABLE IF NOT EXISTS ${TABLE_SCORE_EVENTS} (
|
|
|
2904
3146
|
metadata Nullable(String),
|
|
2905
3147
|
scope Nullable(String)
|
|
2906
3148
|
)
|
|
2907
|
-
ENGINE =
|
|
3149
|
+
ENGINE = ReplacingMergeTree
|
|
2908
3150
|
PARTITION BY toDate(timestamp)
|
|
2909
|
-
ORDER BY (traceId, timestamp)
|
|
3151
|
+
ORDER BY (traceId, timestamp, scoreId)
|
|
2910
3152
|
SETTINGS allow_nullable_key = 1
|
|
2911
3153
|
`;
|
|
2912
3154
|
var FEEDBACK_EVENTS_DDL = `
|
|
@@ -2915,6 +3157,7 @@ CREATE TABLE IF NOT EXISTS ${TABLE_FEEDBACK_EVENTS} (
|
|
|
2915
3157
|
timestamp DateTime64(3, 'UTC'),
|
|
2916
3158
|
|
|
2917
3159
|
-- IDs
|
|
3160
|
+
feedbackId String,
|
|
2918
3161
|
traceId Nullable(String),
|
|
2919
3162
|
spanId Nullable(String),
|
|
2920
3163
|
experimentId Nullable(String),
|
|
@@ -2967,9 +3210,9 @@ CREATE TABLE IF NOT EXISTS ${TABLE_FEEDBACK_EVENTS} (
|
|
|
2967
3210
|
metadata Nullable(String),
|
|
2968
3211
|
scope Nullable(String)
|
|
2969
3212
|
)
|
|
2970
|
-
ENGINE =
|
|
3213
|
+
ENGINE = ReplacingMergeTree
|
|
2971
3214
|
PARTITION BY toDate(timestamp)
|
|
2972
|
-
ORDER BY (traceId, timestamp)
|
|
3215
|
+
ORDER BY (traceId, timestamp, feedbackId)
|
|
2973
3216
|
SETTINGS allow_nullable_key = 1
|
|
2974
3217
|
`;
|
|
2975
3218
|
var DISCOVERY_VALUES_DDL = `
|
|
@@ -3324,6 +3567,7 @@ function spanRecordToRow(span) {
|
|
|
3324
3567
|
}
|
|
3325
3568
|
function rowToLogRecord(row) {
|
|
3326
3569
|
return {
|
|
3570
|
+
logId: row.logId,
|
|
3327
3571
|
timestamp: toDate(row.timestamp),
|
|
3328
3572
|
level: row.level,
|
|
3329
3573
|
message: row.message,
|
|
@@ -3360,6 +3604,7 @@ function rowToLogRecord(row) {
|
|
|
3360
3604
|
}
|
|
3361
3605
|
function logRecordToRow(log) {
|
|
3362
3606
|
return {
|
|
3607
|
+
logId: log.logId,
|
|
3363
3608
|
timestamp: toISOString(log.timestamp),
|
|
3364
3609
|
level: log.level,
|
|
3365
3610
|
message: log.message,
|
|
@@ -3396,6 +3641,7 @@ function logRecordToRow(log) {
|
|
|
3396
3641
|
}
|
|
3397
3642
|
function rowToMetricRecord(row) {
|
|
3398
3643
|
return {
|
|
3644
|
+
metricId: row.metricId,
|
|
3399
3645
|
timestamp: toDate(row.timestamp),
|
|
3400
3646
|
name: row.name,
|
|
3401
3647
|
value: Number(row.value),
|
|
@@ -3437,6 +3683,7 @@ function rowToMetricRecord(row) {
|
|
|
3437
3683
|
}
|
|
3438
3684
|
function metricRecordToRow(metric) {
|
|
3439
3685
|
return {
|
|
3686
|
+
metricId: metric.metricId,
|
|
3440
3687
|
timestamp: toISOString(metric.timestamp),
|
|
3441
3688
|
name: metric.name,
|
|
3442
3689
|
value: metric.value,
|
|
@@ -3478,6 +3725,7 @@ function metricRecordToRow(metric) {
|
|
|
3478
3725
|
}
|
|
3479
3726
|
function rowToScoreRecord(row) {
|
|
3480
3727
|
return {
|
|
3728
|
+
scoreId: row.scoreId,
|
|
3481
3729
|
timestamp: toDate(row.timestamp),
|
|
3482
3730
|
// Core score/feedback shapes still type traceId as required for now.
|
|
3483
3731
|
traceId: nullableString(row.traceId),
|
|
@@ -3520,6 +3768,7 @@ function scoreRecordToRow(score) {
|
|
|
3520
3768
|
const metadata = score.metadata ?? null;
|
|
3521
3769
|
const scoreSource = score.scoreSource ?? score.source ?? null;
|
|
3522
3770
|
return {
|
|
3771
|
+
scoreId: score.scoreId,
|
|
3523
3772
|
timestamp: toISOString(score.timestamp),
|
|
3524
3773
|
traceId: score.traceId ?? null,
|
|
3525
3774
|
spanId: score.spanId ?? null,
|
|
@@ -3562,6 +3811,7 @@ function rowToFeedbackRecord(row) {
|
|
|
3562
3811
|
const feedbackSource = nullableString(row.feedbackSource);
|
|
3563
3812
|
const feedbackUserId = nullableString(row.feedbackUserId) ?? nullableString(row.userId);
|
|
3564
3813
|
return {
|
|
3814
|
+
feedbackId: row.feedbackId,
|
|
3565
3815
|
timestamp: toDate(row.timestamp),
|
|
3566
3816
|
// Core score/feedback shapes still type traceId as required for now.
|
|
3567
3817
|
traceId: nullableString(row.traceId),
|
|
@@ -3605,6 +3855,7 @@ function feedbackRecordToRow(feedback) {
|
|
|
3605
3855
|
const feedbackSource = feedback.feedbackSource ?? feedback.source ?? "";
|
|
3606
3856
|
const feedbackUserId = feedback.feedbackUserId ?? feedback.userId ?? null;
|
|
3607
3857
|
return {
|
|
3858
|
+
feedbackId: feedback.feedbackId,
|
|
3608
3859
|
timestamp: toISOString(feedback.timestamp),
|
|
3609
3860
|
traceId: feedback.traceId ?? null,
|
|
3610
3861
|
spanId: feedback.spanId ?? null,
|
|
@@ -4724,6 +4975,93 @@ async function getMetricLabelValues(client, args) {
|
|
|
4724
4975
|
);
|
|
4725
4976
|
return { values: rows.map((r) => r.value) };
|
|
4726
4977
|
}
|
|
4978
|
+
var SIGNAL_MIGRATIONS = [
|
|
4979
|
+
{ table: TABLE_METRIC_EVENTS, createDDL: METRIC_EVENTS_DDL, idColumn: "metricId" },
|
|
4980
|
+
{ table: TABLE_LOG_EVENTS, createDDL: LOG_EVENTS_DDL, idColumn: "logId" },
|
|
4981
|
+
{ table: TABLE_SCORE_EVENTS, createDDL: SCORE_EVENTS_DDL, idColumn: "scoreId" },
|
|
4982
|
+
{ table: TABLE_FEEDBACK_EVENTS, createDDL: FEEDBACK_EVENTS_DDL, idColumn: "feedbackId" }
|
|
4983
|
+
];
|
|
4984
|
+
async function getTableEngine(client, table) {
|
|
4985
|
+
const result = await client.query({
|
|
4986
|
+
query: `SELECT engine FROM system.tables WHERE database = currentDatabase() AND name = {table:String}`,
|
|
4987
|
+
query_params: { table },
|
|
4988
|
+
format: "JSONEachRow"
|
|
4989
|
+
});
|
|
4990
|
+
const rows = await result.json();
|
|
4991
|
+
return rows[0]?.engine ?? null;
|
|
4992
|
+
}
|
|
4993
|
+
async function getTableColumns(client, table) {
|
|
4994
|
+
const result = await client.query({ query: `DESCRIBE TABLE ${table}`, format: "JSONEachRow" });
|
|
4995
|
+
const rows = await result.json();
|
|
4996
|
+
return rows.map((r) => r.name);
|
|
4997
|
+
}
|
|
4998
|
+
function buildTemporaryTableDDL(createDDL, table, tempTable) {
|
|
4999
|
+
return createDDL.replace(`CREATE TABLE IF NOT EXISTS ${table}`, `CREATE TABLE ${tempTable}`);
|
|
5000
|
+
}
|
|
5001
|
+
async function dropTableIfExists(client, table) {
|
|
5002
|
+
if (await getTableEngine(client, table) !== null) {
|
|
5003
|
+
await client.command({ query: `DROP TABLE ${table}` });
|
|
5004
|
+
}
|
|
5005
|
+
}
|
|
5006
|
+
function createMigrationError(args, error) {
|
|
5007
|
+
return new MastraError(
|
|
5008
|
+
{
|
|
5009
|
+
id: createStorageErrorId("CLICKHOUSE", "MIGRATE_SIGNAL_TABLES", "FAILED"),
|
|
5010
|
+
domain: ErrorDomain.STORAGE,
|
|
5011
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
5012
|
+
details: args
|
|
5013
|
+
},
|
|
5014
|
+
error
|
|
5015
|
+
);
|
|
5016
|
+
}
|
|
5017
|
+
async function checkSignalTablesMigrationStatus(client) {
|
|
5018
|
+
const tables = [];
|
|
5019
|
+
for (const { table, idColumn } of SIGNAL_MIGRATIONS) {
|
|
5020
|
+
const engine = await getTableEngine(client, table);
|
|
5021
|
+
if (!engine || engine === "ReplacingMergeTree") {
|
|
5022
|
+
continue;
|
|
5023
|
+
}
|
|
5024
|
+
tables.push({ table, engine, idColumn });
|
|
5025
|
+
}
|
|
5026
|
+
return {
|
|
5027
|
+
needsMigration: tables.length > 0,
|
|
5028
|
+
tables
|
|
5029
|
+
};
|
|
5030
|
+
}
|
|
5031
|
+
async function migrateSignalTables(client, logger) {
|
|
5032
|
+
for (const { table, createDDL, idColumn } of SIGNAL_MIGRATIONS) {
|
|
5033
|
+
const engine = await getTableEngine(client, table);
|
|
5034
|
+
if (!engine || engine === "ReplacingMergeTree") continue;
|
|
5035
|
+
logger?.info?.(`Migrating ${table} from ${engine} to ReplacingMergeTree with ${idColumn} column`);
|
|
5036
|
+
const temp = `${table}_migrating_${Date.now()}`;
|
|
5037
|
+
try {
|
|
5038
|
+
await client.command({ query: buildTemporaryTableDDL(createDDL, table, temp) });
|
|
5039
|
+
const newColumns = await getTableColumns(client, temp);
|
|
5040
|
+
const currentColumns = new Set(await getTableColumns(client, table));
|
|
5041
|
+
const columnList = newColumns.map((c) => `"${c}"`).join(", ");
|
|
5042
|
+
const selectExprs = newColumns.map((c) => {
|
|
5043
|
+
if (c === idColumn) {
|
|
5044
|
+
return currentColumns.has(c) ? `COALESCE(nullIf("${c}", ''), toString(generateUUIDv4())) AS "${c}"` : `toString(generateUUIDv4()) AS "${c}"`;
|
|
5045
|
+
}
|
|
5046
|
+
return currentColumns.has(c) ? `"${c}"` : `NULL AS "${c}"`;
|
|
5047
|
+
}).join(", ");
|
|
5048
|
+
await client.command({
|
|
5049
|
+
query: `INSERT INTO ${temp} (${columnList}) SELECT ${selectExprs} FROM ${table}`
|
|
5050
|
+
});
|
|
5051
|
+
await client.command({ query: `EXCHANGE TABLES ${temp} AND ${table}` });
|
|
5052
|
+
await client.command({ query: `DROP TABLE ${temp}` });
|
|
5053
|
+
logger?.info?.(`Successfully migrated ${table}`);
|
|
5054
|
+
} catch (error) {
|
|
5055
|
+
logger?.error?.(`Migration of ${table} failed: ${error.message}`);
|
|
5056
|
+
try {
|
|
5057
|
+
await dropTableIfExists(client, temp);
|
|
5058
|
+
} catch (restoreError) {
|
|
5059
|
+
logger?.error?.(`Failed to clean up temporary table ${temp}: ${restoreError.message}`);
|
|
5060
|
+
}
|
|
5061
|
+
throw createMigrationError({ table, idColumn }, error);
|
|
5062
|
+
}
|
|
5063
|
+
}
|
|
5064
|
+
}
|
|
4727
5065
|
var SCORE_TYPED_COLUMNS = /* @__PURE__ */ new Set([
|
|
4728
5066
|
"timestamp",
|
|
4729
5067
|
"traceId",
|
|
@@ -5187,6 +5525,31 @@ async function getTrace(client, args) {
|
|
|
5187
5525
|
const spans = rows.map(rowToSpanRecord);
|
|
5188
5526
|
return { traceId: args.traceId, spans };
|
|
5189
5527
|
}
|
|
5528
|
+
async function getTraceLight(client, args) {
|
|
5529
|
+
const result = await client.query({
|
|
5530
|
+
query: `
|
|
5531
|
+
SELECT traceId, spanId, parentSpanId, name,
|
|
5532
|
+
entityType, entityId, entityName,
|
|
5533
|
+
spanType, error, isEvent,
|
|
5534
|
+
startedAt, endedAt
|
|
5535
|
+
FROM (
|
|
5536
|
+
SELECT *
|
|
5537
|
+
FROM ${TABLE_SPAN_EVENTS}
|
|
5538
|
+
WHERE traceId = {traceId:String}
|
|
5539
|
+
ORDER BY dedupeKey, endedAt DESC
|
|
5540
|
+
LIMIT 1 BY dedupeKey
|
|
5541
|
+
)
|
|
5542
|
+
ORDER BY startedAt ASC
|
|
5543
|
+
`,
|
|
5544
|
+
query_params: { traceId: args.traceId },
|
|
5545
|
+
format: "JSONEachRow",
|
|
5546
|
+
clickhouse_settings: CH_SETTINGS
|
|
5547
|
+
});
|
|
5548
|
+
const rows = await result.json();
|
|
5549
|
+
if (!rows || rows.length === 0) return null;
|
|
5550
|
+
const spans = rows.map(rowToSpanRecord);
|
|
5551
|
+
return { traceId: args.traceId, spans };
|
|
5552
|
+
}
|
|
5190
5553
|
async function batchDeleteTraces(client, args) {
|
|
5191
5554
|
if (args.traceIds.length === 0) return;
|
|
5192
5555
|
const params = {};
|
|
@@ -5215,6 +5578,32 @@ async function batchDeleteTraces(client, args) {
|
|
|
5215
5578
|
}
|
|
5216
5579
|
|
|
5217
5580
|
// src/storage/domains/observability/v-next/index.ts
|
|
5581
|
+
function buildSignalMigrationRequiredMessage(args) {
|
|
5582
|
+
const tableList = args.tables.map((table) => ` - ${table.table} (${table.engine})`).join("\n");
|
|
5583
|
+
return `
|
|
5584
|
+
===========================================================================
|
|
5585
|
+
MIGRATION REQUIRED: ${args.store} observability signal tables need signal IDs
|
|
5586
|
+
===========================================================================
|
|
5587
|
+
|
|
5588
|
+
The following signal tables still use the legacy schema and must be migrated
|
|
5589
|
+
before observability storage can initialize:
|
|
5590
|
+
|
|
5591
|
+
${tableList}
|
|
5592
|
+
|
|
5593
|
+
To fix this, run the manual migration command:
|
|
5594
|
+
|
|
5595
|
+
npx mastra migrate
|
|
5596
|
+
|
|
5597
|
+
This command will:
|
|
5598
|
+
1. Create replacement signal tables with signal-ID dedupe keys
|
|
5599
|
+
2. Backfill missing signal IDs for legacy rows
|
|
5600
|
+
3. Swap the migrated tables into place
|
|
5601
|
+
|
|
5602
|
+
WARNING: This migration recreates the signal tables and may take significant
|
|
5603
|
+
time for large databases. Please ensure you have a backup before proceeding.
|
|
5604
|
+
===========================================================================
|
|
5605
|
+
`;
|
|
5606
|
+
}
|
|
5218
5607
|
var ObservabilityStorageClickhouseVNext = class extends ObservabilityStorage {
|
|
5219
5608
|
#client;
|
|
5220
5609
|
#retention;
|
|
@@ -5228,6 +5617,18 @@ var ObservabilityStorageClickhouseVNext = class extends ObservabilityStorage {
|
|
|
5228
5617
|
// Initialization
|
|
5229
5618
|
// -------------------------------------------------------------------------
|
|
5230
5619
|
async init() {
|
|
5620
|
+
const migrationStatus = await checkSignalTablesMigrationStatus(this.#client);
|
|
5621
|
+
if (migrationStatus.needsMigration) {
|
|
5622
|
+
throw new MastraError({
|
|
5623
|
+
id: createStorageErrorId("CLICKHOUSE", "MIGRATION_REQUIRED", "SIGNAL_TABLES"),
|
|
5624
|
+
domain: ErrorDomain.STORAGE,
|
|
5625
|
+
category: ErrorCategory.USER,
|
|
5626
|
+
text: buildSignalMigrationRequiredMessage({
|
|
5627
|
+
store: "ClickHouse",
|
|
5628
|
+
tables: migrationStatus.tables.map(({ table, engine }) => ({ table, engine }))
|
|
5629
|
+
})
|
|
5630
|
+
});
|
|
5631
|
+
}
|
|
5231
5632
|
try {
|
|
5232
5633
|
for (const ddl of [...ALL_TABLE_DDL, ...ALL_MV_DDL]) {
|
|
5233
5634
|
await this.#client.command({ query: ddl });
|
|
@@ -5242,6 +5643,9 @@ var ObservabilityStorageClickhouseVNext = class extends ObservabilityStorage {
|
|
|
5242
5643
|
}
|
|
5243
5644
|
}
|
|
5244
5645
|
} catch (error) {
|
|
5646
|
+
if (error instanceof MastraError) {
|
|
5647
|
+
throw error;
|
|
5648
|
+
}
|
|
5245
5649
|
throw new MastraError(
|
|
5246
5650
|
{
|
|
5247
5651
|
id: createStorageErrorId("CLICKHOUSE", "VNEXT_INIT", "FAILED"),
|
|
@@ -5263,6 +5667,29 @@ var ObservabilityStorageClickhouseVNext = class extends ObservabilityStorage {
|
|
|
5263
5667
|
} catch {
|
|
5264
5668
|
}
|
|
5265
5669
|
}
|
|
5670
|
+
/**
|
|
5671
|
+
* Manually migrate legacy signal tables to the signal-ID ReplacingMergeTree schema.
|
|
5672
|
+
* The public method name is historical; the CLI still calls `migrateSpans()`
|
|
5673
|
+
* for observability migrations even though this now also migrates signal tables.
|
|
5674
|
+
*/
|
|
5675
|
+
async migrateSpans() {
|
|
5676
|
+
const migrationStatus = await checkSignalTablesMigrationStatus(this.#client);
|
|
5677
|
+
if (!migrationStatus.needsMigration) {
|
|
5678
|
+
return {
|
|
5679
|
+
success: true,
|
|
5680
|
+
alreadyMigrated: true,
|
|
5681
|
+
duplicatesRemoved: 0,
|
|
5682
|
+
message: "Migration already complete. Signal tables already use signal-ID dedupe keys."
|
|
5683
|
+
};
|
|
5684
|
+
}
|
|
5685
|
+
await migrateSignalTables(this.#client, this.logger);
|
|
5686
|
+
return {
|
|
5687
|
+
success: true,
|
|
5688
|
+
alreadyMigrated: false,
|
|
5689
|
+
duplicatesRemoved: 0,
|
|
5690
|
+
message: `Migration complete. Migrated signal tables: ${migrationStatus.tables.map((t) => t.table).join(", ")}.`
|
|
5691
|
+
};
|
|
5692
|
+
}
|
|
5266
5693
|
// -------------------------------------------------------------------------
|
|
5267
5694
|
// Strategy
|
|
5268
5695
|
// -------------------------------------------------------------------------
|
|
@@ -5358,6 +5785,22 @@ var ObservabilityStorageClickhouseVNext = class extends ObservabilityStorage {
|
|
|
5358
5785
|
);
|
|
5359
5786
|
}
|
|
5360
5787
|
}
|
|
5788
|
+
async getTraceLight(args) {
|
|
5789
|
+
try {
|
|
5790
|
+
return await getTraceLight(this.#client, args);
|
|
5791
|
+
} catch (error) {
|
|
5792
|
+
if (error instanceof MastraError) throw error;
|
|
5793
|
+
throw new MastraError(
|
|
5794
|
+
{
|
|
5795
|
+
id: createStorageErrorId("CLICKHOUSE", "GET_TRACE_LIGHT", "FAILED"),
|
|
5796
|
+
domain: ErrorDomain.STORAGE,
|
|
5797
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
5798
|
+
details: { traceId: args.traceId }
|
|
5799
|
+
},
|
|
5800
|
+
error
|
|
5801
|
+
);
|
|
5802
|
+
}
|
|
5803
|
+
}
|
|
5361
5804
|
async listTraces(args) {
|
|
5362
5805
|
try {
|
|
5363
5806
|
return await listTraces(this.#client, args);
|
|
@@ -6646,7 +7089,8 @@ var ClickhouseStore = class extends MastraCompositeStore {
|
|
|
6646
7089
|
workflows,
|
|
6647
7090
|
scores,
|
|
6648
7091
|
memory,
|
|
6649
|
-
observability
|
|
7092
|
+
observability,
|
|
7093
|
+
backgroundTasks: new BackgroundTasksStorageClickhouse(domainConfig)
|
|
6650
7094
|
};
|
|
6651
7095
|
}
|
|
6652
7096
|
async optimizeTable({ tableName }) {
|
|
@@ -6705,6 +7149,6 @@ var ClickhouseStore = class extends MastraCompositeStore {
|
|
|
6705
7149
|
}
|
|
6706
7150
|
};
|
|
6707
7151
|
|
|
6708
|
-
export { COLUMN_TYPES, ClickhouseStore, MemoryStorageClickhouse, ObservabilityStorageClickhouse, ObservabilityStorageClickhouseVNext, ScoresStorageClickhouse, TABLE_ENGINES, WorkflowsStorageClickhouse };
|
|
7152
|
+
export { BackgroundTasksStorageClickhouse, COLUMN_TYPES, ClickhouseStore, MemoryStorageClickhouse, ObservabilityStorageClickhouse, ObservabilityStorageClickhouseVNext, ScoresStorageClickhouse, TABLE_ENGINES, WorkflowsStorageClickhouse };
|
|
6709
7153
|
//# sourceMappingURL=index.js.map
|
|
6710
7154
|
//# sourceMappingURL=index.js.map
|