@codemation/host 0.2.5 → 0.3.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/CHANGELOG.md +30 -0
- package/dist/{AppConfigFactory-CqKWXqOm.js → AppConfigFactory-BPp02HMv.js} +82 -5
- package/dist/{AppConfigFactory-CqKWXqOm.js.map → AppConfigFactory-BPp02HMv.js.map} +1 -1
- package/dist/{AppConfigFactory-CK28UPK0.d.ts → AppConfigFactory-Dq7ttwQ_.d.ts} +6001 -144
- package/dist/{AppContainerFactory-CcSGFNLW.js → AppContainerFactory-D9je1sSV.js} +2604 -155
- package/dist/AppContainerFactory-D9je1sSV.js.map +1 -0
- package/dist/{CodemationAppContext-KqDoeHqN.d.ts → CodemationAppContext-P7P-xZhQ.d.ts} +2 -2
- package/dist/{CodemationAuthoring.types-BP6Inucu.d.ts → CodemationAuthoring.types-OMYu7vKP.d.ts} +3 -3
- package/dist/{CodemationConfigNormalizer-DIAE0VHw.d.ts → CodemationConfigNormalizer-BCtBrJDe.d.ts} +2 -2
- package/dist/{CodemationConsumerConfigLoader-nj9kTmfJ.d.ts → CodemationConsumerConfigLoader-evvw4b_a.d.ts} +2 -2
- package/dist/CodemationPluginListMerger-PSTtEQjC.d.ts +674 -0
- package/dist/{CredentialServices-BFQD_VN1.d.ts → CredentialServices-0Hk8RFY1.d.ts} +3 -3
- package/dist/{CredentialServices-BPKUF8Xs.js → CredentialServices-BNBMFOPt.js} +6 -1
- package/dist/CredentialServices-BNBMFOPt.js.map +1 -0
- package/dist/{PublicFrontendBootstrapFactory-DTA1iDo0.d.ts → PublicFrontendBootstrapFactory-D0_ds7nS.d.ts} +2 -2
- package/dist/authoring.d.ts +3 -3
- package/dist/consumer.d.ts +4 -4
- package/dist/credentials.d.ts +3 -3
- package/dist/credentials.js +1 -1
- package/dist/devServerSidecar.d.ts +1 -1
- package/dist/{index-Dd6BrWyH.d.ts → index-CeS2saCe.d.ts} +105 -2
- package/dist/index.d.ts +12 -11
- package/dist/index.js +5 -5
- package/dist/nextServer.d.ts +8 -58
- package/dist/nextServer.js +6 -100
- package/dist/{persistenceServer-DKbFDxoS.js → persistenceServer-CA0_q0D7.js} +2 -2
- package/dist/{persistenceServer-DKbFDxoS.js.map → persistenceServer-CA0_q0D7.js.map} +1 -1
- package/dist/{persistenceServer-Y-u7lV7f.d.ts → persistenceServer-CJeu1STC.d.ts} +2 -2
- package/dist/persistenceServer.d.ts +5 -5
- package/dist/persistenceServer.js +2 -2
- package/dist/{server-CFpgKuVE.js → server-C_ZIEOTY.js} +4 -4
- package/dist/{server-CFpgKuVE.js.map → server-C_ZIEOTY.js.map} +1 -1
- package/dist/{server-axppTMgo.d.ts → server-Clvg5x1w.d.ts} +11 -5
- package/dist/server.d.ts +8 -8
- package/dist/server.js +5 -5
- package/package.json +6 -5
- package/prisma/migrations/20260414120000_telemetry_foundation/migration.sql +112 -0
- package/prisma/migrations/20260414153000_telemetry_retention_metrics_refactor/migration.sql +239 -0
- package/prisma/migrations.sqlite/20260414120000_telemetry_foundation/migration.sql +103 -0
- package/prisma/migrations.sqlite/20260414153000_telemetry_retention_metrics_refactor/migration.sql +540 -0
- package/prisma/schema.postgresql.prisma +100 -1
- package/prisma/schema.sqlite.prisma +100 -1
- package/scripts/generate-prisma-clients.mjs +89 -1
- package/src/application/contracts/TelemetryDashboardContracts.ts +113 -0
- package/src/application/contracts/TelemetryRunTraceContracts.ts +13 -0
- package/src/application/cost/FrameworkCostCatalogEntries.ts +126 -0
- package/src/application/queries/GetTelemetryDashboardDimensionsQuery.ts +11 -0
- package/src/application/queries/GetTelemetryDashboardDimensionsQueryHandler.ts +20 -0
- package/src/application/queries/GetTelemetryDashboardRunsQuery.ts +11 -0
- package/src/application/queries/GetTelemetryDashboardRunsQueryHandler.ts +20 -0
- package/src/application/queries/GetTelemetryDashboardSummaryQuery.ts +11 -0
- package/src/application/queries/GetTelemetryDashboardSummaryQueryHandler.ts +29 -0
- package/src/application/queries/GetTelemetryDashboardTimeseriesQuery.ts +11 -0
- package/src/application/queries/GetTelemetryDashboardTimeseriesQueryHandler.ts +36 -0
- package/src/application/queries/GetTelemetryRunTraceQuery.ts +8 -0
- package/src/application/queries/GetTelemetryRunTraceQueryHandler.ts +20 -0
- package/src/application/queries/WorkflowQueryHandlers.ts +5 -0
- package/src/application/runs/WorkflowRunRetentionPruneScheduler.ts +71 -26
- package/src/application/telemetry/CompositeTelemetryExporter.ts +13 -0
- package/src/application/telemetry/LazyExecutionTelemetryFactory.ts +21 -0
- package/src/application/telemetry/NoOpTelemetryExporter.ts +7 -0
- package/src/application/telemetry/OtelExecutionTelemetry.types.ts +41 -0
- package/src/application/telemetry/OtelExecutionTelemetryFactory.ts +56 -0
- package/src/application/telemetry/OtelIdentityFactory.ts +41 -0
- package/src/application/telemetry/RunEventBusTelemetryReporter.ts +188 -0
- package/src/application/telemetry/StoredExecutionTelemetry.ts +56 -0
- package/src/application/telemetry/StoredNodeExecutionTelemetry.ts +35 -0
- package/src/application/telemetry/StoredTelemetrySpanScope.ts +188 -0
- package/src/application/telemetry/TelemetryEnricherChain.ts +85 -0
- package/src/application/telemetry/TelemetryPrivacyPolicy.ts +19 -0
- package/src/application/telemetry/TelemetryQueryService.ts +815 -0
- package/src/application/telemetry/TelemetryRetentionTimestampFactory.ts +40 -0
- package/src/applicationTokens.ts +18 -0
- package/src/bootstrap/AppContainerFactory.ts +124 -1
- package/src/bootstrap/AppContainerLifecycle.ts +8 -0
- package/src/bootstrap/runtime/FrontendRuntime.ts +8 -0
- package/src/bootstrap/runtime/WorkerRuntime.ts +8 -0
- package/src/domain/runs/WorkflowRunRepository.ts +3 -1
- package/src/domain/telemetry/TelemetryContracts.ts +197 -0
- package/src/infrastructure/persistence/InMemoryRunTraceContextRepository.ts +56 -0
- package/src/infrastructure/persistence/InMemoryTelemetryArtifactStore.ts +56 -0
- package/src/infrastructure/persistence/InMemoryTelemetryMetricPointStore.ts +97 -0
- package/src/infrastructure/persistence/InMemoryTelemetrySpanStore.ts +113 -0
- package/src/infrastructure/persistence/InMemoryWorkflowRunRepository.ts +4 -2
- package/src/infrastructure/persistence/PrismaRunTraceContextRepository.ts +92 -0
- package/src/infrastructure/persistence/PrismaTelemetryArtifactStore.ts +125 -0
- package/src/infrastructure/persistence/PrismaTelemetryMetricPointStore.ts +134 -0
- package/src/infrastructure/persistence/PrismaTelemetrySpanStore.ts +166 -0
- package/src/infrastructure/persistence/PrismaWorkflowRunRepository.ts +20 -7
- package/src/infrastructure/persistence/generated/prisma-postgresql-client/edge.js +85 -4
- package/src/infrastructure/persistence/generated/prisma-postgresql-client/index-browser.js +81 -0
- package/src/infrastructure/persistence/generated/prisma-postgresql-client/index.d.ts +6488 -102
- package/src/infrastructure/persistence/generated/prisma-postgresql-client/index.js +85 -4
- package/src/infrastructure/persistence/generated/prisma-postgresql-client/package.json +1 -1
- package/src/infrastructure/persistence/generated/prisma-postgresql-client/schema.prisma +100 -0
- package/src/infrastructure/persistence/generated/prisma-sqlite-client/edge.js +85 -4
- package/src/infrastructure/persistence/generated/prisma-sqlite-client/index-browser.js +81 -0
- package/src/infrastructure/persistence/generated/prisma-sqlite-client/index.d.ts +6476 -98
- package/src/infrastructure/persistence/generated/prisma-sqlite-client/index.js +85 -4
- package/src/infrastructure/persistence/generated/prisma-sqlite-client/package.json +1 -1
- package/src/infrastructure/persistence/generated/prisma-sqlite-client/schema.prisma +100 -0
- package/src/presentation/http/ApiPaths.ts +22 -0
- package/src/presentation/http/hono/registrars/TelemetryHonoApiRouteRegistrar.ts +19 -0
- package/src/presentation/http/routeHandlers/TelemetryDashboardRequestError.ts +1 -0
- package/src/presentation/http/routeHandlers/TelemetryHttpRouteHandler.ts +181 -0
- package/dist/AppContainerFactory-CcSGFNLW.js.map +0 -1
- package/dist/CodemationPluginListMerger-QvUa2SIt.d.ts +0 -357
- package/dist/CredentialServices-BPKUF8Xs.js.map +0 -1
- package/dist/nextServer.js.map +0 -1
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { inject, injectable, type TelemetryAttributes } from "@codemation/core";
|
|
2
|
+
import { OtelIdentityFactory } from "../../application/telemetry/OtelIdentityFactory";
|
|
3
|
+
import type {
|
|
4
|
+
TelemetryMetricPointListQuery,
|
|
5
|
+
TelemetryMetricPointRecord,
|
|
6
|
+
TelemetryMetricPointStore,
|
|
7
|
+
TelemetryMetricPointWrite,
|
|
8
|
+
} from "../../domain/telemetry/TelemetryContracts";
|
|
9
|
+
import { PrismaDatabaseClientToken, type PrismaDatabaseClient } from "./PrismaDatabaseClient";
|
|
10
|
+
|
|
11
|
+
@injectable()
|
|
12
|
+
export class PrismaTelemetryMetricPointStore implements TelemetryMetricPointStore {
|
|
13
|
+
constructor(
|
|
14
|
+
@inject(PrismaDatabaseClientToken)
|
|
15
|
+
private readonly prisma: PrismaDatabaseClient,
|
|
16
|
+
@inject(OtelIdentityFactory)
|
|
17
|
+
private readonly otelIdentityFactory: OtelIdentityFactory,
|
|
18
|
+
) {}
|
|
19
|
+
|
|
20
|
+
async save(record: TelemetryMetricPointWrite): Promise<TelemetryMetricPointRecord> {
|
|
21
|
+
const metricPointId = this.otelIdentityFactory.createArtifactId();
|
|
22
|
+
await this.prisma.telemetryMetricPoint.create({
|
|
23
|
+
data: {
|
|
24
|
+
metricPointId,
|
|
25
|
+
traceId: record.traceId ?? null,
|
|
26
|
+
spanId: record.spanId ?? null,
|
|
27
|
+
runId: record.runId ?? null,
|
|
28
|
+
workflowId: record.workflowId,
|
|
29
|
+
nodeId: record.nodeId ?? null,
|
|
30
|
+
activationId: record.activationId ?? null,
|
|
31
|
+
metricName: record.name,
|
|
32
|
+
value: record.value,
|
|
33
|
+
unit: record.unit ?? null,
|
|
34
|
+
observedAt: record.observedAt,
|
|
35
|
+
workflowFolder: record.workflowFolder ?? null,
|
|
36
|
+
nodeType: record.nodeType ?? null,
|
|
37
|
+
nodeRole: record.nodeRole ?? null,
|
|
38
|
+
modelName: record.modelName ?? null,
|
|
39
|
+
dimensionsJson: record.attributes ? JSON.stringify(record.attributes) : null,
|
|
40
|
+
retentionExpiresAt: record.retentionExpiresAt ?? null,
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
return {
|
|
44
|
+
metricPointId,
|
|
45
|
+
traceId: record.traceId,
|
|
46
|
+
spanId: record.spanId,
|
|
47
|
+
runId: record.runId,
|
|
48
|
+
workflowId: record.workflowId,
|
|
49
|
+
nodeId: record.nodeId,
|
|
50
|
+
activationId: record.activationId,
|
|
51
|
+
metricName: record.name,
|
|
52
|
+
value: record.value,
|
|
53
|
+
unit: record.unit,
|
|
54
|
+
observedAt: record.observedAt,
|
|
55
|
+
workflowFolder: record.workflowFolder,
|
|
56
|
+
nodeType: record.nodeType,
|
|
57
|
+
nodeRole: record.nodeRole,
|
|
58
|
+
modelName: record.modelName,
|
|
59
|
+
dimensions: record.attributes,
|
|
60
|
+
retentionExpiresAt: record.retentionExpiresAt,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async list(args: TelemetryMetricPointListQuery = {}): Promise<ReadonlyArray<TelemetryMetricPointRecord>> {
|
|
65
|
+
const rows = await this.prisma.telemetryMetricPoint.findMany({
|
|
66
|
+
where: {
|
|
67
|
+
...(args.traceId ? { traceId: args.traceId } : {}),
|
|
68
|
+
...(args.runId ? { runId: args.runId } : {}),
|
|
69
|
+
...(args.runIds && args.runIds.length > 0 ? { runId: { in: [...args.runIds] } } : {}),
|
|
70
|
+
...(args.workflowId ? { workflowId: args.workflowId } : {}),
|
|
71
|
+
...(args.workflowIds && args.workflowIds.length > 0 ? { workflowId: { in: [...args.workflowIds] } } : {}),
|
|
72
|
+
...(args.nodeId ? { nodeId: args.nodeId } : {}),
|
|
73
|
+
...(args.metricNames && args.metricNames.length > 0 ? { metricName: { in: [...args.metricNames] } } : {}),
|
|
74
|
+
...(args.modelNames && args.modelNames.length > 0 ? { modelName: { in: [...args.modelNames] } } : {}),
|
|
75
|
+
...(args.observedAtGte ? { observedAt: { gte: args.observedAtGte } } : {}),
|
|
76
|
+
...(args.observedAtLte ? { observedAt: { lte: args.observedAtLte } } : {}),
|
|
77
|
+
},
|
|
78
|
+
orderBy: [{ observedAt: "asc" }, { metricPointId: "asc" }],
|
|
79
|
+
...(args.limit ? { take: args.limit } : {}),
|
|
80
|
+
});
|
|
81
|
+
return rows.map((row) => ({
|
|
82
|
+
metricPointId: row.metricPointId,
|
|
83
|
+
traceId: row.traceId ?? undefined,
|
|
84
|
+
spanId: row.spanId ?? undefined,
|
|
85
|
+
runId: row.runId ?? undefined,
|
|
86
|
+
workflowId: row.workflowId,
|
|
87
|
+
nodeId: row.nodeId ?? undefined,
|
|
88
|
+
activationId: row.activationId ?? undefined,
|
|
89
|
+
metricName: row.metricName,
|
|
90
|
+
value: row.value,
|
|
91
|
+
unit: row.unit ?? undefined,
|
|
92
|
+
observedAt: row.observedAt,
|
|
93
|
+
workflowFolder: row.workflowFolder ?? undefined,
|
|
94
|
+
nodeType: row.nodeType ?? undefined,
|
|
95
|
+
nodeRole: row.nodeRole ?? undefined,
|
|
96
|
+
modelName: row.modelName ?? undefined,
|
|
97
|
+
dimensions: this.parseJson<TelemetryAttributes>(row.dimensionsJson),
|
|
98
|
+
retentionExpiresAt: row.retentionExpiresAt ?? undefined,
|
|
99
|
+
}));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async pruneExpired(args: Readonly<{ nowIso: string; limit?: number }>): Promise<number> {
|
|
103
|
+
const rows = await this.prisma.telemetryMetricPoint.findMany({
|
|
104
|
+
where: {
|
|
105
|
+
retentionExpiresAt: {
|
|
106
|
+
lte: args.nowIso,
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
select: {
|
|
110
|
+
metricPointId: true,
|
|
111
|
+
},
|
|
112
|
+
orderBy: [{ retentionExpiresAt: "asc" }, { metricPointId: "asc" }],
|
|
113
|
+
...(args.limit ? { take: args.limit } : {}),
|
|
114
|
+
});
|
|
115
|
+
if (rows.length === 0) {
|
|
116
|
+
return 0;
|
|
117
|
+
}
|
|
118
|
+
const result = await this.prisma.telemetryMetricPoint.deleteMany({
|
|
119
|
+
where: {
|
|
120
|
+
metricPointId: {
|
|
121
|
+
in: rows.map((row) => row.metricPointId),
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
return result.count;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private parseJson<T>(value: string | null): T | undefined {
|
|
129
|
+
if (!value) {
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
132
|
+
return JSON.parse(value) as T;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { inject, injectable, type TelemetryAttributes, type TelemetrySpanEventRecord } from "@codemation/core";
|
|
2
|
+
import type {
|
|
3
|
+
TelemetrySpanListQuery,
|
|
4
|
+
TelemetrySpanRecord,
|
|
5
|
+
TelemetrySpanStore,
|
|
6
|
+
TelemetrySpanUpsert,
|
|
7
|
+
} from "../../domain/telemetry/TelemetryContracts";
|
|
8
|
+
import { PrismaDatabaseClientToken, type PrismaDatabaseClient } from "./PrismaDatabaseClient";
|
|
9
|
+
|
|
10
|
+
@injectable()
|
|
11
|
+
export class PrismaTelemetrySpanStore implements TelemetrySpanStore {
|
|
12
|
+
constructor(
|
|
13
|
+
@inject(PrismaDatabaseClientToken)
|
|
14
|
+
private readonly prisma: PrismaDatabaseClient,
|
|
15
|
+
) {}
|
|
16
|
+
|
|
17
|
+
async upsert(record: TelemetrySpanUpsert): Promise<void> {
|
|
18
|
+
const telemetrySpanId = this.createTelemetrySpanId(record.traceId, record.spanId);
|
|
19
|
+
const existing = await this.prisma.telemetrySpan.findUnique({
|
|
20
|
+
where: { telemetrySpanId },
|
|
21
|
+
});
|
|
22
|
+
const attributes = this.mergeAttributes(
|
|
23
|
+
this.parseJson<TelemetryAttributes>(existing?.attributesJson ?? null),
|
|
24
|
+
record.attributes,
|
|
25
|
+
);
|
|
26
|
+
const events = this.mergeEvents(
|
|
27
|
+
this.parseJson<ReadonlyArray<TelemetrySpanEventRecord>>(existing?.eventsJson ?? null),
|
|
28
|
+
record.events,
|
|
29
|
+
);
|
|
30
|
+
const data = {
|
|
31
|
+
traceId: record.traceId,
|
|
32
|
+
spanId: record.spanId,
|
|
33
|
+
parentSpanId: record.parentSpanId ?? existing?.parentSpanId ?? null,
|
|
34
|
+
runId: record.runId,
|
|
35
|
+
workflowId: record.workflowId,
|
|
36
|
+
nodeId: record.nodeId ?? existing?.nodeId ?? null,
|
|
37
|
+
activationId: record.activationId ?? existing?.activationId ?? null,
|
|
38
|
+
connectionInvocationId: record.connectionInvocationId ?? existing?.connectionInvocationId ?? null,
|
|
39
|
+
name: record.name ?? existing?.name ?? "codemation.span",
|
|
40
|
+
kind: record.kind ?? existing?.kind ?? "internal",
|
|
41
|
+
status: record.status ?? existing?.status ?? null,
|
|
42
|
+
statusMessage: record.statusMessage ?? existing?.statusMessage ?? null,
|
|
43
|
+
startTime: record.startTime ?? existing?.startTime ?? null,
|
|
44
|
+
endTime: record.endTime ?? existing?.endTime ?? null,
|
|
45
|
+
workflowFolder: record.workflowFolder ?? existing?.workflowFolder ?? null,
|
|
46
|
+
nodeType: record.nodeType ?? existing?.nodeType ?? null,
|
|
47
|
+
nodeRole: record.nodeRole ?? existing?.nodeRole ?? null,
|
|
48
|
+
modelName: record.modelName ?? existing?.modelName ?? null,
|
|
49
|
+
attributesJson: attributes ? JSON.stringify(attributes) : null,
|
|
50
|
+
eventsJson: events.length > 0 ? JSON.stringify(events) : null,
|
|
51
|
+
retentionExpiresAt: record.retentionExpiresAt ?? existing?.retentionExpiresAt ?? null,
|
|
52
|
+
updatedAt: new Date().toISOString(),
|
|
53
|
+
};
|
|
54
|
+
await this.prisma.telemetrySpan.upsert({
|
|
55
|
+
where: { telemetrySpanId },
|
|
56
|
+
create: {
|
|
57
|
+
telemetrySpanId,
|
|
58
|
+
...data,
|
|
59
|
+
},
|
|
60
|
+
update: data,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async list(args: TelemetrySpanListQuery = {}): Promise<ReadonlyArray<TelemetrySpanRecord>> {
|
|
65
|
+
const rows = await this.prisma.telemetrySpan.findMany({
|
|
66
|
+
where: {
|
|
67
|
+
...(args.traceId ? { traceId: args.traceId } : {}),
|
|
68
|
+
...(args.runId ? { runId: args.runId } : {}),
|
|
69
|
+
...(args.runIds && args.runIds.length > 0 ? { runId: { in: [...args.runIds] } } : {}),
|
|
70
|
+
...(args.workflowId ? { workflowId: args.workflowId } : {}),
|
|
71
|
+
...(args.workflowIds && args.workflowIds.length > 0 ? { workflowId: { in: [...args.workflowIds] } } : {}),
|
|
72
|
+
...(args.statuses && args.statuses.length > 0 ? { status: { in: [...args.statuses] } } : {}),
|
|
73
|
+
...(args.names && args.names.length > 0 ? { name: { in: [...args.names] } } : {}),
|
|
74
|
+
...(args.modelNames && args.modelNames.length > 0 ? { modelName: { in: [...args.modelNames] } } : {}),
|
|
75
|
+
...(args.startTimeGte ? { startTime: { gte: args.startTimeGte } } : {}),
|
|
76
|
+
...(args.endTimeLte ? { endTime: { lte: args.endTimeLte } } : {}),
|
|
77
|
+
},
|
|
78
|
+
orderBy: [{ startTime: "asc" }, { spanId: "asc" }],
|
|
79
|
+
...(args.limit ? { take: args.limit } : {}),
|
|
80
|
+
});
|
|
81
|
+
return rows.map((row) => ({
|
|
82
|
+
traceId: row.traceId,
|
|
83
|
+
spanId: row.spanId,
|
|
84
|
+
parentSpanId: row.parentSpanId ?? undefined,
|
|
85
|
+
runId: row.runId,
|
|
86
|
+
workflowId: row.workflowId,
|
|
87
|
+
nodeId: row.nodeId ?? undefined,
|
|
88
|
+
activationId: row.activationId ?? undefined,
|
|
89
|
+
connectionInvocationId: row.connectionInvocationId ?? undefined,
|
|
90
|
+
name: row.name,
|
|
91
|
+
kind: row.kind as TelemetrySpanRecord["kind"],
|
|
92
|
+
status: row.status ? (row.status as TelemetrySpanRecord["status"]) : undefined,
|
|
93
|
+
statusMessage: row.statusMessage ?? undefined,
|
|
94
|
+
startTime: row.startTime ?? undefined,
|
|
95
|
+
endTime: row.endTime ?? undefined,
|
|
96
|
+
workflowFolder: row.workflowFolder ?? undefined,
|
|
97
|
+
nodeType: row.nodeType ?? undefined,
|
|
98
|
+
nodeRole: row.nodeRole ?? undefined,
|
|
99
|
+
modelName: row.modelName ?? undefined,
|
|
100
|
+
attributes: this.parseJson<TelemetryAttributes>(row.attributesJson),
|
|
101
|
+
events: this.parseJson<ReadonlyArray<TelemetrySpanEventRecord>>(row.eventsJson) ?? [],
|
|
102
|
+
retentionExpiresAt: row.retentionExpiresAt ?? undefined,
|
|
103
|
+
}));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async listByTraceId(traceId: string): Promise<ReadonlyArray<TelemetrySpanRecord>> {
|
|
107
|
+
return await this.list({ traceId });
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async pruneExpired(args: Readonly<{ nowIso: string; limit?: number }>): Promise<number> {
|
|
111
|
+
const rows = await this.prisma.telemetrySpan.findMany({
|
|
112
|
+
where: {
|
|
113
|
+
retentionExpiresAt: {
|
|
114
|
+
lte: args.nowIso,
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
select: {
|
|
118
|
+
telemetrySpanId: true,
|
|
119
|
+
},
|
|
120
|
+
orderBy: [{ retentionExpiresAt: "asc" }, { telemetrySpanId: "asc" }],
|
|
121
|
+
...(args.limit ? { take: args.limit } : {}),
|
|
122
|
+
});
|
|
123
|
+
if (rows.length === 0) {
|
|
124
|
+
return 0;
|
|
125
|
+
}
|
|
126
|
+
const result = await this.prisma.telemetrySpan.deleteMany({
|
|
127
|
+
where: {
|
|
128
|
+
telemetrySpanId: {
|
|
129
|
+
in: rows.map((row) => row.telemetrySpanId),
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
return result.count;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
private createTelemetrySpanId(traceId: string, spanId: string): string {
|
|
137
|
+
return `${traceId}:${spanId}`;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private parseJson<T>(value: string | null): T | undefined {
|
|
141
|
+
if (!value) {
|
|
142
|
+
return undefined;
|
|
143
|
+
}
|
|
144
|
+
return JSON.parse(value) as T;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
private mergeAttributes(
|
|
148
|
+
existing: TelemetryAttributes | undefined,
|
|
149
|
+
update: TelemetryAttributes | undefined,
|
|
150
|
+
): TelemetryAttributes | undefined {
|
|
151
|
+
if (!existing && !update) {
|
|
152
|
+
return undefined;
|
|
153
|
+
}
|
|
154
|
+
return {
|
|
155
|
+
...(existing ?? {}),
|
|
156
|
+
...(update ?? {}),
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
private mergeEvents(
|
|
161
|
+
existing: ReadonlyArray<TelemetrySpanEventRecord> | undefined,
|
|
162
|
+
update: ReadonlyArray<TelemetrySpanEventRecord> | undefined,
|
|
163
|
+
): Array<TelemetrySpanEventRecord> {
|
|
164
|
+
return [...(existing ?? []), ...(update ?? [])];
|
|
165
|
+
}
|
|
166
|
+
}
|
|
@@ -402,21 +402,34 @@ export class PrismaWorkflowRunRepository implements WorkflowRunRepository, Workf
|
|
|
402
402
|
}
|
|
403
403
|
|
|
404
404
|
async listRunsOlderThan(
|
|
405
|
-
args: Readonly<{
|
|
405
|
+
args: Readonly<{ nowIso: string; defaultRetentionSeconds: number; limit?: number }>,
|
|
406
406
|
): Promise<ReadonlyArray<RunPruneCandidate>> {
|
|
407
407
|
const limit = args.limit ?? 100;
|
|
408
408
|
const rows = await this.prisma.run.findMany({
|
|
409
409
|
where: {
|
|
410
410
|
status: { in: ["completed", "failed"] },
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
411
|
+
},
|
|
412
|
+
select: {
|
|
413
|
+
runId: true,
|
|
414
|
+
workflowId: true,
|
|
415
|
+
startedAt: true,
|
|
416
|
+
finishedAt: true,
|
|
417
|
+
updatedAt: true,
|
|
418
|
+
policySnapshotJson: true,
|
|
415
419
|
},
|
|
416
420
|
orderBy: { updatedAt: "asc" },
|
|
417
|
-
take: limit,
|
|
421
|
+
take: limit * 4,
|
|
418
422
|
});
|
|
419
|
-
return rows
|
|
423
|
+
return rows
|
|
424
|
+
.filter((row) => {
|
|
425
|
+
const finishedAt = row.finishedAt ?? row.updatedAt;
|
|
426
|
+
const policySnapshot = this.parseJson<PersistedRunState["policySnapshot"]>(row.policySnapshotJson);
|
|
427
|
+
const retentionSeconds = policySnapshot?.retentionSeconds ?? args.defaultRetentionSeconds;
|
|
428
|
+
const cutoffIso = new Date(new Date(args.nowIso).getTime() - retentionSeconds * 1000).toISOString();
|
|
429
|
+
return finishedAt < cutoffIso;
|
|
430
|
+
})
|
|
431
|
+
.slice(0, limit)
|
|
432
|
+
.map((row) => this.rowToPruneCandidate(row));
|
|
420
433
|
}
|
|
421
434
|
|
|
422
435
|
private instancesToDomain(
|