@obsunified/collector 2.0.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/LICENSE +21 -0
- package/README.md +60 -0
- package/dist/auth.d.ts +2 -0
- package/dist/auth.js +9 -0
- package/dist/auth.js.map +1 -0
- package/dist/chunk-UGWDOGGH.js +319 -0
- package/dist/chunk-UGWDOGGH.js.map +1 -0
- package/dist/chunk-XKQTVIRE.js +671 -0
- package/dist/chunk-XKQTVIRE.js.map +1 -0
- package/dist/index.d.ts +1197 -0
- package/dist/index.js +17707 -0
- package/dist/index.js.map +1 -0
- package/dist/ingest-auth-HFVO1zIp.d.ts +165 -0
- package/dist/openai-FJYOQ7WO.js +384 -0
- package/dist/openai-FJYOQ7WO.js.map +1 -0
- package/package.json +73 -0
- package/src/ai-parity/ai-parity.acceptance.test.ts +449 -0
- package/src/analyses/derive.ts +642 -0
- package/src/analyses/index.ts +50 -0
- package/src/analyses/investigations.ts +277 -0
- package/src/analyses/tier0.ts +568 -0
- package/src/auth/dashboard-auth.ts +269 -0
- package/src/auth/ingest-auth.ts +147 -0
- package/src/durable-objects/tail-hub.test.ts +32 -0
- package/src/durable-objects/tail-hub.ts +158 -0
- package/src/framework/collector.ts +467 -0
- package/src/framework/env.ts +90 -0
- package/src/framework/logger.ts +81 -0
- package/src/index.ts +211 -0
- package/src/lib/action-aggregates.ts +1031 -0
- package/src/lib/action-identity.ts +106 -0
- package/src/lib/actions-store.ts +397 -0
- package/src/lib/ai-pricing.ts +80 -0
- package/src/lib/ai-store/calls.ts +148 -0
- package/src/lib/ai-store/evaluations.test.ts +47 -0
- package/src/lib/ai-store/evaluations.ts +125 -0
- package/src/lib/ai-store/helpers.ts +37 -0
- package/src/lib/ai-store/retention.ts +17 -0
- package/src/lib/ai-store/sessions.ts +277 -0
- package/src/lib/ai-store/spans.ts +109 -0
- package/src/lib/ai-store/types.ts +112 -0
- package/src/lib/ai-store.ts +76 -0
- package/src/lib/alerts-store.ts +458 -0
- package/src/lib/analyses-runner.ts +439 -0
- package/src/lib/analyses-store.test.ts +243 -0
- package/src/lib/analyses-store.ts +454 -0
- package/src/lib/ask.test.ts +302 -0
- package/src/lib/ask.ts +390 -0
- package/src/lib/blob-store-s3.ts +213 -0
- package/src/lib/blob-store.ts +366 -0
- package/src/lib/eval-cases-store.test.ts +171 -0
- package/src/lib/eval-cases-store.ts +1033 -0
- package/src/lib/evidence-references.test.ts +10 -0
- package/src/lib/evidence-references.ts +684 -0
- package/src/lib/hash.ts +26 -0
- package/src/lib/identity-index/action-lookups.ts +805 -0
- package/src/lib/identity-index/constants.ts +1 -0
- package/src/lib/identity-index/key-lookups.ts +199 -0
- package/src/lib/identity-index/mappers.ts +349 -0
- package/src/lib/identity-index/types.ts +196 -0
- package/src/lib/identity-index/user-lookup.ts +137 -0
- package/src/lib/identity-index.test.ts +624 -0
- package/src/lib/identity-index.ts +85 -0
- package/src/lib/json.ts +39 -0
- package/src/lib/llm.ts +263 -0
- package/src/lib/logs-store.ts +188 -0
- package/src/lib/metrics-store.test.ts +189 -0
- package/src/lib/metrics-store.ts +312 -0
- package/src/lib/narrate-gate.test.ts +260 -0
- package/src/lib/narrate-gate.ts +253 -0
- package/src/lib/openai.test.ts +300 -0
- package/src/lib/openai.ts +512 -0
- package/src/lib/otlp.ts +212 -0
- package/src/lib/parse-pprof.test.ts +64 -0
- package/src/lib/parse-pprof.ts +184 -0
- package/src/lib/payload-capture.ts +22 -0
- package/src/lib/projects-store.ts +207 -0
- package/src/lib/propagation-metric.test.ts +196 -0
- package/src/lib/propagation-metric.ts +175 -0
- package/src/lib/sql-db-dialect.test.ts +22 -0
- package/src/lib/sql-db-postgres.ts +229 -0
- package/src/lib/sql-db.ts +166 -0
- package/src/lib/stage6.test.ts +216 -0
- package/src/lib/store/export.ts +65 -0
- package/src/lib/store/helpers.ts +367 -0
- package/src/lib/store/ingest.ts +68 -0
- package/src/lib/store/issues.ts +158 -0
- package/src/lib/store/overview.ts +127 -0
- package/src/lib/store/retention.ts +10 -0
- package/src/lib/store/service-map.ts +384 -0
- package/src/lib/store/trace-detail.bench.ts +76 -0
- package/src/lib/store/trace-detail.test.ts +410 -0
- package/src/lib/store/trace-detail.ts +427 -0
- package/src/lib/store/trace-queries.test.ts +29 -0
- package/src/lib/store/trace-queries.ts +89 -0
- package/src/lib/store-overview.test.ts +77 -0
- package/src/lib/store.ts +96 -0
- package/src/lib/tail-publisher.ts +64 -0
- package/src/lib/test-utils/mem-sql-db.test.ts +93 -0
- package/src/lib/test-utils/mem-sql-db.ts +86 -0
- package/src/lib/ua-parser.ts +61 -0
- package/src/lib/usage-store.ts +567 -0
- package/src/migrations/001_telemetry_spans.sql +28 -0
- package/src/migrations/002_usage_events.sql +24 -0
- package/src/migrations/003_usage_analytics_columns.sql +13 -0
- package/src/migrations/004_logs.sql +19 -0
- package/src/migrations/005_ai_calls.sql +25 -0
- package/src/migrations/006_user_profiles.sql +11 -0
- package/src/migrations/007_session_replay_metadata.sql +10 -0
- package/src/migrations/008_session_replay_storage_bytes.sql +1 -0
- package/src/migrations/009_projects_and_keys.sql +22 -0
- package/src/migrations/010_signal_project_id.sql +15 -0
- package/src/migrations/011_default_project_seed.sql +3 -0
- package/src/migrations/012_alert_rules.sql +17 -0
- package/src/migrations/013_alert_evaluations.sql +13 -0
- package/src/migrations/014_alert_state.sql +9 -0
- package/src/migrations/015_metrics.sql +58 -0
- package/src/migrations/016_metrics_exp_histogram_summary.sql +5 -0
- package/src/migrations/017_span_dropped_counts.sql +11 -0
- package/src/migrations/018_log_flags.sql +8 -0
- package/src/migrations/019_ai_span_payloads.sql +30 -0
- package/src/migrations/020_ai_span_evaluations.sql +33 -0
- package/src/migrations/021_ai_span_session.sql +13 -0
- package/src/migrations/022_span_log_session_id.sql +13 -0
- package/src/migrations/023_service_map_indexes.sql +32 -0
- package/src/migrations/024_analysis_results.sql +49 -0
- package/src/migrations/025_stage6_autopinning_alerts.sql +38 -0
- package/src/migrations/026_obs_dashboard_project_seed.sql +18 -0
- package/src/migrations/027_identity_propagation.sql +50 -0
- package/src/migrations/028_profile_blobs.sql +57 -0
- package/src/migrations/029_telemetry_sdk_name.sql +20 -0
- package/src/migrations/030_analysis_claim.sql +18 -0
- package/src/migrations/031_agent_action_graph.sql +154 -0
- package/src/migrations/032_payload_capture_flags.sql +8 -0
- package/src/migrations/033_eval_cases.sql +52 -0
- package/src/migrations/034_eval_case_results.sql +23 -0
- package/src/migrations/035_metric_exemplars.sql +33 -0
- package/src/migrations/036_trace_instrumentation_gaps.sql +19 -0
- package/src/migrations/037_eval_runs.sql +37 -0
- package/src/migrations/038_tool_call_evidence.sql +11 -0
- package/src/migrations/039_drop_trace_instrumentation_gaps.sql +11 -0
- package/src/migrations/040_evidence_retrieval_refs.sql +50 -0
- package/src/migrations-postgres/001_telemetry_spans.sql +33 -0
- package/src/migrations-postgres/002_usage_events.sql +24 -0
- package/src/migrations-postgres/003_usage_analytics_columns.sql +13 -0
- package/src/migrations-postgres/004_logs.sql +19 -0
- package/src/migrations-postgres/005_ai_calls.sql +25 -0
- package/src/migrations-postgres/006_user_profiles.sql +11 -0
- package/src/migrations-postgres/007_session_replay_metadata.sql +10 -0
- package/src/migrations-postgres/008_session_replay_storage_bytes.sql +2 -0
- package/src/migrations-postgres/009_projects_and_keys.sql +25 -0
- package/src/migrations-postgres/010_signal_project_id.sql +18 -0
- package/src/migrations-postgres/011_default_project_seed.sql +5 -0
- package/src/migrations-postgres/012_alert_rules.sql +17 -0
- package/src/migrations-postgres/013_alert_evaluations.sql +13 -0
- package/src/migrations-postgres/014_alert_state.sql +9 -0
- package/src/migrations-postgres/015_metrics.sql +47 -0
- package/src/migrations-postgres/016_metrics_exp_histogram_summary.sql +1 -0
- package/src/migrations-postgres/017_span_dropped_counts.sql +4 -0
- package/src/migrations-postgres/018_log_flags.sql +2 -0
- package/src/migrations-postgres/019_ai_span_payloads.sql +18 -0
- package/src/migrations-postgres/020_ai_span_evaluations.sql +21 -0
- package/src/migrations-postgres/021_ai_span_session.sql +7 -0
- package/src/migrations-postgres/022_span_log_session_id.sql +7 -0
- package/src/migrations-postgres/023_service_map_indexes.sql +9 -0
- package/src/migrations-postgres/024_analysis_results.sql +41 -0
- package/src/migrations-postgres/025_stage6_autopinning_alerts.sql +17 -0
- package/src/migrations-postgres/026_obs_dashboard_project_seed.sql +14 -0
- package/src/migrations-postgres/027_identity_propagation.sql +27 -0
- package/src/migrations-postgres/028_profile_blobs.sql +48 -0
- package/src/migrations-postgres/029_telemetry_sdk_name.sql +5 -0
- package/src/migrations-postgres/030_analysis_claim.sql +5 -0
- package/src/migrations-postgres/031_agent_action_graph.sql +190 -0
- package/src/migrations-postgres/032_payload_capture_flags.sql +8 -0
- package/src/migrations-postgres/033_eval_cases.sql +55 -0
- package/src/migrations-postgres/034_eval_case_results.sql +26 -0
- package/src/migrations-postgres/035_metric_exemplars.sql +31 -0
- package/src/migrations-postgres/036_trace_instrumentation_gaps.sql +19 -0
- package/src/migrations-postgres/037_eval_runs.sql +40 -0
- package/src/migrations-postgres/038_tool_call_evidence.sql +11 -0
- package/src/migrations-postgres/039_drop_trace_instrumentation_gaps.sql +11 -0
- package/src/migrations-postgres/040_evidence_retrieval_refs.sql +47 -0
- package/src/migrations-postgres/README.md +40 -0
- package/src/otlp/acceptance.test.ts +373 -0
- package/src/otlp/decode/body.ts +127 -0
- package/src/otlp/decode/logs.ts +96 -0
- package/src/otlp/decode/metrics.ts +402 -0
- package/src/otlp/decode/traces.ts +92 -0
- package/src/otlp/decode/values.ts +150 -0
- package/src/otlp/decode.test.ts +800 -0
- package/src/otlp/decode.ts +11 -0
- package/src/otlp/gen/opentelemetry/proto/collector/logs/v1/logs_service_pb.ts +132 -0
- package/src/otlp/gen/opentelemetry/proto/collector/metrics/v1/metrics_service_pb.ts +158 -0
- package/src/otlp/gen/opentelemetry/proto/collector/trace/v1/trace_service_pb.ts +158 -0
- package/src/otlp/gen/opentelemetry/proto/common/v1/common_pb.ts +289 -0
- package/src/otlp/gen/opentelemetry/proto/logs/v1/logs_pb.ts +397 -0
- package/src/otlp/gen/opentelemetry/proto/metrics/v1/metrics_pb.ts +1329 -0
- package/src/otlp/gen/opentelemetry/proto/resource/v1/resource_pb.ts +86 -0
- package/src/otlp/gen/opentelemetry/proto/trace/v1/trace_pb.ts +710 -0
- package/src/otlp/response.test.ts +158 -0
- package/src/otlp/response.ts +129 -0
- package/src/plugins/_context.ts +15 -0
- package/src/plugins/action-graph-processor/enrichers.ts +45 -0
- package/src/plugins/action-graph-processor/redaction.ts +84 -0
- package/src/plugins/action-graph-processor.test.ts +1023 -0
- package/src/plugins/action-graph-processor.ts +1172 -0
- package/src/plugins/action-routes.test.ts +716 -0
- package/src/plugins/action-routes.ts +343 -0
- package/src/plugins/ai-receiver.ts +254 -0
- package/src/plugins/ai-span-payloads-processor.test.ts +90 -0
- package/src/plugins/ai-span-payloads-processor.ts +180 -0
- package/src/plugins/alerts-evaluator.test.ts +91 -0
- package/src/plugins/alerts-evaluator.ts +328 -0
- package/src/plugins/alerts-routes.ts +101 -0
- package/src/plugins/analyses-routes.ts +138 -0
- package/src/plugins/ask-routes.ts +182 -0
- package/src/plugins/bot-filter.ts +19 -0
- package/src/plugins/connected-routes/manifest.ts +531 -0
- package/src/plugins/connected-routes.test.ts +1791 -0
- package/src/plugins/connected-routes.ts +1103 -0
- package/src/plugins/dashboard-routes.ts +41 -0
- package/src/plugins/default-span-enrichment.ts +92 -0
- package/src/plugins/eval-cases-routes.test.ts +391 -0
- package/src/plugins/eval-cases-routes.ts +529 -0
- package/src/plugins/evidence-retrieval-routes.benchmark.test.ts +245 -0
- package/src/plugins/evidence-retrieval-routes.test.ts +932 -0
- package/src/plugins/evidence-retrieval-routes.ts +2179 -0
- package/src/plugins/gen-ai-normalizer.test.ts +680 -0
- package/src/plugins/gen-ai-normalizer.ts +302 -0
- package/src/plugins/identity-receiver.ts +96 -0
- package/src/plugins/issue-enrichment.ts +132 -0
- package/src/plugins/issue-insights.ts +81 -0
- package/src/plugins/logs-receiver.ts +160 -0
- package/src/plugins/metrics-receiver.ts +91 -0
- package/src/plugins/onboarding-routes.ts +54 -0
- package/src/plugins/otlp-receiver.ts +124 -0
- package/src/plugins/platform-routes.ts +165 -0
- package/src/plugins/profile-routes.ts +468 -0
- package/src/plugins/projects-routes.ts +92 -0
- package/src/plugins/query-routes.ts +220 -0
- package/src/plugins/redaction-processor.ts +132 -0
- package/src/plugins/replay-query-routes.test.ts +38 -0
- package/src/plugins/replay-query-routes.ts +199 -0
- package/src/plugins/replay-receiver.ts +81 -0
- package/src/plugins/tail-routes.test.ts +47 -0
- package/src/plugins/tail-routes.ts +42 -0
- package/src/plugins/timeline-routes.ts +244 -0
- package/src/plugins/ua-enrichment.ts +27 -0
- package/src/plugins/usage-normalization.ts +46 -0
- package/src/plugins/usage-privacy.ts +55 -0
- package/src/plugins/usage-query-routes.ts +102 -0
- package/src/plugins/usage-receiver.ts +129 -0
- package/src/plugins/users-query-routes.ts +78 -0
- package/src/plugins/utm-enrichment.ts +36 -0
|
@@ -0,0 +1,805 @@
|
|
|
1
|
+
import { ActionConfidence } from "@obsunified/types/constants";
|
|
2
|
+
import type { SqlDb } from "../sql-db";
|
|
3
|
+
import { FETCH_LIMIT } from "./constants";
|
|
4
|
+
import { manifestByInteraction } from "./key-lookups";
|
|
5
|
+
import {
|
|
6
|
+
mapAction,
|
|
7
|
+
mapAgentRun,
|
|
8
|
+
mapAi,
|
|
9
|
+
mapArtifact,
|
|
10
|
+
mapEvalResult,
|
|
11
|
+
mapLog,
|
|
12
|
+
mapMetricExemplar,
|
|
13
|
+
mapRetrievalEvent,
|
|
14
|
+
mapSpan,
|
|
15
|
+
mapToolCall,
|
|
16
|
+
} from "./mappers";
|
|
17
|
+
import type { ActionRef, EntityManifestExtended, ReplayRow } from "./types";
|
|
18
|
+
|
|
19
|
+
const emptyExtendedManifest = (): EntityManifestExtended => ({
|
|
20
|
+
spans: [],
|
|
21
|
+
logs: [],
|
|
22
|
+
usageEvents: [],
|
|
23
|
+
aiCalls: [],
|
|
24
|
+
metricExemplars: [],
|
|
25
|
+
replay: null,
|
|
26
|
+
actions: [],
|
|
27
|
+
agentRuns: [],
|
|
28
|
+
toolCalls: [],
|
|
29
|
+
retrievalEvents: [],
|
|
30
|
+
evalResults: [],
|
|
31
|
+
artifacts: [],
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const earliest = (values: Array<string | null | undefined>): string =>
|
|
35
|
+
values.filter((v): v is string => Boolean(v)).sort()[0] ??
|
|
36
|
+
new Date(0).toISOString();
|
|
37
|
+
|
|
38
|
+
const latest = (values: Array<string | null | undefined>): string | null => {
|
|
39
|
+
const sorted = values.filter((v): v is string => Boolean(v)).sort();
|
|
40
|
+
return sorted[sorted.length - 1] ?? null;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const durationMs = (
|
|
44
|
+
startedAt: string,
|
|
45
|
+
endedAt: string | null,
|
|
46
|
+
): number | null => {
|
|
47
|
+
if (!endedAt) return null;
|
|
48
|
+
const duration = Date.parse(endedAt) - Date.parse(startedAt);
|
|
49
|
+
return Number.isFinite(duration) && duration >= 0 ? duration : null;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const projectLegacyInteraction = async (
|
|
53
|
+
db: SqlDb,
|
|
54
|
+
projectId: string,
|
|
55
|
+
interactionId: string,
|
|
56
|
+
): Promise<EntityManifestExtended> => {
|
|
57
|
+
const manifest = await manifestByInteraction(db, projectId, interactionId);
|
|
58
|
+
const hasSignals =
|
|
59
|
+
manifest.spans.length > 0 ||
|
|
60
|
+
manifest.logs.length > 0 ||
|
|
61
|
+
manifest.usageEvents.length > 0 ||
|
|
62
|
+
manifest.aiCalls.length > 0;
|
|
63
|
+
|
|
64
|
+
if (!hasSignals) return emptyExtendedManifest();
|
|
65
|
+
|
|
66
|
+
const startedAt = earliest([
|
|
67
|
+
...manifest.spans.map((s) => s.startTime),
|
|
68
|
+
...manifest.logs.map((l) => l.occurredAt),
|
|
69
|
+
...manifest.usageEvents.map((u) => u.occurredAt),
|
|
70
|
+
...manifest.aiCalls.map((a) => a.occurredAt),
|
|
71
|
+
]);
|
|
72
|
+
const endedAt = latest([
|
|
73
|
+
...manifest.logs.map((l) => l.occurredAt),
|
|
74
|
+
...manifest.usageEvents.map((u) => u.occurredAt),
|
|
75
|
+
...manifest.aiCalls.map((a) => a.occurredAt),
|
|
76
|
+
]);
|
|
77
|
+
const firstSpan = manifest.spans[0];
|
|
78
|
+
const firstUsage = manifest.usageEvents[0];
|
|
79
|
+
const action: ActionRef = {
|
|
80
|
+
id: interactionId,
|
|
81
|
+
projectId,
|
|
82
|
+
rootActionId: interactionId,
|
|
83
|
+
causedByActionId: null,
|
|
84
|
+
actorType: "human",
|
|
85
|
+
actorId: null,
|
|
86
|
+
actionKind: "browser.interaction",
|
|
87
|
+
name: firstUsage?.eventName ?? firstSpan?.spanName ?? "Legacy interaction",
|
|
88
|
+
status:
|
|
89
|
+
manifest.logs.some((l) => l.severity.toLowerCase() === "error") ||
|
|
90
|
+
manifest.spans.some((s) => s.statusCode >= 2)
|
|
91
|
+
? "error"
|
|
92
|
+
: "ok",
|
|
93
|
+
startedAt,
|
|
94
|
+
endedAt,
|
|
95
|
+
durationMs: durationMs(startedAt, endedAt),
|
|
96
|
+
traceId: firstSpan?.traceId ?? manifest.aiCalls[0]?.traceId ?? null,
|
|
97
|
+
spanId: firstSpan?.spanId ?? null,
|
|
98
|
+
sessionId: firstUsage?.sessionId ?? null,
|
|
99
|
+
interactionId,
|
|
100
|
+
userId: null,
|
|
101
|
+
agentRunId: null,
|
|
102
|
+
stepId: null,
|
|
103
|
+
toolCallId: null,
|
|
104
|
+
promptVersion: null,
|
|
105
|
+
modelName: manifest.aiCalls[0]?.modelName ?? null,
|
|
106
|
+
provider: manifest.aiCalls[0]?.provider ?? null,
|
|
107
|
+
totalCostUsd:
|
|
108
|
+
manifest.aiCalls.length > 0
|
|
109
|
+
? manifest.aiCalls.reduce(
|
|
110
|
+
(sum, call) => sum + (call.totalCostUsd ?? 0),
|
|
111
|
+
0,
|
|
112
|
+
)
|
|
113
|
+
: null,
|
|
114
|
+
causalConfidence: ActionConfidence.Fallback,
|
|
115
|
+
attrsJson: JSON.stringify({ projectedFrom: "interaction_id" }),
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
...manifest,
|
|
120
|
+
actions: [action],
|
|
121
|
+
agentRuns: [],
|
|
122
|
+
toolCalls: [],
|
|
123
|
+
retrievalEvents: [],
|
|
124
|
+
evalResults: [],
|
|
125
|
+
artifacts: [],
|
|
126
|
+
};
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
export async function manifestByAction(
|
|
130
|
+
db: SqlDb,
|
|
131
|
+
projectId: string,
|
|
132
|
+
actionId: string,
|
|
133
|
+
): Promise<EntityManifestExtended> {
|
|
134
|
+
const action = await db
|
|
135
|
+
.prepare(`SELECT * FROM actions WHERE project_id = ? AND id = ? LIMIT 1`)
|
|
136
|
+
.bind(projectId, actionId)
|
|
137
|
+
.first<{
|
|
138
|
+
id: string;
|
|
139
|
+
project_id: string;
|
|
140
|
+
root_action_id: string;
|
|
141
|
+
caused_by_action_id: string | null;
|
|
142
|
+
actor_type: string;
|
|
143
|
+
actor_id: string | null;
|
|
144
|
+
action_kind: string;
|
|
145
|
+
name: string | null;
|
|
146
|
+
status: string;
|
|
147
|
+
started_at: string;
|
|
148
|
+
ended_at: string | null;
|
|
149
|
+
duration_ms: number | null;
|
|
150
|
+
trace_id: string | null;
|
|
151
|
+
span_id: string | null;
|
|
152
|
+
session_id: string | null;
|
|
153
|
+
interaction_id: string | null;
|
|
154
|
+
user_id: string | null;
|
|
155
|
+
agent_run_id: string | null;
|
|
156
|
+
step_id: string | null;
|
|
157
|
+
tool_call_id: string | null;
|
|
158
|
+
prompt_version: string | null;
|
|
159
|
+
model_name: string | null;
|
|
160
|
+
provider: string | null;
|
|
161
|
+
total_cost_usd: number | null;
|
|
162
|
+
attrs_json: string | null;
|
|
163
|
+
}>();
|
|
164
|
+
|
|
165
|
+
if (!action) {
|
|
166
|
+
return projectLegacyInteraction(db, projectId, actionId);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Retrieve all actions that share the same root_action_id
|
|
170
|
+
const actionsRes = await db
|
|
171
|
+
.prepare(
|
|
172
|
+
`SELECT * FROM actions
|
|
173
|
+
WHERE project_id = ? AND root_action_id = ?
|
|
174
|
+
ORDER BY started_at ASC LIMIT ?`,
|
|
175
|
+
)
|
|
176
|
+
.bind(projectId, action.root_action_id, FETCH_LIMIT)
|
|
177
|
+
.all<Parameters<typeof mapAction>[0]>();
|
|
178
|
+
|
|
179
|
+
const matchedActions = actionsRes.results;
|
|
180
|
+
const actionIds = matchedActions.map((a) => a.id);
|
|
181
|
+
|
|
182
|
+
// Execute child queries in parallel
|
|
183
|
+
const placeholders = actionIds.map(() => "?").join(", ");
|
|
184
|
+
const [agentRuns, toolCalls, retrievalEvents, evalResults, artifacts] =
|
|
185
|
+
await Promise.all([
|
|
186
|
+
db
|
|
187
|
+
.prepare(
|
|
188
|
+
`SELECT * FROM agent_runs
|
|
189
|
+
WHERE project_id = ? AND id = ?
|
|
190
|
+
LIMIT 1`,
|
|
191
|
+
)
|
|
192
|
+
.bind(projectId, action.root_action_id)
|
|
193
|
+
.all<Parameters<typeof mapAgentRun>[0]>(),
|
|
194
|
+
actionIds.length > 0
|
|
195
|
+
? db
|
|
196
|
+
.prepare(
|
|
197
|
+
`SELECT * FROM tool_calls
|
|
198
|
+
WHERE project_id = ? AND action_id IN (${placeholders})
|
|
199
|
+
LIMIT ?`,
|
|
200
|
+
)
|
|
201
|
+
.bind(projectId, ...actionIds, FETCH_LIMIT)
|
|
202
|
+
.all<Parameters<typeof mapToolCall>[0]>()
|
|
203
|
+
: { results: [] },
|
|
204
|
+
actionIds.length > 0
|
|
205
|
+
? db
|
|
206
|
+
.prepare(
|
|
207
|
+
`SELECT * FROM retrieval_events
|
|
208
|
+
WHERE project_id = ? AND action_id IN (${placeholders})
|
|
209
|
+
LIMIT ?`,
|
|
210
|
+
)
|
|
211
|
+
.bind(projectId, ...actionIds, FETCH_LIMIT)
|
|
212
|
+
.all<Parameters<typeof mapRetrievalEvent>[0]>()
|
|
213
|
+
: { results: [] },
|
|
214
|
+
actionIds.length > 0
|
|
215
|
+
? db
|
|
216
|
+
.prepare(
|
|
217
|
+
`SELECT * FROM eval_results
|
|
218
|
+
WHERE project_id = ? AND action_id IN (${placeholders})
|
|
219
|
+
LIMIT ?`,
|
|
220
|
+
)
|
|
221
|
+
.bind(projectId, ...actionIds, FETCH_LIMIT)
|
|
222
|
+
.all<Parameters<typeof mapEvalResult>[0]>()
|
|
223
|
+
: { results: [] },
|
|
224
|
+
actionIds.length > 0
|
|
225
|
+
? db
|
|
226
|
+
.prepare(
|
|
227
|
+
`SELECT * FROM artifacts
|
|
228
|
+
WHERE project_id = ? AND action_id IN (${placeholders})
|
|
229
|
+
LIMIT ?`,
|
|
230
|
+
)
|
|
231
|
+
.bind(projectId, ...actionIds, FETCH_LIMIT)
|
|
232
|
+
.all<Parameters<typeof mapArtifact>[0]>()
|
|
233
|
+
: { results: [] },
|
|
234
|
+
]);
|
|
235
|
+
|
|
236
|
+
// Extract unique trace IDs and session IDs
|
|
237
|
+
const traceIds = Array.from(
|
|
238
|
+
new Set(
|
|
239
|
+
matchedActions
|
|
240
|
+
.map((a) => a.trace_id)
|
|
241
|
+
.filter((t): t is string => Boolean(t)),
|
|
242
|
+
),
|
|
243
|
+
);
|
|
244
|
+
const sessionIds = Array.from(
|
|
245
|
+
new Set(
|
|
246
|
+
matchedActions
|
|
247
|
+
.map((a) => a.session_id)
|
|
248
|
+
.filter((s): s is string => Boolean(s)),
|
|
249
|
+
),
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
let spansRes = { results: [] as Parameters<typeof mapSpan>[0][] };
|
|
253
|
+
let logsRes = { results: [] as Parameters<typeof mapLog>[0][] };
|
|
254
|
+
let aiCallsRes = { results: [] as Parameters<typeof mapAi>[0][] };
|
|
255
|
+
let metricExemplarsRes = {
|
|
256
|
+
results: [] as Parameters<typeof mapMetricExemplar>[0][],
|
|
257
|
+
};
|
|
258
|
+
let replayRes: ReplayRow | null = null;
|
|
259
|
+
|
|
260
|
+
if (traceIds.length > 0) {
|
|
261
|
+
const tracePlaceholders = traceIds.map(() => "?").join(", ");
|
|
262
|
+
const [s, l, a, e] = await Promise.all([
|
|
263
|
+
db
|
|
264
|
+
.prepare(
|
|
265
|
+
`SELECT trace_id, span_id, parent_span_id, service_name, span_name,
|
|
266
|
+
status_code, status_message, start_time, duration_ms, interaction_id
|
|
267
|
+
FROM telemetry_spans
|
|
268
|
+
WHERE project_id = ? AND trace_id IN (${tracePlaceholders})
|
|
269
|
+
ORDER BY start_time ASC LIMIT ?`,
|
|
270
|
+
)
|
|
271
|
+
.bind(projectId, ...traceIds, FETCH_LIMIT)
|
|
272
|
+
.all<Parameters<typeof mapSpan>[0]>(),
|
|
273
|
+
db
|
|
274
|
+
.prepare(
|
|
275
|
+
`SELECT log_id, trace_id, span_id, service_name, logger_name,
|
|
276
|
+
severity, message, occurred_at, interaction_id, session_id
|
|
277
|
+
FROM logs
|
|
278
|
+
WHERE project_id = ? AND trace_id IN (${tracePlaceholders})
|
|
279
|
+
ORDER BY occurred_at ASC LIMIT ?`,
|
|
280
|
+
)
|
|
281
|
+
.bind(projectId, ...traceIds, FETCH_LIMIT)
|
|
282
|
+
.all<Parameters<typeof mapLog>[0]>(),
|
|
283
|
+
db
|
|
284
|
+
.prepare(
|
|
285
|
+
`SELECT call_id, trace_id, model_name, provider, total_cost_usd,
|
|
286
|
+
occurred_at, interaction_id, session_id
|
|
287
|
+
FROM ai_calls
|
|
288
|
+
WHERE project_id = ? AND trace_id IN (${tracePlaceholders})
|
|
289
|
+
ORDER BY occurred_at ASC LIMIT ?`,
|
|
290
|
+
)
|
|
291
|
+
.bind(projectId, ...traceIds, FETCH_LIMIT)
|
|
292
|
+
.all<Parameters<typeof mapAi>[0]>(),
|
|
293
|
+
db
|
|
294
|
+
.prepare(
|
|
295
|
+
`SELECT id, point_id, series_id, metric_name, service_name,
|
|
296
|
+
trace_id, span_id, ts_ns, value, received_at
|
|
297
|
+
FROM metric_exemplars
|
|
298
|
+
WHERE project_id = ? AND trace_id IN (${tracePlaceholders})
|
|
299
|
+
ORDER BY ts_ns DESC LIMIT ?`,
|
|
300
|
+
)
|
|
301
|
+
.bind(projectId, ...traceIds, FETCH_LIMIT)
|
|
302
|
+
.all<Parameters<typeof mapMetricExemplar>[0]>(),
|
|
303
|
+
]);
|
|
304
|
+
spansRes = s;
|
|
305
|
+
logsRes = l;
|
|
306
|
+
aiCallsRes = a;
|
|
307
|
+
metricExemplarsRes = e;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (sessionIds.length > 0) {
|
|
311
|
+
const sessionPlaceholders = sessionIds.map(() => "?").join(", ");
|
|
312
|
+
replayRes = await db
|
|
313
|
+
.prepare(
|
|
314
|
+
`SELECT session_id, first_chunk_at, last_chunk_at, chunk_count, events_count
|
|
315
|
+
FROM session_replay_metadata
|
|
316
|
+
WHERE session_id IN (${sessionPlaceholders})
|
|
317
|
+
ORDER BY last_chunk_at DESC LIMIT 1`,
|
|
318
|
+
)
|
|
319
|
+
.bind(...sessionIds)
|
|
320
|
+
.first<{
|
|
321
|
+
session_id: string;
|
|
322
|
+
first_chunk_at: string;
|
|
323
|
+
last_chunk_at: string;
|
|
324
|
+
chunk_count: number;
|
|
325
|
+
events_count: number;
|
|
326
|
+
}>();
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const sumAiCost = aiCallsRes.results.reduce(
|
|
330
|
+
(sum, a) => sum + (a.total_cost_usd ?? 0),
|
|
331
|
+
0,
|
|
332
|
+
);
|
|
333
|
+
const startTimes = matchedActions.map((a) => a.started_at).filter(Boolean);
|
|
334
|
+
const endTimes = matchedActions.map((a) => a.ended_at).filter(Boolean);
|
|
335
|
+
const earliestStart = startTimes.sort()[0] ?? null;
|
|
336
|
+
const latestEnd = endTimes.sort()[endTimes.length - 1] ?? null;
|
|
337
|
+
const totalDurationMs =
|
|
338
|
+
earliestStart && latestEnd
|
|
339
|
+
? Date.parse(latestEnd) - Date.parse(earliestStart)
|
|
340
|
+
: null;
|
|
341
|
+
|
|
342
|
+
const mappedRuns = agentRuns.results.map((r) => {
|
|
343
|
+
const mapped = mapAgentRun(r);
|
|
344
|
+
return {
|
|
345
|
+
...mapped,
|
|
346
|
+
totalCostUsd: sumAiCost || mapped.totalCostUsd || 0.0,
|
|
347
|
+
totalDurationMs: totalDurationMs || mapped.totalDurationMs,
|
|
348
|
+
};
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
return {
|
|
352
|
+
spans: spansRes.results.map(mapSpan),
|
|
353
|
+
logs: logsRes.results.map(mapLog),
|
|
354
|
+
usageEvents: [],
|
|
355
|
+
aiCalls: aiCallsRes.results.map(mapAi),
|
|
356
|
+
metricExemplars: metricExemplarsRes.results.map(mapMetricExemplar),
|
|
357
|
+
replay: replayRes
|
|
358
|
+
? {
|
|
359
|
+
sessionId: replayRes.session_id,
|
|
360
|
+
firstChunkAt: replayRes.first_chunk_at,
|
|
361
|
+
lastChunkAt: replayRes.last_chunk_at,
|
|
362
|
+
chunkCount: replayRes.chunk_count,
|
|
363
|
+
eventsCount: replayRes.events_count,
|
|
364
|
+
}
|
|
365
|
+
: null,
|
|
366
|
+
actions: matchedActions.map(mapAction),
|
|
367
|
+
agentRuns: mappedRuns,
|
|
368
|
+
toolCalls: toolCalls.results.map(mapToolCall),
|
|
369
|
+
retrievalEvents: retrievalEvents.results.map(mapRetrievalEvent),
|
|
370
|
+
evalResults: evalResults.results.map(mapEvalResult),
|
|
371
|
+
artifacts: artifacts.results.map(mapArtifact),
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
export async function manifestByAgentRun(
|
|
376
|
+
db: SqlDb,
|
|
377
|
+
projectId: string,
|
|
378
|
+
agentRunId: string,
|
|
379
|
+
): Promise<EntityManifestExtended> {
|
|
380
|
+
const run = await db
|
|
381
|
+
.prepare(`SELECT * FROM agent_runs WHERE project_id = ? AND id = ? LIMIT 1`)
|
|
382
|
+
.bind(projectId, agentRunId)
|
|
383
|
+
.first<{
|
|
384
|
+
id: string;
|
|
385
|
+
project_id: string;
|
|
386
|
+
agent_id: string;
|
|
387
|
+
agent_name: string;
|
|
388
|
+
agent_version: string;
|
|
389
|
+
goal: string | null;
|
|
390
|
+
outcome: string | null;
|
|
391
|
+
autonomy_level: string;
|
|
392
|
+
status: string;
|
|
393
|
+
error_message: string | null;
|
|
394
|
+
total_cost_usd: number | null;
|
|
395
|
+
total_duration_ms: number | null;
|
|
396
|
+
metadata_json: string | null;
|
|
397
|
+
}>();
|
|
398
|
+
|
|
399
|
+
if (!run) {
|
|
400
|
+
return emptyExtendedManifest();
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const actionsRes = await db
|
|
404
|
+
.prepare(
|
|
405
|
+
`SELECT * FROM actions
|
|
406
|
+
WHERE project_id = ? AND (root_action_id = ? OR agent_run_id = ?)
|
|
407
|
+
ORDER BY started_at ASC LIMIT ?`,
|
|
408
|
+
)
|
|
409
|
+
.bind(projectId, agentRunId, agentRunId, FETCH_LIMIT)
|
|
410
|
+
.all<Parameters<typeof mapAction>[0]>();
|
|
411
|
+
|
|
412
|
+
const matchedActions = actionsRes.results;
|
|
413
|
+
const actionIds = matchedActions.map((a) => a.id);
|
|
414
|
+
|
|
415
|
+
// Execute child queries in parallel
|
|
416
|
+
const placeholders = actionIds.map(() => "?").join(", ");
|
|
417
|
+
const [agentRuns, toolCalls, retrievalEvents, evalResults, artifacts] =
|
|
418
|
+
await Promise.all([
|
|
419
|
+
db
|
|
420
|
+
.prepare(
|
|
421
|
+
`SELECT * FROM agent_runs
|
|
422
|
+
WHERE project_id = ? AND id = ?
|
|
423
|
+
LIMIT 1`,
|
|
424
|
+
)
|
|
425
|
+
.bind(projectId, agentRunId)
|
|
426
|
+
.all<Parameters<typeof mapAgentRun>[0]>(),
|
|
427
|
+
actionIds.length > 0
|
|
428
|
+
? db
|
|
429
|
+
.prepare(
|
|
430
|
+
`SELECT * FROM tool_calls
|
|
431
|
+
WHERE project_id = ? AND action_id IN (${placeholders})
|
|
432
|
+
LIMIT ?`,
|
|
433
|
+
)
|
|
434
|
+
.bind(projectId, ...actionIds, FETCH_LIMIT)
|
|
435
|
+
.all<Parameters<typeof mapToolCall>[0]>()
|
|
436
|
+
: { results: [] },
|
|
437
|
+
actionIds.length > 0
|
|
438
|
+
? db
|
|
439
|
+
.prepare(
|
|
440
|
+
`SELECT * FROM retrieval_events
|
|
441
|
+
WHERE project_id = ? AND action_id IN (${placeholders})
|
|
442
|
+
LIMIT ?`,
|
|
443
|
+
)
|
|
444
|
+
.bind(projectId, ...actionIds, FETCH_LIMIT)
|
|
445
|
+
.all<Parameters<typeof mapRetrievalEvent>[0]>()
|
|
446
|
+
: { results: [] },
|
|
447
|
+
actionIds.length > 0
|
|
448
|
+
? db
|
|
449
|
+
.prepare(
|
|
450
|
+
`SELECT * FROM eval_results
|
|
451
|
+
WHERE project_id = ? AND action_id IN (${placeholders})
|
|
452
|
+
LIMIT ?`,
|
|
453
|
+
)
|
|
454
|
+
.bind(projectId, ...actionIds, FETCH_LIMIT)
|
|
455
|
+
.all<Parameters<typeof mapEvalResult>[0]>()
|
|
456
|
+
: { results: [] },
|
|
457
|
+
actionIds.length > 0
|
|
458
|
+
? db
|
|
459
|
+
.prepare(
|
|
460
|
+
`SELECT * FROM artifacts
|
|
461
|
+
WHERE project_id = ? AND action_id IN (${placeholders})
|
|
462
|
+
LIMIT ?`,
|
|
463
|
+
)
|
|
464
|
+
.bind(projectId, ...actionIds, FETCH_LIMIT)
|
|
465
|
+
.all<Parameters<typeof mapArtifact>[0]>()
|
|
466
|
+
: { results: [] },
|
|
467
|
+
]);
|
|
468
|
+
|
|
469
|
+
// Extract unique trace IDs and session IDs
|
|
470
|
+
const traceIds = Array.from(
|
|
471
|
+
new Set(
|
|
472
|
+
matchedActions
|
|
473
|
+
.map((a) => a.trace_id)
|
|
474
|
+
.filter((t): t is string => Boolean(t)),
|
|
475
|
+
),
|
|
476
|
+
);
|
|
477
|
+
const sessionIds = Array.from(
|
|
478
|
+
new Set(
|
|
479
|
+
matchedActions
|
|
480
|
+
.map((a) => a.session_id)
|
|
481
|
+
.filter((s): s is string => Boolean(s)),
|
|
482
|
+
),
|
|
483
|
+
);
|
|
484
|
+
|
|
485
|
+
let spansRes = { results: [] as Parameters<typeof mapSpan>[0][] };
|
|
486
|
+
let logsRes = { results: [] as Parameters<typeof mapLog>[0][] };
|
|
487
|
+
let aiCallsRes = { results: [] as Parameters<typeof mapAi>[0][] };
|
|
488
|
+
let metricExemplarsRes = {
|
|
489
|
+
results: [] as Parameters<typeof mapMetricExemplar>[0][],
|
|
490
|
+
};
|
|
491
|
+
let replayRes: ReplayRow | null = null;
|
|
492
|
+
|
|
493
|
+
if (traceIds.length > 0) {
|
|
494
|
+
const tracePlaceholders = traceIds.map(() => "?").join(", ");
|
|
495
|
+
const [s, l, a, e] = await Promise.all([
|
|
496
|
+
db
|
|
497
|
+
.prepare(
|
|
498
|
+
`SELECT trace_id, span_id, parent_span_id, service_name, span_name,
|
|
499
|
+
status_code, status_message, start_time, duration_ms, interaction_id
|
|
500
|
+
FROM telemetry_spans
|
|
501
|
+
WHERE project_id = ? AND trace_id IN (${tracePlaceholders})
|
|
502
|
+
ORDER BY start_time ASC LIMIT ?`,
|
|
503
|
+
)
|
|
504
|
+
.bind(projectId, ...traceIds, FETCH_LIMIT)
|
|
505
|
+
.all<Parameters<typeof mapSpan>[0]>(),
|
|
506
|
+
db
|
|
507
|
+
.prepare(
|
|
508
|
+
`SELECT log_id, trace_id, span_id, service_name, logger_name,
|
|
509
|
+
severity, message, occurred_at, interaction_id, session_id
|
|
510
|
+
FROM logs
|
|
511
|
+
WHERE project_id = ? AND trace_id IN (${tracePlaceholders})
|
|
512
|
+
ORDER BY occurred_at ASC LIMIT ?`,
|
|
513
|
+
)
|
|
514
|
+
.bind(projectId, ...traceIds, FETCH_LIMIT)
|
|
515
|
+
.all<Parameters<typeof mapLog>[0]>(),
|
|
516
|
+
db
|
|
517
|
+
.prepare(
|
|
518
|
+
`SELECT call_id, trace_id, model_name, provider, total_cost_usd,
|
|
519
|
+
occurred_at, interaction_id, session_id
|
|
520
|
+
FROM ai_calls
|
|
521
|
+
WHERE project_id = ? AND trace_id IN (${tracePlaceholders})
|
|
522
|
+
ORDER BY occurred_at ASC LIMIT ?`,
|
|
523
|
+
)
|
|
524
|
+
.bind(projectId, ...traceIds, FETCH_LIMIT)
|
|
525
|
+
.all<Parameters<typeof mapAi>[0]>(),
|
|
526
|
+
db
|
|
527
|
+
.prepare(
|
|
528
|
+
`SELECT id, point_id, series_id, metric_name, service_name,
|
|
529
|
+
trace_id, span_id, ts_ns, value, received_at
|
|
530
|
+
FROM metric_exemplars
|
|
531
|
+
WHERE project_id = ? AND trace_id IN (${tracePlaceholders})
|
|
532
|
+
ORDER BY ts_ns DESC LIMIT ?`,
|
|
533
|
+
)
|
|
534
|
+
.bind(projectId, ...traceIds, FETCH_LIMIT)
|
|
535
|
+
.all<Parameters<typeof mapMetricExemplar>[0]>(),
|
|
536
|
+
]);
|
|
537
|
+
spansRes = s;
|
|
538
|
+
logsRes = l;
|
|
539
|
+
aiCallsRes = a;
|
|
540
|
+
metricExemplarsRes = e;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
if (sessionIds.length > 0) {
|
|
544
|
+
const sessionPlaceholders = sessionIds.map(() => "?").join(", ");
|
|
545
|
+
replayRes = await db
|
|
546
|
+
.prepare(
|
|
547
|
+
`SELECT session_id, first_chunk_at, last_chunk_at, chunk_count, events_count
|
|
548
|
+
FROM session_replay_metadata
|
|
549
|
+
WHERE session_id IN (${sessionPlaceholders})
|
|
550
|
+
ORDER BY last_chunk_at DESC LIMIT 1`,
|
|
551
|
+
)
|
|
552
|
+
.bind(...sessionIds)
|
|
553
|
+
.first<{
|
|
554
|
+
session_id: string;
|
|
555
|
+
first_chunk_at: string;
|
|
556
|
+
last_chunk_at: string;
|
|
557
|
+
chunk_count: number;
|
|
558
|
+
events_count: number;
|
|
559
|
+
}>();
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
const sumAiCost = aiCallsRes.results.reduce(
|
|
563
|
+
(sum, a) => sum + (a.total_cost_usd ?? 0),
|
|
564
|
+
0,
|
|
565
|
+
);
|
|
566
|
+
const startTimes = matchedActions.map((a) => a.started_at).filter(Boolean);
|
|
567
|
+
const endTimes = matchedActions.map((a) => a.ended_at).filter(Boolean);
|
|
568
|
+
const earliestStart = startTimes.sort()[0] ?? null;
|
|
569
|
+
const latestEnd = endTimes.sort()[endTimes.length - 1] ?? null;
|
|
570
|
+
const totalDurationMs =
|
|
571
|
+
earliestStart && latestEnd
|
|
572
|
+
? Date.parse(latestEnd) - Date.parse(earliestStart)
|
|
573
|
+
: null;
|
|
574
|
+
|
|
575
|
+
const mappedRuns = agentRuns.results.map((r) => {
|
|
576
|
+
const mapped = mapAgentRun(r);
|
|
577
|
+
return {
|
|
578
|
+
...mapped,
|
|
579
|
+
totalCostUsd: sumAiCost || mapped.totalCostUsd || 0.0,
|
|
580
|
+
totalDurationMs: totalDurationMs || mapped.totalDurationMs,
|
|
581
|
+
};
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
return {
|
|
585
|
+
spans: spansRes.results.map(mapSpan),
|
|
586
|
+
logs: logsRes.results.map(mapLog),
|
|
587
|
+
usageEvents: [],
|
|
588
|
+
aiCalls: aiCallsRes.results.map(mapAi),
|
|
589
|
+
metricExemplars: metricExemplarsRes.results.map(mapMetricExemplar),
|
|
590
|
+
replay: replayRes
|
|
591
|
+
? {
|
|
592
|
+
sessionId: replayRes.session_id,
|
|
593
|
+
firstChunkAt: replayRes.first_chunk_at,
|
|
594
|
+
lastChunkAt: replayRes.last_chunk_at,
|
|
595
|
+
chunkCount: replayRes.chunk_count,
|
|
596
|
+
eventsCount: replayRes.events_count,
|
|
597
|
+
}
|
|
598
|
+
: null,
|
|
599
|
+
actions: matchedActions.map(mapAction),
|
|
600
|
+
agentRuns: mappedRuns,
|
|
601
|
+
toolCalls: toolCalls.results.map(mapToolCall),
|
|
602
|
+
retrievalEvents: retrievalEvents.results.map(mapRetrievalEvent),
|
|
603
|
+
evalResults: evalResults.results.map(mapEvalResult),
|
|
604
|
+
artifacts: artifacts.results.map(mapArtifact),
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
export async function manifestByActor(
|
|
609
|
+
db: SqlDb,
|
|
610
|
+
projectId: string,
|
|
611
|
+
actorType: string,
|
|
612
|
+
actorId: string,
|
|
613
|
+
): Promise<EntityManifestExtended> {
|
|
614
|
+
const actionsRes = await db
|
|
615
|
+
.prepare(
|
|
616
|
+
`SELECT * FROM actions
|
|
617
|
+
WHERE project_id = ? AND actor_type = ? AND actor_id = ?
|
|
618
|
+
ORDER BY started_at DESC LIMIT ?`,
|
|
619
|
+
)
|
|
620
|
+
.bind(projectId, actorType, actorId, FETCH_LIMIT)
|
|
621
|
+
.all<Parameters<typeof mapAction>[0]>();
|
|
622
|
+
|
|
623
|
+
const matchedActions = actionsRes.results;
|
|
624
|
+
if (matchedActions.length === 0) {
|
|
625
|
+
return emptyExtendedManifest();
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
const actionIds = matchedActions.map((a) => a.id);
|
|
629
|
+
const rootActionIds = Array.from(
|
|
630
|
+
new Set(matchedActions.map((a) => a.root_action_id)),
|
|
631
|
+
);
|
|
632
|
+
|
|
633
|
+
// Execute child queries in parallel
|
|
634
|
+
const placeholders = actionIds.map(() => "?").join(", ");
|
|
635
|
+
const rootPlaceholders = rootActionIds.map(() => "?").join(", ");
|
|
636
|
+
const [agentRuns, toolCalls, retrievalEvents, evalResults, artifacts] =
|
|
637
|
+
await Promise.all([
|
|
638
|
+
rootActionIds.length > 0
|
|
639
|
+
? db
|
|
640
|
+
.prepare(
|
|
641
|
+
`SELECT * FROM agent_runs
|
|
642
|
+
WHERE project_id = ? AND id IN (${rootPlaceholders})
|
|
643
|
+
LIMIT ?`,
|
|
644
|
+
)
|
|
645
|
+
.bind(projectId, ...rootActionIds, FETCH_LIMIT)
|
|
646
|
+
.all<Parameters<typeof mapAgentRun>[0]>()
|
|
647
|
+
: { results: [] },
|
|
648
|
+
actionIds.length > 0
|
|
649
|
+
? db
|
|
650
|
+
.prepare(
|
|
651
|
+
`SELECT * FROM tool_calls
|
|
652
|
+
WHERE project_id = ? AND action_id IN (${placeholders})
|
|
653
|
+
LIMIT ?`,
|
|
654
|
+
)
|
|
655
|
+
.bind(projectId, ...actionIds, FETCH_LIMIT)
|
|
656
|
+
.all<Parameters<typeof mapToolCall>[0]>()
|
|
657
|
+
: { results: [] },
|
|
658
|
+
actionIds.length > 0
|
|
659
|
+
? db
|
|
660
|
+
.prepare(
|
|
661
|
+
`SELECT * FROM retrieval_events
|
|
662
|
+
WHERE project_id = ? AND action_id IN (${placeholders})
|
|
663
|
+
LIMIT ?`,
|
|
664
|
+
)
|
|
665
|
+
.bind(projectId, ...actionIds, FETCH_LIMIT)
|
|
666
|
+
.all<Parameters<typeof mapRetrievalEvent>[0]>()
|
|
667
|
+
: { results: [] },
|
|
668
|
+
actionIds.length > 0
|
|
669
|
+
? db
|
|
670
|
+
.prepare(
|
|
671
|
+
`SELECT * FROM eval_results
|
|
672
|
+
WHERE project_id = ? AND action_id IN (${placeholders})
|
|
673
|
+
LIMIT ?`,
|
|
674
|
+
)
|
|
675
|
+
.bind(projectId, ...actionIds, FETCH_LIMIT)
|
|
676
|
+
.all<Parameters<typeof mapEvalResult>[0]>()
|
|
677
|
+
: { results: [] },
|
|
678
|
+
actionIds.length > 0
|
|
679
|
+
? db
|
|
680
|
+
.prepare(
|
|
681
|
+
`SELECT * FROM artifacts
|
|
682
|
+
WHERE project_id = ? AND action_id IN (${placeholders})
|
|
683
|
+
LIMIT ?`,
|
|
684
|
+
)
|
|
685
|
+
.bind(projectId, ...actionIds, FETCH_LIMIT)
|
|
686
|
+
.all<Parameters<typeof mapArtifact>[0]>()
|
|
687
|
+
: { results: [] },
|
|
688
|
+
]);
|
|
689
|
+
|
|
690
|
+
// Extract unique trace IDs and session IDs
|
|
691
|
+
const traceIds = Array.from(
|
|
692
|
+
new Set(
|
|
693
|
+
matchedActions
|
|
694
|
+
.map((a) => a.trace_id)
|
|
695
|
+
.filter((t): t is string => Boolean(t)),
|
|
696
|
+
),
|
|
697
|
+
);
|
|
698
|
+
const sessionIds = Array.from(
|
|
699
|
+
new Set(
|
|
700
|
+
matchedActions
|
|
701
|
+
.map((a) => a.session_id)
|
|
702
|
+
.filter((s): s is string => Boolean(s)),
|
|
703
|
+
),
|
|
704
|
+
);
|
|
705
|
+
|
|
706
|
+
let spansRes = { results: [] as Parameters<typeof mapSpan>[0][] };
|
|
707
|
+
let logsRes = { results: [] as Parameters<typeof mapLog>[0][] };
|
|
708
|
+
let aiCallsRes = { results: [] as Parameters<typeof mapAi>[0][] };
|
|
709
|
+
let metricExemplarsRes = {
|
|
710
|
+
results: [] as Parameters<typeof mapMetricExemplar>[0][],
|
|
711
|
+
};
|
|
712
|
+
let replayRes: ReplayRow | null = null;
|
|
713
|
+
|
|
714
|
+
if (traceIds.length > 0) {
|
|
715
|
+
const tracePlaceholders = traceIds.map(() => "?").join(", ");
|
|
716
|
+
const [s, l, a, e] = await Promise.all([
|
|
717
|
+
db
|
|
718
|
+
.prepare(
|
|
719
|
+
`SELECT trace_id, span_id, parent_span_id, service_name, span_name,
|
|
720
|
+
status_code, status_message, start_time, duration_ms, interaction_id
|
|
721
|
+
FROM telemetry_spans
|
|
722
|
+
WHERE project_id = ? AND trace_id IN (${tracePlaceholders})
|
|
723
|
+
ORDER BY start_time ASC LIMIT ?`,
|
|
724
|
+
)
|
|
725
|
+
.bind(projectId, ...traceIds, FETCH_LIMIT)
|
|
726
|
+
.all<Parameters<typeof mapSpan>[0]>(),
|
|
727
|
+
db
|
|
728
|
+
.prepare(
|
|
729
|
+
`SELECT log_id, trace_id, span_id, service_name, logger_name,
|
|
730
|
+
severity, message, occurred_at, interaction_id, session_id
|
|
731
|
+
FROM logs
|
|
732
|
+
WHERE project_id = ? AND trace_id IN (${tracePlaceholders})
|
|
733
|
+
ORDER BY occurred_at ASC LIMIT ?`,
|
|
734
|
+
)
|
|
735
|
+
.bind(projectId, ...traceIds, FETCH_LIMIT)
|
|
736
|
+
.all<Parameters<typeof mapLog>[0]>(),
|
|
737
|
+
db
|
|
738
|
+
.prepare(
|
|
739
|
+
`SELECT call_id, trace_id, model_name, provider, total_cost_usd,
|
|
740
|
+
occurred_at, interaction_id, session_id
|
|
741
|
+
FROM ai_calls
|
|
742
|
+
WHERE project_id = ? AND trace_id IN (${tracePlaceholders})
|
|
743
|
+
ORDER BY occurred_at ASC LIMIT ?`,
|
|
744
|
+
)
|
|
745
|
+
.bind(projectId, ...traceIds, FETCH_LIMIT)
|
|
746
|
+
.all<Parameters<typeof mapAi>[0]>(),
|
|
747
|
+
db
|
|
748
|
+
.prepare(
|
|
749
|
+
`SELECT id, point_id, series_id, metric_name, service_name,
|
|
750
|
+
trace_id, span_id, ts_ns, value, received_at
|
|
751
|
+
FROM metric_exemplars
|
|
752
|
+
WHERE project_id = ? AND trace_id IN (${tracePlaceholders})
|
|
753
|
+
ORDER BY ts_ns DESC LIMIT ?`,
|
|
754
|
+
)
|
|
755
|
+
.bind(projectId, ...traceIds, FETCH_LIMIT)
|
|
756
|
+
.all<Parameters<typeof mapMetricExemplar>[0]>(),
|
|
757
|
+
]);
|
|
758
|
+
spansRes = s;
|
|
759
|
+
logsRes = l;
|
|
760
|
+
aiCallsRes = a;
|
|
761
|
+
metricExemplarsRes = e;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
if (sessionIds.length > 0) {
|
|
765
|
+
const sessionPlaceholders = sessionIds.map(() => "?").join(", ");
|
|
766
|
+
replayRes = await db
|
|
767
|
+
.prepare(
|
|
768
|
+
`SELECT session_id, first_chunk_at, last_chunk_at, chunk_count, events_count
|
|
769
|
+
FROM session_replay_metadata
|
|
770
|
+
WHERE session_id IN (${sessionPlaceholders})
|
|
771
|
+
ORDER BY last_chunk_at DESC LIMIT 1`,
|
|
772
|
+
)
|
|
773
|
+
.bind(...sessionIds)
|
|
774
|
+
.first<{
|
|
775
|
+
session_id: string;
|
|
776
|
+
first_chunk_at: string;
|
|
777
|
+
last_chunk_at: string;
|
|
778
|
+
chunk_count: number;
|
|
779
|
+
events_count: number;
|
|
780
|
+
}>();
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
return {
|
|
784
|
+
spans: spansRes.results.map(mapSpan),
|
|
785
|
+
logs: logsRes.results.map(mapLog),
|
|
786
|
+
usageEvents: [],
|
|
787
|
+
aiCalls: aiCallsRes.results.map(mapAi),
|
|
788
|
+
metricExemplars: metricExemplarsRes.results.map(mapMetricExemplar),
|
|
789
|
+
replay: replayRes
|
|
790
|
+
? {
|
|
791
|
+
sessionId: replayRes.session_id,
|
|
792
|
+
firstChunkAt: replayRes.first_chunk_at,
|
|
793
|
+
lastChunkAt: replayRes.last_chunk_at,
|
|
794
|
+
chunkCount: replayRes.chunk_count,
|
|
795
|
+
eventsCount: replayRes.events_count,
|
|
796
|
+
}
|
|
797
|
+
: null,
|
|
798
|
+
actions: matchedActions.map(mapAction),
|
|
799
|
+
agentRuns: agentRuns.results.map(mapAgentRun),
|
|
800
|
+
toolCalls: toolCalls.results.map(mapToolCall),
|
|
801
|
+
retrievalEvents: retrievalEvents.results.map(mapRetrievalEvent),
|
|
802
|
+
evalResults: evalResults.results.map(mapEvalResult),
|
|
803
|
+
artifacts: artifacts.results.map(mapArtifact),
|
|
804
|
+
};
|
|
805
|
+
}
|