agent-inspect 1.5.0 → 1.7.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 +41 -0
- package/README.md +14 -4
- package/docs/ADAPTER-CONFORMANCE.md +35 -0
- package/docs/ADAPTERS.md +79 -1
- package/docs/API.md +184 -10
- package/docs/ARCHITECTURE.md +4 -0
- package/docs/CLI.md +41 -12
- package/docs/KNOWN-ISSUES.md +11 -1
- package/docs/LIMITATIONS.md +19 -2
- package/docs/SCHEMA.md +17 -7
- package/package.json +23 -2
- package/packages/cli/dist/index.cjs +2449 -157
- package/packages/cli/dist/index.cjs.map +1 -1
- package/packages/cli/dist/index.mjs +2450 -158
- package/packages/cli/dist/index.mjs.map +1 -1
- package/packages/core/dist/advanced.cjs +839 -18
- package/packages/core/dist/advanced.cjs.map +1 -1
- package/packages/core/dist/advanced.d.cts +98 -3
- package/packages/core/dist/advanced.d.ts +98 -3
- package/packages/core/dist/advanced.mjs +7 -4
- package/packages/core/dist/chunk-57S5D6HR.mjs +655 -0
- package/packages/core/dist/chunk-57S5D6HR.mjs.map +1 -0
- package/packages/core/dist/chunk-6QSLZCBJ.mjs +743 -0
- package/packages/core/dist/chunk-6QSLZCBJ.mjs.map +1 -0
- package/packages/core/dist/chunk-6SZPTECC.mjs +342 -0
- package/packages/core/dist/chunk-6SZPTECC.mjs.map +1 -0
- package/packages/core/dist/{chunk-QX3ZMPUF.mjs → chunk-74XZ6N7Q.mjs} +13 -55
- package/packages/core/dist/chunk-74XZ6N7Q.mjs.map +1 -0
- package/packages/core/dist/{chunk-QPAU2TPA.mjs → chunk-HR7G62IE.mjs} +4 -4
- package/packages/core/dist/{chunk-QPAU2TPA.mjs.map → chunk-HR7G62IE.mjs.map} +1 -1
- package/packages/core/dist/chunk-S4YWKV4G.mjs +48 -0
- package/packages/core/dist/chunk-S4YWKV4G.mjs.map +1 -0
- package/packages/core/dist/chunk-TFLPUZ56.mjs +1571 -0
- package/packages/core/dist/chunk-TFLPUZ56.mjs.map +1 -0
- package/packages/core/dist/{chunk-Q6EPNB3V.mjs → chunk-TZISEVLQ.mjs} +34 -183
- package/packages/core/dist/chunk-TZISEVLQ.mjs.map +1 -0
- package/packages/core/dist/chunk-U2BGPESY.mjs +150 -0
- package/packages/core/dist/chunk-U2BGPESY.mjs.map +1 -0
- package/packages/core/dist/chunk-VTIB5MDK.mjs +304 -0
- package/packages/core/dist/chunk-VTIB5MDK.mjs.map +1 -0
- package/packages/core/dist/{chunk-5EMIZZXD.mjs → chunk-Y56BPA3B.mjs} +87 -4
- package/packages/core/dist/chunk-Y56BPA3B.mjs.map +1 -0
- package/packages/core/dist/diff.d.cts +3 -2
- package/packages/core/dist/diff.d.ts +3 -2
- package/packages/core/dist/exporters.cjs.map +1 -1
- package/packages/core/dist/exporters.d.cts +3 -2
- package/packages/core/dist/exporters.d.ts +3 -2
- package/packages/core/dist/exporters.mjs +2 -2
- package/packages/core/dist/index.cjs +2975 -229
- package/packages/core/dist/index.cjs.map +1 -1
- package/packages/core/dist/index.d.cts +27 -6
- package/packages/core/dist/index.d.ts +27 -6
- package/packages/core/dist/index.mjs +113 -60
- package/packages/core/dist/index.mjs.map +1 -1
- package/packages/core/dist/{log-config-BzGmDYum.d.cts → inspect-event-Des4JDHo.d.cts} +1 -31
- package/packages/core/dist/{log-config-BzGmDYum.d.ts → inspect-event-Des4JDHo.d.ts} +1 -31
- package/packages/core/dist/log-config-BnH8Ykcb.d.cts +33 -0
- package/packages/core/dist/log-config-C1GcJPIM.d.ts +33 -0
- package/packages/core/dist/logs.d.cts +3 -2
- package/packages/core/dist/logs.d.ts +3 -2
- package/packages/core/dist/logs.mjs +3 -3
- package/packages/core/dist/persisted-inspect-event-0kaRADsp.d.cts +56 -0
- package/packages/core/dist/persisted-inspect-event-DiFto0K2.d.ts +56 -0
- package/packages/core/dist/persisted.cjs +38 -40
- package/packages/core/dist/persisted.cjs.map +1 -1
- package/packages/core/dist/persisted.d.cts +6 -55
- package/packages/core/dist/persisted.d.ts +6 -55
- package/packages/core/dist/persisted.mjs +4 -2
- package/packages/core/dist/readers.cjs +2590 -0
- package/packages/core/dist/readers.cjs.map +1 -0
- package/packages/core/dist/readers.d.cts +80 -0
- package/packages/core/dist/readers.d.ts +80 -0
- package/packages/core/dist/readers.mjs +9 -0
- package/packages/core/dist/readers.mjs.map +1 -0
- package/packages/core/dist/{types-CNbheSdk.d.cts → types-DB8jB6Jg.d.cts} +7 -1
- package/packages/core/dist/{types-Bkt7LS01.d.ts → types-tSix7tfv.d.ts} +7 -1
- package/packages/core/dist/writers.cjs +997 -0
- package/packages/core/dist/writers.cjs.map +1 -0
- package/packages/core/dist/writers.d.cts +62 -0
- package/packages/core/dist/writers.d.ts +62 -0
- package/packages/core/dist/writers.mjs +9 -0
- package/packages/core/dist/writers.mjs.map +1 -0
- package/packages/core/dist/chunk-5EMIZZXD.mjs.map +0 -1
- package/packages/core/dist/chunk-Q6EPNB3V.mjs.map +0 -1
- package/packages/core/dist/chunk-QX3ZMPUF.mjs.map +0 -1
- package/packages/core/dist/chunk-XDBND27A.mjs +0 -975
- package/packages/core/dist/chunk-XDBND27A.mjs.map +0 -1
|
@@ -0,0 +1,2590 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var promises = require('fs/promises');
|
|
4
|
+
var path = require('path');
|
|
5
|
+
var os = require('os');
|
|
6
|
+
|
|
7
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
8
|
+
|
|
9
|
+
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
10
|
+
var os__default = /*#__PURE__*/_interopDefault(os);
|
|
11
|
+
|
|
12
|
+
// packages/core/src/readers/index.ts
|
|
13
|
+
|
|
14
|
+
// packages/core/src/types/persisted-inspect-event.ts
|
|
15
|
+
var INSPECT_KINDS = [
|
|
16
|
+
"RUN",
|
|
17
|
+
"AGENT",
|
|
18
|
+
"LLM",
|
|
19
|
+
"TOOL",
|
|
20
|
+
"CHAIN",
|
|
21
|
+
"RETRIEVER",
|
|
22
|
+
"DECISION",
|
|
23
|
+
"RESULT",
|
|
24
|
+
"ERROR",
|
|
25
|
+
"LOGIC",
|
|
26
|
+
"LOG"
|
|
27
|
+
];
|
|
28
|
+
var ATTRIBUTION_CONFIDENCES = [
|
|
29
|
+
"explicit",
|
|
30
|
+
"correlated",
|
|
31
|
+
"heuristic",
|
|
32
|
+
"unknown"
|
|
33
|
+
];
|
|
34
|
+
var PERSISTED_EVENT_SOURCE_TYPES = [
|
|
35
|
+
"manual",
|
|
36
|
+
"json-log",
|
|
37
|
+
"log4js",
|
|
38
|
+
"adapter",
|
|
39
|
+
"ai-sdk",
|
|
40
|
+
"otel"
|
|
41
|
+
];
|
|
42
|
+
var PERSISTED_EVENT_STATUSES = [
|
|
43
|
+
"running",
|
|
44
|
+
"ok",
|
|
45
|
+
"error",
|
|
46
|
+
"unknown"
|
|
47
|
+
];
|
|
48
|
+
function isRecord(value) {
|
|
49
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
50
|
+
}
|
|
51
|
+
function isString(value) {
|
|
52
|
+
return typeof value === "string";
|
|
53
|
+
}
|
|
54
|
+
function isNonEmptyString(value) {
|
|
55
|
+
return typeof value === "string" && value.length > 0;
|
|
56
|
+
}
|
|
57
|
+
function isOptionalString(value) {
|
|
58
|
+
return value === void 0 || isString(value);
|
|
59
|
+
}
|
|
60
|
+
function isNonNegativeNumber(value) {
|
|
61
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
62
|
+
}
|
|
63
|
+
function isOptionalNonNegativeNumber(value) {
|
|
64
|
+
return value === void 0 || isNonNegativeNumber(value);
|
|
65
|
+
}
|
|
66
|
+
function isInspectKind(value) {
|
|
67
|
+
return typeof value === "string" && INSPECT_KINDS.includes(value);
|
|
68
|
+
}
|
|
69
|
+
function isAttributionConfidence(value) {
|
|
70
|
+
return typeof value === "string" && ATTRIBUTION_CONFIDENCES.includes(value);
|
|
71
|
+
}
|
|
72
|
+
function isPersistedEventSourceType(value) {
|
|
73
|
+
return typeof value === "string" && PERSISTED_EVENT_SOURCE_TYPES.includes(value);
|
|
74
|
+
}
|
|
75
|
+
function isPersistedEventStatus(value) {
|
|
76
|
+
return typeof value === "string" && PERSISTED_EVENT_STATUSES.includes(value);
|
|
77
|
+
}
|
|
78
|
+
function isPersistedEventSource(value) {
|
|
79
|
+
if (!isRecord(value)) return false;
|
|
80
|
+
if (!isPersistedEventSourceType(value.type)) return false;
|
|
81
|
+
if (!isOptionalString(value.name)) return false;
|
|
82
|
+
if (!isOptionalString(value.version)) return false;
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
function isPersistedInspectError(value) {
|
|
86
|
+
if (!isRecord(value)) return false;
|
|
87
|
+
if (!isNonEmptyString(value.message)) return false;
|
|
88
|
+
if (!isOptionalString(value.name)) return false;
|
|
89
|
+
if (!isOptionalString(value.code)) return false;
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
function isPersistedTokenUsage(value) {
|
|
93
|
+
if (!isRecord(value)) return false;
|
|
94
|
+
if (!isOptionalNonNegativeNumber(value.input)) return false;
|
|
95
|
+
if (!isOptionalNonNegativeNumber(value.output)) return false;
|
|
96
|
+
if (!isOptionalNonNegativeNumber(value.total)) return false;
|
|
97
|
+
if (!isOptionalNonNegativeNumber(value.cached)) return false;
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
function isPersistedTraceContext(value) {
|
|
101
|
+
if (!isRecord(value)) return false;
|
|
102
|
+
if (!isOptionalString(value.traceId)) return false;
|
|
103
|
+
if (!isOptionalString(value.spanId)) return false;
|
|
104
|
+
if (!isOptionalString(value.parentSpanId)) return false;
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
function isPersistedInspectEvent(value) {
|
|
108
|
+
if (!isRecord(value)) return false;
|
|
109
|
+
if (value.schemaVersion !== "0.2") return false;
|
|
110
|
+
if (!isNonEmptyString(value.eventId)) return false;
|
|
111
|
+
if (!isNonEmptyString(value.runId)) return false;
|
|
112
|
+
if (!isInspectKind(value.kind)) return false;
|
|
113
|
+
if (!isNonEmptyString(value.name)) return false;
|
|
114
|
+
if (!isNonEmptyString(value.timestamp)) return false;
|
|
115
|
+
if (!isAttributionConfidence(value.confidence)) return false;
|
|
116
|
+
if (!isPersistedEventSource(value.source)) return false;
|
|
117
|
+
if (value.parentId !== void 0 && !isNonEmptyString(value.parentId)) {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
if (value.status !== void 0 && !isPersistedEventStatus(value.status)) {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
if (!isOptionalString(value.startedAt)) return false;
|
|
124
|
+
if (!isOptionalString(value.endedAt)) return false;
|
|
125
|
+
if (value.durationMs !== void 0 && !isNonNegativeNumber(value.durationMs)) {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
if (value.attributes !== void 0 && !isRecord(value.attributes)) {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
if (value.error !== void 0 && !isPersistedInspectError(value.error)) {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
if (value.tokenUsage !== void 0 && !isPersistedTokenUsage(value.tokenUsage)) {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
if (value.trace !== void 0 && !isPersistedTraceContext(value.trace)) {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// packages/core/src/persisted/to-trace-event.ts
|
|
144
|
+
function parseIsoToMs(iso) {
|
|
145
|
+
const parsed = Date.parse(iso);
|
|
146
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
147
|
+
}
|
|
148
|
+
function mapInspectKindToStepType(kind) {
|
|
149
|
+
switch (kind) {
|
|
150
|
+
case "LLM":
|
|
151
|
+
return "llm";
|
|
152
|
+
case "TOOL":
|
|
153
|
+
return "tool";
|
|
154
|
+
case "DECISION":
|
|
155
|
+
return "decision";
|
|
156
|
+
case "RUN":
|
|
157
|
+
return "run";
|
|
158
|
+
default:
|
|
159
|
+
return "logic";
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
function mapPersistedStatusToStepStatus(status) {
|
|
163
|
+
switch (status) {
|
|
164
|
+
case "ok":
|
|
165
|
+
return "success";
|
|
166
|
+
case "error":
|
|
167
|
+
return "error";
|
|
168
|
+
case "running":
|
|
169
|
+
return "running";
|
|
170
|
+
default:
|
|
171
|
+
return void 0;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
function mapPersistedStatusToRunStatus(status) {
|
|
175
|
+
switch (status) {
|
|
176
|
+
case "ok":
|
|
177
|
+
return "success";
|
|
178
|
+
case "error":
|
|
179
|
+
return "error";
|
|
180
|
+
case "running":
|
|
181
|
+
return "running";
|
|
182
|
+
default:
|
|
183
|
+
return void 0;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function mapPersistedError(error, attributes) {
|
|
187
|
+
if (!error?.message) return void 0;
|
|
188
|
+
const out = { message: error.message };
|
|
189
|
+
const stack = typeof attributes?.errorStack === "string" && attributes.errorStack.length > 0 ? attributes.errorStack : void 0;
|
|
190
|
+
if (stack) {
|
|
191
|
+
out.stack = stack;
|
|
192
|
+
}
|
|
193
|
+
return out;
|
|
194
|
+
}
|
|
195
|
+
function mapTokenUsageToMetadata(tokenUsage, attributes) {
|
|
196
|
+
const metadata = {};
|
|
197
|
+
if (attributes?.metadata && typeof attributes.metadata === "object") {
|
|
198
|
+
Object.assign(metadata, attributes.metadata);
|
|
199
|
+
}
|
|
200
|
+
if (tokenUsage) {
|
|
201
|
+
metadata.tokens = {
|
|
202
|
+
...tokenUsage.input !== void 0 ? { input: tokenUsage.input } : {},
|
|
203
|
+
...tokenUsage.output !== void 0 ? { output: tokenUsage.output } : {},
|
|
204
|
+
...tokenUsage.total !== void 0 ? { total: tokenUsage.total } : {},
|
|
205
|
+
...tokenUsage.cached !== void 0 ? { cached: tokenUsage.cached } : {}
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
return Object.keys(metadata).length > 0 ? metadata : void 0;
|
|
209
|
+
}
|
|
210
|
+
function pickRunMetadata(attributes) {
|
|
211
|
+
if (!attributes) return void 0;
|
|
212
|
+
const metadata = attributes.metadata && typeof attributes.metadata === "object" ? { ...attributes.metadata } : {};
|
|
213
|
+
for (const key of [
|
|
214
|
+
"correlationId",
|
|
215
|
+
"requestId",
|
|
216
|
+
"decisionId",
|
|
217
|
+
"groupId"
|
|
218
|
+
]) {
|
|
219
|
+
const value = attributes[key];
|
|
220
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
221
|
+
metadata[key] = value;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return Object.keys(metadata).length > 0 ? metadata : void 0;
|
|
225
|
+
}
|
|
226
|
+
function resolveStepId(event) {
|
|
227
|
+
const attrs = event.attributes;
|
|
228
|
+
if (attrs && typeof attrs.stepId === "string" && attrs.stepId.trim() !== "") {
|
|
229
|
+
return attrs.stepId;
|
|
230
|
+
}
|
|
231
|
+
return event.eventId;
|
|
232
|
+
}
|
|
233
|
+
function resolveStepType(event) {
|
|
234
|
+
const attrs = event.attributes;
|
|
235
|
+
if (attrs && typeof attrs.stepType === "string") {
|
|
236
|
+
const t = attrs.stepType;
|
|
237
|
+
if (t === "run" || t === "llm" || t === "tool" || t === "decision" || t === "logic" || t === "state" || t === "custom") {
|
|
238
|
+
return t;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return mapInspectKindToStepType(event.kind);
|
|
242
|
+
}
|
|
243
|
+
function resolveTimes(event) {
|
|
244
|
+
const timestamp = parseIsoToMs(event.timestamp);
|
|
245
|
+
const startTime = event.startedAt !== void 0 ? parseIsoToMs(event.startedAt) : timestamp;
|
|
246
|
+
let endTime = event.endedAt !== void 0 ? parseIsoToMs(event.endedAt) : timestamp;
|
|
247
|
+
if (event.durationMs !== void 0 && Number.isFinite(event.durationMs) && event.durationMs >= 0 && event.endedAt === void 0) {
|
|
248
|
+
endTime = startTime + event.durationMs;
|
|
249
|
+
}
|
|
250
|
+
return { timestamp, startTime, endTime };
|
|
251
|
+
}
|
|
252
|
+
function fromLegacyRunStarted(event) {
|
|
253
|
+
const { timestamp, startTime } = resolveTimes(event);
|
|
254
|
+
const out = {
|
|
255
|
+
schemaVersion: "0.1",
|
|
256
|
+
event: "run_started",
|
|
257
|
+
timestamp,
|
|
258
|
+
runId: event.runId,
|
|
259
|
+
name: event.name,
|
|
260
|
+
startTime
|
|
261
|
+
};
|
|
262
|
+
const metadata = pickRunMetadata(event.attributes);
|
|
263
|
+
if (metadata) out.metadata = metadata;
|
|
264
|
+
return out;
|
|
265
|
+
}
|
|
266
|
+
function fromLegacyRunCompleted(event) {
|
|
267
|
+
const { timestamp, endTime } = resolveTimes(event);
|
|
268
|
+
const status = mapPersistedStatusToRunStatus(event.status) ?? "success";
|
|
269
|
+
const out = {
|
|
270
|
+
schemaVersion: "0.1",
|
|
271
|
+
event: "run_completed",
|
|
272
|
+
timestamp,
|
|
273
|
+
runId: event.runId,
|
|
274
|
+
status: status === "running" ? "success" : status,
|
|
275
|
+
endTime,
|
|
276
|
+
durationMs: event.durationMs ?? Math.max(0, endTime - timestamp)
|
|
277
|
+
};
|
|
278
|
+
const error = mapPersistedError(event.error, event.attributes);
|
|
279
|
+
if (error) out.error = error;
|
|
280
|
+
return out;
|
|
281
|
+
}
|
|
282
|
+
function fromLegacyStepStarted(event) {
|
|
283
|
+
const { timestamp, startTime } = resolveTimes(event);
|
|
284
|
+
const out = {
|
|
285
|
+
schemaVersion: "0.1",
|
|
286
|
+
event: "step_started",
|
|
287
|
+
timestamp,
|
|
288
|
+
runId: event.runId,
|
|
289
|
+
stepId: resolveStepId(event),
|
|
290
|
+
name: event.name,
|
|
291
|
+
type: resolveStepType(event),
|
|
292
|
+
startTime
|
|
293
|
+
};
|
|
294
|
+
if (event.parentId !== void 0) out.parentId = event.parentId;
|
|
295
|
+
const metadata = mapTokenUsageToMetadata(event.tokenUsage, event.attributes);
|
|
296
|
+
if (metadata) out.metadata = metadata;
|
|
297
|
+
return out;
|
|
298
|
+
}
|
|
299
|
+
function fromLegacyStepCompleted(event) {
|
|
300
|
+
const { timestamp, endTime } = resolveTimes(event);
|
|
301
|
+
const status = mapPersistedStatusToStepStatus(event.status) ?? "success";
|
|
302
|
+
const out = {
|
|
303
|
+
schemaVersion: "0.1",
|
|
304
|
+
event: "step_completed",
|
|
305
|
+
timestamp,
|
|
306
|
+
runId: event.runId,
|
|
307
|
+
stepId: resolveStepId(event),
|
|
308
|
+
status: status === "running" ? "success" : status,
|
|
309
|
+
endTime,
|
|
310
|
+
durationMs: event.durationMs ?? Math.max(0, endTime - timestamp)
|
|
311
|
+
};
|
|
312
|
+
const error = mapPersistedError(event.error, event.attributes);
|
|
313
|
+
if (error) out.error = error;
|
|
314
|
+
return out;
|
|
315
|
+
}
|
|
316
|
+
function fromNativeRun(event) {
|
|
317
|
+
const { timestamp, startTime, endTime } = resolveTimes(event);
|
|
318
|
+
const runStatus = mapPersistedStatusToRunStatus(event.status);
|
|
319
|
+
const out = [];
|
|
320
|
+
if (runStatus === "running" || event.startedAt !== void 0) {
|
|
321
|
+
const started = {
|
|
322
|
+
schemaVersion: "0.1",
|
|
323
|
+
event: "run_started",
|
|
324
|
+
timestamp,
|
|
325
|
+
runId: event.runId,
|
|
326
|
+
name: event.name,
|
|
327
|
+
startTime
|
|
328
|
+
};
|
|
329
|
+
const metadata = pickRunMetadata(event.attributes);
|
|
330
|
+
if (metadata) started.metadata = metadata;
|
|
331
|
+
out.push(started);
|
|
332
|
+
}
|
|
333
|
+
if (runStatus === "success" || runStatus === "error" || event.endedAt !== void 0) {
|
|
334
|
+
const completed = {
|
|
335
|
+
schemaVersion: "0.1",
|
|
336
|
+
event: "run_completed",
|
|
337
|
+
timestamp,
|
|
338
|
+
runId: event.runId,
|
|
339
|
+
status: runStatus === "error" ? "error" : "success",
|
|
340
|
+
endTime,
|
|
341
|
+
durationMs: event.durationMs ?? Math.max(0, endTime - startTime)
|
|
342
|
+
};
|
|
343
|
+
const error = mapPersistedError(event.error, event.attributes);
|
|
344
|
+
if (error) completed.error = error;
|
|
345
|
+
out.push(completed);
|
|
346
|
+
}
|
|
347
|
+
if (out.length === 0) {
|
|
348
|
+
out.push(fromLegacyRunStarted(event));
|
|
349
|
+
}
|
|
350
|
+
return out;
|
|
351
|
+
}
|
|
352
|
+
function fromNativeStep(event) {
|
|
353
|
+
const { timestamp, startTime, endTime } = resolveTimes(event);
|
|
354
|
+
const stepStatus = mapPersistedStatusToStepStatus(event.status);
|
|
355
|
+
const stepId = resolveStepId(event);
|
|
356
|
+
const out = [];
|
|
357
|
+
const shouldEmitStarted = stepStatus === "running" || event.startedAt !== void 0 || stepStatus === "success" || stepStatus === "error";
|
|
358
|
+
if (shouldEmitStarted) {
|
|
359
|
+
const started = {
|
|
360
|
+
schemaVersion: "0.1",
|
|
361
|
+
event: "step_started",
|
|
362
|
+
timestamp,
|
|
363
|
+
runId: event.runId,
|
|
364
|
+
stepId,
|
|
365
|
+
name: event.name,
|
|
366
|
+
type: resolveStepType(event),
|
|
367
|
+
startTime
|
|
368
|
+
};
|
|
369
|
+
if (event.parentId !== void 0) started.parentId = event.parentId;
|
|
370
|
+
const metadata = mapTokenUsageToMetadata(event.tokenUsage, event.attributes);
|
|
371
|
+
if (metadata) started.metadata = metadata;
|
|
372
|
+
out.push(started);
|
|
373
|
+
}
|
|
374
|
+
if (stepStatus === "success" || stepStatus === "error" || event.endedAt !== void 0 || event.durationMs !== void 0) {
|
|
375
|
+
const completed = {
|
|
376
|
+
schemaVersion: "0.1",
|
|
377
|
+
event: "step_completed",
|
|
378
|
+
timestamp,
|
|
379
|
+
runId: event.runId,
|
|
380
|
+
stepId,
|
|
381
|
+
status: stepStatus === "error" ? "error" : "success",
|
|
382
|
+
endTime,
|
|
383
|
+
durationMs: event.durationMs ?? Math.max(0, endTime - startTime)
|
|
384
|
+
};
|
|
385
|
+
const error = mapPersistedError(event.error, event.attributes);
|
|
386
|
+
if (error) completed.error = error;
|
|
387
|
+
out.push(completed);
|
|
388
|
+
}
|
|
389
|
+
if (out.length === 0) {
|
|
390
|
+
out.push(fromLegacyStepStarted(event));
|
|
391
|
+
}
|
|
392
|
+
return out;
|
|
393
|
+
}
|
|
394
|
+
function persistedInspectEventToTraceEvents(event) {
|
|
395
|
+
if (!isPersistedInspectEvent(event)) {
|
|
396
|
+
throw new Error("Invalid PersistedInspectEvent: failed isPersistedInspectEvent");
|
|
397
|
+
}
|
|
398
|
+
const legacyEvent = event.attributes?.legacyEvent;
|
|
399
|
+
if (legacyEvent === "run_started") return [fromLegacyRunStarted(event)];
|
|
400
|
+
if (legacyEvent === "run_completed") return [fromLegacyRunCompleted(event)];
|
|
401
|
+
if (legacyEvent === "step_started") return [fromLegacyStepStarted(event)];
|
|
402
|
+
if (legacyEvent === "step_completed") return [fromLegacyStepCompleted(event)];
|
|
403
|
+
if (event.kind === "RUN") {
|
|
404
|
+
return fromNativeRun(event);
|
|
405
|
+
}
|
|
406
|
+
return fromNativeStep(event);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// packages/core/src/types.ts
|
|
410
|
+
var STEP_TYPES = [
|
|
411
|
+
"run",
|
|
412
|
+
"llm",
|
|
413
|
+
"tool",
|
|
414
|
+
"decision",
|
|
415
|
+
"logic",
|
|
416
|
+
"state",
|
|
417
|
+
"custom"
|
|
418
|
+
];
|
|
419
|
+
function isRecord2(value) {
|
|
420
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
421
|
+
}
|
|
422
|
+
function isStepType(value) {
|
|
423
|
+
return typeof value === "string" && STEP_TYPES.includes(value);
|
|
424
|
+
}
|
|
425
|
+
function isTraceEvent(value) {
|
|
426
|
+
if (!isRecord2(value)) return false;
|
|
427
|
+
if (value.schemaVersion !== "0.1") return false;
|
|
428
|
+
if (typeof value.timestamp !== "number") return false;
|
|
429
|
+
if (typeof value.event !== "string") return false;
|
|
430
|
+
switch (value.event) {
|
|
431
|
+
case "run_started": {
|
|
432
|
+
return typeof value.runId === "string" && typeof value.name === "string" && typeof value.startTime === "number";
|
|
433
|
+
}
|
|
434
|
+
case "run_completed": {
|
|
435
|
+
return typeof value.runId === "string" && (value.status === "success" || value.status === "error") && typeof value.endTime === "number" && typeof value.durationMs === "number";
|
|
436
|
+
}
|
|
437
|
+
case "step_started": {
|
|
438
|
+
return typeof value.runId === "string" && typeof value.stepId === "string" && typeof value.name === "string" && isStepType(value.type) && typeof value.startTime === "number";
|
|
439
|
+
}
|
|
440
|
+
case "step_completed": {
|
|
441
|
+
return typeof value.runId === "string" && typeof value.stepId === "string" && (value.status === "success" || value.status === "error") && typeof value.endTime === "number" && typeof value.durationMs === "number";
|
|
442
|
+
}
|
|
443
|
+
default:
|
|
444
|
+
return false;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
var RUNS_DIR_NAME = "runs";
|
|
448
|
+
path__default.default.join(
|
|
449
|
+
os__default.default.tmpdir(),
|
|
450
|
+
"agent-inspect",
|
|
451
|
+
RUNS_DIR_NAME
|
|
452
|
+
);
|
|
453
|
+
function warn(message, error) {
|
|
454
|
+
const base = `[AgentInspect] ${message}`;
|
|
455
|
+
{
|
|
456
|
+
console.warn(base);
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// packages/core/src/read-trace.ts
|
|
462
|
+
function isRecord3(value) {
|
|
463
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
464
|
+
}
|
|
465
|
+
function detectLineFormat(parsed) {
|
|
466
|
+
if (!isRecord3(parsed)) return "unknown";
|
|
467
|
+
if (parsed.schemaVersion === "0.1") return "0.1";
|
|
468
|
+
if (parsed.schemaVersion === "0.2") return "0.2";
|
|
469
|
+
return "unknown";
|
|
470
|
+
}
|
|
471
|
+
function parseTraceJsonl(raw, options = {}) {
|
|
472
|
+
const validate = options.validate ?? isTraceEvent;
|
|
473
|
+
const emitWarning = (message) => {
|
|
474
|
+
if (options.warnings !== false) warn(message);
|
|
475
|
+
};
|
|
476
|
+
const persisted = [];
|
|
477
|
+
const traceEvents = [];
|
|
478
|
+
const rows = [];
|
|
479
|
+
let sourceEventCount = 0;
|
|
480
|
+
let saw01 = false;
|
|
481
|
+
let saw02 = false;
|
|
482
|
+
let lineNumber = 0;
|
|
483
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
484
|
+
lineNumber += 1;
|
|
485
|
+
const trimmed = line.trim();
|
|
486
|
+
if (trimmed === "") continue;
|
|
487
|
+
let parsed;
|
|
488
|
+
try {
|
|
489
|
+
parsed = JSON.parse(trimmed);
|
|
490
|
+
} catch {
|
|
491
|
+
emitWarning("Skipped invalid JSON line in trace file");
|
|
492
|
+
continue;
|
|
493
|
+
}
|
|
494
|
+
const format2 = detectLineFormat(parsed);
|
|
495
|
+
if (format2 === "0.1") {
|
|
496
|
+
saw01 = true;
|
|
497
|
+
if (validate(parsed)) {
|
|
498
|
+
sourceEventCount += 1;
|
|
499
|
+
traceEvents.push(parsed);
|
|
500
|
+
rows.push({ format: "0.1", event: parsed, sourceLine: lineNumber });
|
|
501
|
+
} else {
|
|
502
|
+
emitWarning("Skipped invalid trace event line in trace file");
|
|
503
|
+
}
|
|
504
|
+
continue;
|
|
505
|
+
}
|
|
506
|
+
if (format2 === "0.2") {
|
|
507
|
+
saw02 = true;
|
|
508
|
+
if (isPersistedInspectEvent(parsed)) {
|
|
509
|
+
sourceEventCount += 1;
|
|
510
|
+
persisted.push(parsed);
|
|
511
|
+
rows.push({ format: "0.2", event: parsed, sourceLine: lineNumber });
|
|
512
|
+
traceEvents.push(...persistedInspectEventToTraceEvents(parsed));
|
|
513
|
+
} else {
|
|
514
|
+
emitWarning("Skipped invalid persisted inspect event line in trace file");
|
|
515
|
+
}
|
|
516
|
+
continue;
|
|
517
|
+
}
|
|
518
|
+
emitWarning("Skipped trace line with unknown schemaVersion");
|
|
519
|
+
}
|
|
520
|
+
if (saw01 && saw02) {
|
|
521
|
+
emitWarning(
|
|
522
|
+
"Trace file mixes schemaVersion 0.1 and 0.2 lines; normalizing all rows"
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
let format = "empty";
|
|
526
|
+
if (saw01 && saw02) format = "mixed";
|
|
527
|
+
else if (saw01) format = "0.1";
|
|
528
|
+
else if (saw02) format = "0.2";
|
|
529
|
+
return { format, sourceEventCount, events: traceEvents, persisted, rows };
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// packages/core/src/correlation-metadata.ts
|
|
533
|
+
var TRACE_CORRELATION_KEYS = [
|
|
534
|
+
"correlationId",
|
|
535
|
+
"requestId",
|
|
536
|
+
"decisionId",
|
|
537
|
+
"groupId"
|
|
538
|
+
];
|
|
539
|
+
function isNonEmptyString2(value) {
|
|
540
|
+
return typeof value === "string" && value.length > 0;
|
|
541
|
+
}
|
|
542
|
+
function extractCorrelationMetadata(record) {
|
|
543
|
+
if (!record) {
|
|
544
|
+
return void 0;
|
|
545
|
+
}
|
|
546
|
+
const out = {};
|
|
547
|
+
let found = false;
|
|
548
|
+
for (const key of TRACE_CORRELATION_KEYS) {
|
|
549
|
+
const value = record[key];
|
|
550
|
+
if (isNonEmptyString2(value)) {
|
|
551
|
+
out[key] = value;
|
|
552
|
+
found = true;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
return found ? out : void 0;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// packages/core/src/persisted/token-usage.ts
|
|
559
|
+
function isRecord4(value) {
|
|
560
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
561
|
+
}
|
|
562
|
+
function nonNegativeFinite(value) {
|
|
563
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
564
|
+
}
|
|
565
|
+
function normalizeTokenUsage(value) {
|
|
566
|
+
if (!isRecord4(value)) return void 0;
|
|
567
|
+
const input = nonNegativeFinite(value.input);
|
|
568
|
+
const output = nonNegativeFinite(value.output);
|
|
569
|
+
const suppliedTotal = nonNegativeFinite(value.total);
|
|
570
|
+
const cached = nonNegativeFinite(value.cached);
|
|
571
|
+
const derivedTotal = input !== void 0 && output !== void 0 && Number.isFinite(input + output) ? input + output : void 0;
|
|
572
|
+
const total = suppliedTotal ?? derivedTotal;
|
|
573
|
+
if (input === void 0 && output === void 0 && total === void 0 && cached === void 0) {
|
|
574
|
+
return void 0;
|
|
575
|
+
}
|
|
576
|
+
return {
|
|
577
|
+
...input !== void 0 ? { input } : {},
|
|
578
|
+
...output !== void 0 ? { output } : {},
|
|
579
|
+
...total !== void 0 ? { total } : {},
|
|
580
|
+
...cached !== void 0 ? { cached } : {}
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// packages/core/src/persisted/from-trace-event.ts
|
|
585
|
+
function sanitizeIdPart(value) {
|
|
586
|
+
return value.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
587
|
+
}
|
|
588
|
+
function nodeIdForEvent(event) {
|
|
589
|
+
switch (event.event) {
|
|
590
|
+
case "run_started":
|
|
591
|
+
case "run_completed":
|
|
592
|
+
return event.runId;
|
|
593
|
+
case "step_started":
|
|
594
|
+
case "step_completed":
|
|
595
|
+
return event.stepId;
|
|
596
|
+
default:
|
|
597
|
+
return "unknown";
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
function createPersistedEventId(event, eventIndex) {
|
|
601
|
+
const runId = sanitizeIdPart(event.runId);
|
|
602
|
+
const ev = sanitizeIdPart(event.event);
|
|
603
|
+
const node = sanitizeIdPart(nodeIdForEvent(event));
|
|
604
|
+
return `manual:${runId}:${ev}:${node}:${eventIndex}`;
|
|
605
|
+
}
|
|
606
|
+
function toIsoTimestamp(ms) {
|
|
607
|
+
if (typeof ms !== "number" || !Number.isFinite(ms)) {
|
|
608
|
+
return { iso: (/* @__PURE__ */ new Date(0)).toISOString(), invalidTimestamp: true };
|
|
609
|
+
}
|
|
610
|
+
return { iso: new Date(ms).toISOString(), invalidTimestamp: false };
|
|
611
|
+
}
|
|
612
|
+
function buildSource(options) {
|
|
613
|
+
return {
|
|
614
|
+
type: "manual",
|
|
615
|
+
name: options?.sourceName ?? "trace-event",
|
|
616
|
+
version: options?.sourceVersion ?? "0.1"
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
function mapStepTypeToInspectKind(type) {
|
|
620
|
+
switch (type) {
|
|
621
|
+
case "run":
|
|
622
|
+
return "RUN";
|
|
623
|
+
case "llm":
|
|
624
|
+
return "LLM";
|
|
625
|
+
case "tool":
|
|
626
|
+
return "TOOL";
|
|
627
|
+
case "decision":
|
|
628
|
+
return "DECISION";
|
|
629
|
+
case "logic":
|
|
630
|
+
case "state":
|
|
631
|
+
case "custom":
|
|
632
|
+
return "LOGIC";
|
|
633
|
+
default:
|
|
634
|
+
return "LOGIC";
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
function mapRunOrStepStatus(status) {
|
|
638
|
+
return status === "success" ? "ok" : "error";
|
|
639
|
+
}
|
|
640
|
+
function mapErrorInfo(error) {
|
|
641
|
+
if (!error?.message) {
|
|
642
|
+
return {};
|
|
643
|
+
}
|
|
644
|
+
const out = {
|
|
645
|
+
persisted: {
|
|
646
|
+
message: error.message,
|
|
647
|
+
name: "Error"
|
|
648
|
+
}
|
|
649
|
+
};
|
|
650
|
+
if (typeof error.stack === "string" && error.stack.length > 0) {
|
|
651
|
+
out.errorStack = error.stack;
|
|
652
|
+
}
|
|
653
|
+
return out;
|
|
654
|
+
}
|
|
655
|
+
function mapTokenUsageFromMetadata(metadata) {
|
|
656
|
+
return normalizeTokenUsage(metadata?.tokens);
|
|
657
|
+
}
|
|
658
|
+
function compactAttributes(entries) {
|
|
659
|
+
const out = {};
|
|
660
|
+
for (const [key, value] of Object.entries(entries)) {
|
|
661
|
+
if (value !== void 0) {
|
|
662
|
+
out[key] = value;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
666
|
+
}
|
|
667
|
+
function traceEventToPersistedInspectEvent(event, options) {
|
|
668
|
+
const eventIndex = options?.eventIndex ?? 0;
|
|
669
|
+
const eventId = createPersistedEventId(event, eventIndex);
|
|
670
|
+
const source = buildSource(options);
|
|
671
|
+
const tsMain = toIsoTimestamp(event.timestamp);
|
|
672
|
+
switch (event.event) {
|
|
673
|
+
case "run_started": {
|
|
674
|
+
const tsStart = toIsoTimestamp(event.startTime);
|
|
675
|
+
const correlation = extractCorrelationMetadata(event.metadata);
|
|
676
|
+
const attributes = compactAttributes({
|
|
677
|
+
legacyEvent: "run_started",
|
|
678
|
+
metadata: event.metadata !== void 0 ? { ...event.metadata } : void 0,
|
|
679
|
+
correlationId: correlation?.correlationId,
|
|
680
|
+
requestId: correlation?.requestId,
|
|
681
|
+
decisionId: correlation?.decisionId,
|
|
682
|
+
groupId: correlation?.groupId,
|
|
683
|
+
invalidTimestamp: tsMain.invalidTimestamp || tsStart.invalidTimestamp ? true : void 0
|
|
684
|
+
});
|
|
685
|
+
return {
|
|
686
|
+
schemaVersion: "0.2",
|
|
687
|
+
eventId,
|
|
688
|
+
runId: event.runId,
|
|
689
|
+
kind: "RUN",
|
|
690
|
+
name: event.name,
|
|
691
|
+
status: "running",
|
|
692
|
+
timestamp: tsMain.iso,
|
|
693
|
+
startedAt: tsStart.iso,
|
|
694
|
+
confidence: "explicit",
|
|
695
|
+
source,
|
|
696
|
+
attributes
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
case "run_completed": {
|
|
700
|
+
const tsEnd = toIsoTimestamp(event.endTime);
|
|
701
|
+
const { persisted: error, errorStack } = mapErrorInfo(event.error);
|
|
702
|
+
const attributes = compactAttributes({
|
|
703
|
+
legacyEvent: "run_completed",
|
|
704
|
+
errorStack,
|
|
705
|
+
invalidTimestamp: tsMain.invalidTimestamp || tsEnd.invalidTimestamp ? true : void 0
|
|
706
|
+
});
|
|
707
|
+
return {
|
|
708
|
+
schemaVersion: "0.2",
|
|
709
|
+
eventId,
|
|
710
|
+
runId: event.runId,
|
|
711
|
+
kind: "RUN",
|
|
712
|
+
name: "run",
|
|
713
|
+
status: mapRunOrStepStatus(event.status),
|
|
714
|
+
timestamp: tsMain.iso,
|
|
715
|
+
endedAt: tsEnd.iso,
|
|
716
|
+
durationMs: event.durationMs,
|
|
717
|
+
confidence: "explicit",
|
|
718
|
+
source,
|
|
719
|
+
attributes,
|
|
720
|
+
error
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
case "step_started": {
|
|
724
|
+
const tsStart = toIsoTimestamp(event.startTime);
|
|
725
|
+
const tokenUsage = mapTokenUsageFromMetadata(event.metadata);
|
|
726
|
+
const attributes = compactAttributes({
|
|
727
|
+
legacyEvent: "step_started",
|
|
728
|
+
stepId: event.stepId,
|
|
729
|
+
stepType: event.type,
|
|
730
|
+
metadata: event.metadata !== void 0 ? { ...event.metadata } : void 0,
|
|
731
|
+
invalidTimestamp: tsMain.invalidTimestamp || tsStart.invalidTimestamp ? true : void 0
|
|
732
|
+
});
|
|
733
|
+
const out = {
|
|
734
|
+
schemaVersion: "0.2",
|
|
735
|
+
eventId,
|
|
736
|
+
runId: event.runId,
|
|
737
|
+
kind: mapStepTypeToInspectKind(event.type),
|
|
738
|
+
name: event.name,
|
|
739
|
+
status: "running",
|
|
740
|
+
timestamp: tsMain.iso,
|
|
741
|
+
startedAt: tsStart.iso,
|
|
742
|
+
confidence: "explicit",
|
|
743
|
+
source,
|
|
744
|
+
attributes
|
|
745
|
+
};
|
|
746
|
+
if (event.parentId !== void 0) {
|
|
747
|
+
out.parentId = event.parentId;
|
|
748
|
+
}
|
|
749
|
+
if (tokenUsage !== void 0) {
|
|
750
|
+
out.tokenUsage = tokenUsage;
|
|
751
|
+
}
|
|
752
|
+
return out;
|
|
753
|
+
}
|
|
754
|
+
case "step_completed": {
|
|
755
|
+
const tsEnd = toIsoTimestamp(event.endTime);
|
|
756
|
+
const { persisted: error, errorStack } = mapErrorInfo(event.error);
|
|
757
|
+
const attributes = compactAttributes({
|
|
758
|
+
legacyEvent: "step_completed",
|
|
759
|
+
stepId: event.stepId,
|
|
760
|
+
errorStack,
|
|
761
|
+
invalidTimestamp: tsMain.invalidTimestamp || tsEnd.invalidTimestamp ? true : void 0
|
|
762
|
+
});
|
|
763
|
+
return {
|
|
764
|
+
schemaVersion: "0.2",
|
|
765
|
+
eventId,
|
|
766
|
+
runId: event.runId,
|
|
767
|
+
kind: "LOGIC",
|
|
768
|
+
name: event.stepId,
|
|
769
|
+
status: mapRunOrStepStatus(event.status),
|
|
770
|
+
timestamp: tsMain.iso,
|
|
771
|
+
endedAt: tsEnd.iso,
|
|
772
|
+
durationMs: event.durationMs,
|
|
773
|
+
confidence: "explicit",
|
|
774
|
+
source,
|
|
775
|
+
attributes,
|
|
776
|
+
error
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
default: {
|
|
780
|
+
const _exhaustive = event;
|
|
781
|
+
throw new Error(`Unsupported trace event: ${_exhaustive.event}`);
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
function traceEventsToPersistedInspectEvents(events, options) {
|
|
786
|
+
return events.map(
|
|
787
|
+
(event, index) => traceEventToPersistedInspectEvent(event, { ...options, eventIndex: index })
|
|
788
|
+
);
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
// packages/core/src/logs/tree-builder.ts
|
|
792
|
+
function inc(map, key) {
|
|
793
|
+
map[key] = (map[key] ?? 0) + 1;
|
|
794
|
+
}
|
|
795
|
+
function computeRunStatus(events) {
|
|
796
|
+
let hasRunning = false;
|
|
797
|
+
for (const e of events) {
|
|
798
|
+
if (e.status === "error") return "error";
|
|
799
|
+
if (e.status === "running") hasRunning = true;
|
|
800
|
+
}
|
|
801
|
+
if (hasRunning) return "running";
|
|
802
|
+
return "ok";
|
|
803
|
+
}
|
|
804
|
+
var TreeBuilder = class {
|
|
805
|
+
constructor(options) {
|
|
806
|
+
void options?.config;
|
|
807
|
+
}
|
|
808
|
+
build(events) {
|
|
809
|
+
const byRun = /* @__PURE__ */ new Map();
|
|
810
|
+
for (const e of events) {
|
|
811
|
+
if (!byRun.has(e.runId)) byRun.set(e.runId, []);
|
|
812
|
+
byRun.get(e.runId).push(e);
|
|
813
|
+
}
|
|
814
|
+
const out = [];
|
|
815
|
+
for (const [runId, runEvents] of byRun.entries()) {
|
|
816
|
+
const sorted = [...runEvents].sort((a, b) => a.timestamp - b.timestamp);
|
|
817
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
818
|
+
for (const e of sorted) {
|
|
819
|
+
nodes.set(e.eventId, { event: e, children: [], depth: 0 });
|
|
820
|
+
}
|
|
821
|
+
const roots = [];
|
|
822
|
+
for (const node of nodes.values()) {
|
|
823
|
+
const parentId = node.event.parentId;
|
|
824
|
+
if (parentId && nodes.has(parentId)) {
|
|
825
|
+
nodes.get(parentId).children.push(node);
|
|
826
|
+
} else {
|
|
827
|
+
roots.push(node);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
const assignDepth = (n, depth) => {
|
|
831
|
+
n.depth = depth;
|
|
832
|
+
for (const c of n.children) assignDepth(c, depth + 1);
|
|
833
|
+
};
|
|
834
|
+
for (const r of roots) assignDepth(r, 0);
|
|
835
|
+
const confidenceBreakdown = {
|
|
836
|
+
explicit: 0,
|
|
837
|
+
correlated: 0,
|
|
838
|
+
heuristic: 0,
|
|
839
|
+
unknown: 0
|
|
840
|
+
};
|
|
841
|
+
const kinds = {};
|
|
842
|
+
for (const e of sorted) {
|
|
843
|
+
inc(confidenceBreakdown, e.confidence);
|
|
844
|
+
kinds[e.kind] = (kinds[e.kind] ?? 0) + 1;
|
|
845
|
+
}
|
|
846
|
+
const startedAt = sorted.length > 0 ? sorted[0].timestamp : void 0;
|
|
847
|
+
const endedAt = sorted.length > 0 ? sorted[sorted.length - 1].timestamp : void 0;
|
|
848
|
+
const status = computeRunStatus(sorted);
|
|
849
|
+
const durationMs = startedAt !== void 0 && endedAt !== void 0 && Number.isFinite(startedAt) && Number.isFinite(endedAt) && endedAt >= startedAt && status !== "running" ? endedAt - startedAt : void 0;
|
|
850
|
+
const name = sorted.find((e) => e.kind === "RUN")?.name;
|
|
851
|
+
out.push({
|
|
852
|
+
runId,
|
|
853
|
+
name,
|
|
854
|
+
status,
|
|
855
|
+
startedAt,
|
|
856
|
+
endedAt: status === "running" ? void 0 : endedAt,
|
|
857
|
+
durationMs,
|
|
858
|
+
children: roots,
|
|
859
|
+
metadata: {
|
|
860
|
+
totalEvents: sorted.length,
|
|
861
|
+
confidenceBreakdown,
|
|
862
|
+
kinds
|
|
863
|
+
}
|
|
864
|
+
});
|
|
865
|
+
}
|
|
866
|
+
out.sort((a, b) => (b.startedAt ?? 0) - (a.startedAt ?? 0));
|
|
867
|
+
return out;
|
|
868
|
+
}
|
|
869
|
+
};
|
|
870
|
+
|
|
871
|
+
// packages/core/src/persisted/to-inspect-event.ts
|
|
872
|
+
function compactAttributes2(entries) {
|
|
873
|
+
const out = {};
|
|
874
|
+
for (const [key, value] of Object.entries(entries)) {
|
|
875
|
+
if (value !== void 0) {
|
|
876
|
+
out[key] = value;
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
880
|
+
}
|
|
881
|
+
function parseIsoToMs2(iso) {
|
|
882
|
+
const parsed = Date.parse(iso);
|
|
883
|
+
if (!Number.isFinite(parsed)) {
|
|
884
|
+
return { ms: 0, invalidTimestamp: true };
|
|
885
|
+
}
|
|
886
|
+
return { ms: parsed, invalidTimestamp: false };
|
|
887
|
+
}
|
|
888
|
+
function mapPersistedSourceToInspect(event) {
|
|
889
|
+
const attrs = event.attributes ?? {};
|
|
890
|
+
const sourceName = event.source.name;
|
|
891
|
+
if (sourceName === "pino") {
|
|
892
|
+
return {
|
|
893
|
+
type: "pino",
|
|
894
|
+
file: typeof attrs.sourceFile === "string" ? attrs.sourceFile : void 0,
|
|
895
|
+
line: typeof attrs.sourceLine === "number" ? attrs.sourceLine : void 0
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
if (sourceName === "winston") {
|
|
899
|
+
return {
|
|
900
|
+
type: "winston",
|
|
901
|
+
file: typeof attrs.sourceFile === "string" ? attrs.sourceFile : void 0,
|
|
902
|
+
line: typeof attrs.sourceLine === "number" ? attrs.sourceLine : void 0
|
|
903
|
+
};
|
|
904
|
+
}
|
|
905
|
+
const mapType = (t) => {
|
|
906
|
+
switch (t) {
|
|
907
|
+
case "manual":
|
|
908
|
+
return "manual";
|
|
909
|
+
case "json-log":
|
|
910
|
+
return "json-log";
|
|
911
|
+
case "log4js":
|
|
912
|
+
return "log4js";
|
|
913
|
+
case "adapter":
|
|
914
|
+
case "ai-sdk":
|
|
915
|
+
case "otel":
|
|
916
|
+
return "adapter";
|
|
917
|
+
default:
|
|
918
|
+
return "json-log";
|
|
919
|
+
}
|
|
920
|
+
};
|
|
921
|
+
return {
|
|
922
|
+
type: mapType(event.source.type),
|
|
923
|
+
file: typeof attrs.sourceFile === "string" ? attrs.sourceFile : void 0,
|
|
924
|
+
line: typeof attrs.sourceLine === "number" ? attrs.sourceLine : void 0
|
|
925
|
+
};
|
|
926
|
+
}
|
|
927
|
+
function buildInspectAttributes(event) {
|
|
928
|
+
const attrs = event.attributes !== void 0 ? { ...event.attributes } : {};
|
|
929
|
+
if (event.inputSummary !== void 0) {
|
|
930
|
+
attrs.inputSummary = event.inputSummary;
|
|
931
|
+
}
|
|
932
|
+
if (event.outputSummary !== void 0) {
|
|
933
|
+
attrs.outputSummary = event.outputSummary;
|
|
934
|
+
}
|
|
935
|
+
if (event.error) {
|
|
936
|
+
if (event.error.name !== void 0) {
|
|
937
|
+
attrs.errorName = event.error.name;
|
|
938
|
+
}
|
|
939
|
+
attrs.errorMessage = event.error.message;
|
|
940
|
+
if (event.error.code !== void 0) {
|
|
941
|
+
attrs.errorCode = event.error.code;
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
if (event.tokenUsage) {
|
|
945
|
+
attrs.tokens = { ...event.tokenUsage };
|
|
946
|
+
}
|
|
947
|
+
if (event.source.type === "ai-sdk" || event.source.type === "otel") {
|
|
948
|
+
attrs.originalSourceType = event.source.type;
|
|
949
|
+
}
|
|
950
|
+
if (event.source.name !== void 0) {
|
|
951
|
+
attrs.sourceName = event.source.name;
|
|
952
|
+
}
|
|
953
|
+
if (event.source.version !== void 0) {
|
|
954
|
+
attrs.sourceVersion = event.source.version;
|
|
955
|
+
}
|
|
956
|
+
return attrs;
|
|
957
|
+
}
|
|
958
|
+
function persistedInspectEventToInspectEvent(event) {
|
|
959
|
+
if (!isPersistedInspectEvent(event)) {
|
|
960
|
+
throw new Error("Invalid PersistedInspectEvent: failed isPersistedInspectEvent");
|
|
961
|
+
}
|
|
962
|
+
const ts = parseIsoToMs2(event.timestamp);
|
|
963
|
+
const attrs = buildInspectAttributes(event);
|
|
964
|
+
if (ts.invalidTimestamp) {
|
|
965
|
+
attrs.invalidTimestamp = true;
|
|
966
|
+
}
|
|
967
|
+
let status;
|
|
968
|
+
if (event.status === "running" || event.status === "ok" || event.status === "error") {
|
|
969
|
+
status = event.status;
|
|
970
|
+
} else if (event.status === "unknown") {
|
|
971
|
+
attrs.persistedStatus = "unknown";
|
|
972
|
+
}
|
|
973
|
+
const out = {
|
|
974
|
+
eventId: event.eventId,
|
|
975
|
+
runId: event.runId,
|
|
976
|
+
name: event.name,
|
|
977
|
+
kind: event.kind,
|
|
978
|
+
timestamp: ts.ms,
|
|
979
|
+
confidence: event.confidence,
|
|
980
|
+
source: mapPersistedSourceToInspect(event),
|
|
981
|
+
attributes: compactAttributes2(attrs)
|
|
982
|
+
};
|
|
983
|
+
if (event.parentId !== void 0) {
|
|
984
|
+
out.parentId = event.parentId;
|
|
985
|
+
}
|
|
986
|
+
if (status !== void 0) {
|
|
987
|
+
out.status = status;
|
|
988
|
+
}
|
|
989
|
+
if (event.durationMs !== void 0 && Number.isFinite(event.durationMs) && event.durationMs >= 0) {
|
|
990
|
+
out.durationMs = event.durationMs;
|
|
991
|
+
}
|
|
992
|
+
return out;
|
|
993
|
+
}
|
|
994
|
+
function persistedInspectEventsToInspectEvents(events, options) {
|
|
995
|
+
const skipInvalid = options?.skipInvalid === true;
|
|
996
|
+
const out = [];
|
|
997
|
+
for (const event of events) {
|
|
998
|
+
if (!isPersistedInspectEvent(event)) {
|
|
999
|
+
if (skipInvalid) {
|
|
1000
|
+
continue;
|
|
1001
|
+
}
|
|
1002
|
+
throw new Error("Invalid PersistedInspectEvent: failed isPersistedInspectEvent");
|
|
1003
|
+
}
|
|
1004
|
+
out.push(persistedInspectEventToInspectEvent(event));
|
|
1005
|
+
}
|
|
1006
|
+
return out;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
// packages/core/src/persisted/tree-bridge.ts
|
|
1010
|
+
function persistedInspectEventsToRunTrees(events, options) {
|
|
1011
|
+
const inspectEvents = persistedInspectEventsToInspectEvents(events, {
|
|
1012
|
+
skipInvalid: options?.skipInvalid
|
|
1013
|
+
});
|
|
1014
|
+
return new TreeBuilder().build(inspectEvents);
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
// packages/core/src/readers/index.ts
|
|
1018
|
+
var DEFAULT_MAX_TRACE_INPUT_BYTES = 10 * 1024 * 1024;
|
|
1019
|
+
var MIN_DETECTION_CONFIDENCE = 0.5;
|
|
1020
|
+
var AMBIGUOUS_CONFIDENCE_DELTA = 0.05;
|
|
1021
|
+
var resolvedInputCache = /* @__PURE__ */ new WeakMap();
|
|
1022
|
+
var OPENINFERENCE_READER_FORMAT = "openinference-json";
|
|
1023
|
+
var OTLP_READER_FORMAT = "otlp-json";
|
|
1024
|
+
var OPENINFERENCE_SPAN_KEYS = /* @__PURE__ */ new Set([
|
|
1025
|
+
"trace_id",
|
|
1026
|
+
"traceId",
|
|
1027
|
+
"span_id",
|
|
1028
|
+
"spanId",
|
|
1029
|
+
"parent_span_id",
|
|
1030
|
+
"parentSpanId",
|
|
1031
|
+
"name",
|
|
1032
|
+
"start_time_unix_nano",
|
|
1033
|
+
"startTimeUnixNano",
|
|
1034
|
+
"end_time_unix_nano",
|
|
1035
|
+
"endTimeUnixNano",
|
|
1036
|
+
"start_time",
|
|
1037
|
+
"startTime",
|
|
1038
|
+
"end_time",
|
|
1039
|
+
"endTime",
|
|
1040
|
+
"attributes",
|
|
1041
|
+
"status",
|
|
1042
|
+
"kind",
|
|
1043
|
+
"span_kind",
|
|
1044
|
+
"spanKind"
|
|
1045
|
+
]);
|
|
1046
|
+
var OPENINFERENCE_SENSITIVE_ATTRIBUTE_KEYS = [
|
|
1047
|
+
"input.value",
|
|
1048
|
+
"output.value",
|
|
1049
|
+
"input.mime_type",
|
|
1050
|
+
"output.mime_type",
|
|
1051
|
+
"llm.input_messages",
|
|
1052
|
+
"llm.output_messages",
|
|
1053
|
+
"llm.prompts",
|
|
1054
|
+
"llm.completions",
|
|
1055
|
+
"retrieval.documents",
|
|
1056
|
+
"reranker.input_documents",
|
|
1057
|
+
"reranker.output_documents",
|
|
1058
|
+
"document.content",
|
|
1059
|
+
"gen_ai.prompt",
|
|
1060
|
+
"gen_ai.completion",
|
|
1061
|
+
"gen_ai.input.messages",
|
|
1062
|
+
"gen_ai.output.messages"
|
|
1063
|
+
];
|
|
1064
|
+
var OTLP_SPAN_KEYS = /* @__PURE__ */ new Set([
|
|
1065
|
+
"traceId",
|
|
1066
|
+
"spanId",
|
|
1067
|
+
"parentSpanId",
|
|
1068
|
+
"name",
|
|
1069
|
+
"kind",
|
|
1070
|
+
"startTimeUnixNano",
|
|
1071
|
+
"endTimeUnixNano",
|
|
1072
|
+
"attributes",
|
|
1073
|
+
"events",
|
|
1074
|
+
"status",
|
|
1075
|
+
"droppedAttributesCount",
|
|
1076
|
+
"droppedEventsCount",
|
|
1077
|
+
"droppedLinksCount",
|
|
1078
|
+
"links",
|
|
1079
|
+
"flags"
|
|
1080
|
+
]);
|
|
1081
|
+
var TraceReadError = class extends Error {
|
|
1082
|
+
code;
|
|
1083
|
+
warnings;
|
|
1084
|
+
constructor(code, message, warnings = []) {
|
|
1085
|
+
super(message);
|
|
1086
|
+
this.name = "TraceReadError";
|
|
1087
|
+
this.code = code;
|
|
1088
|
+
this.warnings = warnings;
|
|
1089
|
+
}
|
|
1090
|
+
};
|
|
1091
|
+
function normalizeCandidate(reader, candidate) {
|
|
1092
|
+
const confidence = Number.isFinite(candidate.confidence) ? Math.max(0, Math.min(1, candidate.confidence)) : 0;
|
|
1093
|
+
return {
|
|
1094
|
+
...candidate,
|
|
1095
|
+
format: candidate.format || reader.format,
|
|
1096
|
+
confidence,
|
|
1097
|
+
readerName: candidate.readerName ?? reader.name
|
|
1098
|
+
};
|
|
1099
|
+
}
|
|
1100
|
+
function sortCandidates(candidates) {
|
|
1101
|
+
return [...candidates].sort((a, b) => {
|
|
1102
|
+
if (b.confidence !== a.confidence) return b.confidence - a.confidence;
|
|
1103
|
+
return a.format.localeCompare(b.format);
|
|
1104
|
+
});
|
|
1105
|
+
}
|
|
1106
|
+
function collectWarnings(candidates) {
|
|
1107
|
+
return candidates.flatMap((candidate) => candidate.warnings ?? []);
|
|
1108
|
+
}
|
|
1109
|
+
function dedupeWarnings(warnings) {
|
|
1110
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1111
|
+
const out = [];
|
|
1112
|
+
for (const warning of warnings) {
|
|
1113
|
+
const key = [
|
|
1114
|
+
warning.code,
|
|
1115
|
+
warning.message,
|
|
1116
|
+
warning.severity ?? "",
|
|
1117
|
+
warning.sourceFile ?? "",
|
|
1118
|
+
warning.line ?? "",
|
|
1119
|
+
warning.field ?? ""
|
|
1120
|
+
].join("\0");
|
|
1121
|
+
if (seen.has(key)) continue;
|
|
1122
|
+
seen.add(key);
|
|
1123
|
+
out.push(warning);
|
|
1124
|
+
}
|
|
1125
|
+
return out;
|
|
1126
|
+
}
|
|
1127
|
+
function attachSingleSourceFile(warnings, resolved) {
|
|
1128
|
+
if (resolved.sourceFiles.length !== 1) return [...warnings];
|
|
1129
|
+
const [sourceFile] = resolved.sourceFiles;
|
|
1130
|
+
return warnings.map((warning) => ({
|
|
1131
|
+
...warning,
|
|
1132
|
+
sourceFile: warning.sourceFile ?? sourceFile
|
|
1133
|
+
}));
|
|
1134
|
+
}
|
|
1135
|
+
function findReaderByFormat(format, readers) {
|
|
1136
|
+
return readers.find((reader) => reader.format === format);
|
|
1137
|
+
}
|
|
1138
|
+
async function jsonlFilesInDirectory(dirPath) {
|
|
1139
|
+
const entries = await promises.readdir(dirPath, { withFileTypes: true });
|
|
1140
|
+
return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map((entry) => path__default.default.join(dirPath, entry.name)).sort((a, b) => a.localeCompare(b));
|
|
1141
|
+
}
|
|
1142
|
+
async function resolveInput(input) {
|
|
1143
|
+
const cached = resolvedInputCache.get(input);
|
|
1144
|
+
if (cached) return cached;
|
|
1145
|
+
const promise = resolveInputUncached(input);
|
|
1146
|
+
resolvedInputCache.set(input, promise);
|
|
1147
|
+
return promise;
|
|
1148
|
+
}
|
|
1149
|
+
function assertInputWithinBounds(content, sourceFile) {
|
|
1150
|
+
const bytes = Buffer.byteLength(content, "utf8");
|
|
1151
|
+
if (bytes <= DEFAULT_MAX_TRACE_INPUT_BYTES) return;
|
|
1152
|
+
throw new TraceReadError("unsupported_format", "Trace input exceeds the local reader size limit.", [
|
|
1153
|
+
{
|
|
1154
|
+
code: "input_too_large",
|
|
1155
|
+
message: `Trace input is ${bytes} bytes; max is ${DEFAULT_MAX_TRACE_INPUT_BYTES} bytes.`,
|
|
1156
|
+
severity: "error",
|
|
1157
|
+
...sourceFile !== void 0 ? { sourceFile } : {}
|
|
1158
|
+
}
|
|
1159
|
+
]);
|
|
1160
|
+
}
|
|
1161
|
+
async function resolveInputUncached(input) {
|
|
1162
|
+
if (input.type === "string") {
|
|
1163
|
+
assertInputWithinBounds(input.content);
|
|
1164
|
+
return { content: input.content, sourceFiles: [] };
|
|
1165
|
+
}
|
|
1166
|
+
if (input.type === "buffer") {
|
|
1167
|
+
const content = input.content.toString("utf-8");
|
|
1168
|
+
assertInputWithinBounds(content);
|
|
1169
|
+
return { content, sourceFiles: [] };
|
|
1170
|
+
}
|
|
1171
|
+
if (input.type === "file") {
|
|
1172
|
+
const content = await promises.readFile(input.path, "utf-8");
|
|
1173
|
+
assertInputWithinBounds(content, input.path);
|
|
1174
|
+
return { content, sourceFiles: [input.path] };
|
|
1175
|
+
}
|
|
1176
|
+
if (input.type === "directory") {
|
|
1177
|
+
const files = await jsonlFilesInDirectory(input.path);
|
|
1178
|
+
const parts = await Promise.all(
|
|
1179
|
+
files.map(async (file) => (await promises.readFile(file, "utf-8")).trimEnd())
|
|
1180
|
+
);
|
|
1181
|
+
const content = parts.filter((part) => part.trim() !== "").join("\n");
|
|
1182
|
+
assertInputWithinBounds(content, input.path);
|
|
1183
|
+
return {
|
|
1184
|
+
content,
|
|
1185
|
+
sourceFiles: files
|
|
1186
|
+
};
|
|
1187
|
+
}
|
|
1188
|
+
return void 0;
|
|
1189
|
+
}
|
|
1190
|
+
function detectJsonlFormat(content) {
|
|
1191
|
+
let saw01 = false;
|
|
1192
|
+
let saw02 = false;
|
|
1193
|
+
let validRows = 0;
|
|
1194
|
+
let invalidJsonRows = 0;
|
|
1195
|
+
let unknownSchemaRows = 0;
|
|
1196
|
+
let firstInvalidJsonLine;
|
|
1197
|
+
let firstUnknownSchemaLine;
|
|
1198
|
+
let lineNumber = 0;
|
|
1199
|
+
for (const line of content.split(/\r?\n/)) {
|
|
1200
|
+
lineNumber += 1;
|
|
1201
|
+
const trimmed = line.trim();
|
|
1202
|
+
if (trimmed === "") continue;
|
|
1203
|
+
let parsed;
|
|
1204
|
+
try {
|
|
1205
|
+
parsed = JSON.parse(trimmed);
|
|
1206
|
+
} catch {
|
|
1207
|
+
invalidJsonRows += 1;
|
|
1208
|
+
firstInvalidJsonLine ??= lineNumber;
|
|
1209
|
+
continue;
|
|
1210
|
+
}
|
|
1211
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) && "schemaVersion" in parsed) {
|
|
1212
|
+
const version = parsed.schemaVersion;
|
|
1213
|
+
if (version === "0.1") {
|
|
1214
|
+
saw01 = true;
|
|
1215
|
+
validRows += 1;
|
|
1216
|
+
continue;
|
|
1217
|
+
}
|
|
1218
|
+
if (version === "0.2") {
|
|
1219
|
+
saw02 = true;
|
|
1220
|
+
validRows += 1;
|
|
1221
|
+
continue;
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
unknownSchemaRows += 1;
|
|
1225
|
+
firstUnknownSchemaLine ??= lineNumber;
|
|
1226
|
+
}
|
|
1227
|
+
const warnings = [];
|
|
1228
|
+
if (invalidJsonRows > 0) {
|
|
1229
|
+
warnings.push({
|
|
1230
|
+
code: "invalid_jsonl_rows",
|
|
1231
|
+
message: `Skipped ${invalidJsonRows} invalid JSONL row(s) during format detection.`,
|
|
1232
|
+
severity: "warning",
|
|
1233
|
+
...firstInvalidJsonLine !== void 0 ? { line: firstInvalidJsonLine } : {}
|
|
1234
|
+
});
|
|
1235
|
+
}
|
|
1236
|
+
if (unknownSchemaRows > 0) {
|
|
1237
|
+
warnings.push({
|
|
1238
|
+
code: "unknown_schema_rows",
|
|
1239
|
+
message: `Skipped ${unknownSchemaRows} row(s) with unknown schemaVersion during format detection.`,
|
|
1240
|
+
severity: "warning",
|
|
1241
|
+
...firstUnknownSchemaLine !== void 0 ? { line: firstUnknownSchemaLine } : {}
|
|
1242
|
+
});
|
|
1243
|
+
}
|
|
1244
|
+
let format = "empty";
|
|
1245
|
+
if (saw01 && saw02) format = "mixed";
|
|
1246
|
+
else if (saw01) format = "0.1";
|
|
1247
|
+
else if (saw02) format = "0.2";
|
|
1248
|
+
return { format, validRows, warnings };
|
|
1249
|
+
}
|
|
1250
|
+
function agentInspectFormatLabel(format) {
|
|
1251
|
+
switch (format) {
|
|
1252
|
+
case "0.1":
|
|
1253
|
+
return "agent-inspect-v0.1-jsonl";
|
|
1254
|
+
case "0.2":
|
|
1255
|
+
return "agent-inspect-v0.2-jsonl";
|
|
1256
|
+
case "mixed":
|
|
1257
|
+
return "agent-inspect-mixed-jsonl";
|
|
1258
|
+
default:
|
|
1259
|
+
return "agent-inspect-jsonl";
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
function persistedEventsForParsedTrace(parsed) {
|
|
1263
|
+
if (parsed.format === "0.2" && parsed.persisted.length > 0) {
|
|
1264
|
+
return [...parsed.persisted];
|
|
1265
|
+
}
|
|
1266
|
+
if (parsed.format === "mixed" && parsed.rows.length > 0) {
|
|
1267
|
+
return parsed.rows.map((row, index) => {
|
|
1268
|
+
if (row.format === "0.2") return row.event;
|
|
1269
|
+
return traceEventToPersistedInspectEvent(row.event, {
|
|
1270
|
+
eventIndex: index,
|
|
1271
|
+
sourceName: "agent-inspect-jsonl-reader"
|
|
1272
|
+
});
|
|
1273
|
+
});
|
|
1274
|
+
}
|
|
1275
|
+
return traceEventsToPersistedInspectEvents(parsed.events, {
|
|
1276
|
+
sourceName: "agent-inspect-jsonl-reader"
|
|
1277
|
+
});
|
|
1278
|
+
}
|
|
1279
|
+
function isRecord5(value) {
|
|
1280
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1281
|
+
}
|
|
1282
|
+
function isNonEmptyString3(value) {
|
|
1283
|
+
return typeof value === "string" && value.trim() !== "";
|
|
1284
|
+
}
|
|
1285
|
+
function readStringField(record, keys) {
|
|
1286
|
+
for (const key of keys) {
|
|
1287
|
+
const value = record[key];
|
|
1288
|
+
if (isNonEmptyString3(value)) return value;
|
|
1289
|
+
}
|
|
1290
|
+
return void 0;
|
|
1291
|
+
}
|
|
1292
|
+
function readRecordField(record, key) {
|
|
1293
|
+
const value = record[key];
|
|
1294
|
+
return isRecord5(value) ? value : void 0;
|
|
1295
|
+
}
|
|
1296
|
+
function parseJsonDocument(content) {
|
|
1297
|
+
return JSON.parse(content);
|
|
1298
|
+
}
|
|
1299
|
+
function looksLikeOpenInferenceSpan(value) {
|
|
1300
|
+
if (!isRecord5(value)) return false;
|
|
1301
|
+
const attributes = readRecordField(value, "attributes");
|
|
1302
|
+
return readStringField(value, ["trace_id", "traceId"]) !== void 0 && readStringField(value, ["span_id", "spanId"]) !== void 0 && (readStringField(value, ["name"]) !== void 0 || attributes?.["openinference.span.kind"] !== void 0);
|
|
1303
|
+
}
|
|
1304
|
+
function extractOpenInferenceDocument(root) {
|
|
1305
|
+
const warnings = [];
|
|
1306
|
+
const unsupportedFields = [];
|
|
1307
|
+
if (Array.isArray(root)) {
|
|
1308
|
+
const spans = root.filter(looksLikeOpenInferenceSpan);
|
|
1309
|
+
if (spans.length === 0) return void 0;
|
|
1310
|
+
if (spans.length !== root.length) {
|
|
1311
|
+
warnings.push({
|
|
1312
|
+
code: "openinference_skipped_items",
|
|
1313
|
+
message: "Skipped non-span item(s) in OpenInference span array.",
|
|
1314
|
+
severity: "warning"
|
|
1315
|
+
});
|
|
1316
|
+
}
|
|
1317
|
+
return {
|
|
1318
|
+
spans,
|
|
1319
|
+
confidence: 0.82,
|
|
1320
|
+
description: "OpenInference span array",
|
|
1321
|
+
warnings,
|
|
1322
|
+
unsupportedFields
|
|
1323
|
+
};
|
|
1324
|
+
}
|
|
1325
|
+
if (!isRecord5(root)) return void 0;
|
|
1326
|
+
const rootFormat = root.format;
|
|
1327
|
+
const rootCompatibility = root.compatibility;
|
|
1328
|
+
const version = typeof root.version === "string" && root.version.trim() !== "" ? root.version : void 0;
|
|
1329
|
+
if (Array.isArray(root.spans)) {
|
|
1330
|
+
const spans = root.spans.filter(looksLikeOpenInferenceSpan);
|
|
1331
|
+
if (spans.length === 0 && (rootFormat === "openinference" || rootCompatibility === "openinference-compatible")) {
|
|
1332
|
+
warnings.push({
|
|
1333
|
+
code: "openinference_no_valid_spans",
|
|
1334
|
+
message: "OpenInference document did not contain any valid spans.",
|
|
1335
|
+
severity: "error"
|
|
1336
|
+
});
|
|
1337
|
+
return {
|
|
1338
|
+
spans,
|
|
1339
|
+
confidence: 0.7,
|
|
1340
|
+
description: "Malformed OpenInference document",
|
|
1341
|
+
version,
|
|
1342
|
+
warnings,
|
|
1343
|
+
unsupportedFields
|
|
1344
|
+
};
|
|
1345
|
+
}
|
|
1346
|
+
if (spans.length === 0) return void 0;
|
|
1347
|
+
if (spans.length !== root.spans.length) {
|
|
1348
|
+
warnings.push({
|
|
1349
|
+
code: "openinference_skipped_spans",
|
|
1350
|
+
message: "Skipped invalid OpenInference span item(s).",
|
|
1351
|
+
severity: "warning"
|
|
1352
|
+
});
|
|
1353
|
+
}
|
|
1354
|
+
return {
|
|
1355
|
+
spans,
|
|
1356
|
+
confidence: rootFormat === "openinference" || rootCompatibility === "openinference-compatible" ? 0.9 : 0.84,
|
|
1357
|
+
description: rootFormat === "openinference" || rootCompatibility === "openinference-compatible" ? "OpenInference document" : "OpenInference spans document",
|
|
1358
|
+
version,
|
|
1359
|
+
warnings,
|
|
1360
|
+
unsupportedFields
|
|
1361
|
+
};
|
|
1362
|
+
}
|
|
1363
|
+
if (Array.isArray(root.data)) {
|
|
1364
|
+
const spans = root.data.filter(looksLikeOpenInferenceSpan);
|
|
1365
|
+
if (spans.length === 0) return void 0;
|
|
1366
|
+
if (spans.length !== root.data.length) {
|
|
1367
|
+
warnings.push({
|
|
1368
|
+
code: "openinference_skipped_data_items",
|
|
1369
|
+
message: "Skipped non-span item(s) in OpenInference data array.",
|
|
1370
|
+
severity: "warning"
|
|
1371
|
+
});
|
|
1372
|
+
}
|
|
1373
|
+
return {
|
|
1374
|
+
spans,
|
|
1375
|
+
confidence: 0.8,
|
|
1376
|
+
description: "OpenInference data document",
|
|
1377
|
+
version,
|
|
1378
|
+
warnings,
|
|
1379
|
+
unsupportedFields
|
|
1380
|
+
};
|
|
1381
|
+
}
|
|
1382
|
+
if (looksLikeOpenInferenceSpan(root)) {
|
|
1383
|
+
return {
|
|
1384
|
+
spans: [root],
|
|
1385
|
+
confidence: 0.76,
|
|
1386
|
+
description: "OpenInference single span",
|
|
1387
|
+
version,
|
|
1388
|
+
warnings,
|
|
1389
|
+
unsupportedFields
|
|
1390
|
+
};
|
|
1391
|
+
}
|
|
1392
|
+
if (rootFormat === "openinference" || rootCompatibility === "openinference-compatible") {
|
|
1393
|
+
warnings.push({
|
|
1394
|
+
code: "openinference_missing_spans",
|
|
1395
|
+
message: "OpenInference document is missing a spans array.",
|
|
1396
|
+
severity: "error"
|
|
1397
|
+
});
|
|
1398
|
+
return {
|
|
1399
|
+
spans: [],
|
|
1400
|
+
confidence: 0.7,
|
|
1401
|
+
description: "Malformed OpenInference document",
|
|
1402
|
+
version,
|
|
1403
|
+
warnings,
|
|
1404
|
+
unsupportedFields
|
|
1405
|
+
};
|
|
1406
|
+
}
|
|
1407
|
+
return void 0;
|
|
1408
|
+
}
|
|
1409
|
+
function parseUnixNanoToIso(value) {
|
|
1410
|
+
if (typeof value === "bigint" && value >= 0n) {
|
|
1411
|
+
return new Date(Number(value / 1000000n)).toISOString();
|
|
1412
|
+
}
|
|
1413
|
+
if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
|
|
1414
|
+
return new Date(Math.floor(value / 1e6)).toISOString();
|
|
1415
|
+
}
|
|
1416
|
+
if (typeof value === "string" && /^\d+$/.test(value)) {
|
|
1417
|
+
return new Date(Number(BigInt(value) / 1000000n)).toISOString();
|
|
1418
|
+
}
|
|
1419
|
+
return void 0;
|
|
1420
|
+
}
|
|
1421
|
+
function parseIsoTime(value) {
|
|
1422
|
+
if (!isNonEmptyString3(value)) return void 0;
|
|
1423
|
+
const ms = Date.parse(value);
|
|
1424
|
+
if (!Number.isFinite(ms)) return void 0;
|
|
1425
|
+
return new Date(ms).toISOString();
|
|
1426
|
+
}
|
|
1427
|
+
function readOpenInferenceTimestamp(span, nanoKeys, isoKeys) {
|
|
1428
|
+
for (const key of nanoKeys) {
|
|
1429
|
+
const iso = parseUnixNanoToIso(span[key]);
|
|
1430
|
+
if (iso !== void 0) return iso;
|
|
1431
|
+
}
|
|
1432
|
+
for (const key of isoKeys) {
|
|
1433
|
+
const iso = parseIsoTime(span[key]);
|
|
1434
|
+
if (iso !== void 0) return iso;
|
|
1435
|
+
}
|
|
1436
|
+
return void 0;
|
|
1437
|
+
}
|
|
1438
|
+
function durationBetweenIso(startedAt, endedAt) {
|
|
1439
|
+
if (startedAt === void 0 || endedAt === void 0) return void 0;
|
|
1440
|
+
const startMs = Date.parse(startedAt);
|
|
1441
|
+
const endMs = Date.parse(endedAt);
|
|
1442
|
+
if (!Number.isFinite(startMs) || !Number.isFinite(endMs) || endMs < startMs) {
|
|
1443
|
+
return void 0;
|
|
1444
|
+
}
|
|
1445
|
+
return endMs - startMs;
|
|
1446
|
+
}
|
|
1447
|
+
function isSensitiveOpenInferenceAttribute(key) {
|
|
1448
|
+
return OPENINFERENCE_SENSITIVE_ATTRIBUTE_KEYS.some(
|
|
1449
|
+
(sensitiveKey) => key === sensitiveKey || key.startsWith(`${sensitiveKey}.`) || key.endsWith(".message.content") || key.endsWith(".document.content")
|
|
1450
|
+
);
|
|
1451
|
+
}
|
|
1452
|
+
function summarizeAttributeValue(value) {
|
|
1453
|
+
if (typeof value === "string") {
|
|
1454
|
+
return { type: "string", length: value.length };
|
|
1455
|
+
}
|
|
1456
|
+
if (typeof value === "number") {
|
|
1457
|
+
return { type: "number", finite: Number.isFinite(value) };
|
|
1458
|
+
}
|
|
1459
|
+
if (typeof value === "boolean") {
|
|
1460
|
+
return { type: "boolean" };
|
|
1461
|
+
}
|
|
1462
|
+
if (Array.isArray(value)) {
|
|
1463
|
+
return { type: "array", length: value.length };
|
|
1464
|
+
}
|
|
1465
|
+
if (isRecord5(value)) {
|
|
1466
|
+
return { type: "object", keyCount: Object.keys(value).length };
|
|
1467
|
+
}
|
|
1468
|
+
if (value === null) {
|
|
1469
|
+
return { type: "null" };
|
|
1470
|
+
}
|
|
1471
|
+
return { type: typeof value };
|
|
1472
|
+
}
|
|
1473
|
+
function sanitizeOpenInferenceAttributes(attributes, pathPrefix) {
|
|
1474
|
+
const out = {};
|
|
1475
|
+
const warnings = [];
|
|
1476
|
+
const unsupportedFields = [];
|
|
1477
|
+
const summarizedKeys = [];
|
|
1478
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
1479
|
+
if (isSensitiveOpenInferenceAttribute(key)) {
|
|
1480
|
+
summarizedKeys.push(key);
|
|
1481
|
+
out[`${key}.summary`] = summarizeAttributeValue(value);
|
|
1482
|
+
unsupportedFields.push(`${pathPrefix}.attributes.${key}`);
|
|
1483
|
+
continue;
|
|
1484
|
+
}
|
|
1485
|
+
out[key] = value;
|
|
1486
|
+
}
|
|
1487
|
+
if (summarizedKeys.length > 0) {
|
|
1488
|
+
out["openinference.summarized_attributes"] = summarizedKeys;
|
|
1489
|
+
warnings.push({
|
|
1490
|
+
code: "openinference_sensitive_attribute_summarized",
|
|
1491
|
+
message: "OpenInference prompt/output/document attribute(s) were summarized instead of copied verbatim.",
|
|
1492
|
+
severity: "warning"
|
|
1493
|
+
});
|
|
1494
|
+
}
|
|
1495
|
+
return { attributes: out, warnings, unsupportedFields };
|
|
1496
|
+
}
|
|
1497
|
+
function mapOpenInferenceKind(span, attributes, pathPrefix) {
|
|
1498
|
+
const warnings = [];
|
|
1499
|
+
const agentInspectKind = attributes["agent_inspect.kind"];
|
|
1500
|
+
if (agentInspectKind === "RUN" || agentInspectKind === "AGENT" || agentInspectKind === "LLM" || agentInspectKind === "TOOL" || agentInspectKind === "CHAIN" || agentInspectKind === "RETRIEVER" || agentInspectKind === "DECISION" || agentInspectKind === "RESULT" || agentInspectKind === "ERROR" || agentInspectKind === "LOGIC" || agentInspectKind === "LOG") {
|
|
1501
|
+
return { kind: agentInspectKind, warnings };
|
|
1502
|
+
}
|
|
1503
|
+
const rawKind = readStringField(span, ["kind", "span_kind", "spanKind"]) ?? (typeof attributes["openinference.span.kind"] === "string" ? attributes["openinference.span.kind"] : void 0);
|
|
1504
|
+
const normalized = rawKind?.toUpperCase();
|
|
1505
|
+
switch (normalized) {
|
|
1506
|
+
case "LLM":
|
|
1507
|
+
return { kind: "LLM", warnings };
|
|
1508
|
+
case "TOOL":
|
|
1509
|
+
return { kind: "TOOL", warnings };
|
|
1510
|
+
case "CHAIN":
|
|
1511
|
+
return { kind: "CHAIN", warnings };
|
|
1512
|
+
case "RETRIEVER":
|
|
1513
|
+
return { kind: "RETRIEVER", warnings };
|
|
1514
|
+
case "AGENT":
|
|
1515
|
+
return { kind: "AGENT", warnings };
|
|
1516
|
+
case "EMBEDDING":
|
|
1517
|
+
warnings.push({
|
|
1518
|
+
code: "openinference_kind_semantic_loss",
|
|
1519
|
+
message: "OpenInference EMBEDDING span kind mapped to AgentInspect LLM.",
|
|
1520
|
+
severity: "warning",
|
|
1521
|
+
field: `${pathPrefix}.attributes.openinference.span.kind`
|
|
1522
|
+
});
|
|
1523
|
+
return { kind: "LLM", warnings };
|
|
1524
|
+
case "RERANKER":
|
|
1525
|
+
warnings.push({
|
|
1526
|
+
code: "openinference_kind_semantic_loss",
|
|
1527
|
+
message: "OpenInference RERANKER span kind mapped to AgentInspect RETRIEVER.",
|
|
1528
|
+
severity: "warning",
|
|
1529
|
+
field: `${pathPrefix}.attributes.openinference.span.kind`
|
|
1530
|
+
});
|
|
1531
|
+
return { kind: "RETRIEVER", warnings };
|
|
1532
|
+
case "UNKNOWN":
|
|
1533
|
+
case void 0:
|
|
1534
|
+
warnings.push({
|
|
1535
|
+
code: "openinference_kind_unknown",
|
|
1536
|
+
message: "OpenInference span kind was missing or unknown; mapped to AgentInspect LOGIC.",
|
|
1537
|
+
severity: "warning",
|
|
1538
|
+
field: `${pathPrefix}.attributes.openinference.span.kind`
|
|
1539
|
+
});
|
|
1540
|
+
return { kind: "LOGIC", warnings };
|
|
1541
|
+
default:
|
|
1542
|
+
warnings.push({
|
|
1543
|
+
code: "openinference_kind_unsupported",
|
|
1544
|
+
message: `Unsupported OpenInference span kind "${rawKind}" mapped to AgentInspect LOGIC.`,
|
|
1545
|
+
severity: "warning",
|
|
1546
|
+
field: `${pathPrefix}.attributes.openinference.span.kind`
|
|
1547
|
+
});
|
|
1548
|
+
return { kind: "LOGIC", warnings };
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
function mapOpenInferenceStatus(status) {
|
|
1552
|
+
if (!isRecord5(status)) return void 0;
|
|
1553
|
+
const rawCode = status.code;
|
|
1554
|
+
if (typeof rawCode !== "string") return void 0;
|
|
1555
|
+
switch (rawCode.toUpperCase()) {
|
|
1556
|
+
case "OK":
|
|
1557
|
+
return "ok";
|
|
1558
|
+
case "ERROR":
|
|
1559
|
+
return "error";
|
|
1560
|
+
case "UNSET":
|
|
1561
|
+
return "unknown";
|
|
1562
|
+
default:
|
|
1563
|
+
return "unknown";
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
function readOpenInferenceTokenUsage(attributes) {
|
|
1567
|
+
const prompt = attributes["llm.token_count.prompt"];
|
|
1568
|
+
const completion = attributes["llm.token_count.completion"];
|
|
1569
|
+
const total = attributes["llm.token_count.total"];
|
|
1570
|
+
const cached = attributes["llm.token_count.prompt_details.cache_read"];
|
|
1571
|
+
const usage = {};
|
|
1572
|
+
if (typeof prompt === "number" && Number.isFinite(prompt) && prompt >= 0) {
|
|
1573
|
+
usage.input = prompt;
|
|
1574
|
+
}
|
|
1575
|
+
if (typeof completion === "number" && Number.isFinite(completion) && completion >= 0) {
|
|
1576
|
+
usage.output = completion;
|
|
1577
|
+
}
|
|
1578
|
+
if (typeof total === "number" && Number.isFinite(total) && total >= 0) {
|
|
1579
|
+
usage.total = total;
|
|
1580
|
+
}
|
|
1581
|
+
if (typeof cached === "number" && Number.isFinite(cached) && cached >= 0) {
|
|
1582
|
+
usage.cached = cached;
|
|
1583
|
+
}
|
|
1584
|
+
if (usage.total === void 0 && usage.input !== void 0 && usage.output !== void 0) {
|
|
1585
|
+
usage.total = usage.input + usage.output;
|
|
1586
|
+
}
|
|
1587
|
+
return Object.keys(usage).length > 0 ? usage : void 0;
|
|
1588
|
+
}
|
|
1589
|
+
function readOpenInferenceConfidence(attributes) {
|
|
1590
|
+
const confidence = attributes["agent_inspect.confidence"];
|
|
1591
|
+
if (confidence === "explicit" || confidence === "correlated" || confidence === "heuristic" || confidence === "unknown") {
|
|
1592
|
+
return confidence;
|
|
1593
|
+
}
|
|
1594
|
+
return "correlated";
|
|
1595
|
+
}
|
|
1596
|
+
function mapOpenInferenceSpan(span, index, version) {
|
|
1597
|
+
const pathPrefix = `spans[${index}]`;
|
|
1598
|
+
const warnings = [];
|
|
1599
|
+
const unsupportedFields = [];
|
|
1600
|
+
const rawAttributes = readRecordField(span, "attributes") ?? {};
|
|
1601
|
+
const sanitized = sanitizeOpenInferenceAttributes(rawAttributes, pathPrefix);
|
|
1602
|
+
warnings.push(...sanitized.warnings);
|
|
1603
|
+
unsupportedFields.push(...sanitized.unsupportedFields);
|
|
1604
|
+
const attributes = { ...sanitized.attributes };
|
|
1605
|
+
for (const [key, value] of Object.entries(span)) {
|
|
1606
|
+
if (OPENINFERENCE_SPAN_KEYS.has(key)) continue;
|
|
1607
|
+
unsupportedFields.push(`${pathPrefix}.${key}`);
|
|
1608
|
+
if (value === null || typeof value !== "object") {
|
|
1609
|
+
attributes[`openinference.${key}`] = value;
|
|
1610
|
+
} else {
|
|
1611
|
+
attributes[`openinference.${key}.summary`] = summarizeAttributeValue(value);
|
|
1612
|
+
warnings.push({
|
|
1613
|
+
code: "openinference_unsupported_field_summarized",
|
|
1614
|
+
message: `Unsupported OpenInference span field "${key}" was summarized.`,
|
|
1615
|
+
severity: "warning",
|
|
1616
|
+
field: `${pathPrefix}.${key}`
|
|
1617
|
+
});
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
const traceId = readStringField(span, ["trace_id", "traceId"]) ?? `trace-${index}`;
|
|
1621
|
+
const spanId = readStringField(span, ["span_id", "spanId"]) ?? `span-${index}`;
|
|
1622
|
+
const parentSpanId = readStringField(span, ["parent_span_id", "parentSpanId"]);
|
|
1623
|
+
const name = readStringField(span, ["name"]) ?? spanId;
|
|
1624
|
+
const startedAt = readOpenInferenceTimestamp(
|
|
1625
|
+
span,
|
|
1626
|
+
["start_time_unix_nano", "startTimeUnixNano"],
|
|
1627
|
+
["start_time", "startTime"]
|
|
1628
|
+
);
|
|
1629
|
+
const endedAt = readOpenInferenceTimestamp(
|
|
1630
|
+
span,
|
|
1631
|
+
["end_time_unix_nano", "endTimeUnixNano"],
|
|
1632
|
+
["end_time", "endTime"]
|
|
1633
|
+
);
|
|
1634
|
+
const timestamp = startedAt ?? "1970-01-01T00:00:00.000Z";
|
|
1635
|
+
if (startedAt === void 0) {
|
|
1636
|
+
warnings.push({
|
|
1637
|
+
code: "openinference_missing_start_time",
|
|
1638
|
+
message: "OpenInference span is missing a valid start time; using Unix epoch.",
|
|
1639
|
+
severity: "warning",
|
|
1640
|
+
field: `${pathPrefix}.start_time_unix_nano`
|
|
1641
|
+
});
|
|
1642
|
+
unsupportedFields.push(`${pathPrefix}.start_time_unix_nano`);
|
|
1643
|
+
}
|
|
1644
|
+
const { kind, warnings: kindWarnings } = mapOpenInferenceKind(
|
|
1645
|
+
span,
|
|
1646
|
+
rawAttributes,
|
|
1647
|
+
pathPrefix
|
|
1648
|
+
);
|
|
1649
|
+
warnings.push(...kindWarnings);
|
|
1650
|
+
const status = mapOpenInferenceStatus(span.status);
|
|
1651
|
+
const tokenUsage = readOpenInferenceTokenUsage(rawAttributes);
|
|
1652
|
+
const errorMessage = isRecord5(span.status) && typeof span.status.message === "string" ? span.status.message : void 0;
|
|
1653
|
+
const event = {
|
|
1654
|
+
schemaVersion: "0.2",
|
|
1655
|
+
eventId: typeof rawAttributes["agent_inspect.event_id"] === "string" ? rawAttributes["agent_inspect.event_id"] : spanId,
|
|
1656
|
+
runId: typeof rawAttributes["agent_inspect.run_id"] === "string" ? rawAttributes["agent_inspect.run_id"] : traceId,
|
|
1657
|
+
kind,
|
|
1658
|
+
name,
|
|
1659
|
+
timestamp,
|
|
1660
|
+
confidence: readOpenInferenceConfidence(rawAttributes),
|
|
1661
|
+
source: {
|
|
1662
|
+
type: "otel",
|
|
1663
|
+
name: "openinference",
|
|
1664
|
+
...version !== void 0 ? { version } : {}
|
|
1665
|
+
},
|
|
1666
|
+
attributes,
|
|
1667
|
+
trace: {
|
|
1668
|
+
traceId,
|
|
1669
|
+
spanId,
|
|
1670
|
+
...parentSpanId !== void 0 ? { parentSpanId } : {}
|
|
1671
|
+
}
|
|
1672
|
+
};
|
|
1673
|
+
if (status !== void 0) {
|
|
1674
|
+
event.status = status;
|
|
1675
|
+
}
|
|
1676
|
+
if (startedAt !== void 0) {
|
|
1677
|
+
event.startedAt = startedAt;
|
|
1678
|
+
}
|
|
1679
|
+
if (endedAt !== void 0) {
|
|
1680
|
+
event.endedAt = endedAt;
|
|
1681
|
+
}
|
|
1682
|
+
const durationMs = durationBetweenIso(startedAt, endedAt);
|
|
1683
|
+
if (durationMs !== void 0) {
|
|
1684
|
+
event.durationMs = durationMs;
|
|
1685
|
+
}
|
|
1686
|
+
if (tokenUsage !== void 0) {
|
|
1687
|
+
event.tokenUsage = tokenUsage;
|
|
1688
|
+
}
|
|
1689
|
+
if (status === "error") {
|
|
1690
|
+
event.error = {
|
|
1691
|
+
message: errorMessage !== void 0 && errorMessage.trim() !== "" ? errorMessage : "OpenInference span error"
|
|
1692
|
+
};
|
|
1693
|
+
}
|
|
1694
|
+
return {
|
|
1695
|
+
event,
|
|
1696
|
+
warnings,
|
|
1697
|
+
unsupportedFields,
|
|
1698
|
+
spanId,
|
|
1699
|
+
...parentSpanId !== void 0 ? { parentSpanId } : {}
|
|
1700
|
+
};
|
|
1701
|
+
}
|
|
1702
|
+
function mapOpenInferenceEvents(document) {
|
|
1703
|
+
const mapped = document.spans.map(
|
|
1704
|
+
(span, index) => mapOpenInferenceSpan(span, index, document.version)
|
|
1705
|
+
);
|
|
1706
|
+
const spanIdToEventId = new Map(
|
|
1707
|
+
mapped.map((span) => [span.spanId, span.event.eventId])
|
|
1708
|
+
);
|
|
1709
|
+
for (const span of mapped) {
|
|
1710
|
+
if (span.parentSpanId === void 0) continue;
|
|
1711
|
+
span.event.parentId = spanIdToEventId.get(span.parentSpanId) ?? span.parentSpanId;
|
|
1712
|
+
}
|
|
1713
|
+
return {
|
|
1714
|
+
events: mapped.map((span) => span.event),
|
|
1715
|
+
warnings: mapped.flatMap((span) => span.warnings),
|
|
1716
|
+
unsupportedFields: mapped.flatMap((span) => span.unsupportedFields)
|
|
1717
|
+
};
|
|
1718
|
+
}
|
|
1719
|
+
var openInferenceJsonReader = {
|
|
1720
|
+
format: OPENINFERENCE_READER_FORMAT,
|
|
1721
|
+
name: "OpenInference JSON",
|
|
1722
|
+
async detect(input) {
|
|
1723
|
+
const resolved = await resolveInput(input);
|
|
1724
|
+
if (!resolved) return void 0;
|
|
1725
|
+
let parsed;
|
|
1726
|
+
try {
|
|
1727
|
+
parsed = parseJsonDocument(resolved.content);
|
|
1728
|
+
} catch {
|
|
1729
|
+
return void 0;
|
|
1730
|
+
}
|
|
1731
|
+
const document = extractOpenInferenceDocument(parsed);
|
|
1732
|
+
if (!document) return void 0;
|
|
1733
|
+
return {
|
|
1734
|
+
format: OPENINFERENCE_READER_FORMAT,
|
|
1735
|
+
confidence: document.confidence,
|
|
1736
|
+
readerName: "OpenInference JSON",
|
|
1737
|
+
description: document.description,
|
|
1738
|
+
warnings: attachSingleSourceFile(document.warnings, resolved)
|
|
1739
|
+
};
|
|
1740
|
+
},
|
|
1741
|
+
async read(input) {
|
|
1742
|
+
const resolved = await resolveInput(input);
|
|
1743
|
+
if (!resolved) {
|
|
1744
|
+
throw new TraceReadError(
|
|
1745
|
+
"unsupported_format",
|
|
1746
|
+
"OpenInference JSON reader requires file, string, or buffer input."
|
|
1747
|
+
);
|
|
1748
|
+
}
|
|
1749
|
+
let parsed;
|
|
1750
|
+
try {
|
|
1751
|
+
parsed = parseJsonDocument(resolved.content);
|
|
1752
|
+
} catch {
|
|
1753
|
+
throw new TraceReadError("unsupported_format", "OpenInference JSON input is not valid JSON.", [
|
|
1754
|
+
{
|
|
1755
|
+
code: "openinference_invalid_json",
|
|
1756
|
+
message: "OpenInference JSON reader could not parse the input as JSON.",
|
|
1757
|
+
severity: "error"
|
|
1758
|
+
}
|
|
1759
|
+
]);
|
|
1760
|
+
}
|
|
1761
|
+
const document = extractOpenInferenceDocument(parsed);
|
|
1762
|
+
if (!document || document.spans.length === 0) {
|
|
1763
|
+
throw new TraceReadError(
|
|
1764
|
+
"unsupported_format",
|
|
1765
|
+
"No valid OpenInference spans found.",
|
|
1766
|
+
attachSingleSourceFile(
|
|
1767
|
+
document?.warnings ?? [
|
|
1768
|
+
{
|
|
1769
|
+
code: "openinference_no_valid_spans",
|
|
1770
|
+
message: "OpenInference JSON input did not contain valid spans.",
|
|
1771
|
+
severity: "error"
|
|
1772
|
+
}
|
|
1773
|
+
],
|
|
1774
|
+
resolved
|
|
1775
|
+
)
|
|
1776
|
+
);
|
|
1777
|
+
}
|
|
1778
|
+
const mapped = mapOpenInferenceEvents(document);
|
|
1779
|
+
const warnings = attachSingleSourceFile(
|
|
1780
|
+
[...document.warnings, ...mapped.warnings],
|
|
1781
|
+
resolved
|
|
1782
|
+
);
|
|
1783
|
+
const unsupportedFields = [
|
|
1784
|
+
...document.unsupportedFields,
|
|
1785
|
+
...mapped.unsupportedFields
|
|
1786
|
+
].sort((a, b) => a.localeCompare(b));
|
|
1787
|
+
return {
|
|
1788
|
+
format: OPENINFERENCE_READER_FORMAT,
|
|
1789
|
+
events: mapped.events,
|
|
1790
|
+
runs: persistedInspectEventsToRunTrees(mapped.events, { skipInvalid: true }),
|
|
1791
|
+
warnings,
|
|
1792
|
+
unsupportedFields,
|
|
1793
|
+
sourceFiles: resolved.sourceFiles
|
|
1794
|
+
};
|
|
1795
|
+
}
|
|
1796
|
+
};
|
|
1797
|
+
function parseOtlpAnyValue(value, field, warnings, unsupportedFields) {
|
|
1798
|
+
if (!isRecord5(value)) {
|
|
1799
|
+
unsupportedFields.push(field);
|
|
1800
|
+
warnings.push({
|
|
1801
|
+
code: "otlp_attribute_value_invalid",
|
|
1802
|
+
message: "OTLP attribute value was not an AnyValue object.",
|
|
1803
|
+
severity: "warning",
|
|
1804
|
+
field
|
|
1805
|
+
});
|
|
1806
|
+
return void 0;
|
|
1807
|
+
}
|
|
1808
|
+
if (typeof value.stringValue === "string") return value.stringValue;
|
|
1809
|
+
if (typeof value.boolValue === "boolean") return value.boolValue;
|
|
1810
|
+
if (typeof value.intValue === "number" && Number.isFinite(value.intValue)) {
|
|
1811
|
+
return value.intValue;
|
|
1812
|
+
}
|
|
1813
|
+
if (typeof value.intValue === "string" && value.intValue.trim() !== "") {
|
|
1814
|
+
const n = Number(value.intValue);
|
|
1815
|
+
if (Number.isFinite(n)) return n;
|
|
1816
|
+
}
|
|
1817
|
+
if (typeof value.doubleValue === "number" && Number.isFinite(value.doubleValue)) {
|
|
1818
|
+
return value.doubleValue;
|
|
1819
|
+
}
|
|
1820
|
+
if (isRecord5(value.arrayValue) && Array.isArray(value.arrayValue.values)) {
|
|
1821
|
+
return value.arrayValue.values.map(
|
|
1822
|
+
(item, index) => parseOtlpAnyValue(item, `${field}.arrayValue.values[${index}]`, warnings, unsupportedFields)
|
|
1823
|
+
);
|
|
1824
|
+
}
|
|
1825
|
+
if (isRecord5(value.kvlistValue) && Array.isArray(value.kvlistValue.values)) {
|
|
1826
|
+
const out = {};
|
|
1827
|
+
for (const [index, item] of value.kvlistValue.values.entries()) {
|
|
1828
|
+
if (!isRecord5(item) || typeof item.key !== "string") {
|
|
1829
|
+
unsupportedFields.push(`${field}.kvlistValue.values[${index}]`);
|
|
1830
|
+
continue;
|
|
1831
|
+
}
|
|
1832
|
+
out[item.key] = parseOtlpAnyValue(
|
|
1833
|
+
item.value,
|
|
1834
|
+
`${field}.kvlistValue.values[${index}].value`,
|
|
1835
|
+
warnings,
|
|
1836
|
+
unsupportedFields
|
|
1837
|
+
);
|
|
1838
|
+
}
|
|
1839
|
+
return out;
|
|
1840
|
+
}
|
|
1841
|
+
if (typeof value.bytesValue === "string") {
|
|
1842
|
+
unsupportedFields.push(field);
|
|
1843
|
+
warnings.push({
|
|
1844
|
+
code: "otlp_bytes_value_summarized",
|
|
1845
|
+
message: "OTLP bytesValue attribute was summarized instead of decoded.",
|
|
1846
|
+
severity: "warning",
|
|
1847
|
+
field
|
|
1848
|
+
});
|
|
1849
|
+
return { type: "bytes", length: value.bytesValue.length };
|
|
1850
|
+
}
|
|
1851
|
+
unsupportedFields.push(field);
|
|
1852
|
+
warnings.push({
|
|
1853
|
+
code: "otlp_attribute_value_unsupported",
|
|
1854
|
+
message: "OTLP attribute value used an unsupported AnyValue shape.",
|
|
1855
|
+
severity: "warning",
|
|
1856
|
+
field
|
|
1857
|
+
});
|
|
1858
|
+
return void 0;
|
|
1859
|
+
}
|
|
1860
|
+
function parseOtlpAttributes(value, pathPrefix) {
|
|
1861
|
+
const attributes = {};
|
|
1862
|
+
const warnings = [];
|
|
1863
|
+
const unsupportedFields = [];
|
|
1864
|
+
if (value === void 0) {
|
|
1865
|
+
return { attributes, warnings, unsupportedFields };
|
|
1866
|
+
}
|
|
1867
|
+
if (!Array.isArray(value)) {
|
|
1868
|
+
unsupportedFields.push(pathPrefix);
|
|
1869
|
+
warnings.push({
|
|
1870
|
+
code: "otlp_attributes_invalid",
|
|
1871
|
+
message: "OTLP attributes field was not an array.",
|
|
1872
|
+
severity: "warning",
|
|
1873
|
+
field: pathPrefix
|
|
1874
|
+
});
|
|
1875
|
+
return { attributes, warnings, unsupportedFields };
|
|
1876
|
+
}
|
|
1877
|
+
for (const [index, item] of value.entries()) {
|
|
1878
|
+
const field = `${pathPrefix}[${index}]`;
|
|
1879
|
+
if (!isRecord5(item) || typeof item.key !== "string") {
|
|
1880
|
+
unsupportedFields.push(field);
|
|
1881
|
+
warnings.push({
|
|
1882
|
+
code: "otlp_attribute_invalid",
|
|
1883
|
+
message: "Skipped OTLP attribute without a string key.",
|
|
1884
|
+
severity: "warning",
|
|
1885
|
+
field
|
|
1886
|
+
});
|
|
1887
|
+
continue;
|
|
1888
|
+
}
|
|
1889
|
+
const parsed = parseOtlpAnyValue(
|
|
1890
|
+
item.value,
|
|
1891
|
+
`${field}.value`,
|
|
1892
|
+
warnings,
|
|
1893
|
+
unsupportedFields
|
|
1894
|
+
);
|
|
1895
|
+
if (parsed !== void 0) {
|
|
1896
|
+
attributes[item.key] = parsed;
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1899
|
+
return { attributes, warnings, unsupportedFields };
|
|
1900
|
+
}
|
|
1901
|
+
function looksLikeOtlpSpan(value) {
|
|
1902
|
+
return isRecord5(value) && readStringField(value, ["traceId"]) !== void 0 && readStringField(value, ["spanId"]) !== void 0 && readStringField(value, ["name"]) !== void 0;
|
|
1903
|
+
}
|
|
1904
|
+
function extractOtlpDocument(root) {
|
|
1905
|
+
if (!isRecord5(root) || !Array.isArray(root.resourceSpans)) return void 0;
|
|
1906
|
+
const spans = [];
|
|
1907
|
+
const warnings = [];
|
|
1908
|
+
const unsupportedFields = [];
|
|
1909
|
+
for (const [resourceIndex, resourceSpan] of root.resourceSpans.entries()) {
|
|
1910
|
+
const resourcePath = `resourceSpans[${resourceIndex}]`;
|
|
1911
|
+
if (!isRecord5(resourceSpan)) {
|
|
1912
|
+
unsupportedFields.push(resourcePath);
|
|
1913
|
+
continue;
|
|
1914
|
+
}
|
|
1915
|
+
const resource = readRecordField(resourceSpan, "resource");
|
|
1916
|
+
const resourceParsed = parseOtlpAttributes(
|
|
1917
|
+
resource?.attributes,
|
|
1918
|
+
`${resourcePath}.resource.attributes`
|
|
1919
|
+
);
|
|
1920
|
+
warnings.push(...resourceParsed.warnings);
|
|
1921
|
+
unsupportedFields.push(...resourceParsed.unsupportedFields);
|
|
1922
|
+
if (!Array.isArray(resourceSpan.scopeSpans)) {
|
|
1923
|
+
unsupportedFields.push(`${resourcePath}.scopeSpans`);
|
|
1924
|
+
warnings.push({
|
|
1925
|
+
code: "otlp_scope_spans_missing",
|
|
1926
|
+
message: "OTLP resourceSpans entry did not contain a scopeSpans array.",
|
|
1927
|
+
severity: "warning",
|
|
1928
|
+
field: `${resourcePath}.scopeSpans`
|
|
1929
|
+
});
|
|
1930
|
+
continue;
|
|
1931
|
+
}
|
|
1932
|
+
for (const [scopeIndex, scopeSpan] of resourceSpan.scopeSpans.entries()) {
|
|
1933
|
+
const scopePath = `${resourcePath}.scopeSpans[${scopeIndex}]`;
|
|
1934
|
+
if (!isRecord5(scopeSpan)) {
|
|
1935
|
+
unsupportedFields.push(scopePath);
|
|
1936
|
+
continue;
|
|
1937
|
+
}
|
|
1938
|
+
const scope = readRecordField(scopeSpan, "scope");
|
|
1939
|
+
const scopeParsed = parseOtlpAttributes(
|
|
1940
|
+
scope?.attributes,
|
|
1941
|
+
`${scopePath}.scope.attributes`
|
|
1942
|
+
);
|
|
1943
|
+
warnings.push(...scopeParsed.warnings);
|
|
1944
|
+
unsupportedFields.push(...scopeParsed.unsupportedFields);
|
|
1945
|
+
if (!Array.isArray(scopeSpan.spans)) {
|
|
1946
|
+
unsupportedFields.push(`${scopePath}.spans`);
|
|
1947
|
+
warnings.push({
|
|
1948
|
+
code: "otlp_spans_missing",
|
|
1949
|
+
message: "OTLP scopeSpans entry did not contain a spans array.",
|
|
1950
|
+
severity: "warning",
|
|
1951
|
+
field: `${scopePath}.spans`
|
|
1952
|
+
});
|
|
1953
|
+
continue;
|
|
1954
|
+
}
|
|
1955
|
+
for (const [spanIndex, span] of scopeSpan.spans.entries()) {
|
|
1956
|
+
const spanPath = `${scopePath}.spans[${spanIndex}]`;
|
|
1957
|
+
if (!looksLikeOtlpSpan(span)) {
|
|
1958
|
+
unsupportedFields.push(spanPath);
|
|
1959
|
+
warnings.push({
|
|
1960
|
+
code: "otlp_invalid_span",
|
|
1961
|
+
message: "Skipped OTLP span without required traceId, spanId, or name.",
|
|
1962
|
+
severity: "warning",
|
|
1963
|
+
field: spanPath
|
|
1964
|
+
});
|
|
1965
|
+
continue;
|
|
1966
|
+
}
|
|
1967
|
+
spans.push({
|
|
1968
|
+
span,
|
|
1969
|
+
resourceAttributes: resourceParsed.attributes,
|
|
1970
|
+
scopeAttributes: scopeParsed.attributes,
|
|
1971
|
+
scopeName: readStringField(scope ?? {}, ["name"]),
|
|
1972
|
+
scopeVersion: readStringField(scope ?? {}, ["version"]),
|
|
1973
|
+
pathPrefix: spanPath
|
|
1974
|
+
});
|
|
1975
|
+
}
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
if (spans.length === 0) {
|
|
1979
|
+
warnings.push({
|
|
1980
|
+
code: "otlp_no_valid_spans",
|
|
1981
|
+
message: "OTLP JSON payload did not contain any valid spans.",
|
|
1982
|
+
severity: "error"
|
|
1983
|
+
});
|
|
1984
|
+
return {
|
|
1985
|
+
spans,
|
|
1986
|
+
confidence: 0.7,
|
|
1987
|
+
description: "Malformed OTLP JSON trace payload",
|
|
1988
|
+
warnings,
|
|
1989
|
+
unsupportedFields
|
|
1990
|
+
};
|
|
1991
|
+
}
|
|
1992
|
+
return {
|
|
1993
|
+
spans,
|
|
1994
|
+
confidence: 0.93,
|
|
1995
|
+
description: "OTLP JSON trace payload",
|
|
1996
|
+
warnings,
|
|
1997
|
+
unsupportedFields
|
|
1998
|
+
};
|
|
1999
|
+
}
|
|
2000
|
+
function mapOtlpStatus(status) {
|
|
2001
|
+
if (!isRecord5(status)) return void 0;
|
|
2002
|
+
const rawCode = status.code;
|
|
2003
|
+
if (typeof rawCode !== "string") return void 0;
|
|
2004
|
+
switch (rawCode.toUpperCase()) {
|
|
2005
|
+
case "STATUS_CODE_OK":
|
|
2006
|
+
case "OK":
|
|
2007
|
+
return "ok";
|
|
2008
|
+
case "STATUS_CODE_ERROR":
|
|
2009
|
+
case "ERROR":
|
|
2010
|
+
return "error";
|
|
2011
|
+
case "STATUS_CODE_UNSET":
|
|
2012
|
+
case "UNSET":
|
|
2013
|
+
return "unknown";
|
|
2014
|
+
default:
|
|
2015
|
+
return "unknown";
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
function readOtlpKind(attributes, pathPrefix) {
|
|
2019
|
+
const warnings = [];
|
|
2020
|
+
const agentInspectKind = attributes["agent_inspect.kind"];
|
|
2021
|
+
if (agentInspectKind === "RUN" || agentInspectKind === "AGENT" || agentInspectKind === "LLM" || agentInspectKind === "TOOL" || agentInspectKind === "CHAIN" || agentInspectKind === "RETRIEVER" || agentInspectKind === "DECISION" || agentInspectKind === "RESULT" || agentInspectKind === "ERROR" || agentInspectKind === "LOGIC" || agentInspectKind === "LOG") {
|
|
2022
|
+
return { kind: agentInspectKind, warnings };
|
|
2023
|
+
}
|
|
2024
|
+
const operation = attributes["gen_ai.operation.name"];
|
|
2025
|
+
if (typeof operation === "string") {
|
|
2026
|
+
switch (operation) {
|
|
2027
|
+
case "generate_content":
|
|
2028
|
+
case "chat":
|
|
2029
|
+
return { kind: "LLM", warnings };
|
|
2030
|
+
case "execute_tool":
|
|
2031
|
+
return { kind: "TOOL", warnings };
|
|
2032
|
+
case "invoke_agent":
|
|
2033
|
+
return { kind: "AGENT", warnings };
|
|
2034
|
+
default:
|
|
2035
|
+
warnings.push({
|
|
2036
|
+
code: "otlp_gen_ai_operation_semantic_loss",
|
|
2037
|
+
message: `OTLP GenAI operation "${operation}" mapped to AgentInspect LOGIC.`,
|
|
2038
|
+
severity: "warning",
|
|
2039
|
+
field: `${pathPrefix}.attributes.gen_ai.operation.name`
|
|
2040
|
+
});
|
|
2041
|
+
return { kind: "LOGIC", warnings };
|
|
2042
|
+
}
|
|
2043
|
+
}
|
|
2044
|
+
warnings.push({
|
|
2045
|
+
code: "otlp_kind_unknown",
|
|
2046
|
+
message: "OTLP span had no AgentInspect kind or GenAI operation; mapped to LOGIC.",
|
|
2047
|
+
severity: "warning",
|
|
2048
|
+
field: `${pathPrefix}.attributes`
|
|
2049
|
+
});
|
|
2050
|
+
return { kind: "LOGIC", warnings };
|
|
2051
|
+
}
|
|
2052
|
+
function readOtlpTokenUsage(attributes) {
|
|
2053
|
+
const input = attributes["gen_ai.usage.input_tokens"];
|
|
2054
|
+
const output = attributes["gen_ai.usage.output_tokens"];
|
|
2055
|
+
const usage = {};
|
|
2056
|
+
if (typeof input === "number" && Number.isFinite(input) && input >= 0) {
|
|
2057
|
+
usage.input = input;
|
|
2058
|
+
}
|
|
2059
|
+
if (typeof output === "number" && Number.isFinite(output) && output >= 0) {
|
|
2060
|
+
usage.output = output;
|
|
2061
|
+
}
|
|
2062
|
+
if (usage.input !== void 0 && usage.output !== void 0) {
|
|
2063
|
+
usage.total = usage.input + usage.output;
|
|
2064
|
+
}
|
|
2065
|
+
return Object.keys(usage).length > 0 ? usage : void 0;
|
|
2066
|
+
}
|
|
2067
|
+
function readOtlpConfidence(attributes) {
|
|
2068
|
+
return readOpenInferenceConfidence(attributes);
|
|
2069
|
+
}
|
|
2070
|
+
function sanitizeOtlpAttributes(attributes, pathPrefix) {
|
|
2071
|
+
const ownerPath = pathPrefix.endsWith(".attributes") ? pathPrefix.slice(0, -".attributes".length) : pathPrefix;
|
|
2072
|
+
const sanitized = sanitizeOpenInferenceAttributes(attributes, ownerPath);
|
|
2073
|
+
return {
|
|
2074
|
+
...sanitized,
|
|
2075
|
+
warnings: sanitized.warnings.map(
|
|
2076
|
+
(warning) => warning.code === "openinference_sensitive_attribute_summarized" ? {
|
|
2077
|
+
...warning,
|
|
2078
|
+
code: "otlp_sensitive_attribute_summarized",
|
|
2079
|
+
message: "OTLP prompt/output/document attribute(s) were summarized instead of copied verbatim."
|
|
2080
|
+
} : warning
|
|
2081
|
+
)
|
|
2082
|
+
};
|
|
2083
|
+
}
|
|
2084
|
+
function mapOtlpEvents(value, pathPrefix) {
|
|
2085
|
+
const warnings = [];
|
|
2086
|
+
const unsupportedFields = [];
|
|
2087
|
+
if (value === void 0) return { warnings, unsupportedFields };
|
|
2088
|
+
if (!Array.isArray(value)) {
|
|
2089
|
+
unsupportedFields.push(pathPrefix);
|
|
2090
|
+
warnings.push({
|
|
2091
|
+
code: "otlp_events_invalid",
|
|
2092
|
+
message: "OTLP events field was not an array.",
|
|
2093
|
+
severity: "warning",
|
|
2094
|
+
field: pathPrefix
|
|
2095
|
+
});
|
|
2096
|
+
return { warnings, unsupportedFields };
|
|
2097
|
+
}
|
|
2098
|
+
const events = [];
|
|
2099
|
+
for (const [index, event] of value.entries()) {
|
|
2100
|
+
const eventPath = `${pathPrefix}[${index}]`;
|
|
2101
|
+
if (!isRecord5(event)) {
|
|
2102
|
+
unsupportedFields.push(eventPath);
|
|
2103
|
+
continue;
|
|
2104
|
+
}
|
|
2105
|
+
const parsedAttributes = parseOtlpAttributes(
|
|
2106
|
+
event.attributes,
|
|
2107
|
+
`${eventPath}.attributes`
|
|
2108
|
+
);
|
|
2109
|
+
warnings.push(...parsedAttributes.warnings);
|
|
2110
|
+
unsupportedFields.push(...parsedAttributes.unsupportedFields);
|
|
2111
|
+
const sanitized = sanitizeOtlpAttributes(
|
|
2112
|
+
parsedAttributes.attributes,
|
|
2113
|
+
`${eventPath}.attributes`
|
|
2114
|
+
);
|
|
2115
|
+
warnings.push(...sanitized.warnings);
|
|
2116
|
+
unsupportedFields.push(...sanitized.unsupportedFields);
|
|
2117
|
+
const out = {};
|
|
2118
|
+
const name = readStringField(event, ["name"]);
|
|
2119
|
+
if (name !== void 0) {
|
|
2120
|
+
out.name = name;
|
|
2121
|
+
}
|
|
2122
|
+
const timestamp = parseUnixNanoToIso(event.timeUnixNano);
|
|
2123
|
+
if (timestamp !== void 0) {
|
|
2124
|
+
out.timestamp = timestamp;
|
|
2125
|
+
} else if (event.timeUnixNano !== void 0) {
|
|
2126
|
+
unsupportedFields.push(`${eventPath}.timeUnixNano`);
|
|
2127
|
+
warnings.push({
|
|
2128
|
+
code: "otlp_event_timestamp_invalid",
|
|
2129
|
+
message: "OTLP event timeUnixNano could not be parsed.",
|
|
2130
|
+
severity: "warning",
|
|
2131
|
+
field: `${eventPath}.timeUnixNano`
|
|
2132
|
+
});
|
|
2133
|
+
}
|
|
2134
|
+
if (Object.keys(sanitized.attributes).length > 0) {
|
|
2135
|
+
out.attributes = sanitized.attributes;
|
|
2136
|
+
}
|
|
2137
|
+
events.push(out);
|
|
2138
|
+
}
|
|
2139
|
+
return {
|
|
2140
|
+
events: events.length > 0 ? events : void 0,
|
|
2141
|
+
warnings,
|
|
2142
|
+
unsupportedFields
|
|
2143
|
+
};
|
|
2144
|
+
}
|
|
2145
|
+
function mapOtlpSpan(context) {
|
|
2146
|
+
const { span, pathPrefix } = context;
|
|
2147
|
+
const warnings = [];
|
|
2148
|
+
const unsupportedFields = [];
|
|
2149
|
+
const parsedSpanAttributes = parseOtlpAttributes(
|
|
2150
|
+
span.attributes,
|
|
2151
|
+
`${pathPrefix}.attributes`
|
|
2152
|
+
);
|
|
2153
|
+
warnings.push(...parsedSpanAttributes.warnings);
|
|
2154
|
+
unsupportedFields.push(...parsedSpanAttributes.unsupportedFields);
|
|
2155
|
+
const sanitizedSpanAttributes = sanitizeOtlpAttributes(
|
|
2156
|
+
parsedSpanAttributes.attributes,
|
|
2157
|
+
`${pathPrefix}.attributes`
|
|
2158
|
+
);
|
|
2159
|
+
warnings.push(...sanitizedSpanAttributes.warnings);
|
|
2160
|
+
unsupportedFields.push(...sanitizedSpanAttributes.unsupportedFields);
|
|
2161
|
+
const attributes = {
|
|
2162
|
+
...sanitizedSpanAttributes.attributes
|
|
2163
|
+
};
|
|
2164
|
+
for (const [key, value] of Object.entries(context.resourceAttributes)) {
|
|
2165
|
+
attributes[`resource.${key}`] = value;
|
|
2166
|
+
}
|
|
2167
|
+
for (const [key, value] of Object.entries(context.scopeAttributes)) {
|
|
2168
|
+
attributes[`scope.${key}`] = value;
|
|
2169
|
+
}
|
|
2170
|
+
if (context.scopeName !== void 0) {
|
|
2171
|
+
attributes["scope.name"] = context.scopeName;
|
|
2172
|
+
}
|
|
2173
|
+
if (context.scopeVersion !== void 0) {
|
|
2174
|
+
attributes["scope.version"] = context.scopeVersion;
|
|
2175
|
+
}
|
|
2176
|
+
for (const [key, value] of Object.entries(span)) {
|
|
2177
|
+
if (OTLP_SPAN_KEYS.has(key)) continue;
|
|
2178
|
+
unsupportedFields.push(`${pathPrefix}.${key}`);
|
|
2179
|
+
if (value === null || typeof value !== "object") {
|
|
2180
|
+
attributes[`otlp.${key}`] = value;
|
|
2181
|
+
} else {
|
|
2182
|
+
attributes[`otlp.${key}.summary`] = summarizeAttributeValue(value);
|
|
2183
|
+
warnings.push({
|
|
2184
|
+
code: "otlp_unsupported_field_summarized",
|
|
2185
|
+
message: `Unsupported OTLP span field "${key}" was summarized.`,
|
|
2186
|
+
severity: "warning",
|
|
2187
|
+
field: `${pathPrefix}.${key}`
|
|
2188
|
+
});
|
|
2189
|
+
}
|
|
2190
|
+
}
|
|
2191
|
+
for (const key of [
|
|
2192
|
+
"droppedAttributesCount",
|
|
2193
|
+
"droppedEventsCount",
|
|
2194
|
+
"droppedLinksCount",
|
|
2195
|
+
"links"
|
|
2196
|
+
]) {
|
|
2197
|
+
if (span[key] !== void 0) {
|
|
2198
|
+
unsupportedFields.push(`${pathPrefix}.${key}`);
|
|
2199
|
+
warnings.push({
|
|
2200
|
+
code: "otlp_span_field_not_mapped",
|
|
2201
|
+
message: `OTLP span field "${key}" is not represented in AgentInspect events.`,
|
|
2202
|
+
severity: "warning",
|
|
2203
|
+
field: `${pathPrefix}.${key}`
|
|
2204
|
+
});
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
const events = mapOtlpEvents(span.events, `${pathPrefix}.events`);
|
|
2208
|
+
warnings.push(...events.warnings);
|
|
2209
|
+
unsupportedFields.push(...events.unsupportedFields);
|
|
2210
|
+
if (events.events !== void 0) {
|
|
2211
|
+
attributes["otlp.events"] = events.events;
|
|
2212
|
+
}
|
|
2213
|
+
const traceId = readStringField(span, ["traceId"]) ?? "trace-unknown";
|
|
2214
|
+
const spanId = readStringField(span, ["spanId"]) ?? "span-unknown";
|
|
2215
|
+
const parentSpanId = readStringField(span, ["parentSpanId"]);
|
|
2216
|
+
const startedAt = readOpenInferenceTimestamp(
|
|
2217
|
+
span,
|
|
2218
|
+
["startTimeUnixNano"],
|
|
2219
|
+
[]
|
|
2220
|
+
);
|
|
2221
|
+
const endedAt = readOpenInferenceTimestamp(span, ["endTimeUnixNano"], []);
|
|
2222
|
+
const timestamp = startedAt ?? "1970-01-01T00:00:00.000Z";
|
|
2223
|
+
if (startedAt === void 0) {
|
|
2224
|
+
unsupportedFields.push(`${pathPrefix}.startTimeUnixNano`);
|
|
2225
|
+
warnings.push({
|
|
2226
|
+
code: "otlp_missing_start_time",
|
|
2227
|
+
message: "OTLP span is missing a valid startTimeUnixNano; using Unix epoch.",
|
|
2228
|
+
severity: "warning",
|
|
2229
|
+
field: `${pathPrefix}.startTimeUnixNano`
|
|
2230
|
+
});
|
|
2231
|
+
}
|
|
2232
|
+
const { kind, warnings: kindWarnings } = readOtlpKind(
|
|
2233
|
+
parsedSpanAttributes.attributes,
|
|
2234
|
+
pathPrefix
|
|
2235
|
+
);
|
|
2236
|
+
warnings.push(...kindWarnings);
|
|
2237
|
+
const status = mapOtlpStatus(span.status);
|
|
2238
|
+
const tokenUsage = readOtlpTokenUsage(parsedSpanAttributes.attributes);
|
|
2239
|
+
const errorMessage = isRecord5(span.status) && typeof span.status.message === "string" ? span.status.message : void 0;
|
|
2240
|
+
const event = {
|
|
2241
|
+
schemaVersion: "0.2",
|
|
2242
|
+
eventId: typeof parsedSpanAttributes.attributes["agent_inspect.event_id"] === "string" ? parsedSpanAttributes.attributes["agent_inspect.event_id"] : spanId,
|
|
2243
|
+
runId: typeof parsedSpanAttributes.attributes["agent_inspect.run_id"] === "string" ? parsedSpanAttributes.attributes["agent_inspect.run_id"] : traceId,
|
|
2244
|
+
kind,
|
|
2245
|
+
name: readStringField(span, ["name"]) ?? spanId,
|
|
2246
|
+
timestamp,
|
|
2247
|
+
confidence: readOtlpConfidence(parsedSpanAttributes.attributes),
|
|
2248
|
+
source: {
|
|
2249
|
+
type: "otel",
|
|
2250
|
+
name: context.scopeName ?? (typeof context.resourceAttributes["service.name"] === "string" ? context.resourceAttributes["service.name"] : "otlp-json"),
|
|
2251
|
+
...context.scopeVersion !== void 0 ? { version: context.scopeVersion } : {}
|
|
2252
|
+
},
|
|
2253
|
+
attributes,
|
|
2254
|
+
trace: {
|
|
2255
|
+
traceId,
|
|
2256
|
+
spanId,
|
|
2257
|
+
...parentSpanId !== void 0 ? { parentSpanId } : {}
|
|
2258
|
+
}
|
|
2259
|
+
};
|
|
2260
|
+
if (status !== void 0) {
|
|
2261
|
+
event.status = status;
|
|
2262
|
+
}
|
|
2263
|
+
if (startedAt !== void 0) {
|
|
2264
|
+
event.startedAt = startedAt;
|
|
2265
|
+
}
|
|
2266
|
+
if (endedAt !== void 0) {
|
|
2267
|
+
event.endedAt = endedAt;
|
|
2268
|
+
}
|
|
2269
|
+
const durationMs = durationBetweenIso(startedAt, endedAt);
|
|
2270
|
+
if (durationMs !== void 0) {
|
|
2271
|
+
event.durationMs = durationMs;
|
|
2272
|
+
}
|
|
2273
|
+
if (tokenUsage !== void 0) {
|
|
2274
|
+
event.tokenUsage = tokenUsage;
|
|
2275
|
+
}
|
|
2276
|
+
if (status === "error") {
|
|
2277
|
+
event.error = {
|
|
2278
|
+
message: errorMessage !== void 0 && errorMessage.trim() !== "" ? errorMessage : "OTLP span error"
|
|
2279
|
+
};
|
|
2280
|
+
}
|
|
2281
|
+
return {
|
|
2282
|
+
event,
|
|
2283
|
+
warnings,
|
|
2284
|
+
unsupportedFields,
|
|
2285
|
+
spanId,
|
|
2286
|
+
...parentSpanId !== void 0 ? { parentSpanId } : {}
|
|
2287
|
+
};
|
|
2288
|
+
}
|
|
2289
|
+
function mapOtlpEventsToPersisted(document) {
|
|
2290
|
+
const mapped = document.spans.map((span) => mapOtlpSpan(span));
|
|
2291
|
+
const spanIdToEventId = new Map(
|
|
2292
|
+
mapped.map((span) => [span.spanId, span.event.eventId])
|
|
2293
|
+
);
|
|
2294
|
+
for (const span of mapped) {
|
|
2295
|
+
if (span.parentSpanId === void 0) continue;
|
|
2296
|
+
span.event.parentId = spanIdToEventId.get(span.parentSpanId) ?? span.parentSpanId;
|
|
2297
|
+
}
|
|
2298
|
+
return {
|
|
2299
|
+
events: mapped.map((span) => span.event),
|
|
2300
|
+
warnings: mapped.flatMap((span) => span.warnings),
|
|
2301
|
+
unsupportedFields: mapped.flatMap((span) => span.unsupportedFields)
|
|
2302
|
+
};
|
|
2303
|
+
}
|
|
2304
|
+
var otlpJsonReader = {
|
|
2305
|
+
format: OTLP_READER_FORMAT,
|
|
2306
|
+
name: "OTLP JSON",
|
|
2307
|
+
async detect(input) {
|
|
2308
|
+
const resolved = await resolveInput(input);
|
|
2309
|
+
if (!resolved) return void 0;
|
|
2310
|
+
let parsed;
|
|
2311
|
+
try {
|
|
2312
|
+
parsed = parseJsonDocument(resolved.content);
|
|
2313
|
+
} catch {
|
|
2314
|
+
return void 0;
|
|
2315
|
+
}
|
|
2316
|
+
const document = extractOtlpDocument(parsed);
|
|
2317
|
+
if (!document) return void 0;
|
|
2318
|
+
return {
|
|
2319
|
+
format: OTLP_READER_FORMAT,
|
|
2320
|
+
confidence: document.confidence,
|
|
2321
|
+
readerName: "OTLP JSON",
|
|
2322
|
+
description: document.description,
|
|
2323
|
+
warnings: attachSingleSourceFile(document.warnings, resolved)
|
|
2324
|
+
};
|
|
2325
|
+
},
|
|
2326
|
+
async read(input) {
|
|
2327
|
+
const resolved = await resolveInput(input);
|
|
2328
|
+
if (!resolved) {
|
|
2329
|
+
throw new TraceReadError(
|
|
2330
|
+
"unsupported_format",
|
|
2331
|
+
"OTLP JSON reader requires file, string, or buffer input."
|
|
2332
|
+
);
|
|
2333
|
+
}
|
|
2334
|
+
let parsed;
|
|
2335
|
+
try {
|
|
2336
|
+
parsed = parseJsonDocument(resolved.content);
|
|
2337
|
+
} catch {
|
|
2338
|
+
throw new TraceReadError("unsupported_format", "OTLP JSON input is not valid JSON.", [
|
|
2339
|
+
{
|
|
2340
|
+
code: "otlp_invalid_json",
|
|
2341
|
+
message: "OTLP JSON reader could not parse the input as JSON.",
|
|
2342
|
+
severity: "error"
|
|
2343
|
+
}
|
|
2344
|
+
]);
|
|
2345
|
+
}
|
|
2346
|
+
const document = extractOtlpDocument(parsed);
|
|
2347
|
+
if (!document || document.spans.length === 0) {
|
|
2348
|
+
throw new TraceReadError(
|
|
2349
|
+
"unsupported_format",
|
|
2350
|
+
"No valid OTLP spans found.",
|
|
2351
|
+
attachSingleSourceFile(
|
|
2352
|
+
document?.warnings ?? [
|
|
2353
|
+
{
|
|
2354
|
+
code: "otlp_no_valid_spans",
|
|
2355
|
+
message: "OTLP JSON input did not contain valid spans.",
|
|
2356
|
+
severity: "error"
|
|
2357
|
+
}
|
|
2358
|
+
],
|
|
2359
|
+
resolved
|
|
2360
|
+
)
|
|
2361
|
+
);
|
|
2362
|
+
}
|
|
2363
|
+
const mapped = mapOtlpEventsToPersisted(document);
|
|
2364
|
+
const warnings = attachSingleSourceFile(
|
|
2365
|
+
[...document.warnings, ...mapped.warnings],
|
|
2366
|
+
resolved
|
|
2367
|
+
);
|
|
2368
|
+
const unsupportedFields = [
|
|
2369
|
+
...document.unsupportedFields,
|
|
2370
|
+
...mapped.unsupportedFields
|
|
2371
|
+
].sort((a, b) => a.localeCompare(b));
|
|
2372
|
+
return {
|
|
2373
|
+
format: OTLP_READER_FORMAT,
|
|
2374
|
+
events: mapped.events,
|
|
2375
|
+
runs: persistedInspectEventsToRunTrees(mapped.events, { skipInvalid: true }),
|
|
2376
|
+
warnings,
|
|
2377
|
+
unsupportedFields,
|
|
2378
|
+
sourceFiles: resolved.sourceFiles
|
|
2379
|
+
};
|
|
2380
|
+
}
|
|
2381
|
+
};
|
|
2382
|
+
var agentInspectJsonlReader = {
|
|
2383
|
+
format: "agent-inspect-jsonl",
|
|
2384
|
+
name: "AgentInspect JSONL",
|
|
2385
|
+
async detect(input) {
|
|
2386
|
+
const resolved = await resolveInput(input);
|
|
2387
|
+
if (!resolved) return void 0;
|
|
2388
|
+
const detected = detectJsonlFormat(resolved.content);
|
|
2389
|
+
if (detected.validRows === 0 || detected.format === "empty") {
|
|
2390
|
+
return void 0;
|
|
2391
|
+
}
|
|
2392
|
+
return {
|
|
2393
|
+
format: "agent-inspect-jsonl",
|
|
2394
|
+
confidence: 0.95,
|
|
2395
|
+
readerName: "AgentInspect JSONL",
|
|
2396
|
+
description: agentInspectFormatLabel(detected.format),
|
|
2397
|
+
warnings: attachSingleSourceFile(detected.warnings, resolved)
|
|
2398
|
+
};
|
|
2399
|
+
},
|
|
2400
|
+
async read(input) {
|
|
2401
|
+
const resolved = await resolveInput(input);
|
|
2402
|
+
if (!resolved) {
|
|
2403
|
+
throw new Error("AgentInspect JSONL reader requires file, directory, string, or buffer input.");
|
|
2404
|
+
}
|
|
2405
|
+
const parsed = parseTraceJsonl(resolved.content, { warnings: false });
|
|
2406
|
+
if (parsed.sourceEventCount === 0) {
|
|
2407
|
+
throw new Error("No valid AgentInspect JSONL events found.");
|
|
2408
|
+
}
|
|
2409
|
+
const events = persistedEventsForParsedTrace(parsed);
|
|
2410
|
+
return {
|
|
2411
|
+
format: agentInspectFormatLabel(parsed.format),
|
|
2412
|
+
events,
|
|
2413
|
+
runs: persistedInspectEventsToRunTrees(events, { skipInvalid: true }),
|
|
2414
|
+
warnings: parsed.format === "mixed" ? attachSingleSourceFile(
|
|
2415
|
+
[
|
|
2416
|
+
{
|
|
2417
|
+
code: "mixed_agent_inspect_jsonl",
|
|
2418
|
+
message: "Trace input mixes schemaVersion 0.1 and 0.2 rows; events were normalized for reading.",
|
|
2419
|
+
severity: "warning"
|
|
2420
|
+
}
|
|
2421
|
+
],
|
|
2422
|
+
resolved
|
|
2423
|
+
) : [],
|
|
2424
|
+
unsupportedFields: [],
|
|
2425
|
+
sourceFiles: resolved.sourceFiles
|
|
2426
|
+
};
|
|
2427
|
+
}
|
|
2428
|
+
};
|
|
2429
|
+
var DEFAULT_TRACE_READERS = [
|
|
2430
|
+
agentInspectJsonlReader,
|
|
2431
|
+
openInferenceJsonReader,
|
|
2432
|
+
otlpJsonReader
|
|
2433
|
+
];
|
|
2434
|
+
async function detectTraceFormat(input, options = {}) {
|
|
2435
|
+
const readers = options.readers ?? DEFAULT_TRACE_READERS;
|
|
2436
|
+
if (options.format !== void 0) {
|
|
2437
|
+
const reader = findReaderByFormat(options.format, readers);
|
|
2438
|
+
if (!reader) {
|
|
2439
|
+
return {
|
|
2440
|
+
status: "unsupported",
|
|
2441
|
+
candidates: [],
|
|
2442
|
+
warnings: [
|
|
2443
|
+
{
|
|
2444
|
+
code: "unsupported_format",
|
|
2445
|
+
message: `No trace reader is registered for format "${options.format}".`,
|
|
2446
|
+
severity: "error"
|
|
2447
|
+
}
|
|
2448
|
+
]
|
|
2449
|
+
};
|
|
2450
|
+
}
|
|
2451
|
+
return {
|
|
2452
|
+
status: "detected",
|
|
2453
|
+
format: reader.format,
|
|
2454
|
+
candidates: [
|
|
2455
|
+
{
|
|
2456
|
+
format: reader.format,
|
|
2457
|
+
confidence: 1,
|
|
2458
|
+
readerName: reader.name,
|
|
2459
|
+
description: "Explicit format override"
|
|
2460
|
+
}
|
|
2461
|
+
],
|
|
2462
|
+
warnings: []
|
|
2463
|
+
};
|
|
2464
|
+
}
|
|
2465
|
+
const candidates = [];
|
|
2466
|
+
const warnings = [];
|
|
2467
|
+
for (const reader of readers) {
|
|
2468
|
+
try {
|
|
2469
|
+
const candidate = await reader.detect(input);
|
|
2470
|
+
if (candidate !== void 0) {
|
|
2471
|
+
candidates.push(normalizeCandidate(reader, candidate));
|
|
2472
|
+
}
|
|
2473
|
+
} catch (error) {
|
|
2474
|
+
if (error instanceof TraceReadError) {
|
|
2475
|
+
warnings.push(...error.warnings);
|
|
2476
|
+
continue;
|
|
2477
|
+
}
|
|
2478
|
+
warnings.push({
|
|
2479
|
+
code: "reader_detect_failed",
|
|
2480
|
+
message: error instanceof Error && error.message.trim() !== "" ? error.message : `Trace reader "${reader.format}" failed during detection.`,
|
|
2481
|
+
severity: "warning"
|
|
2482
|
+
});
|
|
2483
|
+
}
|
|
2484
|
+
}
|
|
2485
|
+
const sorted = sortCandidates(
|
|
2486
|
+
candidates.filter((candidate) => candidate.confidence >= MIN_DETECTION_CONFIDENCE)
|
|
2487
|
+
);
|
|
2488
|
+
const candidateWarnings = collectWarnings(sorted);
|
|
2489
|
+
const lowConfidenceWarnings = candidates.length > sorted.length ? [
|
|
2490
|
+
{
|
|
2491
|
+
code: "low_confidence_candidates",
|
|
2492
|
+
message: `Ignored ${candidates.length - sorted.length} low-confidence format candidate(s).`,
|
|
2493
|
+
severity: "info"
|
|
2494
|
+
}
|
|
2495
|
+
] : [];
|
|
2496
|
+
const allWarnings = dedupeWarnings([
|
|
2497
|
+
...warnings,
|
|
2498
|
+
...candidateWarnings,
|
|
2499
|
+
...lowConfidenceWarnings
|
|
2500
|
+
]);
|
|
2501
|
+
if (sorted.length === 0) {
|
|
2502
|
+
return {
|
|
2503
|
+
status: "unsupported",
|
|
2504
|
+
candidates: [],
|
|
2505
|
+
warnings: allWarnings
|
|
2506
|
+
};
|
|
2507
|
+
}
|
|
2508
|
+
const [best, second] = sorted;
|
|
2509
|
+
if (second !== void 0 && best.confidence - second.confidence <= AMBIGUOUS_CONFIDENCE_DELTA) {
|
|
2510
|
+
return {
|
|
2511
|
+
status: "ambiguous",
|
|
2512
|
+
candidates: sorted,
|
|
2513
|
+
warnings: [
|
|
2514
|
+
...allWarnings,
|
|
2515
|
+
{
|
|
2516
|
+
code: "ambiguous_format_candidates",
|
|
2517
|
+
message: `Top trace format candidates are within ${AMBIGUOUS_CONFIDENCE_DELTA} confidence.`,
|
|
2518
|
+
severity: "warning"
|
|
2519
|
+
}
|
|
2520
|
+
]
|
|
2521
|
+
};
|
|
2522
|
+
}
|
|
2523
|
+
return {
|
|
2524
|
+
status: "detected",
|
|
2525
|
+
format: best.format,
|
|
2526
|
+
candidates: sorted,
|
|
2527
|
+
warnings: allWarnings
|
|
2528
|
+
};
|
|
2529
|
+
}
|
|
2530
|
+
async function readTrace(input, options = {}) {
|
|
2531
|
+
const readers = options.readers ?? DEFAULT_TRACE_READERS;
|
|
2532
|
+
const detection = await detectTraceFormat(input, options);
|
|
2533
|
+
if (detection.status === "unsupported" || detection.format === void 0) {
|
|
2534
|
+
throw new TraceReadError(
|
|
2535
|
+
"unsupported_format",
|
|
2536
|
+
"No trace reader could detect the input format.",
|
|
2537
|
+
detection.warnings
|
|
2538
|
+
);
|
|
2539
|
+
}
|
|
2540
|
+
if (detection.status === "ambiguous") {
|
|
2541
|
+
throw new TraceReadError(
|
|
2542
|
+
"ambiguous_format",
|
|
2543
|
+
"Multiple trace readers matched the input with equal confidence.",
|
|
2544
|
+
detection.warnings
|
|
2545
|
+
);
|
|
2546
|
+
}
|
|
2547
|
+
const reader = findReaderByFormat(detection.format, readers);
|
|
2548
|
+
if (!reader) {
|
|
2549
|
+
throw new TraceReadError(
|
|
2550
|
+
"unsupported_format",
|
|
2551
|
+
`No trace reader is registered for format "${detection.format}".`,
|
|
2552
|
+
detection.warnings
|
|
2553
|
+
);
|
|
2554
|
+
}
|
|
2555
|
+
try {
|
|
2556
|
+
const result = await reader.read(input, { format: detection.format });
|
|
2557
|
+
return {
|
|
2558
|
+
...result,
|
|
2559
|
+
format: result.format || detection.format,
|
|
2560
|
+
warnings: [...detection.warnings, ...result.warnings]
|
|
2561
|
+
};
|
|
2562
|
+
} catch (error) {
|
|
2563
|
+
if (error instanceof TraceReadError) {
|
|
2564
|
+
throw new TraceReadError(
|
|
2565
|
+
error.code,
|
|
2566
|
+
error.message,
|
|
2567
|
+
dedupeWarnings([...detection.warnings, ...error.warnings])
|
|
2568
|
+
);
|
|
2569
|
+
}
|
|
2570
|
+
throw new TraceReadError(
|
|
2571
|
+
"reader_failed",
|
|
2572
|
+
error instanceof Error && error.message.trim() !== "" ? error.message : `Trace reader "${reader.format}" failed.`,
|
|
2573
|
+
detection.warnings
|
|
2574
|
+
);
|
|
2575
|
+
}
|
|
2576
|
+
}
|
|
2577
|
+
function openTrace(input, options = {}) {
|
|
2578
|
+
return readTrace(input, options);
|
|
2579
|
+
}
|
|
2580
|
+
|
|
2581
|
+
exports.DEFAULT_TRACE_READERS = DEFAULT_TRACE_READERS;
|
|
2582
|
+
exports.TraceReadError = TraceReadError;
|
|
2583
|
+
exports.agentInspectJsonlReader = agentInspectJsonlReader;
|
|
2584
|
+
exports.detectTraceFormat = detectTraceFormat;
|
|
2585
|
+
exports.openInferenceJsonReader = openInferenceJsonReader;
|
|
2586
|
+
exports.openTrace = openTrace;
|
|
2587
|
+
exports.otlpJsonReader = otlpJsonReader;
|
|
2588
|
+
exports.readTrace = readTrace;
|
|
2589
|
+
//# sourceMappingURL=readers.cjs.map
|
|
2590
|
+
//# sourceMappingURL=readers.cjs.map
|