@mastra/duckdb 1.15.0-alpha.2 → 1.15.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/{features-BAGbFa2n.cjs → features-D7U4aB_V.cjs} +25 -1
- package/dist/{features-BAGbFa2n.cjs.map → features-D7U4aB_V.cjs.map} +1 -1
- package/dist/{features-vjVgmb36.js → features-gvUXKO_o.js} +8 -2
- package/dist/{features-vjVgmb36.js.map → features-gvUXKO_o.js.map} +1 -1
- package/dist/index.cjs +10 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +10 -4
- package/dist/index.js.map +1 -1
- package/dist/{observability-HUYFWVy-.js → observability-DWPWPNlL.js} +341 -59
- package/dist/observability-DWPWPNlL.js.map +1 -0
- package/dist/{observability-DJs6G-BH.cjs → observability-TeHp5JpN.cjs} +341 -59
- package/dist/observability-TeHp5JpN.cjs.map +1 -0
- package/dist/storage/domains/observability/index.d.ts +4 -1
- package/dist/storage/domains/observability/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/preview-compat.d.ts +1 -0
- package/dist/storage/domains/observability/preview-compat.d.ts.map +1 -1
- package/dist/storage/domains/observability/span-query.d.ts +1 -0
- package/dist/storage/domains/observability/span-query.d.ts.map +1 -1
- package/dist/storage/domains/observability/thread-query.d.ts +49 -0
- package/dist/storage/domains/observability/thread-query.d.ts.map +1 -0
- package/dist/storage/domains/observability/trace-aggregate.d.ts.map +1 -1
- package/dist/storage/domains/observability/trace-query.d.ts +1 -0
- package/dist/storage/domains/observability/trace-query.d.ts.map +1 -1
- package/dist/storage/features.d.ts +4 -0
- package/dist/storage/features.d.ts.map +1 -1
- package/dist/storage/index.d.ts +4 -2
- package/dist/storage/index.d.ts.map +1 -1
- package/package.json +5 -6
- package/dist/observability-DJs6G-BH.cjs.map +0 -1
- package/dist/observability-HUYFWVy-.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as DuckDBConnection, i as timeToFirstChunkSupported, o as DuckDBQueryTimeoutError, r as timeToFirstChunkFeatures, t as spanQueryFeatures } from "./features-gvUXKO_o.js";
|
|
2
2
|
import { ErrorCategory, ErrorDomain, MastraError } from "@mastra/core/error";
|
|
3
3
|
import * as coreStorage from "@mastra/core/storage";
|
|
4
4
|
import { BRANCH_SPAN_TYPES, METRIC_DISTINCT_COLUMNS, ObservabilityStorage, computeTraceStatus, createStorageErrorId, executeRetentionPrune, feedbackRecordSchema, listBranchesArgsSchema, listFeedbackArgsSchema, listLogsArgsSchema, listMetricsArgsSchema, listScoresArgsSchema, listTracesArgsSchema, resolveRetentionTargets, retentionCutoffMs, toTraceSpans } from "@mastra/core/storage";
|
|
@@ -2876,6 +2876,10 @@ const TRACE_FIELDS = {
|
|
|
2876
2876
|
sql: durationMsSql("r.startedAt", "r.endedAt"),
|
|
2877
2877
|
parameterType: "scalar"
|
|
2878
2878
|
},
|
|
2879
|
+
timeToFirstChunkMs: {
|
|
2880
|
+
sql: "r.timeToFirstChunkMs",
|
|
2881
|
+
parameterType: "scalar"
|
|
2882
|
+
},
|
|
2879
2883
|
entityName: {
|
|
2880
2884
|
sql: "r.entityName",
|
|
2881
2885
|
parameterType: "scalar"
|
|
@@ -2884,6 +2888,14 @@ const TRACE_FIELDS = {
|
|
|
2884
2888
|
sql: "r.entityType",
|
|
2885
2889
|
parameterType: "scalar"
|
|
2886
2890
|
},
|
|
2891
|
+
name: {
|
|
2892
|
+
sql: "r.name",
|
|
2893
|
+
parameterType: "scalar"
|
|
2894
|
+
},
|
|
2895
|
+
spanType: {
|
|
2896
|
+
sql: "r.spanType",
|
|
2897
|
+
parameterType: "scalar"
|
|
2898
|
+
},
|
|
2887
2899
|
environment: {
|
|
2888
2900
|
sql: "r.environment",
|
|
2889
2901
|
parameterType: "scalar"
|
|
@@ -2926,6 +2938,10 @@ const SPAN_FIELDS = {
|
|
|
2926
2938
|
sql: "s.durationMs",
|
|
2927
2939
|
parameterType: "scalar"
|
|
2928
2940
|
},
|
|
2941
|
+
timeToFirstChunkMs: {
|
|
2942
|
+
sql: "s.timeToFirstChunkMs",
|
|
2943
|
+
parameterType: "scalar"
|
|
2944
|
+
},
|
|
2929
2945
|
status: {
|
|
2930
2946
|
sql: "s.status",
|
|
2931
2947
|
parameterType: "scalar"
|
|
@@ -3135,6 +3151,7 @@ const TRACE_SELECT = `
|
|
|
3135
3151
|
r.resourceId AS resourceId,
|
|
3136
3152
|
r.startedAt AS startedAt,
|
|
3137
3153
|
r.endedAt AS endedAt,
|
|
3154
|
+
r.timeToFirstChunkMs AS timeToFirstChunkMs,
|
|
3138
3155
|
r.entityName AS entityName,
|
|
3139
3156
|
r.entityType AS entityType,
|
|
3140
3157
|
r.environment AS environment,
|
|
@@ -3370,8 +3387,11 @@ const ROOT_PUSHDOWN_FIELDS = /* @__PURE__ */ new Set([
|
|
|
3370
3387
|
"userId",
|
|
3371
3388
|
"organizationId",
|
|
3372
3389
|
"endedAt",
|
|
3390
|
+
"timeToFirstChunkMs",
|
|
3373
3391
|
"entityName",
|
|
3374
3392
|
"entityType",
|
|
3393
|
+
"name",
|
|
3394
|
+
"spanType",
|
|
3375
3395
|
"environment"
|
|
3376
3396
|
]);
|
|
3377
3397
|
function isRootPushdownPredicate(predicate) {
|
|
@@ -3497,6 +3517,7 @@ function compileDuckDBTraceScope(relatedCollections, scope, timeRange, rootPredi
|
|
|
3497
3517
|
startedAt,
|
|
3498
3518
|
endedAt,
|
|
3499
3519
|
${durationMsSql("startedAt", "endedAt")} AS durationMs,
|
|
3520
|
+
timeToFirstChunkMs,
|
|
3500
3521
|
CASE WHEN error IS NOT NULL THEN 'error' ELSE 'success' END AS status,
|
|
3501
3522
|
error,
|
|
3502
3523
|
entityType,
|
|
@@ -3596,15 +3617,15 @@ ORDER BY delta_rows.deltaWatermark ASC NULLS LAST, delta_rows.traceId ASC`,
|
|
|
3596
3617
|
};
|
|
3597
3618
|
}
|
|
3598
3619
|
const orderField = plan.orderBy.field;
|
|
3599
|
-
const
|
|
3620
|
+
const order = `${orderField} ${plan.orderBy.direction === "asc" ? "ASC" : "DESC"} NULLS LAST, traceId ASC`;
|
|
3600
3621
|
if (plan.paginationMode === "page") {
|
|
3601
3622
|
values.push(plan.perPage, plan.page * plan.perPage);
|
|
3602
3623
|
return {
|
|
3603
3624
|
sql: `${candidates},
|
|
3604
3625
|
page_rows AS (
|
|
3605
|
-
SELECT *, row_number() OVER (ORDER BY ${
|
|
3626
|
+
SELECT *, row_number() OVER (ORDER BY ${order}) AS __row_position
|
|
3606
3627
|
FROM candidates
|
|
3607
|
-
ORDER BY ${
|
|
3628
|
+
ORDER BY ${order}
|
|
3608
3629
|
LIMIT ? OFFSET ?
|
|
3609
3630
|
),
|
|
3610
3631
|
page_total AS (
|
|
@@ -3619,8 +3640,13 @@ ORDER BY page_rows.__row_position ASC NULLS LAST`,
|
|
|
3619
3640
|
};
|
|
3620
3641
|
}
|
|
3621
3642
|
let pageCondition = "";
|
|
3622
|
-
if (plan.cursor) {
|
|
3623
|
-
pageCondition = `WHERE
|
|
3643
|
+
if (plan.cursor?.sortValue === null) {
|
|
3644
|
+
pageCondition = `WHERE ${orderField} IS NULL AND traceId > ?`;
|
|
3645
|
+
values.push(plan.cursor.traceId);
|
|
3646
|
+
} else if (plan.cursor) {
|
|
3647
|
+
const comparison = plan.orderBy.direction === "asc" ? ">" : "<";
|
|
3648
|
+
const value = `CAST(? AS ${typeof plan.cursor.sortValue === "number" ? "DOUBLE" : "TIMESTAMP"})`;
|
|
3649
|
+
pageCondition = `WHERE (${orderField} ${comparison} ${value} OR ${orderField} IS NULL OR (${orderField} = ${value} AND traceId > ?))`;
|
|
3624
3650
|
values.push(plan.cursor.sortValue, plan.cursor.sortValue, plan.cursor.traceId);
|
|
3625
3651
|
}
|
|
3626
3652
|
values.push(plan.limit + 1);
|
|
@@ -3629,7 +3655,7 @@ ORDER BY page_rows.__row_position ASC NULLS LAST`,
|
|
|
3629
3655
|
SELECT *
|
|
3630
3656
|
FROM candidates
|
|
3631
3657
|
${pageCondition}
|
|
3632
|
-
ORDER BY ${
|
|
3658
|
+
ORDER BY ${order}
|
|
3633
3659
|
LIMIT ?`,
|
|
3634
3660
|
values
|
|
3635
3661
|
};
|
|
@@ -3812,6 +3838,29 @@ async function fillMissingTracePreviews(db, rows) {
|
|
|
3812
3838
|
for (const [preview, column] of TRACE_PREVIEW_PAYLOADS) if (needsPreview(row[preview])) row[preview] = previewFromPayload(payload?.[column] ?? null);
|
|
3813
3839
|
}
|
|
3814
3840
|
}
|
|
3841
|
+
function traceFromRow(row) {
|
|
3842
|
+
return {
|
|
3843
|
+
traceId: String(row.traceId),
|
|
3844
|
+
rootSpanId: String(row.rootSpanId),
|
|
3845
|
+
name: row.name,
|
|
3846
|
+
entityId: row.entityId ?? null,
|
|
3847
|
+
parentSpanId: row.parentSpanId ?? null,
|
|
3848
|
+
createdAt: asIsoTimestamp(row.startedAt),
|
|
3849
|
+
metadata: parseJson(row.metadata) ?? null,
|
|
3850
|
+
inputPreview: apiPreview(row.inputPreview),
|
|
3851
|
+
outputPreview: apiPreview(row.outputPreview),
|
|
3852
|
+
errorPreview: apiPreview(row.errorPreview),
|
|
3853
|
+
threadId: row.threadId == null ? null : String(row.threadId),
|
|
3854
|
+
resourceId: row.resourceId == null ? null : String(row.resourceId),
|
|
3855
|
+
startedAt: asIsoTimestamp(row.startedAt),
|
|
3856
|
+
endedAt: asIsoTimestamp(row.endedAt),
|
|
3857
|
+
entityName: row.entityName == null ? null : String(row.entityName),
|
|
3858
|
+
entityType: row.entityType == null ? null : String(row.entityType),
|
|
3859
|
+
environment: row.environment == null ? null : String(row.environment),
|
|
3860
|
+
status: row.status,
|
|
3861
|
+
...timeToFirstChunkSupported && { timeToFirstChunkMs: row.timeToFirstChunkMs == null ? null : Number(row.timeToFirstChunkMs) }
|
|
3862
|
+
};
|
|
3863
|
+
}
|
|
3815
3864
|
async function queryTraces(db, plan) {
|
|
3816
3865
|
if (plan.paginationMode === "delta") assertDeltaPollingEnabled();
|
|
3817
3866
|
if (plan.paginationMode === "page") {
|
|
@@ -3819,26 +3868,7 @@ async function queryTraces(db, plan) {
|
|
|
3819
3868
|
const rows = await db.query(query.sql, query.values);
|
|
3820
3869
|
const total = Number(rows[0]?.total ?? 0);
|
|
3821
3870
|
await fillMissingTracePreviews(db, rows);
|
|
3822
|
-
const traces = rows.filter((row) => row.traceId != null).map(
|
|
3823
|
-
traceId: String(row.traceId),
|
|
3824
|
-
rootSpanId: String(row.rootSpanId),
|
|
3825
|
-
name: row.name,
|
|
3826
|
-
entityId: row.entityId ?? null,
|
|
3827
|
-
parentSpanId: row.parentSpanId ?? null,
|
|
3828
|
-
createdAt: asIsoTimestamp(row.startedAt),
|
|
3829
|
-
metadata: parseJson(row.metadata) ?? null,
|
|
3830
|
-
inputPreview: apiPreview(row.inputPreview),
|
|
3831
|
-
outputPreview: apiPreview(row.outputPreview),
|
|
3832
|
-
errorPreview: apiPreview(row.errorPreview),
|
|
3833
|
-
threadId: row.threadId == null ? null : String(row.threadId),
|
|
3834
|
-
resourceId: row.resourceId == null ? null : String(row.resourceId),
|
|
3835
|
-
startedAt: asIsoTimestamp(row.startedAt),
|
|
3836
|
-
endedAt: asIsoTimestamp(row.endedAt),
|
|
3837
|
-
entityName: row.entityName == null ? null : String(row.entityName),
|
|
3838
|
-
entityType: row.entityType == null ? null : String(row.entityType),
|
|
3839
|
-
environment: row.environment == null ? null : String(row.environment),
|
|
3840
|
-
status: row.status
|
|
3841
|
-
}));
|
|
3871
|
+
const traces = rows.filter((row) => row.traceId != null).map(traceFromRow);
|
|
3842
3872
|
return coreStorage.traceQueryResponseSchema.parse({
|
|
3843
3873
|
traces,
|
|
3844
3874
|
...deltaPollingFeatureEnabled() && typeof coreStorage.encodeTraceQueryDeltaCursor === "function" ? { deltaCursor: coreStorage.encodeTraceQueryDeltaCursor(plan, "duckdb", String(rows[0]?.streamHead ?? 0)) } : {},
|
|
@@ -3866,26 +3896,7 @@ async function queryTraces(db, plan) {
|
|
|
3866
3896
|
});
|
|
3867
3897
|
}
|
|
3868
3898
|
await fillMissingTracePreviews(db, visibleRows);
|
|
3869
|
-
const traces = visibleRows.map(
|
|
3870
|
-
traceId: String(row.traceId),
|
|
3871
|
-
rootSpanId: String(row.rootSpanId),
|
|
3872
|
-
name: row.name,
|
|
3873
|
-
entityId: row.entityId ?? null,
|
|
3874
|
-
parentSpanId: row.parentSpanId ?? null,
|
|
3875
|
-
createdAt: asIsoTimestamp(row.startedAt),
|
|
3876
|
-
metadata: parseJson(row.metadata) ?? null,
|
|
3877
|
-
inputPreview: apiPreview(row.inputPreview),
|
|
3878
|
-
outputPreview: apiPreview(row.outputPreview),
|
|
3879
|
-
errorPreview: apiPreview(row.errorPreview),
|
|
3880
|
-
threadId: row.threadId == null ? null : String(row.threadId),
|
|
3881
|
-
resourceId: row.resourceId == null ? null : String(row.resourceId),
|
|
3882
|
-
startedAt: asIsoTimestamp(row.startedAt),
|
|
3883
|
-
endedAt: asIsoTimestamp(row.endedAt),
|
|
3884
|
-
entityName: row.entityName == null ? null : String(row.entityName),
|
|
3885
|
-
entityType: row.entityType == null ? null : String(row.entityType),
|
|
3886
|
-
environment: row.environment == null ? null : String(row.environment),
|
|
3887
|
-
status: row.status
|
|
3888
|
-
}));
|
|
3899
|
+
const traces = visibleRows.map(traceFromRow);
|
|
3889
3900
|
if (plan.paginationMode === "delta") {
|
|
3890
3901
|
const previous = coreStorage.getTraceQueryDeltaWatermark(plan, "duckdb") ?? "0";
|
|
3891
3902
|
const head = String(rows[0]?.streamHead ?? 0);
|
|
@@ -3904,7 +3915,7 @@ async function queryTraces(db, plan) {
|
|
|
3904
3915
|
traces,
|
|
3905
3916
|
page: { next: rows.length > plan.limit && last ? coreStorage.encodeTraceQueryCursor(plan, {
|
|
3906
3917
|
result: "traces",
|
|
3907
|
-
sortValue: last[plan.orderBy.field],
|
|
3918
|
+
sortValue: last[plan.orderBy.field] ?? null,
|
|
3908
3919
|
traceId: last.traceId
|
|
3909
3920
|
}) : null }
|
|
3910
3921
|
});
|
|
@@ -3926,6 +3937,12 @@ async function queryThreads(db, plan) {
|
|
|
3926
3937
|
function compileSpanQueryPredicate(predicate) {
|
|
3927
3938
|
return compileScalarPredicate(predicate, SPAN_QUERY_FIELDS);
|
|
3928
3939
|
}
|
|
3940
|
+
function compileThreadQueryPredicate(predicate) {
|
|
3941
|
+
return compileScalarPredicate(predicate, Object.fromEntries(Object.entries(coreStorage.THREAD_QUERY_FIELD_REGISTRY).map(([field, rule]) => [field, {
|
|
3942
|
+
sql: `s.${field}`,
|
|
3943
|
+
parameterType: rule.valueKind === "timestamp" ? "timestamp" : "scalar"
|
|
3944
|
+
}])));
|
|
3945
|
+
}
|
|
3929
3946
|
//#endregion
|
|
3930
3947
|
//#region src/storage/domains/observability/span-query.ts
|
|
3931
3948
|
const identityFields = [
|
|
@@ -3969,7 +3986,8 @@ const fields = [
|
|
|
3969
3986
|
"serviceName",
|
|
3970
3987
|
"experimentId",
|
|
3971
3988
|
"environment",
|
|
3972
|
-
"executionSource"
|
|
3989
|
+
"executionSource",
|
|
3990
|
+
"timeToFirstChunkMs"
|
|
3973
3991
|
];
|
|
3974
3992
|
function keys(rows, values, alias = "") {
|
|
3975
3993
|
return rows.map((row) => `(${identityFields.map((field) => {
|
|
@@ -3983,13 +4001,16 @@ function afterCursor(plan) {
|
|
|
3983
4001
|
values: []
|
|
3984
4002
|
};
|
|
3985
4003
|
const cursor = plan.cursor;
|
|
3986
|
-
const
|
|
3987
|
-
const
|
|
4004
|
+
const sort = `s.${plan.orderBy.field}`;
|
|
4005
|
+
const empty = cursor.sortValue === null;
|
|
4006
|
+
const value = `CAST(? AS ${typeof cursor.sortValue === "number" ? "DOUBLE" : "TIMESTAMP"})`;
|
|
4007
|
+
const comparisons = empty ? [] : [`${sort} ${plan.orderBy.direction === "asc" ? ">" : "<"} ${value}`, `${sort} IS NULL`];
|
|
4008
|
+
const values = empty ? [] : [cursor.sortValue];
|
|
3988
4009
|
for (let i = 0; i < identityFields.length; i++) {
|
|
3989
4010
|
const field = identityFields[i];
|
|
3990
4011
|
if (cursor[field] === null) continue;
|
|
3991
|
-
const equal = [`
|
|
3992
|
-
values.push(cursor.sortValue);
|
|
4012
|
+
const equal = [empty ? `${sort} IS NULL` : `${sort} = ${value}`];
|
|
4013
|
+
if (!empty) values.push(cursor.sortValue);
|
|
3993
4014
|
for (const prior of identityFields.slice(0, i)) {
|
|
3994
4015
|
equal.push(`s.${prior} IS NOT DISTINCT FROM ?`);
|
|
3995
4016
|
values.push(cursor[prior]);
|
|
@@ -4048,7 +4069,7 @@ function compileDuckDBSpanQuery(plan) {
|
|
|
4048
4069
|
"cursorId"
|
|
4049
4070
|
].map((field) => `s.${field}`).join(", ")}
|
|
4050
4071
|
FROM ranked s WHERE rank = 1 AND endedAt IS NOT NULL AND startedAt >= CAST(? AS TIMESTAMP) AND startedAt < CAST(? AS TIMESTAMP) AND (${predicate.sql}) AND ${after.sql}
|
|
4051
|
-
ORDER BY s.${plan.orderBy.field} ${plan.orderBy.direction === "asc" ? "ASC" : "DESC"}, ${identityFields.map((field) => `s.${field} ASC NULLS LAST`).join(", ")}
|
|
4072
|
+
ORDER BY s.${plan.orderBy.field} ${plan.orderBy.direction === "asc" ? "ASC" : "DESC"} NULLS LAST, ${identityFields.map((field) => `s.${field} ASC NULLS LAST`).join(", ")}
|
|
4052
4073
|
LIMIT ?`,
|
|
4053
4074
|
values
|
|
4054
4075
|
};
|
|
@@ -4073,10 +4094,11 @@ async function querySpans(db, plan) {
|
|
|
4073
4094
|
};
|
|
4074
4095
|
const compiled = compileDuckDBSpanQuery(plan);
|
|
4075
4096
|
const rows = await query(compiled.sql, compiled.values);
|
|
4076
|
-
const selected = rows.map(({ cursorId: _cursorId, startedAt, endedAt, ...row }) => ({
|
|
4097
|
+
const selected = rows.map(({ cursorId: _cursorId, startedAt, endedAt, timeToFirstChunkMs, ...row }) => ({
|
|
4077
4098
|
...row,
|
|
4078
4099
|
startedAt: startedAt.toISOString(),
|
|
4079
|
-
endedAt: endedAt.toISOString()
|
|
4100
|
+
endedAt: endedAt.toISOString(),
|
|
4101
|
+
...timeToFirstChunkSupported && { timeToFirstChunkMs: timeToFirstChunkMs == null ? null : Number(timeToFirstChunkMs) }
|
|
4080
4102
|
}));
|
|
4081
4103
|
const page = rows.slice(0, plan.limit);
|
|
4082
4104
|
if (!page.length) return buildSpanQueryResponse(plan, [], []);
|
|
@@ -4127,11 +4149,264 @@ async function querySpans(db, plan) {
|
|
|
4127
4149
|
return buildSpanQueryResponse(plan, hydrated, payloads, metrics);
|
|
4128
4150
|
}
|
|
4129
4151
|
//#endregion
|
|
4152
|
+
//#region src/storage/domains/observability/thread-query.ts
|
|
4153
|
+
const contextFields = [
|
|
4154
|
+
"resourceId",
|
|
4155
|
+
"userId",
|
|
4156
|
+
"entityType",
|
|
4157
|
+
"entityId",
|
|
4158
|
+
"entityName",
|
|
4159
|
+
"entityVersionId",
|
|
4160
|
+
"runId",
|
|
4161
|
+
"sessionId",
|
|
4162
|
+
"requestId",
|
|
4163
|
+
"serviceName",
|
|
4164
|
+
"experimentId",
|
|
4165
|
+
"environment",
|
|
4166
|
+
"executionSource"
|
|
4167
|
+
];
|
|
4168
|
+
function predicateFields(predicate, fields = /* @__PURE__ */ new Set()) {
|
|
4169
|
+
if (!predicate) return fields;
|
|
4170
|
+
if (predicate.type === "boolean") for (const arg of predicate.args) predicateFields(arg, fields);
|
|
4171
|
+
else if (predicate.type === "not") predicateFields(predicate.arg, fields);
|
|
4172
|
+
else fields.add(predicate.field);
|
|
4173
|
+
return fields;
|
|
4174
|
+
}
|
|
4175
|
+
function canUseThreadCandidates(probe) {
|
|
4176
|
+
return Number(probe.threadCount) <= 16384 && Number(probe.traceCount) <= 1e5 && Number(probe.threadBytes) + Number(probe.traceBytes) <= 4 * 1024 * 1024;
|
|
4177
|
+
}
|
|
4178
|
+
function compileDuckDBThreadRowsQuery(plan, strategy = "full") {
|
|
4179
|
+
const values = [];
|
|
4180
|
+
const scope = [];
|
|
4181
|
+
if (plan.scope) {
|
|
4182
|
+
values.push(plan.scope.organizationId);
|
|
4183
|
+
scope.push("organizationId = ?");
|
|
4184
|
+
if (plan.scope.resourceId !== void 0) {
|
|
4185
|
+
values.push(plan.scope.resourceId);
|
|
4186
|
+
scope.push("resourceId = ?");
|
|
4187
|
+
}
|
|
4188
|
+
}
|
|
4189
|
+
const identity = (field) => `(organizationId IS NULL, COALESCE(organizationId, ''), ${field})`;
|
|
4190
|
+
const threadFilter = `${identity("threadId")} IN (SELECT ${identity("threadId")} FROM candidate_threads)`;
|
|
4191
|
+
const endWindow = plan.timeRange.mode === "threads" && plan.timeRange.field === "endedAt";
|
|
4192
|
+
const scopedEvents = `scoped_events AS NOT MATERIALIZED (
|
|
4193
|
+
SELECT * FROM span_events WHERE parentSpanId IS NULL ${scope.length ? `AND ${scope.join(" AND ")}` : ""}
|
|
4194
|
+
)`;
|
|
4195
|
+
const candidates = `window_traces AS MATERIALIZED (
|
|
4196
|
+
SELECT DISTINCT organizationId, traceId FROM scoped_events
|
|
4197
|
+
WHERE ${endWindow ? "endedAt" : "timestamp"} >= CAST(? AS TIMESTAMP) AND ${endWindow ? "endedAt" : "timestamp"} < CAST(? AS TIMESTAMP)
|
|
4198
|
+
${endWindow ? "" : "AND eventType = 'start'"}
|
|
4199
|
+
), candidate_threads AS MATERIALIZED (
|
|
4200
|
+
SELECT DISTINCT organizationId, threadId FROM scoped_events
|
|
4201
|
+
WHERE threadId IS NOT NULL AND ${identity("traceId")} IN (SELECT ${identity("traceId")} FROM window_traces)
|
|
4202
|
+
${strategy === "probe" ? "LIMIT 16385" : ""}
|
|
4203
|
+
), candidate_traces AS MATERIALIZED (
|
|
4204
|
+
SELECT DISTINCT organizationId, traceId FROM scoped_events WHERE ${threadFilter}
|
|
4205
|
+
${strategy === "probe" ? "LIMIT 100001" : ""}
|
|
4206
|
+
)`;
|
|
4207
|
+
if (strategy !== "full") values.push(plan.timeRange.from, plan.timeRange.to);
|
|
4208
|
+
if (strategy === "probe") return {
|
|
4209
|
+
sql: `WITH ${scopedEvents}, ${candidates}
|
|
4210
|
+
SELECT (SELECT count(*) FROM candidate_threads) AS threadCount,
|
|
4211
|
+
(SELECT COALESCE(sum(octet_length(encode(COALESCE(organizationId, ''))) + octet_length(encode(threadId))), 0) FROM candidate_threads) AS threadBytes,
|
|
4212
|
+
count(*) AS traceCount, COALESCE(sum(octet_length(encode(COALESCE(organizationId, ''))) + octet_length(encode(traceId))), 0) AS traceBytes
|
|
4213
|
+
FROM candidate_traces`,
|
|
4214
|
+
values
|
|
4215
|
+
};
|
|
4216
|
+
values.push(plan.timeRange.from, plan.timeRange.to);
|
|
4217
|
+
const qualify = plan.timeRange.mode === "traces" ? "s.inWindow" : `s.${plan.timeRange.field} >= CAST(? AS TIMESTAMP) AND s.${plan.timeRange.field} < CAST(? AS TIMESTAMP)`;
|
|
4218
|
+
if (plan.timeRange.mode === "threads") values.push(plan.timeRange.from, plan.timeRange.to);
|
|
4219
|
+
const predicate = plan.where ? compileThreadQueryPredicate(plan.where) : {
|
|
4220
|
+
sql: "TRUE",
|
|
4221
|
+
values: []
|
|
4222
|
+
};
|
|
4223
|
+
values.push(...predicate.values);
|
|
4224
|
+
let after = "TRUE";
|
|
4225
|
+
if (plan.cursor) {
|
|
4226
|
+
const cursor = plan.cursor;
|
|
4227
|
+
const terms = [`s.${plan.orderBy.field} ${plan.orderBy.direction === "asc" ? ">" : "<"} CAST(? AS TIMESTAMP)`];
|
|
4228
|
+
values.push(cursor.sortValue);
|
|
4229
|
+
const ids = ["organizationId", "threadId"];
|
|
4230
|
+
for (let i = 0; i < ids.length; i++) {
|
|
4231
|
+
const field = ids[i];
|
|
4232
|
+
if (cursor[field] === null) continue;
|
|
4233
|
+
const equal = [`s.${plan.orderBy.field} = CAST(? AS TIMESTAMP)`];
|
|
4234
|
+
values.push(cursor.sortValue);
|
|
4235
|
+
for (const prior of ids.slice(0, i)) {
|
|
4236
|
+
equal.push(`s.${prior} IS NOT DISTINCT FROM ?`);
|
|
4237
|
+
values.push(cursor[prior]);
|
|
4238
|
+
}
|
|
4239
|
+
values.push(cursor[field]);
|
|
4240
|
+
terms.push(`(${equal.join(" AND ")} AND (s.${field} > ? OR s.${field} IS NULL))`);
|
|
4241
|
+
}
|
|
4242
|
+
after = `(${terms.join(" OR ")})`;
|
|
4243
|
+
}
|
|
4244
|
+
values.push(plan.limit + 1);
|
|
4245
|
+
const filtered = predicateFields(plan.where);
|
|
4246
|
+
const firstContext = [...contextFields, "timeToFirstChunkMs"].filter((field) => field === "resourceId" || filtered.has(field));
|
|
4247
|
+
const contextExpression = (field) => field === "executionSource" ? "source AS executionSource" : field === "timeToFirstChunkMs" ? "CASE WHEN spanType = 'agent_run' THEN timeToFirstChunkMs END AS timeToFirstChunkMs" : field;
|
|
4248
|
+
const lastFields = [
|
|
4249
|
+
"traceId",
|
|
4250
|
+
"spanId",
|
|
4251
|
+
"cursorId",
|
|
4252
|
+
"resourceId",
|
|
4253
|
+
"endedAt",
|
|
4254
|
+
...filtered.has("status") ? ["status"] : []
|
|
4255
|
+
];
|
|
4256
|
+
return {
|
|
4257
|
+
sql: `WITH ${scopedEvents}, ${strategy === "candidates" ? `${candidates}, ` : ""}root_events AS (
|
|
4258
|
+
SELECT organizationId, threadId, traceId, spanId, cursorId, endedAt,
|
|
4259
|
+
${firstContext.map(contextExpression).join(", ")},
|
|
4260
|
+
min(timestamp) FILTER (WHERE eventType = 'start') OVER (PARTITION BY organizationId, traceId, spanId) AS startedAt
|
|
4261
|
+
${filtered.has("status") ? ", CASE WHEN error IS NULL THEN 'success' ELSE 'error' END AS status" : ""}
|
|
4262
|
+
FROM scoped_events ${strategy === "candidates" ? `WHERE ${identity("traceId")} IN (SELECT ${identity("traceId")} FROM candidate_traces)` : ""}
|
|
4263
|
+
), current_roots AS (
|
|
4264
|
+
SELECT *, row_number() OVER (PARTITION BY organizationId, traceId ORDER BY startedAt ASC, spanId, cursorId DESC) AS rootRank
|
|
4265
|
+
FROM root_events
|
|
4266
|
+
), ranked AS (
|
|
4267
|
+
SELECT *, row_number() OVER first_order AS rank,
|
|
4268
|
+
max(endedAt) OVER population AS lastActivity, count(*) OVER population AS traceCount,
|
|
4269
|
+
bool_or(startedAt >= CAST(? AS TIMESTAMP) AND startedAt < CAST(? AS TIMESTAMP)) OVER population AS inWindow,
|
|
4270
|
+
${lastFields.map((field) => `first_value(${field}) OVER last_order AS last${field[0].toUpperCase()}${field.slice(1)}`).join(", ")}
|
|
4271
|
+
FROM current_roots WHERE rootRank = 1 AND endedAt IS NOT NULL AND startedAt IS NOT NULL AND threadId IS NOT NULL ${strategy === "candidates" ? `AND ${threadFilter}` : ""}
|
|
4272
|
+
WINDOW population AS (PARTITION BY organizationId, threadId),
|
|
4273
|
+
first_order AS (PARTITION BY organizationId, threadId ORDER BY startedAt, traceId),
|
|
4274
|
+
last_order AS (PARTITION BY organizationId, threadId ORDER BY startedAt DESC, traceId DESC)
|
|
4275
|
+
), rows AS (
|
|
4276
|
+
SELECT organizationId, threadId, traceId AS firstTraceId, spanId AS firstSpanId, cursorId AS firstCursorId,
|
|
4277
|
+
endedAt AS firstEndedAt, startedAt, lastActivity AS endedAt, traceCount, inWindow,
|
|
4278
|
+
${firstContext.join(", ")},
|
|
4279
|
+
${lastFields.map((field) => `last${field[0].toUpperCase()}${field.slice(1)}${field === "status" ? " AS status" : ""}`).join(", ")}
|
|
4280
|
+
FROM ranked WHERE rank = 1
|
|
4281
|
+
) SELECT * FROM rows s WHERE ${qualify} AND (${predicate.sql}) AND ${after}
|
|
4282
|
+
ORDER BY s.${plan.orderBy.field} ${plan.orderBy.direction === "asc" ? "ASC" : "DESC"}, s.organizationId ASC NULLS LAST, s.threadId ASC LIMIT ?`,
|
|
4283
|
+
values
|
|
4284
|
+
};
|
|
4285
|
+
}
|
|
4286
|
+
async function queryThreadRows(db, plan, strategy = "auto") {
|
|
4287
|
+
const deadline = performance.now() + coreStorage.TRACE_QUERY_DEFAULT_TIMEOUT_MS;
|
|
4288
|
+
const run = async (sql, values) => {
|
|
4289
|
+
const remaining = Math.floor(deadline - performance.now());
|
|
4290
|
+
if (remaining <= 0) throw new coreStorage.TraceQueryExecutionError();
|
|
4291
|
+
try {
|
|
4292
|
+
return await db.query(sql, values, remaining);
|
|
4293
|
+
} catch (error) {
|
|
4294
|
+
if (error instanceof DuckDBQueryTimeoutError) throw new coreStorage.TraceQueryExecutionError();
|
|
4295
|
+
if (error instanceof Error && /out of memory/i.test(error.message)) throw new coreStorage.TraceQueryResourceLimitError();
|
|
4296
|
+
throw error;
|
|
4297
|
+
}
|
|
4298
|
+
};
|
|
4299
|
+
if (strategy === "auto") {
|
|
4300
|
+
const probeQuery = compileDuckDBThreadRowsQuery(plan, "probe");
|
|
4301
|
+
const [probe] = await run(probeQuery.sql, probeQuery.values);
|
|
4302
|
+
if (Number(probe.threadCount) === 0) return {
|
|
4303
|
+
threads: [],
|
|
4304
|
+
page: { next: null }
|
|
4305
|
+
};
|
|
4306
|
+
strategy = canUseThreadCandidates(probe) ? "candidates" : "full";
|
|
4307
|
+
}
|
|
4308
|
+
const compiled = compileDuckDBThreadRowsQuery(plan, strategy);
|
|
4309
|
+
const selected = await run(compiled.sql, compiled.values);
|
|
4310
|
+
const page = selected.slice(0, plan.limit);
|
|
4311
|
+
if (!page.length) return {
|
|
4312
|
+
threads: [],
|
|
4313
|
+
page: { next: null }
|
|
4314
|
+
};
|
|
4315
|
+
const values = [];
|
|
4316
|
+
const keys = page.flatMap((row) => ["first", "last"].map((which) => {
|
|
4317
|
+
const entries = {
|
|
4318
|
+
organizationId: row.organizationId,
|
|
4319
|
+
resourceId: which === "first" ? row.resourceId : row.lastResourceId,
|
|
4320
|
+
traceId: row[`${which}TraceId`],
|
|
4321
|
+
spanId: row[`${which}SpanId`],
|
|
4322
|
+
cursorId: row[`${which}CursorId`],
|
|
4323
|
+
endedAt: row[`${which}EndedAt`]
|
|
4324
|
+
};
|
|
4325
|
+
return `(${Object.entries(entries).map(([field, value]) => {
|
|
4326
|
+
values.push(value);
|
|
4327
|
+
return `${field} ${field === "organizationId" || field === "resourceId" ? "IS NOT DISTINCT FROM" : "="} ?`;
|
|
4328
|
+
}).join(" AND ")})`;
|
|
4329
|
+
}));
|
|
4330
|
+
const payloads = await run(`SELECT organizationId, traceId, cursorId, spanType,
|
|
4331
|
+
${contextFields.map((field) => field === "executionSource" ? "source AS executionSource" : field).join(", ")},
|
|
4332
|
+
CASE WHEN spanType = 'agent_run' THEN timeToFirstChunkMs END AS timeToFirstChunkMs,
|
|
4333
|
+
CASE WHEN error IS NULL THEN 'success' ELSE 'error' END AS status,
|
|
4334
|
+
${[
|
|
4335
|
+
"input",
|
|
4336
|
+
"output",
|
|
4337
|
+
"error"
|
|
4338
|
+
].map((field) => `${field}Preview, CASE WHEN ${missingPreviewSql(`${field}Preview`)} THEN CAST(${field === "error" ? field : payloadColumnSql(field)} AS VARCHAR) END AS ${field}`).join(", ")}
|
|
4339
|
+
FROM span_events WHERE ${keys.join(" OR ")}`, values);
|
|
4340
|
+
const key = (org, trace, version) => JSON.stringify([
|
|
4341
|
+
org,
|
|
4342
|
+
trace,
|
|
4343
|
+
String(version)
|
|
4344
|
+
]);
|
|
4345
|
+
const byRoot = new Map(payloads.map((row) => [key(row.organizationId, row.traceId, row.cursorId), row]));
|
|
4346
|
+
const agents = page.filter((row) => byRoot.get(key(row.organizationId, row.firstTraceId, row.firstCursorId))?.spanType === "agent_run");
|
|
4347
|
+
const models = /* @__PURE__ */ new Map();
|
|
4348
|
+
if (agents.length) {
|
|
4349
|
+
const modelValues = [];
|
|
4350
|
+
const rows = await run(`WITH generations AS (
|
|
4351
|
+
SELECT organizationId, traceId, spanId, endedAt, cursorId, attributes,
|
|
4352
|
+
min(timestamp) FILTER (WHERE eventType = 'start') OVER (PARTITION BY organizationId, traceId, spanId) AS startedAt
|
|
4353
|
+
FROM span_events WHERE spanType = 'model_generation' AND (${agents.map((row) => `(${Object.entries({
|
|
4354
|
+
organizationId: row.organizationId,
|
|
4355
|
+
resourceId: row.resourceId,
|
|
4356
|
+
traceId: row.firstTraceId,
|
|
4357
|
+
parentSpanId: row.firstSpanId
|
|
4358
|
+
}).map(([field, value]) => {
|
|
4359
|
+
modelValues.push(value);
|
|
4360
|
+
return `${field} ${field === "organizationId" || field === "resourceId" ? "IS NOT DISTINCT FROM" : "="} ?`;
|
|
4361
|
+
}).join(" AND ")})`).join(" OR ")})
|
|
4362
|
+
) SELECT organizationId, traceId,
|
|
4363
|
+
CASE WHEN json_type(attributes, '$.model') = 'VARCHAR' THEN json_extract_string(attributes, '$.model') END AS model,
|
|
4364
|
+
CASE WHEN json_type(attributes, '$.provider') = 'VARCHAR' THEN json_extract_string(attributes, '$.provider') END AS provider
|
|
4365
|
+
FROM generations WHERE endedAt IS NOT NULL
|
|
4366
|
+
QUALIFY row_number() OVER (PARTITION BY organizationId, traceId ORDER BY startedAt, spanId, cursorId DESC) = 1`, modelValues);
|
|
4367
|
+
for (const row of rows) models.set(JSON.stringify([row.organizationId, row.traceId]), row);
|
|
4368
|
+
}
|
|
4369
|
+
const threads = page.flatMap((row) => {
|
|
4370
|
+
const first = byRoot.get(key(row.organizationId, row.firstTraceId, row.firstCursorId));
|
|
4371
|
+
const last = byRoot.get(key(row.organizationId, row.lastTraceId, row.lastCursorId));
|
|
4372
|
+
if (!first || !last) return [];
|
|
4373
|
+
const model = models.get(JSON.stringify([row.organizationId, row.firstTraceId]));
|
|
4374
|
+
return [coreStorage.threadQueryRowSchema.parse({
|
|
4375
|
+
organizationId: row.organizationId,
|
|
4376
|
+
threadId: row.threadId,
|
|
4377
|
+
firstTraceId: row.firstTraceId,
|
|
4378
|
+
lastTraceId: row.lastTraceId,
|
|
4379
|
+
...Object.fromEntries(contextFields.map((field) => [field, first[field]])),
|
|
4380
|
+
status: last.status,
|
|
4381
|
+
startedAt: row.startedAt.toISOString(),
|
|
4382
|
+
endedAt: row.endedAt.toISOString(),
|
|
4383
|
+
traceCount: Number(row.traceCount),
|
|
4384
|
+
timeToFirstChunkMs: first.timeToFirstChunkMs,
|
|
4385
|
+
model: model?.model ?? null,
|
|
4386
|
+
provider: model?.provider ?? null,
|
|
4387
|
+
inputPreview: readPreview(first.inputPreview, first.input, true),
|
|
4388
|
+
outputPreview: readPreview(last.outputPreview, last.output, true),
|
|
4389
|
+
errorPreview: readPreview(last.errorPreview, last.error, true)
|
|
4390
|
+
})];
|
|
4391
|
+
});
|
|
4392
|
+
const last = page.at(-1);
|
|
4393
|
+
return {
|
|
4394
|
+
threads,
|
|
4395
|
+
page: { next: selected.length > plan.limit ? coreStorage.encodeThreadQueryCursor(plan, {
|
|
4396
|
+
organizationId: last.organizationId,
|
|
4397
|
+
threadId: last.threadId,
|
|
4398
|
+
sortValue: last[plan.orderBy.field].toISOString()
|
|
4399
|
+
}) : null }
|
|
4400
|
+
};
|
|
4401
|
+
}
|
|
4402
|
+
//#endregion
|
|
4130
4403
|
//#region src/storage/domains/observability/trace-aggregate.ts
|
|
4131
4404
|
/** Trace-root column expressions (alias `r`) for each groupable dimension; same values `where` sees. */
|
|
4132
4405
|
const DIMENSION_SQL = {
|
|
4133
4406
|
entityType: "r.entityType",
|
|
4134
4407
|
entityName: "r.entityName",
|
|
4408
|
+
name: "r.name",
|
|
4409
|
+
spanType: "r.spanType",
|
|
4135
4410
|
environment: "r.environment",
|
|
4136
4411
|
status: TRACE_STATUS_SQL,
|
|
4137
4412
|
serviceName: "r.serviceName",
|
|
@@ -5900,12 +6175,15 @@ var ObservabilityStorageDuckDB = class ObservabilityStorageDuckDB extends Observ
|
|
|
5900
6175
|
"trace-query",
|
|
5901
6176
|
"trace-aggregate",
|
|
5902
6177
|
...spanQueryFeatures,
|
|
6178
|
+
...typeof coreStorage.planThreadRowsQuery === "function" ? ["thread-query-rows"] : [],
|
|
5903
6179
|
"trace-query-root-duration",
|
|
5904
6180
|
"trace-query-discovery",
|
|
5905
6181
|
"thread-query",
|
|
5906
6182
|
"trace-query-tenant-scope",
|
|
5907
6183
|
"feedback",
|
|
5908
|
-
"trace-query-context-ids"
|
|
6184
|
+
"trace-query-context-ids",
|
|
6185
|
+
...timeToFirstChunkFeatures,
|
|
6186
|
+
"trace-query-root-span-fields"
|
|
5909
6187
|
];
|
|
5910
6188
|
return [
|
|
5911
6189
|
"metrics",
|
|
@@ -5920,12 +6198,15 @@ var ObservabilityStorageDuckDB = class ObservabilityStorageDuckDB extends Observ
|
|
|
5920
6198
|
"trace-query",
|
|
5921
6199
|
"trace-aggregate",
|
|
5922
6200
|
...spanQueryFeatures,
|
|
6201
|
+
...typeof coreStorage.planThreadRowsQuery === "function" ? ["thread-query-rows"] : [],
|
|
5923
6202
|
"trace-query-root-duration",
|
|
5924
6203
|
"trace-query-discovery",
|
|
5925
6204
|
"thread-query",
|
|
5926
6205
|
"trace-query-tenant-scope",
|
|
5927
6206
|
"feedback",
|
|
5928
|
-
"trace-query-context-ids"
|
|
6207
|
+
"trace-query-context-ids",
|
|
6208
|
+
...timeToFirstChunkFeatures,
|
|
6209
|
+
"trace-query-root-span-fields"
|
|
5929
6210
|
];
|
|
5930
6211
|
}
|
|
5931
6212
|
async createSpan(args) {
|
|
@@ -5971,6 +6252,7 @@ var ObservabilityStorageDuckDB = class ObservabilityStorageDuckDB extends Observ
|
|
|
5971
6252
|
return getTraceQueryValues(this.db, plan);
|
|
5972
6253
|
}
|
|
5973
6254
|
async queryThreads(plan) {
|
|
6255
|
+
if (plan.result === "threadRows") return queryThreadRows(this.db, plan);
|
|
5974
6256
|
return queryThreads(this.db, plan);
|
|
5975
6257
|
}
|
|
5976
6258
|
async listTracesLight(args) {
|
|
@@ -6086,4 +6368,4 @@ var ObservabilityStorageDuckDB = class ObservabilityStorageDuckDB extends Observ
|
|
|
6086
6368
|
//#endregion
|
|
6087
6369
|
export { ObservabilityStorageDuckDB };
|
|
6088
6370
|
|
|
6089
|
-
//# sourceMappingURL=observability-
|
|
6371
|
+
//# sourceMappingURL=observability-DWPWPNlL.js.map
|